From 208a372ff2ab2ae552a76d177f50792005166ebf Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Sat, 16 Jul 2022 16:07:28 -0400 Subject: [PATCH 1/6] Implement Euclidean division/remainder & add tests --- src/bigint/division.rs | 50 +++++++++++++++++++++++++++++++++++++- tests/bigint.rs | 54 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/bigint/division.rs b/src/bigint/division.rs index e8b880fe..d19cf13f 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, 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 = self / v; + if self % v < BigInt::zero() { + if *v > BigInt::zero() { + q - 1 + } else { + q + 1 + } + } else { + q + } + } + + #[inline] + fn rem_euclid(&self, v: &BigInt) -> BigInt { + let r = self % v; + if r < BigInt::zero() { + if *v < BigInt::zero() { + r - v + } else { + r + v + } + } else { + r + } + } +} diff --git a/tests/bigint.rs b/tests/bigint.rs index c7fda4a7..bc14d964 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() { From 4d5e10062da3e5147c240a83b7a0a091f9d6294c Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Fri, 31 Mar 2023 16:37:46 -0400 Subject: [PATCH 2/6] Add the trivial Euclid implementations for BigUint --- src/biguint/division.rs | 34 +++++++++++++++++++++++++++++++++- tests/biguint.rs | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 3 deletions(-) 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/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() { From 8a0c8cbb3904eb9afce40349dd64f0ba0ecfe173 Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Fri, 31 Mar 2023 16:40:02 -0400 Subject: [PATCH 3/6] Use `div_rem` to prevent dividing twice --- src/bigint/division.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bigint/division.rs b/src/bigint/division.rs index d19cf13f..1d6280a2 100644 --- a/src/bigint/division.rs +++ b/src/bigint/division.rs @@ -468,8 +468,8 @@ impl CheckedEuclid for BigInt { impl Euclid for BigInt { #[inline] fn div_euclid(&self, v: &BigInt) -> BigInt { - let q = self / v; - if self % v < BigInt::zero() { + let (q, r) = self.div_rem(v); + if r < BigInt::zero() { if *v > BigInt::zero() { q - 1 } else { From 200675d7bb046725a6e91bc866f022af5095d9ea Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Fri, 31 Mar 2023 16:43:05 -0400 Subject: [PATCH 4/6] `is_negative`/`is_positive` instead of comparisons --- src/bigint/division.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bigint/division.rs b/src/bigint/division.rs index 1d6280a2..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, CheckedEuclid, Euclid, ToPrimitive, Zero}; +use num_traits::{CheckedDiv, CheckedEuclid, Euclid, Signed, ToPrimitive, Zero}; forward_all_binop_to_ref_ref!(impl Div for BigInt, div); @@ -469,8 +469,8 @@ impl Euclid for BigInt { #[inline] fn div_euclid(&self, v: &BigInt) -> BigInt { let (q, r) = self.div_rem(v); - if r < BigInt::zero() { - if *v > BigInt::zero() { + if r.is_negative() { + if v.is_positive() { q - 1 } else { q + 1 @@ -483,11 +483,11 @@ impl Euclid for BigInt { #[inline] fn rem_euclid(&self, v: &BigInt) -> BigInt { let r = self % v; - if r < BigInt::zero() { - if *v < BigInt::zero() { - r - v - } else { + if r.is_negative() { + if v.is_positive() { r + v + } else { + r - v } } else { r From cb797d05bd8cf03fb95372757ac208d7fed23245 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 22 Aug 2023 13:26:44 -0700 Subject: [PATCH 5/6] Use old-style format for compatibility --- tests/bigint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bigint.rs b/tests/bigint.rs index bc14d964..b071c6d8 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -899,7 +899,7 @@ 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}"); + 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()); From 7269ad68b6412c927094109cfbafc3206d8ceca0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 22 Aug 2023 13:33:22 -0700 Subject: [PATCH 6/6] ci: downgrade serde_test --- ci/test_full.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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