diff --git a/ci/test_full.sh b/ci/test_full.sh index 454f9eac..0130964b 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -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 diff --git a/src/bigint/division.rs b/src/bigint/division.rs index e8b880fe..318d1fb6 100644 --- a/src/bigint/division.rs +++ b/src/bigint/division.rs @@ -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); @@ -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 { + if v.is_zero() { + return None; + } + Some(self.div_euclid(v)) + } + + #[inline] + fn checked_rem_euclid(&self, v: &BigInt) -> Option { + 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 + } + } +} diff --git a/src/biguint/division.rs b/src/biguint/division.rs index 4766319e..29998382 100644 --- a/src/biguint/division.rs +++ b/src/biguint/division.rs @@ -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: /// @@ -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 { + if v.is_zero() { + return None; + } + Some(self.div_euclid(v)) + } + + #[inline] + fn checked_rem_euclid(&self, v: &BigUint) -> Option { + 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 + } +} diff --git a/tests/bigint.rs b/tests/bigint.rs index c7fda4a7..b071c6d8 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -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; @@ -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() { diff --git a/tests/biguint.rs b/tests/biguint.rs index 87eacba0..a3c107e7 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -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; @@ -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() {