Skip to content

Commit

Permalink
Merge #245
Browse files Browse the repository at this point in the history
245: Implement Euclid division and remainder r=cuviper a=jaybosamiya

Now that a common trait for this has been sorted out (rust-num/num-traits#159 implemented and merged in rust-num/num-traits#195), we can now close #146 by implementing the trait for `BigInt`s. This commit does just that.

Co-authored-by: Jay Bosamiya <jaybosamiya@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
3 people committed Aug 22, 2023
2 parents 65f62a8 + 7269ad6 commit c133d4e
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 6 deletions.
10 changes: 9 additions & 1 deletion ci/test_full.sh
Expand Up @@ -80,7 +80,15 @@ if rustc --version | grep -q nightly; then
fi

case "${STD_FEATURES[*]}" in
*serde*) cargo test --manifest-path ci/big_serde/Cargo.toml ;;&
*serde*) (
cd ci/big_serde
# serde_test updated to 2021 edition after this version
check_version 1.56.0 || (
cargo generate-lockfile
cargo update -p serde_test --precise 1.0.175
)
cargo test
) ;;&
*rand*) cargo test --manifest-path ci/big_rand/Cargo.toml ;;&
*quickcheck*) (
cd ci/big_quickcheck
Expand Down
50 changes: 49 additions & 1 deletion src/bigint/division.rs
Expand Up @@ -6,7 +6,7 @@ use crate::{IsizePromotion, UsizePromotion};

use core::ops::{Div, DivAssign, Rem, RemAssign};
use num_integer::Integer;
use num_traits::{CheckedDiv, ToPrimitive, Zero};
use num_traits::{CheckedDiv, CheckedEuclid, Euclid, Signed, ToPrimitive, Zero};

forward_all_binop_to_ref_ref!(impl Div for BigInt, div);

Expand Down Expand Up @@ -446,3 +446,51 @@ impl CheckedDiv for BigInt {
Some(self.div(v))
}
}

impl CheckedEuclid for BigInt {
#[inline]
fn checked_div_euclid(&self, v: &BigInt) -> Option<BigInt> {
if v.is_zero() {
return None;
}
Some(self.div_euclid(v))
}

#[inline]
fn checked_rem_euclid(&self, v: &BigInt) -> Option<BigInt> {
if v.is_zero() {
return None;
}
Some(self.rem_euclid(v))
}
}

impl Euclid for BigInt {
#[inline]
fn div_euclid(&self, v: &BigInt) -> BigInt {
let (q, r) = self.div_rem(v);
if r.is_negative() {
if v.is_positive() {
q - 1
} else {
q + 1
}
} else {
q
}
}

#[inline]
fn rem_euclid(&self, v: &BigInt) -> BigInt {
let r = self % v;
if r.is_negative() {
if v.is_positive() {
r + v
} else {
r - v
}
} else {
r
}
}
}
34 changes: 33 additions & 1 deletion src/biguint/division.rs
Expand Up @@ -10,7 +10,7 @@ use core::cmp::Ordering::{Equal, Greater, Less};
use core::mem;
use core::ops::{Div, DivAssign, Rem, RemAssign};
use num_integer::Integer;
use num_traits::{CheckedDiv, One, ToPrimitive, Zero};
use num_traits::{CheckedDiv, CheckedEuclid, Euclid, One, ToPrimitive, Zero};

/// Divide a two digit numerator by a one digit divisor, returns quotient and remainder:
///
Expand Down Expand Up @@ -618,3 +618,35 @@ impl CheckedDiv for BigUint {
Some(self.div(v))
}
}

impl CheckedEuclid for BigUint {
#[inline]
fn checked_div_euclid(&self, v: &BigUint) -> Option<BigUint> {
if v.is_zero() {
return None;
}
Some(self.div_euclid(v))
}

#[inline]
fn checked_rem_euclid(&self, v: &BigUint) -> Option<BigUint> {
if v.is_zero() {
return None;
}
Some(self.rem_euclid(v))
}
}

