Skip to content

Commit 27e4acf

Browse files
bors[bot]jaybosamiyacuviper
authored
Merge #245
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 <[email protected]> Co-authored-by: Josh Stone <[email protected]>
2 parents 65f62a8 + 7269ad6 commit 27e4acf

File tree

5 files changed

+180
-6
lines changed

5 files changed

+180
-6
lines changed

ci/test_full.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,15 @@ if rustc --version | grep -q nightly; then
8080
fi
8181

8282
case "${STD_FEATURES[*]}" in
83-
*serde*) cargo test --manifest-path ci/big_serde/Cargo.toml ;;&
83+
*serde*) (
84+
cd ci/big_serde
85+
# serde_test updated to 2021 edition after this version
86+
check_version 1.56.0 || (
87+
cargo generate-lockfile
88+
cargo update -p serde_test --precise 1.0.175
89+
)
90+
cargo test
91+
) ;;&
8492
*rand*) cargo test --manifest-path ci/big_rand/Cargo.toml ;;&
8593
*quickcheck*) (
8694
cd ci/big_quickcheck

src/bigint/division.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{IsizePromotion, UsizePromotion};
66

77
use core::ops::{Div, DivAssign, Rem, RemAssign};
88
use num_integer::Integer;
9-
use num_traits::{CheckedDiv, ToPrimitive, Zero};
9+
use num_traits::{CheckedDiv, CheckedEuclid, Euclid, Signed, ToPrimitive, Zero};
1010

1111
forward_all_binop_to_ref_ref!(impl Div for BigInt, div);
1212

@@ -446,3 +446,51 @@ impl CheckedDiv for BigInt {
446446
Some(self.div(v))
447447
}
448448
}
449+
450+
impl CheckedEuclid for BigInt {
451+
#[inline]
452+
fn checked_div_euclid(&self, v: &BigInt) -> Option<BigInt> {
453+
if v.is_zero() {
454+
return None;
455+
}
456+
Some(self.div_euclid(v))
457+
}
458+
459+
#[inline]
460+
fn checked_rem_euclid(&self, v: &BigInt) -> Option<BigInt> {
461+
if v.is_zero() {
462+
return None;
463+
}
464+
Some(self.rem_euclid(v))
465+
}
466+
}
467+
468+
impl Euclid for BigInt {
469+
#[inline]
470+
fn div_euclid(&self, v: &BigInt) -> BigInt {
471+
let (q, r) = self.div_rem(v);
472+
if r.is_negative() {
473+
if v.is_positive() {
474+
q - 1
475+
} else {
476+
q + 1
477+
}
478+
} else {
479+
q
480+
}
481+
}
482+
483+
#[inline]
484+
fn rem_euclid(&self, v: &BigInt) -> BigInt {
485+
let r = self % v;
486+
if r.is_negative() {
487+
if v.is_positive() {
488+
r + v
489+
} else {
490+
r - v
491+
}
492+
} else {
493+
r
494+
}
495+
}
496+
}

src/biguint/division.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use core::cmp::Ordering::{Equal, Greater, Less};
1010
use core::mem;
1111
use core::ops::{Div, DivAssign, Rem, RemAssign};
1212
use num_integer::Integer;
13-
use num_traits::{CheckedDiv, One, ToPrimitive, Zero};
13+
use num_traits::{CheckedDiv, CheckedEuclid, Euclid, One, ToPrimitive, Zero};
1414

1515
/// Divide a two digit numerator by a one digit divisor, returns quotient and remainder:
1616
///
@@ -618,3 +618,35 @@ impl CheckedDiv for BigUint {
618618
Some(self.div(v))
619619
}
620620
}
621+
622+
impl CheckedEuclid for BigUint {
623+
#[inline]
624+
fn checked_div_euclid(&self, v: &BigUint) -> Option<BigUint> {
625+
if v.is_zero() {
626+
return None;
627+
}
628+
Some(self.div_euclid(v))
629+
}
630+
631+
#[inline]
632+
fn checked_rem_euclid(&self, v: &BigUint) -> Option<BigUint> {
633+
if v.is_zero() {
634+
return None;
635+
}
636+
Some(self.rem_euclid(v))
637+
}
638+
}
639+
640+
impl Euclid for BigUint {
641+
#[inline]
642+
fn div_euclid(&self, v: &BigUint) -> BigUint {
643+
// trivially same as regular division
644+
self / v
645+
}
646+
647+
#[inline]
648+
fn rem_euclid(&self, v: &BigUint) -> BigUint {
649+
// trivially same as regular remainder
650+
self % v
651+
}
652+
}

tests/bigint.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{u16, u32, u64, u8, usize};
1414

1515
use num_integer::Integer;
1616
use num_traits::{
17-
pow, FromBytes, FromPrimitive, Num, One, Pow, Signed, ToBytes, ToPrimitive, Zero,
17+
pow, Euclid, FromBytes, FromPrimitive, Num, One, Pow, Signed, ToBytes, ToPrimitive, Zero,
1818
};
1919

