From 7809cefc9b7663ba187276a87e107f6c17d0f7c7 Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Fri, 14 Feb 2025 14:43:53 -0800 Subject: [PATCH] Implement wrapping and overflowing traits Add wrapping and overflowing add/sub for completeness. These are otherwise equivalent to checked() versions, except in unsigned subtraction wrapping around zero. --- src/bigint/addition.rs | 18 ++++++++ src/bigint/subtraction.rs | 18 ++++++++ src/biguint/addition.rs | 18 ++++++++ src/biguint/subtraction.rs | 24 +++++++++++ tests/bigint.rs | 79 ++++++++++++++++++++++++++++++++++ tests/biguint.rs | 86 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 243 insertions(+) diff --git a/src/bigint/addition.rs b/src/bigint/addition.rs index 0d3a0e2a..6134dcf5 100644 --- a/src/bigint/addition.rs +++ b/src/bigint/addition.rs @@ -9,6 +9,8 @@ use core::iter::Sum; use core::mem; use core::ops::{Add, AddAssign}; use num_traits::CheckedAdd; +use num_traits::WrappingAdd; +use num_traits::ops::overflowing::OverflowingAdd; // We want to forward to BigUint::add, but it's not clear how that will go until // we compare both sign and magnitude. So we duplicate this body for every @@ -236,4 +238,20 @@ impl CheckedAdd for BigInt { } } +// Never wraps, unless we are out of memory +impl WrappingAdd for BigInt { + #[inline] + fn wrapping_add(&self, v: &BigInt) -> BigInt { + self.add(v) + } +} + +// Overflow never occurs, unless we are out of memory +impl OverflowingAdd for BigInt { + #[inline] + fn overflowing_add(&self, v: &BigInt) -> (BigInt, bool) { + (self.add(v), false) + } +} + impl_sum_iter_type!(BigInt); diff --git a/src/bigint/subtraction.rs b/src/bigint/subtraction.rs index ef778549..25081626 100644 --- a/src/bigint/subtraction.rs +++ b/src/bigint/subtraction.rs @@ -8,6 +8,8 @@ use core::cmp::Ordering::{Equal, Greater, Less}; use core::mem; use core::ops::{Sub, SubAssign}; use num_traits::CheckedSub; +use num_traits::WrappingSub; +use num_traits::ops::overflowing::OverflowingSub; // We want to forward to BigUint::sub, but it's not clear how that will go until // we compare both sign and magnitude. So we duplicate this body for every @@ -298,3 +300,19 @@ impl CheckedSub for BigInt { Some(self.sub(v)) } } + +// Never wraps, unless we are out of memory +impl WrappingSub for BigInt { + #[inline] + fn wrapping_sub(&self, v: &BigInt) -> BigInt { + self.sub(v) + } +} + +// Overflow never occurs, unless we are out of memory +impl OverflowingSub for BigInt { + #[inline] + fn overflowing_sub(&self, v: &BigInt) -> (BigInt, bool) { + (self.sub(v), false) + } +} diff --git a/src/biguint/addition.rs b/src/biguint/addition.rs index b6711314..8fefe31f 100644 --- a/src/biguint/addition.rs +++ b/src/biguint/addition.rs @@ -6,6 +6,8 @@ use crate::UsizePromotion; use core::iter::Sum; use core::ops::{Add, AddAssign}; use num_traits::CheckedAdd; +use num_traits::WrappingAdd; +use num_traits::ops::overflowing::OverflowingAdd; #[cfg(target_arch = "x86_64")] use core::arch::x86_64 as arch; @@ -253,4 +255,20 @@ impl CheckedAdd for BigUint { } } +// Never wraps, unless we are out of memory +impl WrappingAdd for BigUint { + #[inline] + fn wrapping_add(&self, v: &BigUint) -> BigUint { + self.add(v) + } +} + +// Never overflows, unless we are out of memory +impl OverflowingAdd for BigUint { + #[inline] + fn overflowing_add(&self, v: &BigUint) -> (BigUint, bool) { + (self.add(v), false) + } +} + impl_sum_iter_type!(BigUint); diff --git a/src/biguint/subtraction.rs b/src/biguint/subtraction.rs index 47a5015f..64232aa1 100644 --- a/src/biguint/subtraction.rs +++ b/src/biguint/subtraction.rs @@ -6,6 +6,8 @@ use crate::UsizePromotion; use core::cmp::Ordering::{Equal, Greater, Less}; use core::ops::{Sub, SubAssign}; use num_traits::CheckedSub; +use num_traits::WrappingSub; +use num_traits::ops::overflowing::OverflowingSub; #[cfg(target_arch = "x86_64")] use core::arch::x86_64 as arch; @@ -310,3 +312,25 @@ impl CheckedSub for BigUint { } } } + +// This would wrap if second argument is greater than first argument +// However, the result would be infinite, so we panic +impl WrappingSub for BigUint { + #[inline] + fn wrapping_sub(&self, v: &BigUint) -> BigUint { + assert!(self >= v, "Wrap would yield an infinite result"); + self.sub(v) + } +} + +// This would overflow if second argument is greater than first argument +// However, the result would be infinite, so we panic +// Note that overflow=true cannot be returned, as we cannot return a +// correct wrapped result. +impl OverflowingSub for BigUint { + #[inline] + fn overflowing_sub(&self, v: &BigUint) -> (BigUint, bool) { + assert!(self >= v, "Overflow would yield an infinite result"); + (self.sub(v), false) + } +} diff --git a/tests/bigint.rs b/tests/bigint.rs index fea5232e..67f54a63 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -13,6 +13,10 @@ use num_integer::Integer; use num_traits::{ pow, Euclid, FromBytes, FromPrimitive, Num, One, Pow, Signed, ToBytes, ToPrimitive, Zero, }; +use num_traits::ops::wrapping::WrappingAdd; +use num_traits::ops::overflowing::OverflowingAdd; +use num_traits::ops::wrapping::WrappingSub; +use num_traits::ops::overflowing::OverflowingSub; mod consts; use crate::consts::*; @@ -978,6 +982,43 @@ fn test_checked_add() { } } +#[test] +fn test_wrapping_add() { + for elm in SUM_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); + + assert_eq!(a.wrapping_add(&b), c); + assert_eq!(b.wrapping_add(&a), c); + assert_eq!(c.wrapping_add(&(-&a)), b); + assert_eq!(c.wrapping_add(&(-&b)), a); + assert_eq!(a.wrapping_add(&(-&c)), (-&b)); + assert_eq!(b.wrapping_add(&(-&c)), (-&a)); + assert_eq!((-&a).wrapping_add(&(-&b)), (-&c)); + assert_eq!(a.wrapping_add(&(-&a)), BigInt::zero()); + } +} + +#[test] +fn test_overflowing_add() { + for elm in SUM_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); + + assert_eq!(a.overflowing_add(&b), (c.clone(), false)); + assert_eq!(b.overflowing_add(&a), (c.clone(), false)); + assert_eq!(c.overflowing_add(&(-&a)), (b.clone(), false)); + assert_eq!(c.overflowing_add(&(-&b)), (a.clone(), false)); + assert_eq!(a.overflowing_add(&(-&c)), ((-&b).clone(), false)); + assert_eq!(b.overflowing_add(&(-&c)), ((-&a).clone(), false)); + assert_eq!(c.overflowing_add(&(-&c)), (BigInt::zero(), false)); + } +} + #[test] fn test_checked_sub() { for elm in SUM_TRIPLES.iter() { @@ -997,6 +1038,44 @@ fn test_checked_sub() { } } +#[test] +fn test_wrapping_sub() { + for elm in SUM_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); + + assert_eq!(c.wrapping_sub(&a), b); + assert_eq!(c.wrapping_sub(&b), a); + assert_eq!((-&b).wrapping_sub(&a), (-&c)); + assert_eq!((-&a).wrapping_sub(&b), (-&c)); + assert_eq!(b.wrapping_sub(&(-&a)), c); + assert_eq!(a.wrapping_sub(&(-&b)), c); + assert_eq!((-&c).wrapping_sub(&(-&a)), (-&b)); + assert_eq!(a.wrapping_sub(&a), BigInt::zero()); + } +} + +#[test] +fn test_overflowing_sub() { + for elm in SUM_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); + + assert_eq!(c.overflowing_sub(&a), (b.clone(), false)); + assert_eq!(c.overflowing_sub(&b), (a.clone(), false)); + assert_eq!((-&b).overflowing_sub(&a), ((-&c).clone(), false)); + assert_eq!((-&a).overflowing_sub(&b), ((-&c).clone(), false)); + assert_eq!(b.overflowing_sub(&(-&a)), (c.clone(), false)); + assert_eq!(a.overflowing_sub(&(-&b)), (c.clone(), false)); + assert_eq!((-&c).overflowing_sub(&(-&a)), ((-&b).clone(), false)); + assert_eq!(a.overflowing_sub(&a), (BigInt::zero(), false)); + } +} + #[test] fn test_checked_mul() { for elm in MUL_TRIPLES.iter() { diff --git a/tests/biguint.rs b/tests/biguint.rs index 4e2c6259..bfefe9c6 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -14,6 +14,10 @@ use num_traits::{ pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Euclid, FromBytes, FromPrimitive, Num, One, Pow, ToBytes, ToPrimitive, Zero, }; +use num_traits::WrappingAdd; +use num_traits::ops::overflowing::OverflowingAdd; +use num_traits::WrappingSub; +use num_traits::ops::overflowing::OverflowingSub; mod consts; use crate::consts::*; @@ -1006,6 +1010,33 @@ fn test_checked_add() { } } +#[test] +fn test_wrapping_add() { + for elm in SUM_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); + + assert_eq!(a.wrapping_add(&b), c); + assert_eq!(b.wrapping_add(&a), c); + } +} + +#[test] +fn test_overflowing_add() { + for elm in SUM_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); + let c_1 = BigUint::from_slice(c_vec); + + assert_eq!(a.overflowing_add(&b), (c, false)); + assert_eq!(b.overflowing_add(&a), (c_1, false)); + } +} + #[test] fn test_checked_sub() { for elm in SUM_TRIPLES.iter() { @@ -1026,6 +1057,61 @@ fn test_checked_sub() { } } +#[test] +fn test_wrapping_sub() { + for elm in SUM_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); + + assert_eq!(c.wrapping_sub(&a), b); + assert_eq!(c.wrapping_sub(&b), a); + if a >= c { + assert_eq!(a.wrapping_sub(&c), b); + } + if b >= c { + assert_eq!(b.wrapping_sub(&c), a); + } + } +} + +#[test] +#[should_panic] +fn test_wrapping_sub_should_panic() { + let a = BigUint::from(100u32); + let b = BigUint::from(200u32); + let _ = a.wrapping_sub(&b); +} + +#[test] +fn test_overflowing_sub() { + for elm in SUM_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); + + assert_eq!(c.overflowing_sub(&a), (b.clone(), false)); + assert_eq!(c.overflowing_sub(&b), (a.clone(), false)); + + if a >= c { + assert_eq!(a.overflowing_sub(&c), (b.clone(), false)); + } + if b >= c { + assert_eq!(b.overflowing_sub(&c), (a.clone(), false)); + } + } +} + +#[test] +#[should_panic] +fn test_overflowing_sub_should_panic() { + let a = BigUint::from(100u32); + let b = BigUint::from(200u32); + let _ = a.overflowing_sub(&b); +} + #[test] fn test_checked_mul() { for elm in MUL_TRIPLES.iter() {