Skip to content

Commit

Permalink
Adds Mixed Integer Ops (#29)
Browse files Browse the repository at this point in the history
This branch implements methods analogous to those of the stable feature mixed_integer_ops.

Closes #24.
  • Loading branch information
NCGThompson authored Oct 25, 2023
1 parent c943fa7 commit 811cec8
Show file tree
Hide file tree
Showing 2 changed files with 291 additions and 1 deletion.
190 changes: 190 additions & 0 deletions src/int/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,31 @@ impl I256 {
}
}

/// Checked addition with an unsigned integer. Computes `self + rhs`,
/// returning `None` if overflow occurred.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(1).checked_add_unsigned(U256::new(2)), Some(I256::new(3)));
/// assert_eq!((I256::MAX - 2).checked_add_unsigned(U256::new(3)), None);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn checked_add_unsigned(self, rhs: U256) -> Option<Self> {
let (a, b) = self.overflowing_add_unsigned(rhs);
if b {
None
} else {
Some(a)
}
}

/// Checked integer subtraction. Computes `self - rhs`, returning `None` if
/// overflow occurred.
///
Expand All @@ -468,6 +493,31 @@ impl I256 {
}
}

/// Checked subtraction with an unsigned integer. Computes `self - rhs`,
/// returning `None` if overflow occurred.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(1).checked_sub_unsigned(U256::new(2)), Some(I256::new(-1)));
/// assert_eq!((I256::MIN + 2).checked_sub_unsigned(U256::new(3)), None);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn checked_sub_unsigned(self, rhs: U256) -> Option<Self> {
let (a, b) = self.overflowing_sub_unsigned(rhs);
if b {
None
} else {
Some(a)
}
}

/// Checked integer multiplication. Computes `self * rhs`, returning `None`
/// if overflow occurred.
///
Expand Down Expand Up @@ -748,6 +798,30 @@ impl I256 {
}
}

/// Saturating addition with an unsigned integer. Computes `self + rhs`,
/// saturating at the numeric bounds instead of overflowing.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(1).saturating_add_unsigned(U256::new(2)), 3);
/// assert_eq!(I256::MAX.saturating_add_unsigned(U256::new(100)), I256::MAX);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn saturating_add_unsigned(self, rhs: U256) -> Self {
// Overflow can only happen at the upper bound
match self.checked_add_unsigned(rhs) {
Some(x) => x,
None => Self::MAX,
}
}

/// Saturating integer subtraction. Computes `self - rhs`, saturating at the
/// numeric bounds instead of overflowing.
///
Expand Down Expand Up @@ -777,6 +851,30 @@ impl I256 {
}
}

/// Saturating subtraction with an unsigned integer. Computes `self - rhs`,
/// saturating at the numeric bounds instead of overflowing.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(100).saturating_sub_unsigned(U256::new(127)), -27);
/// assert_eq!(I256::MIN.saturating_sub_unsigned(U256::new(100)), I256::MIN);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn saturating_sub_unsigned(self, rhs: U256) -> Self {
// Overflow can only happen at the lower bound
match self.checked_sub_unsigned(rhs) {
Some(x) => x,
None => Self::MIN,
}
}

/// Saturating integer negation. Computes `-self`, returning `MAX` if
/// `self == MIN` instead of overflowing.
///
Expand Down Expand Up @@ -923,6 +1021,26 @@ impl I256 {
unsafe { result.assume_init() }
}

/// Wrapping (modular) addition with an unsigned integer. Computes
/// `self + rhs`, wrapping around at the boundary of the type.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(100).wrapping_add_unsigned(U256::new(27)), 127);
/// assert_eq!(I256::MAX.wrapping_add_unsigned(U256::new(2)), I256::MIN + 1);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub fn wrapping_add_unsigned(self, rhs: U256) -> Self {
self.wrapping_add(rhs.as_i256())
}

/// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around
/// at the boundary of the type.
///
Expand All @@ -944,6 +1062,26 @@ impl I256 {
unsafe { result.assume_init() }
}

/// Wrapping (modular) subtraction with an unsigned integer. Computes
/// `self - rhs`, wrapping around at the boundary of the type.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(0).wrapping_sub_unsigned(U256::new(127)), -127);
/// assert_eq!(I256::new(-2).wrapping_sub_unsigned(U256::MAX), -1);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub fn wrapping_sub_unsigned(self, rhs: U256) -> Self {
self.wrapping_sub(rhs.as_i256())
}

/// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping
/// around at the boundary of the type.
///
Expand Down Expand Up @@ -1279,6 +1417,32 @@ impl I256 {
(unsafe { result.assume_init() }, overflow)
}

/// Calculates `self` + `rhs` with an unsigned `rhs`
///
/// Returns a tuple of the addition along with a boolean indicating
/// whether an arithmetic overflow would occur. If an overflow would
/// have occurred then the wrapped value is returned.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(1).overflowing_add_unsigned(U256::new(2)), (I256::new(3), false));
/// assert_eq!((I256::MIN).overflowing_add_unsigned(U256::MAX), (I256::MAX, false));
/// assert_eq!((I256::MAX - 2).overflowing_add_unsigned(U256::new(3)), (I256::MIN, true));
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn overflowing_add_unsigned(self, rhs: U256) -> (Self, bool) {
let rhs = rhs.as_i256();
let (res, overflowed) = self.overflowing_add(rhs);
(res, overflowed ^ (rhs < 0))
}