2020
mod consts;
@@ -896,6 +896,58 @@ fn test_div_ceil() {
896896
}
897897
}
898898

899+
#[test]
900+
fn test_div_rem_euclid() {
901+
fn check_sub(a: &BigInt, b: &BigInt, ans_d: &BigInt, ans_m: &BigInt) {
902+
eprintln!("{} {} {} {}", a, b, ans_d, ans_m);
903+
assert_eq!(a.div_euclid(b), *ans_d);
904+
assert_eq!(a.rem_euclid(b), *ans_m);
905+
assert!(*ans_m >= BigInt::zero());
906+
assert!(*ans_m < b.abs());
907+
}
908+
909+
fn check(a: &BigInt, b: &BigInt, d: &BigInt, m: &BigInt) {
910+
if m.is_zero() {
911+
check_sub(a, b, d, m);
912+
check_sub(a, &b.neg(), &d.neg(), m);
913+
check_sub(&a.neg(), b, &d.neg(), m);
914+
check_sub(&a.neg(), &b.neg(), d, m);
915+
} else {
916+
let one: BigInt = One::one();
917+
check_sub(a, b, d, m);
918+
check_sub(a, &b.neg(), &d.neg(), m);
919+
check_sub(&a.neg(), b, &(d + &one).neg(), &(b - m));
920+
check_sub(&a.neg(), &b.neg(), &(d + &one), &(b.abs() - m));
921+
}
922+
}
923+
924+
for elm in MUL_TRIPLES.iter() {
925+
let (a_vec, b_vec, c_vec) = *elm;
926+
let a = BigInt::from_slice(Plus, a_vec);
927+
let b = BigInt::from_slice(Plus, b_vec);
928+
let c = BigInt::from_slice(Plus, c_vec);
929+
930+
if !a.is_zero() {
931+
check(&c, &a, &b, &Zero::zero());
932+
}
933+
if !b.is_zero() {
934+
check(&c, &b, &a, &Zero::zero());
935+
}
936+
}
937+
938+
for elm in DIV_REM_QUADRUPLES.iter() {
939+
let (a_vec, b_vec, c_vec, d_vec) = *elm;
940+
let a = BigInt::from_slice(Plus, a_vec);
941+
let b = BigInt::from_slice(Plus, b_vec);
942+
let c = BigInt::from_slice(Plus, c_vec);
943+
let d = BigInt::from_slice(Plus, d_vec);
944+
945+
if !b.is_zero() {
946+
check(&a, &b, &c, &d);
947+
}
948+
}
949+
}
950+
899951
#[test]
900952
fn test_checked_add() {
901953
for elm in SUM_TRIPLES.iter() {

tests/biguint.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use std::{i128, u128};
1414
use std::{u16, u32, u64, u8, usize};
1515

1616
use num_traits::{
17-
pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromBytes, FromPrimitive, Num, One, Pow,
18-
ToBytes, ToPrimitive, Zero,
17+
pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Euclid, FromBytes, FromPrimitive, Num,
18+
One, Pow, ToBytes, ToPrimitive, Zero,
1919
};
2020

2121
mod consts;
@@ -948,6 +948,40 @@ fn test_div_ceil() {
948948
}
949949
}
950950

951+
#[test]
952+
fn test_div_rem_euclid() {
953+
fn check(a: &BigUint, b: &BigUint, d: &BigUint, m: &BigUint) {
954+
assert_eq!(a.div_euclid(b), *d);
955+
assert_eq!(a.rem_euclid(b), *m);
956+
}
957+
958+
for elm in MUL_TRIPLES.iter() {
959+
let (a_vec, b_vec, c_vec) = *elm;
960+
let a = BigUint::from_slice(a_vec);
961+
let b = BigUint::from_slice(b_vec);
962+
let c = BigUint::from_slice(c_vec);
963+
964+
if !a.is_zero() {
965+
check(&c, &a, &b, &Zero::zero());
966+
}
967+
if !b.is_zero() {
968+
check(&c, &b, &a, &Zero::zero());
969+
}
970+
}
971+
972+
for elm in DIV_REM_QUADRUPLES.iter() {
973+
let (a_vec, b_vec, c_vec, d_vec) = *elm;
974+
let a = BigUint::from_slice(a_vec);
975+
let b = BigUint::from_slice(b_vec);
976+
let c = BigUint::from_slice(c_vec);
977+
let d = BigUint::from_slice(d_vec);
978+
979+
if !b.is_zero() {
980+
check(&a, &b, &c, &d);
981+
}
982+
}
983+
}
984+
951985
#[test]
952986
fn test_checked_add() {
953987
for elm in SUM_TRIPLES.iter() {

0 commit comments

Comments
 (0)