Skip to content

Commit

Permalink
Implement wrapping and overflowing traits
Browse files Browse the repository at this point in the history
Add wrapping and overflowing add/sub for completeness. These
are otherwise equivalent to checked() versions, except in unsigned
subtraction wrapping around zero.
  • Loading branch information
kaidokert committed Feb 14, 2025
1 parent a25836e commit 7809cef
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/bigint/addition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
18 changes: 18 additions & 0 deletions src/bigint/subtraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
18 changes: 18 additions & 0 deletions src/biguint/addition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
24 changes: 24 additions & 0 deletions src/biguint/subtraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
}
}
79 changes: 79 additions & 0 deletions tests/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down
86 changes: 86 additions & 0 deletions tests/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down

0 comments on commit 7809cef

Please sign in to comment.