impl Euclid for BigUint {
#[inline]
fn div_euclid(&self, v: &BigUint) -> BigUint {
// trivially same as regular division
self / v
}

#[inline]
fn rem_euclid(&self, v: &BigUint) -> BigUint {
// trivially same as regular remainder
self % v
}
}
54 changes: 53 additions & 1 deletion tests/bigint.rs
Expand Up @@ -14,7 +14,7 @@ use std::{u16, u32, u64, u8, usize};

use num_integer::Integer;
use num_traits::{
pow, FromBytes, FromPrimitive, Num, One, Pow, Signed, ToBytes, ToPrimitive, Zero,
pow, Euclid, FromBytes, FromPrimitive, Num, One, Pow, Signed, ToBytes, ToPrimitive, Zero,
};

mod consts;
Expand Down Expand Up @@ -896,6 +896,58 @@ fn test_div_ceil() {
}
}

#[test]
fn test_div_rem_euclid() {
fn check_sub(a: &BigInt, b: &BigInt, ans_d: &BigInt, ans_m: &BigInt) {
eprintln!("{} {} {} {}", a, b, ans_d, ans_m);
assert_eq!(a.div_euclid(b), *ans_d);
assert_eq!(a.rem_euclid(b), *ans_m);
assert!(*ans_m >= BigInt::zero());
assert!(*ans_m < b.abs());
}

fn check(a: &BigInt, b: &BigInt, d: &BigInt, m: &BigInt) {
if m.is_zero() {
check_sub(a, b, d, m);
check_sub(a, &b.neg(), &d.neg(), m);
check_sub(&a.neg(), b, &d.neg(), m);
check_sub(&a.neg(), &b.neg(), d, m);
} else {
let one: BigInt = One::one();
check_sub(a, b, d, m);
check_sub(a, &b.neg(), &d.neg(), m);
check_sub(&a.neg(), b, &(d + &one).neg(), &(b - m));
check_sub(&a.neg(), &b.neg(), &(d + &one), &(b.abs() - m));
}
}

for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);

if !a.is_zero() {
check(&c, &a, &b, &Zero::zero());
}
if !b.is_zero() {
check(&c, &b, &a, &Zero::zero());
}
}

for elm in DIV_REM_QUADRUPLES.iter() {
let (a_vec, b_vec, c_vec, d_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);
let d = BigInt::from_slice(Plus, d_vec);

if !b.is_zero() {
check(&a, &b, &c, &d);
}
}
}

#[test]
fn test_checked_add() {
for elm in SUM_TRIPLES.iter() {
Expand Down
38 changes: 36 additions & 2 deletions tests/biguint.rs
Expand Up @@ -14,8 +14,8 @@ use std::{i128, u128};
use std::{u16, u32, u64, u8, usize};

use num_traits::{
pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromBytes, FromPrimitive, Num, One, Pow,
ToBytes, ToPrimitive, Zero,
pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Euclid, FromBytes, FromPrimitive, Num,
One, Pow, ToBytes, ToPrimitive, Zero,
};

mod consts;
Expand Down Expand Up @@ -948,6 +948,40 @@ fn test_div_ceil() {
}
}

#[test]
fn test_div_rem_euclid() {
fn check(a: &BigUint, b: &BigUint, d: &BigUint, m: &BigUint) {
assert_eq!(a.div_euclid(b), *d);
assert_eq!(a.rem_euclid(b), *m);
}

for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);

if !a.is_zero() {
check(&c, &a, &b, &Zero::zero());
}
if !b.is_zero() {
check(&c, &b, &a, &Zero::zero());
}
}

for elm in DIV_REM_QUADRUPLES.iter() {
let (a_vec, b_vec, c_vec, d_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
let d = BigUint::from_slice(d_vec);

if !b.is_zero() {
check(&a, &b, &c, &d);
}
}
}

#[test]
fn test_checked_add() {
for elm in SUM_TRIPLES.iter() {
Expand Down

0 comments on commit c133d4e

Please sign in to comment.