Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Mixed Integer Ops #29

Merged
merged 4 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Comment on lines +455 to +456
Copy link
Owner

@nlordell nlordell Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would write this as:

Suggested change
/// # use ethnum::I256;
/// use ethnum::U256;
/// # use ethnum::{I256, U256};

And same for other occurrences 👇.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale is that it feels weird to only show one of the use statements in the doc test but not the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The majority of the methods only require importing Self, and that import is hidden. I tried to match the patterns of the context as closely as possible. I think since the docs will appear on the Self page, Self's use statement is implied, but Other's use statement is not. There is some precedent in std.

A trilema:

  • Hide one use statement but not the other in the same doc test.
  • Hide all use statements in one doc test but none in the other doc test.
  • Hide/Show all use statements in all doc tests.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point... In my mind there is actually a quadlema:

  • Hide all ethnum use statements but not other use statements in the same doc test

Which, while completely arbitrary, kind of makes sense in the context of usage documentation from the ethnum crate's perspective.

I will merge this as is, and think about it a bit more to see if I want to change it or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit addressing this right after you merged.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - your points were valid though, and I'm still a bit on the fence on whether only Self use statements should be hidden or not.

/// 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
Loading