/// Calculates `self` - `rhs`
///
/// Returns a tuple of the subtraction along with a boolean indicating
Expand All @@ -1303,6 +1467,32 @@ impl I256 {
(unsafe { result.assume_init() }, overflow)
}

/// Calculates `self` - `rhs` with an unsigned `rhs`
///
/// Returns a tuple of the subtraction along with a boolean indicating
/// whether an arithmetic overflow would occur. If an overflow would
/// have occurred then the wrapped value is returned.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::I256;
/// use ethnum::U256;
/// assert_eq!(I256::new(1).overflowing_sub_unsigned(U256::new(2)), (I256::new(-1), false));
/// assert_eq!((I256::MAX).overflowing_sub_unsigned(U256::MAX), (I256::MIN, false));
/// assert_eq!((I256::MIN + 2).overflowing_sub_unsigned(U256::new(3)), (I256::MAX, true));
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn overflowing_sub_unsigned(self, rhs: U256) -> (Self, bool) {
let rhs = rhs.as_i256();
let (res, overflowed) = self.overflowing_sub(rhs);
(res, overflowed ^ (rhs < 0))
}

/// Computes the absolute difference between `self` and `other`.
///
/// This function always returns the correct answer without overflow or
Expand Down
102 changes: 101 additions & 1 deletion src/uint/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! standard library API for `uN` types.

use super::U256;
use crate::intrinsics;
use crate::{intrinsics, I256};
use core::{
mem::{self, MaybeUninit},
num::ParseIntError,
Expand Down Expand Up @@ -425,6 +425,32 @@ impl U256 {
}
}

/// Checked addition with a signed integer. Computes `self + rhs`,
/// returning `None` if overflow occurred.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::U256;
/// use ethnum::I256;
/// assert_eq!(U256::new(1).checked_add_signed(I256::new(2)), Some(U256::new(3)));
/// assert_eq!(U256::new(1).checked_add_signed(I256::new(-2)), None);
/// assert_eq!((U256::MAX - 2).checked_add_signed(I256::new(3)), None);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn checked_add_signed(self, rhs: I256) -> Option<Self> {
let (a, b) = self.overflowing_add_signed(rhs);
if b {
None
} else {
Some(a)
}
}

/// Checked integer subtraction. Computes `self - rhs`, returning `None` if
/// overflow occurred.
///
Expand Down Expand Up @@ -693,6 +719,34 @@ impl U256 {
self.checked_add(rhs).unwrap_or(U256::MAX)
}

/// Saturating addition with a signed integer. Computes `self + rhs`,
/// saturating at the numeric bounds instead of overflowing.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::U256;
/// use ethnum::I256;
/// assert_eq!(U256::new(1).saturating_add_signed(I256::new(2)), U256::new(3));
/// assert_eq!(U256::new(1).saturating_add_signed(I256::new(-2)), U256::new(0));
/// assert_eq!((U256::MAX - 2).saturating_add_signed(I256::new(4)), U256::MAX);
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn saturating_add_signed(self, rhs: I256) -> Self {
let (res, overflow) = self.overflowing_add(rhs.as_u256());
if overflow == (rhs < 0) {
res
} else if overflow {
Self::MAX
} else {
Self::ZERO
}
}

/// Saturating integer subtraction. Computes `self - rhs`, saturating at the
/// numeric bounds instead of overflowing.
///
Expand Down Expand Up @@ -801,6 +855,27 @@ impl U256 {
unsafe { result.assume_init() }
}

/// Wrapping (modular) addition with a signed integer. Computes
/// `self + rhs`, wrapping around at the boundary of the type.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::U256;
/// use ethnum::I256;
/// assert_eq!(U256::new(1).wrapping_add_signed(I256::new(2)), U256::new(3));
/// assert_eq!(U256::new(1).wrapping_add_signed(I256::new(-2)), U256::MAX);
/// assert_eq!((U256::MAX - 2).wrapping_add_signed(I256::new(4)), U256::new(1));
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn wrapping_add_signed(self, rhs: I256) -> Self {
self.wrapping_add(rhs.as_u256())
}

/// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around
/// at the boundary of the type.
///
Expand Down Expand Up @@ -1087,6 +1162,31 @@ impl U256 {
(unsafe { result.assume_init() }, overflow)
}

/// Calculates `self` + `rhs` with a signed `rhs`
///
/// Returns a tuple of the addition along with a boolean indicating
/// whether an arithmetic overflow would occur. If an overflow would
/// have occurred then the wrapped value is returned.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ethnum::U256;
/// use ethnum::I256;
/// assert_eq!(U256::new(1).overflowing_add_signed(I256::new(2)), (U256::new(3), false));
/// assert_eq!(U256::new(1).overflowing_add_signed(I256::new(-2)), (U256::MAX, true));
/// assert_eq!((U256::MAX - 2).overflowing_add_signed(I256::new(4)), (U256::new(1), true));
/// ```
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub fn overflowing_add_signed(self, rhs: I256) -> (Self, bool) {
let (res, overflowed) = self.overflowing_add(rhs.as_u256());
(res, overflowed ^ (rhs < 0))
}

/// Calculates `self` - `rhs`
///
/// Returns a tuple of the subtraction along with a boolean indicating
Expand Down

0 comments on commit 811cec8

Please sign in to comment.