Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- (`ark-serialize`) Implementation of `CanonicalSerialize` and `CanonicalDeserialize` for signed integer types
- [\#1084](https://github.com/arkworks-rs/algebra/pull/1084) (`ark-ff`) Add `from_u128` const constructor for `SmallFp` fields
- [\#1086](https://github.com/arkworks-rs/algebra/pull/1086) (`ark-ff-macros`) Auto-detect small prime subgroup (bases 3, 5, 7) in `define_field!` for both `SmallFp` and `Fp` fields
- (`ark-ff`) Add `div_rem` method for `BigInteger`, returning the quotient and remainder of division by another `BigInteger`.

### Improvements

Expand Down
67 changes: 67 additions & 0 deletions ff/src/biginteger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,73 @@ pub trait BigInteger:
/// ```
fn mul(&self, other: &Self) -> (Self, Self);

/// Divides this [`BigInteger`] by `divisor`, returning the quotient and the
/// remainder, i.e. the unique pair `(q, r)` such that
/// `self = q * divisor + r` with `r < divisor`.
///
/// # Panics
///
/// Panics if `divisor` is zero.
///
/// # Example
///
/// ```
Comment thread
pjdurden marked this conversation as resolved.
/// use ark_ff::{biginteger::BigInteger64 as B, BigInteger as _};
///
/// // Basic
/// let a = B::from(100u64);
/// let b = B::from(7u64);
/// let (q, r) = a.div_rem(&b);
/// assert_eq!(q, B::from(14u64));
/// assert_eq!(r, B::from(2u64));
///
/// // Edge-Case: divisor larger than dividend
/// let a = B::from(3u64);
/// let b = B::from(5u64);
/// let (q, r) = a.div_rem(&b);
/// assert_eq!(q, B::from(0u64));
/// assert_eq!(r, B::from(3u64));
/// ```
///
/// Provided in terms of the other trait methods, so adding it does not
/// require existing implementors to supply a body.
fn div_rem(&self, divisor: &Self) -> (Self, Self) {
assert!(!divisor.is_zero(), "attempt to divide by zero");

// Base-2 long division: the same algorithm as the `const_modulo!`
// helper, extended to also accumulate the quotient. We scan the bits of
// `self` from the most significant down to the least, shifting each into
// a running remainder; whenever the remainder reaches `divisor` we
// subtract it and set the corresponding bit of the quotient.
let mut quotient = Self::from(0u64);
let mut remainder = Self::from(0u64);

for i in (0..self.num_bits() as usize).rev() {
// Make room for this iteration's quotient bit. `quotient` never
// exceeds `self`, so the discarded carry is always zero.
quotient.mul2();

// Shift the next bit of `self` into the remainder. `remainder` is
// always `< divisor` at the top of the loop, so doubling it can
// overflow `Self` by at most one bit, which `carry` captures.
let carry = remainder.mul2();
// After `mul2` the low bit is zero, so setting it is equivalent to
// OR-ing the next dividend bit in. Use the guaranteed `AsMut<[u64]>`
// view rather than a concrete field so this stays a default method.
if self.get_bit(i) {
remainder.as_mut()[0] |= 1;
}

if carry || remainder >= *divisor {
let borrow = remainder.sub_with_borrow(divisor);
debug_assert_eq!(borrow, carry);
quotient.as_mut()[0] |= 1;
}
}

(quotient, remainder)
}

/// Performs a rightwise bitshift of this number, effectively dividing
/// it by 2.
/// # Example
Expand Down
44 changes: 44 additions & 0 deletions ff/src/biginteger/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,49 @@ fn biginteger_conversion_test<B: BigInteger>() {
assert_eq!(x, x_recovered);
}

// Test `div_rem` against `num_bigint` as an oracle, plus hand-picked edge cases.
fn biginteger_div_rem_test<B: BigInteger>() {
let mut rng = ark_std::test_rng();

let zero = B::from(0u64);
let one = B::from(1u64);

for _ in 0..1000 {
let a: B = UniformRand::rand(&mut rng);
let mut b: B = UniformRand::rand(&mut rng);
if b.is_zero() {
b = one;
}

let (q, r) = a.div_rem(&b);

// The remainder is always strictly smaller than the divisor. Checked
// before the `BigUint` conversions below so the test does not rely on
// `B: Copy` to reuse `r` and `b`.
assert!(r < b);

let a_big: BigUint = a.into();
let b_big: BigUint = b.into();
assert_eq!(Into::<BigUint>::into(q), &a_big / &b_big);
assert_eq!(Into::<BigUint>::into(r), &a_big % &b_big);
}

// 0 / d == (0, 0)
assert_eq!(zero.div_rem(&one), (zero, zero));
// n / n == (1, 0)
let five = B::from(5u64);
assert_eq!(five.div_rem(&five), (one, zero));
// dividend < divisor == (0, dividend)
assert_eq!(B::from(3u64).div_rem(&five), (zero, B::from(3u64)));
// exact division by one
assert_eq!(B::from(42u64).div_rem(&one), (B::from(42u64), zero));
// basic case with a non-zero remainder
assert_eq!(
B::from(100u64).div_rem(&B::from(7u64)),
(B::from(14u64), B::from(2u64))
);
}

// Wrapper test function for BigInteger
fn test_biginteger<B: BigInteger>(max: B, zero: B) {
let mut rng = ark_std::test_rng();
Expand All @@ -238,6 +281,7 @@ fn test_biginteger<B: BigInteger>(max: B, zero: B) {
biginteger_bitwise_ops_test::<B>();
biginteger_shr::<B>();
biginteger_shl::<B>();
biginteger_div_rem_test::<B>();
}

#[test]
Expand Down
Loading