From 7e12f3ba363158a2d0e2598523e92ead7beb9386 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Fri, 24 Feb 2023 01:21:41 +0100
Subject: [PATCH] feat(core): improve I256 implementation (#2180)
* feat(core): improve I256 implementation
* chore: reorganize impls
* chore: update docs, add attributes, use Self
* fix: signum
* chore: use inline(always), update implementations
* fix: use usize for shifting, add more shift ops
* feat: make arithmetic ops generic to match U256
* chore: clippy
* chore: return
---
ethers-core/src/abi/mod.rs | 1 +
ethers-core/src/types/i256.rs | 1596 +++++++++++++++++++--------------
ethers-core/src/utils/mod.rs | 2 +-
3 files changed, 940 insertions(+), 659 deletions(-)
diff --git a/ethers-core/src/abi/mod.rs b/ethers-core/src/abi/mod.rs
index 5fd5c3b0..7cc733a5 100644
--- a/ethers-core/src/abi/mod.rs
+++ b/ethers-core/src/abi/mod.rs
@@ -240,6 +240,7 @@ impl_abi_type_tuple!(19, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S
impl_abi_type_tuple!(20, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
impl_abi_type_tuple!(21, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
+#[allow(clippy::extra_unused_type_parameters)]
#[cfg(test)]
mod tests {
use super::*;
diff --git a/ethers-core/src/types/i256.rs b/ethers-core/src/types/i256.rs
index 28660e13..d0e86ab4 100644
--- a/ethers-core/src/types/i256.rs
+++ b/ethers-core/src/types/i256.rs
@@ -1,6 +1,9 @@
//! This module contains an 256-bit signed integer implementation.
+//!
//! This module was derived for ethers-core via
-#![allow(clippy::wrong_self_convention)]
+
+#![warn(clippy::missing_const_for_fn)]
+
use crate::{
abi::{InvalidOutputType, Token, Tokenizable},
types::U256,
@@ -10,15 +13,13 @@ use ethabi::ethereum_types::FromDecStrErr;
use serde::{Deserialize, Serialize};
use std::{
cmp,
- convert::{TryFrom, TryInto},
- fmt, i128, i64, iter, ops,
- ops::Sub,
- str, u64,
+ fmt::{self, Write},
+ iter, ops,
+ str::FromStr,
};
use thiserror::Error;
-/// The error type that is returned when conversion to or from a 256-bit integer
-/// fails.
+/// The error type that is returned when conversion to or from a 256-bit integer fails.
#[derive(Clone, Copy, Debug, Error)]
#[error("output of range integer conversion attempted")]
pub struct TryFromBigIntError;
@@ -45,30 +46,6 @@ impl From for ParseI256Error {
}
}
-/// Compute the two's complement of a U256.
-fn twos_complement(u: U256) -> U256 {
- let (twos_complement, _) = (!u).overflowing_add(U256::one());
- twos_complement
-}
-
-/// Overflow handling that depends on whether or not the binary is built in
-/// debug mode.
-///
-/// # Panics
-///
-/// This function will panic on overflows in debug mode, and simply ignore the
-/// overflow in non-debug mode.
-#[inline(always)]
-fn handle_overflow((result, overflow): (T, bool)) -> T {
- #[cfg(debug_assertions)]
- {
- assert!(!overflow, "overflow");
- }
-
- let _ = overflow;
- result
-}
-
/// Enum to represent the sign of a 256-bit signed integer.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Sign {
@@ -78,9 +55,40 @@ pub enum Sign {
Negative,
}
+impl fmt::Display for Sign {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match (self, f.sign_plus()) {
+ (Self::Positive, false) => Ok(()),
+ _ => f.write_char(self.as_char()),
+ }
+ }
+}
+
impl Sign {
+ /// Returns whether the sign is positive.
+ #[inline(always)]
+ pub const fn is_positive(&self) -> bool {
+ matches!(self, Self::Positive)
+ }
+
+ /// Returns whether the sign is negative.
+ #[inline(always)]
+ pub const fn is_negative(&self) -> bool {
+ matches!(self, Self::Negative)
+ }
+
+ /// Returns the sign character.
+ #[inline(always)]
+ pub const fn as_char(&self) -> char {
+ match self {
+ Self::Positive => '+',
+ Self::Negative => '-',
+ }
+ }
+
/// Computes the `Sign` given a signum.
- fn from_signum64(sign: i64) -> Self {
+ #[inline(always)]
+ const fn from_signum64(sign: i64) -> Self {
match sign {
0 | 1 => Sign::Positive,
-1 => Sign::Negative,
@@ -92,60 +100,68 @@ impl Sign {
/// Little-endian 256-bit signed integer.
///
/// ## Diversion from standard numeric types
+///
/// The right shift operator on I256 doesn't act in the same manner as standard numeric types
/// (e.g. `i8`, `i16` etc). On standard types if the number is negative right shift will perform
/// an arithmetic shift, whereas on I256 this will perform a bit-wise shift.
/// Arithmetic shift on I256 is done via the [asr](I256::asr) and [asl](I256::asl) functions.
-#[derive(Clone, Copy, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
+#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct I256(U256);
impl I256 {
/// Maximum value.
- pub const MAX: I256 = I256(U256([u64::MAX, u64::MAX, u64::MAX, i64::MAX as _]));
+ pub const MAX: Self = Self(U256([u64::MAX, u64::MAX, u64::MAX, i64::MAX as _]));
/// Minimum value.
- pub const MIN: I256 = I256(U256([0, 0, 0, i64::MIN as _]));
+ pub const MIN: Self = Self(U256([0, 0, 0, i64::MIN as _]));
/// Zero (additive identity) of this type.
+ #[inline(always)]
pub const fn zero() -> Self {
- I256(U256([0, 0, 0, 0]))
+ Self(U256::zero())
}
/// One (multiplicative identity) of this type.
+ #[inline(always)]
pub const fn one() -> Self {
- I256(U256([1, 0, 0, 0]))
+ Self(U256::one())
}
/// Minus one (multiplicative inverse) of this type.
+ #[inline(always)]
pub const fn minus_one() -> Self {
- I256(U256([u64::MAX, u64::MAX, u64::MAX, u64::MAX]))
+ Self(U256::MAX)
}
/// The maximum value which can be inhabited by this type.
+ #[inline(always)]
pub const fn max_value() -> Self {
- I256::MAX
+ Self::MAX
}
/// The minimum value which can be inhabited by this type.
+ #[inline(always)]
pub const fn min_value() -> Self {
- I256::MIN
+ Self::MIN
}
- /// Creates an I256 from a sign and an absolute value. Returns the value and
- /// a bool that is true if the conversion caused an overflow.
+ /// Creates an I256 from a sign and an absolute value. Returns the value and a bool that is true
+ /// if the conversion caused an overflow.
+ #[inline(always)]
pub fn overflowing_from_sign_and_abs(sign: Sign, abs: U256) -> (Self, bool) {
- let value = I256(match sign {
+ let value = Self(match sign {
Sign::Positive => abs,
Sign::Negative => twos_complement(abs),
});
(value, value.sign() != sign)
}
- /// Creates an I256 from an absolute value and a negative flag. Returns
- /// `None` if it would overflow an `I256`.
+ /// Creates an I256 from an absolute value and a negative flag. Returns `None` if it would
+ /// overflow an `I256`.
+ #[inline(always)]
pub fn checked_from_sign_and_abs(sign: Sign, abs: U256) -> Option {
- let (result, overflow) = I256::overflowing_from_sign_and_abs(sign, abs);
+ let (result, overflow) = Self::overflowing_from_sign_and_abs(sign, abs);
if overflow {
None
} else {
@@ -154,6 +170,7 @@ impl I256 {
}
/// Splits a I256 into its absolute value and negative flag.
+ #[inline(always)]
pub fn into_sign_and_abs(self) -> (Sign, U256) {
let sign = self.sign();
let abs = match sign {
@@ -164,7 +181,8 @@ impl I256 {
}
/// Returns the sign of self.
- pub fn sign(self) -> Sign {
+ #[inline(always)]
+ pub const fn sign(self) -> Sign {
let most_significant_word = (self.0).0[3];
match most_significant_word & (1 << 63) {
0 => Sign::Positive,
@@ -175,129 +193,18 @@ impl I256 {
/// Coerces an unsigned integer into a signed one. If the unsigned integer
/// is greater than the greater than or equal to `1 << 255`, then the result
/// will overflow into a negative value.
+ #[inline(always)]
pub const fn from_raw(raw: U256) -> Self {
- I256(raw)
+ Self(raw)
}
- /// Returns the signed integer as a unsigned integer. If the value of `self`
- /// negative, then the two's complement of its absolute value will be
- /// returned.
- pub fn into_raw(self) -> U256 {
+ /// Returns the signed integer as a unsigned integer. If the value of `self` negative, then the
+ /// two's complement of its absolute value will be returned.
+ #[inline(always)]
+ pub const fn into_raw(self) -> U256 {
self.0
}
- /// Conversion to i32
- pub fn low_i32(&self) -> i32 {
- self.0.low_u32() as _
- }
-
- /// Conversion to u32
- pub fn low_u32(&self) -> u32 {
- self.0.low_u32()
- }
-
- /// Conversion to i64
- pub fn low_i64(&self) -> i64 {
- self.0.low_u64() as _
- }
-
- /// Conversion to u64
- pub fn low_u64(&self) -> u64 {
- self.0.low_u64() as _
- }
-
- /// Conversion to i128
- pub fn low_i128(&self) -> i128 {
- self.0.low_u128() as _
- }
-
- /// Conversion to u128
- pub fn low_u128(&self) -> u128 {
- self.0.low_u128() as _
- }
-
- /// Conversion to i128
- pub fn low_isize(&self) -> isize {
- self.0.low_u64() as _
- }
-
- /// Conversion to usize
- pub fn low_usize(&self) -> usize {
- self.0.low_u64() as _
- }
-
- /// Conversion to i32 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`i32::MIN`, `i32::MAX`].
- pub fn as_i32(&self) -> i32 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to u32 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`0`, `u32::MAX`].
- pub fn as_u32(&self) -> u32 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to i64 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`i64::MIN`, `i64::MAX`].
- pub fn as_i64(&self) -> i64 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to u64 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`0`, `u64::MAX`].
- pub fn as_u64(&self) -> u64 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to i128 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`i128::MIN`, `i128::MAX`].
- pub fn as_i128(&self) -> i128 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to u128 with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`0`, `u128::MAX`].
- pub fn as_u128(&self) -> u128 {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to isize with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`isize::MIN`, `isize::MAX`].
- pub fn as_isize(&self) -> usize {
- (*self).try_into().unwrap()
- }
-
- /// Conversion to usize with overflow checking
- ///
- /// # Panics
- ///
- /// Panics if the number is outside the range [`0`, `usize::MAX`].
- pub fn as_usize(&self) -> usize {
- (*self).try_into().unwrap()
- }
-
/// Convert from a decimal string.
pub fn from_dec_str(value: &str) -> Result {
let (sign, value) = match value.as_bytes().first() {
@@ -305,12 +212,8 @@ impl I256 {
Some(b'-') => (Sign::Negative, &value[1..]),
_ => (Sign::Positive, value),
};
-
let abs = U256::from_dec_str(value)?;
- let result =
- I256::checked_from_sign_and_abs(sign, abs).ok_or(ParseI256Error::IntegerOverflow)?;
-
- Ok(result)
+ Self::checked_from_sign_and_abs(sign, abs).ok_or(ParseI256Error::IntegerOverflow)
}
/// Convert from a hexadecimal string.
@@ -325,26 +228,29 @@ impl I256 {
if value.len() > 64 {
return Err(ParseI256Error::IntegerOverflow)
}
+
let mut abs = U256::zero();
for (i, word) in value.as_bytes().rchunks(16).enumerate() {
- let word = str::from_utf8(word).map_err(|_| ParseI256Error::InvalidDigit)?;
+ let word = core::str::from_utf8(word).map_err(|_| ParseI256Error::InvalidDigit)?;
abs.0[i] = u64::from_str_radix(word, 16).map_err(|_| ParseI256Error::InvalidDigit)?;
}
- let result =
- I256::checked_from_sign_and_abs(sign, abs).ok_or(ParseI256Error::IntegerOverflow)?;
-
- Ok(result)
+ Self::checked_from_sign_and_abs(sign, abs).ok_or(ParseI256Error::IntegerOverflow)
}
- /// Returns the sign of the number.
- #[must_use]
+ /// Returns a number representing sign of `self`.
+ ///
+ /// - `0` if the number is zero
+ /// - `1` if the number is positive
+ /// - `-1` if the number is negative
+ #[inline(always)]
pub fn signum(self) -> Self {
self.signum64().into()
}
/// Returns an `i64` representing the sign of the number.
- fn signum64(self) -> i64 {
+ #[inline(always)]
+ const fn signum64(self) -> i64 {
match self.sign() {
Sign::Positive => (!self.is_zero()) as i64,
Sign::Negative => -1,
@@ -353,122 +259,29 @@ impl I256 {
/// Returns `true` if `self` is positive and `false` if the number is zero
/// or negative.
- pub fn is_positive(self) -> bool {
+ #[inline(always)]
+ pub const fn is_positive(self) -> bool {
self.signum64().is_positive()
}
/// Returns `true` if `self` is negative and `false` if the number is zero
/// or negative.
- pub fn is_negative(self) -> bool {
+ #[inline(always)]
+ pub const fn is_negative(self) -> bool {
self.signum64().is_negative()
}
/// Returns `true` if `self` is negative and `false` if the number is zero
/// or positive.
- pub fn is_zero(self) -> bool {
+ #[inline(always)]
+ pub const fn is_zero(self) -> bool {
self.0.is_zero()
}
- /// Gets the absolute value.
- ///
- /// # Panics
- ///
- /// In debug mode, will panic if it overflows.
- #[must_use]
- pub fn abs(self) -> Self {
- handle_overflow(self.overflowing_abs())
- }
-
- /// Computes the absolute value of self.
- ///
- /// Returns a tuple of the absolute version of self along with a boolean
- /// indicating whether an overflow happened. If self is the minimum value
- /// (e.g., `I256::MIN` for values of type `I256`), then the minimum value
- /// will be returned again and true will be returned for an overflow
- /// happening.
- pub fn overflowing_abs(self) -> (Self, bool) {
- if self == I256::MIN {
- (self, true)
- } else {
- (I256(self.abs_unsigned()), false)
- }
- }
-
- /// Checked absolute value. Computes `self.abs()`, returning `None` if
- /// `self == MIN`.
- pub fn checked_abs(self) -> Option {
- let (result, overflow) = self.overflowing_abs();
- if overflow {
- None
- } else {
- Some(result)
- }
- }
-
- /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if
- /// `self == MIN` instead of overflowing.
- #[must_use]
- pub fn saturating_abs(self) -> Self {
- self.checked_abs().unwrap_or(I256::MAX)
- }
-
- /// Wrapping absolute value. Computes `self.abs()`, wrapping around at the
- /// boundary of the type.
- #[must_use]
- pub fn wrapping_abs(self) -> Self {
- let (result, _) = self.overflowing_abs();
- result
- }
-
- /// Gets the absolute value as an unsigned integer.
- fn abs_unsigned(self) -> U256 {
- let (_, abs) = self.into_sign_and_abs();
- abs
- }
-
- /// Negates self, overflowing if this is equal to the minimum value.
- ///
- /// Returns a tuple of the negated version of `self` along with a boolean
- /// indicating whether an overflow happened. If `self` is the minimum value,
- /// then the minimum value will be returned again and `true` will be
- /// returned for an overflow happening.
- pub fn overflowing_neg(self) -> (Self, bool) {
- if self == I256::MIN {
- (self, true)
- } else {
- (I256(twos_complement(self.0)), false)
- }
- }
-
- /// Checked negation. Computes `self.neg()`, returning `None` if
- /// `self == MIN`.
- pub fn checked_neg(self) -> Option {
- let (result, overflow) = self.overflowing_neg();
- if overflow {
- None
- } else {
- Some(result)
- }
- }
-
- /// Saturating negation. Computes `self.neg()`, returning `MAX` if
- /// `self == MIN` instead of overflowing.
- #[must_use]
- pub fn saturating_neg(self) -> Self {
- self.checked_neg().unwrap_or(I256::MAX)
- }
-
- /// Wrapping negation. Computes `self.neg()`, returning `MIN` if
- /// `self == MIN` instead of overflowing.
- #[must_use]
- pub fn wrapping_neg(self) -> Self {
- let (result, _) = self.overflowing_neg();
- result
- }
-
/// Return the least number of bits needed to represent the number.
+ #[inline(always)]
pub fn bits(&self) -> u32 {
- let unsigned = self.abs_unsigned();
+ let unsigned = self.unsigned_abs();
let unsigned_bits = unsigned.bits();
// NOTE: We need to deal with two special cases:
@@ -499,60 +312,195 @@ impl I256 {
///
/// # Panics
///
- /// Panics if index exceeds the bit width of the number.
- pub fn bit(&self, index: usize) -> bool {
+ /// If index exceeds the bit width of the number.
+ #[inline(always)]
+ #[track_caller]
+ pub const fn bit(&self, index: usize) -> bool {
self.0.bit(index)
}
- /// Returns the number of ones in the binary representation of `self`.
- pub fn count_ones(&self) -> u32 {
- (self.0).0.iter().map(|word| word.count_ones()).sum()
- }
-
- /// Returns the number of zeros in the binary representation of `self`.
- pub fn count_zeros(&self) -> u32 {
- (self.0).0.iter().map(|word| word.count_zeros()).sum()
- }
-
- /// Returns the number of leading zeros in the binary representation of
- /// `self`.
- pub fn leading_zeros(&self) -> u32 {
- self.0.leading_zeros()
- }
-
- /// Returns the number of leading zeros in the binary representation of
- /// `self`.
- pub fn trailing_zeros(&self) -> u32 {
- self.0.trailing_zeros()
- }
-
/// Return specific byte.
///
/// # Panics
///
- /// Panics if index exceeds the byte width of the number.
- pub fn byte(&self, index: usize) -> u8 {
+ /// If index exceeds the byte width of the number.
+ #[inline(always)]
+ #[track_caller]
+ pub const fn byte(&self, index: usize) -> u8 {
self.0.byte(index)
}
+ /// Returns the number of ones in the binary representation of `self`.
+ #[inline(always)]
+ pub fn count_ones(&self) -> u32 {
+ (self.0).0.iter().map(|word| word.count_ones()).sum()
+ }
+
+ /// Returns the number of zeros in the binary representation of `self`.
+ #[inline(always)]
+ pub fn count_zeros(&self) -> u32 {
+ (self.0).0.iter().map(|word| word.count_zeros()).sum()
+ }
+
+ /// Returns the number of leading zeros in the binary representation of
+ /// `self`.
+ #[inline(always)]
+ pub fn leading_zeros(&self) -> u32 {
+ self.0.leading_zeros()
+ }
+
+ /// Returns the number of leading zeros in the binary representation of
+ /// `self`.
+ #[inline(always)]
+ pub fn trailing_zeros(&self) -> u32 {
+ self.0.trailing_zeros()
+ }
+
/// Write to the slice in big-endian format.
+ ///
+ /// # Panics
+ ///
+ /// If the given slice is not exactly 32 bytes long.
+ #[inline(always)]
+ #[track_caller]
pub fn to_big_endian(&self, bytes: &mut [u8]) {
self.0.to_big_endian(bytes)
}
/// Write to the slice in little-endian format.
+ ///
+ /// # Panics
+ ///
+ /// If the given slice is not exactly 32 bytes long.
+ #[inline(always)]
+ #[track_caller]
pub fn to_little_endian(&self, bytes: &mut [u8]) {
self.0.to_little_endian(bytes)
}
+}
+
+// ops impl
+impl I256 {
+ /// Computes the absolute value of `self`.
+ ///
+ /// # Overflow behavior
+ ///
+ /// The absolute value of `I256::MIN` cannot be represented as an `I256` and attempting to
+ /// calculate it will cause an overflow. This means that code in debug mode will trigger a panic
+ /// on this case and optimized code will return `I256::MIN` without a panic.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
+ pub fn abs(self) -> Self {
+ handle_overflow(self.overflowing_abs())
+ }
+
+ /// Computes the absolute value of `self`.
+ ///
+ /// Returns a tuple of the absolute version of self along with a boolean indicating whether an
+ /// overflow happened. If self is the minimum value then the minimum value will be returned
+ /// again and true will be returned for an overflow happening.
+ #[inline(always)]
+ #[must_use]
+ pub fn overflowing_abs(self) -> (Self, bool) {
+ if self == Self::MIN {
+ (self, true)
+ } else {
+ (Self(self.unsigned_abs()), false)
+ }
+ }
+
+ /// Checked absolute value. Computes `self.abs()`, returning `None` if `self == MIN`.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_abs(self) -> Option {
+ match self.overflowing_abs() {
+ (value, false) => Some(value),
+ _ => None,
+ }
+ }
+
+ /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == MIN` instead
+ /// of overflowing.
+ #[inline(always)]
+ #[must_use]
+ pub fn saturating_abs(self) -> Self {
+ match self.overflowing_abs() {
+ (value, false) => value,
+ _ => Self::MAX,
+ }
+ }
+
+ /// Wrapping absolute value. Computes `self.abs()`, wrapping around at the boundary of the type.
+ #[inline(always)]
+ #[must_use]
+ pub fn wrapping_abs(self) -> Self {
+ self.overflowing_abs().0
+ }
+
+ /// Computes the absolute value of `self` without any wrapping or panicking.
+ #[inline(always)]
+ #[must_use]
+ pub fn unsigned_abs(self) -> U256 {
+ self.into_sign_and_abs().1
+ }
+
+ /// Negates self, overflowing if this is equal to the minimum value.
+ ///
+ /// Returns a tuple of the negated version of self along with a boolean indicating whether an
+ /// overflow happened. If `self` is the minimum value, then the minimum value will be returned
+ /// again and `true` will be returned for an overflow happening.
+ #[inline(always)]
+ #[must_use]
+ pub fn overflowing_neg(self) -> (Self, bool) {
+ if self == Self::MIN {
+ (self, true)
+ } else {
+ (Self(twos_complement(self.0)), false)
+ }
+ }
+
+ /// Checked negation. Computes `-self`, returning `None` if `self == MIN`.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_neg(self) -> Option {
+ match self.overflowing_neg() {
+ (value, false) => Some(value),
+ _ => None,
+ }
+ }
+
+ /// Saturating negation. Computes `-self`, returning `MAX` if `self == MIN` instead of
+ /// overflowing.
+ #[inline(always)]
+ #[must_use]
+ pub fn saturating_neg(self) -> Self {
+ match self.overflowing_neg() {
+ (value, false) => value,
+ _ => Self::MAX,
+ }
+ }
+
+ /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary of the type.
+ ///
+ /// The only case where such wrapping can occur is when one negates `MIN` on a signed type
+ /// (where `MIN` is the negative minimal value for the type); this is a positive value that is
+ /// too large to represent in the type. In such a case, this function returns `MIN` itself.
+ #[inline(always)]
+ #[must_use]
+ pub fn wrapping_neg(self) -> Self {
+ self.overflowing_neg().0
+ }
/// Calculates `self` + `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.
+ /// 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.
+ #[inline(always)]
+ #[must_use]
pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let (unsigned, _) = self.0.overflowing_add(rhs.0);
- let result = I256(unsigned);
+ let result = Self(unsigned);
// NOTE: Overflow is determined by checking the sign of the operands and
// the result.
@@ -565,24 +513,26 @@ impl I256 {
(result, overflow)
}
- /// Checked addition. Returns None if overflow occurred.
- pub fn checked_add(self, other: Self) -> Option {
- let (result, overflow) = self.overflowing_add(other);
- if overflow {
- None
- } else {
- Some(result)
+ /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_add(self, rhs: Self) -> Option {
+ match self.overflowing_add(rhs) {
+ (value, false) => Some(value),
+ _ => None,
}
}
- /// Addition which saturates at the maximum value (Self::max_value()).
+ /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric bounds instead
+ /// of overflowing.
+ #[inline(always)]
#[must_use]
- pub fn saturating_add(self, other: Self) -> Self {
- let (result, overflow) = self.overflowing_add(other);
+ pub fn saturating_add(self, rhs: Self) -> Self {
+ let (result, overflow) = self.overflowing_add(rhs);
if overflow {
match result.sign() {
- Sign::Positive => I256::MIN,
- Sign::Negative => I256::MAX,
+ Sign::Positive => Self::MIN,
+ Sign::Negative => Self::MAX,
}
} else {
result
@@ -590,24 +540,25 @@ impl I256 {
}
/// Wrapping addition.
+ #[inline(always)]
#[must_use]
- pub fn wrapping_add(self, other: Self) -> Self {
- let (result, _) = self.overflowing_add(other);
- result
+ pub fn wrapping_add(self, rhs: Self) -> Self {
+ self.overflowing_add(rhs).0
}
- /// Calculates `self` - `rhs`
+ /// Calculates ``self` - `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.
+ /// 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.
+ #[inline(always)]
+ #[must_use]
pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
// NOTE: We can't just compute the `self + (-rhs)` because `-rhs` does
// not always exist, specifically this would be a problem in case
- // `rhs == I256::MIN`
+ // `rhs == Self::MIN`
let (unsigned, _) = self.0.overflowing_sub(rhs.0);
- let result = I256(unsigned);
+ let result = Self(unsigned);
// NOTE: Overflow is determined by checking the sign of the operands and
// the result.
@@ -620,110 +571,151 @@ impl I256 {
(result, overflow)
}
- /// Checked subtraction. Returns None if overflow occurred.
- pub fn checked_sub(self, other: Self) -> Option {
- let (result, overflow) = self.overflowing_sub(other);
- if overflow {
- None
- } else {
- Some(result)
+ /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_sub(self, rhs: Self) -> Option {
+ match self.overflowing_sub(rhs) {
+ (value, false) => Some(value),
+ _ => None,
}
}
- /// Subtraction which saturates at zero.
+ /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric bounds
+ /// instead of overflowing.
+ #[inline(always)]
#[must_use]
- pub fn saturating_sub(self, other: Self) -> Self {
- let (result, overflow) = self.overflowing_sub(other);
+ pub fn saturating_sub(self, rhs: Self) -> Self {
+ let (result, overflow) = self.overflowing_sub(rhs);
if overflow {
match result.sign() {
- Sign::Positive => I256::MIN,
- Sign::Negative => I256::MAX,
+ Sign::Positive => Self::MIN,
+ Sign::Negative => Self::MAX,
}
} else {
result
}
}
- /// Wrapping subtraction.
+ /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the boundary of
+ /// the type.
+ #[inline(always)]
#[must_use]
- pub fn wrapping_sub(self, other: Self) -> Self {
- let (result, _) = self.overflowing_sub(other);
- result
+ pub fn wrapping_sub(self, rhs: Self) -> Self {
+ self.overflowing_sub(rhs).0
}
/// Calculates `self` * `rhs`
///
- /// Returns a tuple of the multiplication along with a boolean indicating
- /// whether an arithmetic overflow would occur. If an overflow would have
- /// occurred then the wrapped value is returned.
+ /// Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic
+ /// overflow would occur. If an overflow would have occurred then the wrapped value is returned.
+ #[inline(always)]
+ #[must_use]
pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let sign = Sign::from_signum64(self.signum64() * rhs.signum64());
- let (unsigned, overflow_mul) = self.abs_unsigned().overflowing_mul(rhs.abs_unsigned());
- let (result, overflow_conv) = I256::overflowing_from_sign_and_abs(sign, unsigned);
+ let (unsigned, overflow_mul) = self.unsigned_abs().overflowing_mul(rhs.unsigned_abs());
+ let (result, overflow_conv) = Self::overflowing_from_sign_and_abs(sign, unsigned);
(result, overflow_mul || overflow_conv)
}
- /// Checked multiplication. Returns None if overflow occurred.
- pub fn checked_mul(self, other: Self) -> Option {
- let (result, overflow) = self.overflowing_mul(other);
- if overflow {
- None
- } else {
- Some(result)
+ /// Checked integer multiplication. Computes `self * rhs`, returning None if overflow occurred.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_mul(self, rhs: Self) -> Option {
+ match self.overflowing_mul(rhs) {
+ (value, false) => Some(value),
+ _ => None,
}
}
- /// Multiplication which saturates at the maximum value..
+ /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric bounds
+ /// instead of overflowing.
+ #[inline(always)]
#[must_use]
pub fn saturating_mul(self, rhs: Self) -> Self {
- self.checked_mul(rhs).unwrap_or_else(|| {
+ let (result, overflow) = self.overflowing_mul(rhs);
+ if overflow {
match Sign::from_signum64(self.signum64() * rhs.signum64()) {
- Sign::Positive => I256::MAX,
- Sign::Negative => I256::MIN,
+ Sign::Positive => Self::MAX,
+ Sign::Negative => Self::MIN,
}
- })
+ } else {
+ result
+ }
}
- /// Wrapping multiplication.
+ /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at the boundary of
+ /// the type.
+ #[inline(always)]
#[must_use]
pub fn wrapping_mul(self, rhs: Self) -> Self {
- let (result, _) = self.overflowing_mul(rhs);
- result
+ self.overflowing_mul(rhs).0
}
/// Calculates `self` / `rhs`
///
- /// Returns a tuple of the division along with a boolean indicating
- /// whether an arithmetic overflow would occur. If an overflow would have
- /// occurred then the wrapped value is returned.
+ /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic
+ /// overflow would occur. If an overflow would occur then self is returned.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
// Panic early when with division by zero while evaluating sign.
let sign = Sign::from_signum64(self.signum64() / rhs.signum64());
// Note, signed division can't overflow!
- let unsigned = self.abs_unsigned() / rhs.abs_unsigned();
- let (result, overflow_conv) = I256::overflowing_from_sign_and_abs(sign, unsigned);
+ let unsigned = self.unsigned_abs() / rhs.unsigned_abs();
+ let (result, overflow_conv) = Self::overflowing_from_sign_and_abs(sign, unsigned);
(result, overflow_conv && !result.is_zero())
}
- /// Checked division. Returns None if overflow occurred or if rhs == 0.
+ /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` or the
+ /// division results in overflow.
+ #[inline(always)]
+ #[must_use]
pub fn checked_div(self, rhs: Self) -> Option {
- if rhs == I256::zero() || (self == Self::min_value() && rhs == -I256::one()) {
+ if rhs.is_zero() || (self == Self::min_value() && rhs == Self::minus_one()) {
None
} else {
Some(self.overflowing_div(rhs).0)
}
}
- /// Division which saturates at the maximum value.
+ /// Saturating integer division. Computes `self / rhs`, saturating at the numeric bounds instead
+ /// of overflowing.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn saturating_div(self, rhs: Self) -> Self {
- // There is only one overflow (I256::MIN / -1 = I256::MAX)
- self.checked_div(rhs).unwrap_or(I256::MAX)
+ match self.overflowing_div(rhs) {
+ (value, false) => value,
+ // MIN / -1 is the only possible saturating overflow
+ _ => Self::MAX,
+ }
}
- /// Wrapping division.
+ /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the boundary of the
+ /// type.
+ ///
+ /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type
+ /// (where `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a
+ /// positive value that is too large to represent in the type. In such a case, this function
+ /// returns `MIN` itself.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn wrapping_div(self, rhs: Self) -> Self {
self.overflowing_div(rhs).0
@@ -731,11 +723,17 @@ impl I256 {
/// Calculates `self` % `rhs`
///
- /// Returns a tuple of the remainder along with a boolean indicating
- /// whether an arithmetic overflow would occur. If an overflow would have
- /// occurred then the wrapped value is returned.
+ /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an
+ /// arithmetic overflow would occur. If an overflow would occur then 0 is returned.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
- if self == Self::MIN && rhs == Self::from(-1) {
+ if self == Self::MIN && rhs == Self::minus_one() {
(Self::zero(), true)
} else {
let div_res = self / rhs;
@@ -743,43 +741,122 @@ impl I256 {
}
}
- /// Checked remainder. Returns None if overflow occurred or rhs == 0
+ /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0` or the
+ /// division results in overflow.
+ #[inline(always)]
+ #[must_use]
pub fn checked_rem(self, rhs: Self) -> Option {
- if rhs == I256::zero() || (self == Self::min_value() && rhs == -I256::one()) {
+ if rhs.is_zero() || (self == Self::MIN && rhs == Self::minus_one()) {
None
} else {
Some(self.overflowing_rem(rhs).0)
}
}
- /// Wrapping remainder. Returns the result of the operation %
- /// regardless of whether or not the division overflowed.
+ /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the boundary of the
+ /// type.
+ ///
+ /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y`
+ /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such
+ /// a case, this function returns `0`.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn wrapping_rem(self, rhs: Self) -> Self {
self.overflowing_rem(rhs).0
}
- /// Calculates the quotient of Euclidean division of self by rhs.
+ /// Calculates the quotient of Euclidean division of `self` by `rhs`.
///
- /// This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`,
- /// with `0 <= self.rem_euclid(rhs) < rhs`.
- /// In other words, the result is `self / rhs` rounded to the integer `n` such that `self >= n *
- /// rhs`:
- /// * If `self > 0`, this is equal to round towards zero (the default in Rust);
- /// * If `self < 0`, this is equal to round towards +/- infinity.
+ /// This computes the integer `q` such that `self = q * rhs + r`, with
+ /// `r = self.rem_euclid(rhs)` and `0 <= r < abs(rhs)`.
+ ///
+ /// In other words, the result is `self / rhs` rounded to the integer `q` such that `self >= q *
+ /// rhs`.
+ /// If `self > 0`, this is equal to round towards zero (the default in Rust);
+ /// if `self < 0`, this is equal to round towards +/- infinity.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0 or the division results in overflow.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn div_euclid(self, rhs: Self) -> Self {
let q = self / rhs;
if (self % rhs).is_negative() {
- return if rhs.is_positive() { q - I256::one() } else { q + I256::one() }
+ if rhs.is_positive() {
+ q - Self::one()
+ } else {
+ q + Self::one()
+ }
+ } else {
+ q
}
- q
}
- /// Calculates the least non-negative remainder of self (mod rhs).
- /// This is done as if by the _Euclidean division algorithm_
- /// given `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r, and 0 <= r <
- /// abs(rhs)`.
+ /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`.
+ ///
+ /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic
+ /// overflow would occur. If an overflow would occur then `self` is returned.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
+ pub fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) {
+ if self == Self::min_value() && rhs == Self::minus_one() {
+ (self, true)
+ } else {
+ (self.div_euclid(rhs), false)
+ }
+ }
+
+ /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` if `rhs == 0`
+ /// or the division results in overflow.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_div_euclid(self, rhs: Self) -> Option {
+ if rhs.is_zero() || (self == Self::min_value() && rhs == Self::minus_one()) {
+ None
+ } else {
+ Some(self.div_euclid(rhs))
+ }
+ }
+
+ /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`,
+ /// wrapping around at the boundary of the type.
+ ///
+ /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal
+ /// value for the type). This is equivalent to `-MIN`, a positive value that is too large to
+ /// represent in the type. In this case, this method returns `MIN` itself.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
+ pub fn wrapping_div_euclid(self, rhs: Self) -> Self {
+ self.overflowing_div_euclid(rhs).0
+ }
+
+ /// Calculates the least nonnegative remainder of `self (mod rhs)`.
+ ///
+ /// This is done as if by the Euclidean division algorithm -- given `r = self.rem_euclid(rhs)`,
+ /// `self = rhs * self.div_euclid(rhs) + r`, and `0 <= r < abs(rhs)`.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0 or the division results in overflow.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn rem_euclid(self, rhs: Self) -> Self {
let r = self % rhs;
@@ -794,65 +871,47 @@ impl I256 {
}
}
- /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`.
- /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic
- /// overflow would occur. If an overflow would occur then `self` is returned.
- pub fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) {
- if self == Self::min_value() && rhs == -I256::one() {
- (self, true)
- } else {
- (self.div_euclid(rhs), false)
- }
- }
-
- /// Checked Euclidean division. Computes `self.div_euclid(rhs)`,
- /// returning None if `rhs == 0` or the division results in overflow.
- pub fn checked_div_euclid(self, rhs: Self) -> Option {
- if rhs == I256::zero() || (self == Self::min_value() && rhs == -I256::one()) {
- None
- } else {
- Some(self.div_euclid(rhs))
- }
- }
-
- /// Wrapping Euclidean division.
- /// Computes `self.div_euclid(rhs)`, wrapping around at the boundary of the type.
- /// Wrapping only occurs in `MIN / -1` on a signed type
- /// (where `MIN` is the negative minimal value for the type).
- /// This is equivalent to `-MIN`, a positive value that is too large to represent in the type.
- /// In this case, this method returns `MIN` itself.
- #[must_use]
- pub fn wrapping_div_euclid(self, rhs: Self) -> Self {
- self.overflowing_div_euclid(rhs).0
- }
-
/// Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`.
- /// Returns a tuple of the remainder after dividing along with a boolean indicating whether
- /// an arithmetic overflow would occur. If an overflow would occur then `0` is returned.
- /// Panics if `rhs == 0`
+ ///
+ /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an
+ /// arithmetic overflow would occur. If an overflow would occur then 0 is returned.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
pub fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) {
- if self == Self::min_value() && rhs == -Self::one() {
+ if self == Self::min_value() && rhs == Self::minus_one() {
(Self::zero(), true)
} else {
(self.rem_euclid(rhs), false)
}
}
- /// Wrapping Euclidean remainder.
- /// Computes `self.rem_euclid(rhs)`, wrapping around at the boundary of the type.
- /// Wrapping will only occur in `MIN % -1` on a signed type
- /// (where `MIN` is the negative minimal value for the type).
- /// In this case, this method returns `0`.
- /// Panics when `rhs == 0`
+ /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around at the
+ /// boundary of the type.
+ ///
+ /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal
+ /// value for the type). In this case, this method returns 0.
+ ///
+ /// # Panics
+ ///
+ /// If `rhs` is 0.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn wrapping_rem_euclid(self, rhs: Self) -> Self {
self.overflowing_rem_euclid(rhs).0
}
- /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`,
- /// returning `None` if `rhs == 0` or the division results in overflow.
+ /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` if `rhs == 0`
+ /// or the division results in overflow.
+ #[inline(always)]
+ #[must_use]
pub fn checked_rem_euclid(self, rhs: Self) -> Option {
- if rhs == I256::zero() || (self == Self::min_value() && rhs == -I256::one()) {
+ if rhs.is_zero() || (self == Self::min_value() && rhs == Self::minus_one()) {
None
} else {
Some(self.rem_euclid(rhs))
@@ -866,7 +925,8 @@ impl I256 {
/// an odd exponent will be negative. This means that the sign of the result
/// of exponentiation can be computed even if the actual result is too large
/// to fit in 256-bit signed integer.
- fn pow_sign(self, exp: u32) -> Sign {
+ #[inline(always)]
+ const fn pow_sign(self, exp: u32) -> Sign {
let is_exp_odd = exp % 2 != 0;
if is_exp_odd && self.is_negative() {
Sign::Negative
@@ -875,38 +935,47 @@ impl I256 {
}
}
- /// Create 10**n as this type.
+ /// Create `10**n` as this type.
///
/// # Panics
///
- /// Panics if the result overflows the type.
+ /// If the result overflows the type.
+ #[inline(always)]
+ #[track_caller]
+ #[must_use]
pub fn exp10(n: usize) -> Self {
U256::exp10(n).try_into().expect("overflow")
}
- /// Raise self to the power of `exp`.
+ /// Raises self to the power of `exp`, using exponentiation by squaring.
///
/// # Panics
///
- /// Panics if the result overflows the type in debug mode.
+ /// If the result overflows the type in debug mode.
+ #[inline(always)]
+ #[track_caller]
#[must_use]
pub fn pow(self, exp: u32) -> Self {
handle_overflow(self.overflowing_pow(exp))
}
- /// Raises self to the power of `exp`.
+ /// Raises self to the power of `exp`, using exponentiation by squaring.
///
- /// Returns a tuple of the exponentiation along with a bool indicating
- /// whether an overflow happened.
+ /// Returns a tuple of the exponentiation along with a bool indicating whether an overflow
+ /// happened.
+ #[inline(always)]
+ #[must_use]
pub fn overflowing_pow(self, exp: u32) -> (Self, bool) {
let sign = self.pow_sign(exp);
- let (unsigned, overflow_pow) = self.abs_unsigned().overflowing_pow(exp.into());
- let (result, overflow_conv) = I256::overflowing_from_sign_and_abs(sign, unsigned);
+ let (unsigned, overflow_pow) = self.unsigned_abs().overflowing_pow(exp.into());
+ let (result, overflow_conv) = Self::overflowing_from_sign_and_abs(sign, unsigned);
(result, overflow_pow || overflow_conv)
}
- /// Raises self to the power of `exp`. Returns None if overflow occurred.
+ /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if overflow occurred.
+ #[inline(always)]
+ #[must_use]
pub fn checked_pow(self, exp: u32) -> Option {
let (result, overflow) = self.overflowing_pow(exp);
if overflow {
@@ -916,55 +985,127 @@ impl I256 {
}
}
- /// Raises self to the power of `exp`, saturating at the numeric bounds
- /// instead of overflowing.
+ /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at the numeric
+ /// bounds instead of overflowing.
+ #[inline(always)]
#[must_use]
pub fn saturating_pow(self, exp: u32) -> Self {
let (result, overflow) = self.overflowing_pow(exp);
if overflow {
match self.pow_sign(exp) {
- Sign::Positive => I256::MAX,
- Sign::Negative => I256::MIN,
+ Sign::Positive => Self::MAX,
+ Sign::Negative => Self::MIN,
}
} else {
result
}
}
- /// Wrapping powolute value. Computes `self.pow()`, wrapping around at the
+ /// Raises self to the power of `exp`, wrapping around at the
/// boundary of the type.
+ #[inline(always)]
#[must_use]
pub fn wrapping_pow(self, exp: u32) -> Self {
- let (result, _) = self.overflowing_pow(exp);
- result
+ self.overflowing_pow(exp).0
+ }
+
+ /// Shifts self left by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean indicating whether the
+ /// shift value was larger than or equal to the number of bits.
+ #[inline(always)]
+ #[must_use]
+ pub fn overflowing_shl(self, rhs: usize) -> (Self, bool) {
+ if rhs >= 256 {
+ (Self::zero(), true)
+ } else {
+ (Self(self.0 << rhs), false)
+ }
+ }
+
+ /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger than or
+ /// equal to the number of bits in `self`.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_shl(self, rhs: usize) -> Option {
+ match self.overflowing_shl(rhs) {
+ (value, false) => Some(value),
+ _ => None,
+ }
+ }
+
+ /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes
+ /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type.
+ ///
+ /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is
+ /// restricted to the range of the type, rather than the bits shifted out of the LHS being
+ /// returned to the other end. The primitive integer types all implement a
+ /// [`rotate_left`](Self::rotate_left) function, which may be what you want instead.
+ #[inline(always)]
+ #[must_use]
+ pub fn wrapping_shl(self, rhs: usize) -> Self {
+ self.overflowing_shl(rhs).0
+ }
+
+ /// Shifts self right by `rhs` bits.
+ ///
+ /// Returns a tuple of the shifted version of self along with a boolean indicating whether the
+ /// shift value was larger than or equal to the number of bits.
+ #[inline(always)]
+ #[must_use]
+ pub fn overflowing_shr(self, rhs: usize) -> (Self, bool) {
+ if rhs >= 256 {
+ (Self::zero(), true)
+ } else {
+ (Self(self.0 >> rhs), false)
+ }
+ }
+
+ /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is larger than or
+ /// equal to the number of bits in `self`.
+ #[inline(always)]
+ #[must_use]
+ pub fn checked_shr(self, rhs: usize) -> Option {
+ match self.overflowing_shr(rhs) {
+ (value, false) => Some(value),
+ _ => None,
+ }
+ }
+
+ /// Right shift by `rhs` bits.
+ #[inline(always)]
+ #[must_use]
+ pub fn wrapping_shr(self, rhs: usize) -> Self {
+ self.overflowing_shr(rhs).0
}
/// Arithmetic Shift Right operation. Shifts `shift` number of times to the right maintaining
/// the original sign. If the number is positive this is the same as logic shift right.
- pub fn asr(self, shift: u32) -> Self {
+ #[inline(always)]
+ #[must_use]
+ pub fn asr(self, shift: usize) -> Self {
// Avoid shifting if we are going to know the result regardless of the value.
- if shift == 0 {
- self
- } else if shift >= 255u32 {
- match self.sign() {
- // It's always going to be zero (i.e. 00000000...00000000)
- Sign::Positive => Self::zero(),
- // It's always going to be -1 (i.e. 11111111...11111111)
- Sign::Negative => Self::minus_one(),
- }
- } else {
+ match (shift, self.sign()) {
+ (0, _) => self,
+
// Perform the shift.
- match self.sign() {
- Sign::Positive => self >> shift,
+ (1..=254, Sign::Positive) => self >> shift,
+ (1..=254, Sign::Negative) => {
// We need to do: `for 0..shift { self >> 1 | 2^255 }`
// We can avoid the loop by doing: `self >> shift | ~(2^(255 - shift) - 1)`
// where '~' represents ones complement
- Sign::Negative => {
- let bitwise_or =
- Self::from_raw(!U256::from(2u8).pow(U256::from(255u32 - shift)).sub(1u8));
- (self >> shift) | bitwise_or
- }
+ const TWO: U256 = U256([2, 0, 0, 0]);
+ let bitwise_or = Self::from_raw(!(TWO.pow(U256::from(255 - shift)) - U256::one()));
+ (self >> shift) | bitwise_or
}
+
+ // It's always going to be zero (i.e. 00000000...00000000)
+ (255.., Sign::Positive) => Self::zero(),
+ // It's always going to be -1 (i.e. 11111111...11111111)
+ (255.., Sign::Negative) => Self::minus_one(),
+
+ // Rust cannot prove that the above is exhaustive for usize (works for any other int)
+ _ => unreachable!(),
}
}
@@ -972,7 +1113,9 @@ impl I256 {
/// overflow on the final result.
///
/// Returns `None` if the operation overflowed (most significant bit changes).
- pub fn asl(self, shift: u32) -> Option {
+ #[inline(always)]
+ #[must_use]
+ pub fn asl(self, shift: usize) -> Option {
if shift == 0 {
Some(self)
} else {
@@ -986,51 +1129,116 @@ impl I256 {
}
}
- /// Compute the twos complement of the I256
+ /// Compute the [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) of this number.
+ #[inline(always)]
+ #[must_use]
pub fn twos_complement(self) -> U256 {
+ let abs = self.into_raw();
match self.sign() {
- Sign::Positive => self.into_raw(),
- Sign::Negative => twos_complement(self.into_raw()),
+ Sign::Positive => abs,
+ Sign::Negative => twos_complement(abs),
}
}
}
-macro_rules! impl_from {
- ($( $t:ty ),*) => {
+// conversions
+macro_rules! impl_conversions {
+ ($(
+ $u:ty [$actual_low_u:ident -> $low_u:ident, $as_u:ident],
+ $i:ty [$actual_low_i:ident -> $low_i:ident, $as_i:ident];
+ )+) => {
+ // low_*, as_*
+ impl I256 {
+ $(
+ impl_conversions!(@impl_fns $u, $actual_low_u $low_u $as_u);
+ impl_conversions!(@impl_fns $i, $actual_low_i $low_i $as_i);
+ )+
+ }
+
+ // From<$>, TryFrom
$(
- impl From<$t> for I256 {
- fn from(value: $t) -> Self {
- #[allow(unused_comparisons)]
- I256(if value < 0 {
- let abs = (!(value as u128)).wrapping_add(1);
- twos_complement(U256::from(abs))
+ impl From<$u> for I256 {
+ #[inline(always)]
+ fn from(value: $u) -> Self {
+ Self(>::from(value))
+ }
+ }
+
+ impl From<$i> for I256 {
+ #[inline(always)]
+ fn from(value: $i) -> Self {
+ let uint: $u = value as $u;
+ Self(if value.is_negative() {
+ let abs = (!uint).wrapping_add(1);
+ twos_complement(>::from(abs))
} else {
- U256::from(value)
+ >::from(uint)
})
}
}
- impl TryFrom for $t {
+ impl TryFrom for $u {
type Error = TryFromBigIntError;
- fn try_from(value: I256) -> Result {
- if value < I256::from(Self::min_value()) ||
- value > I256::from(Self::max_value()) {
+ #[inline(always)]
+ fn try_from(value: I256) -> Result<$u, Self::Error> {
+ if value.is_negative() || value > I256::from(<$u>::MAX) {
return Err(TryFromBigIntError);
}
- Ok(value.0.low_u128() as _)
+ Ok(value.$actual_low_u() as $u)
}
}
- )*
+
+ impl TryFrom for $i {
+ type Error = TryFromBigIntError;
+
+ #[inline(always)]
+ fn try_from(value: I256) -> Result<$i, Self::Error> {
+ if value < I256::from(<$i>::MIN) || value > I256::from(<$i>::MAX) {
+ return Err(TryFromBigIntError);
+ }
+
+ Ok(value.$actual_low_i() as $i)
+ }
+ }
+ )+
+ };
+
+ (@impl_fns $t:ty, $actual_low:ident $low:ident $as:ident) => {
+ /// Low word.
+ #[inline(always)]
+ pub const fn $low(&self) -> $t {
+ self.0.$actual_low() as $t
+ }
+
+ #[doc = concat!("Conversion to ", stringify!($t) ," with overflow checking.")]
+ ///
+ /// # Panics
+ ///
+ #[doc = concat!("If the number is outside the ", stringify!($t), " valid range.")]
+ #[inline(always)]
+ #[track_caller]
+ pub fn $as(&self) -> $t {
+ <$t as TryFrom>::try_from(*self).unwrap()
+ }
};
}
-impl_from!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
+// Use `U256::low_u64` for types which fit in one word.
+impl_conversions! {
+ u8 [low_u64 -> low_u8, as_u8], i8 [low_u64 -> low_i8, as_i8];
+ u16 [low_u64 -> low_u16, as_u16], i16 [low_u64 -> low_i16, as_i16];
+ u32 [low_u64 -> low_u32, as_u32], i32 [low_u64 -> low_i32, as_i32];
+ u64 [low_u64 -> low_u64, as_u64], i64 [low_u64 -> low_i64, as_i64];
+ usize[low_u64 -> low_usize, as_usize], isize[low_u64 -> low_isize, as_isize];
+ u128 [low_u128 -> low_u128, as_u128], i128 [low_u128 -> low_i128, as_i128];
+}
impl TryFrom for I256 {
type Error = TryFromBigIntError;
+ #[inline(always)]
fn try_from(from: U256) -> Result {
let value = I256(from);
match value.sign() {
@@ -1043,6 +1251,7 @@ impl TryFrom for I256 {
impl TryFrom for U256 {
type Error = TryFromBigIntError;
+ #[inline(always)]
fn try_from(value: I256) -> Result {
match value.sign() {
Sign::Positive => Ok(value.0),
@@ -1052,6 +1261,7 @@ impl TryFrom for U256 {
}
impl From for I256 {
+ #[inline(always)]
fn from(n: ParseUnits) -> Self {
match n {
ParseUnits::U256(n) => Self::from_raw(n),
@@ -1060,34 +1270,53 @@ impl From for I256 {
}
}
-impl str::FromStr for I256 {
+impl TryFrom<&str> for I256 {
+ type Error = ::Err;
+
+ #[inline(always)]
+ fn try_from(value: &str) -> Result {
+ Self::from_str(value)
+ }
+}
+
+impl TryFrom<&String> for I256 {
+ type Error = ::Err;
+
+ #[inline(always)]
+ fn try_from(value: &String) -> Result {
+ Self::from_str(value.as_str())
+ }
+}
+
+impl TryFrom for I256 {
+ type Error = ::Err;
+
+ #[inline(always)]
+ fn try_from(value: String) -> Result {
+ Self::from_str(value.as_str())
+ }
+}
+
+impl FromStr for I256 {
type Err = ParseI256Error;
+ #[inline(always)]
fn from_str(value: &str) -> Result {
I256::from_hex_str(value)
}
}
+// formatting
impl fmt::Debug for I256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
-impl fmt::Display for Sign {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match (self, f.sign_plus()) {
- (Sign::Positive, false) => Ok(()),
- (Sign::Positive, true) => write!(f, "+"),
- (Sign::Negative, _) => write!(f, "-"),
- }
- }
-}
-
impl fmt::Display for I256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (sign, abs) = self.into_sign_and_abs();
- sign.fmt(f)?;
+ fmt::Display::fmt(&sign, f)?;
write!(f, "{abs}")
}
}
@@ -1108,17 +1337,20 @@ impl fmt::UpperHex for I256 {
// NOTE: Work around `U256: !UpperHex`.
let mut buffer = format!("{abs:x}");
buffer.make_ascii_uppercase();
- write!(f, "{buffer}")
+ f.write_str(&buffer)
}
}
+// cmp
impl cmp::PartialOrd for I256 {
+ #[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option {
Some(self.cmp(other))
}
}
impl cmp::Ord for I256 {
+ #[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
// TODO(nlordell): Once subtraction is implemented:
// self.saturating_sub(*other).signum64().partial_cmp(&0)
@@ -1135,31 +1367,119 @@ impl cmp::Ord for I256 {
}
}
-impl ops::Neg for I256 {
- type Output = I256;
+// arithmetic ops - implemented above
+impl> ops::Add for I256 {
+ type Output = Self;
- fn neg(self) -> Self::Output {
- handle_overflow(self.overflowing_neg())
+ #[track_caller]
+ fn add(self, rhs: T) -> Self::Output {
+ handle_overflow(self.overflowing_add(rhs.into()))
}
}
-impl ops::Not for I256 {
- type Output = I256;
-
- fn not(self) -> Self::Output {
- I256(!self.0)
+impl> ops::AddAssign for I256 {
+ #[track_caller]
+ fn add_assign(&mut self, rhs: T) {
+ *self = *self + rhs
}
}
+impl> ops::Sub for I256 {
+ type Output = Self;
+
+ #[track_caller]
+ fn sub(self, rhs: T) -> Self::Output {
+ handle_overflow(self.overflowing_sub(rhs.into()))
+ }
+}
+
+impl> ops::SubAssign for I256 {
+ #[track_caller]
+ fn sub_assign(&mut self, rhs: T) {
+ *self = *self - rhs;
+ }
+}
+
+impl> ops::Mul for I256 {
+ type Output = Self;
+
+ #[track_caller]
+ fn mul(self, rhs: T) -> Self::Output {
+ handle_overflow(self.overflowing_mul(rhs.into()))
+ }
+}
+
+impl> ops::MulAssign for I256 {
+ #[track_caller]
+ fn mul_assign(&mut self, rhs: T) {
+ *self = *self * rhs;
+ }
+}
+
+impl> ops::Div for I256 {
+ type Output = Self;
+
+ #[track_caller]
+ fn div(self, rhs: T) -> Self::Output {
+ handle_overflow(self.overflowing_div(rhs.into()))
+ }
+}
+
+impl> ops::DivAssign for I256 {
+ #[track_caller]
+ fn div_assign(&mut self, rhs: T) {
+ *self = *self / rhs;
+ }
+}
+
+impl> ops::Rem for I256 {
+ type Output = Self;
+
+ #[track_caller]
+ fn rem(self, rhs: T) -> Self::Output {
+ handle_overflow(self.overflowing_rem(rhs.into()))
+ }
+}
+
+impl> ops::RemAssign for I256 {
+ #[track_caller]
+ fn rem_assign(&mut self, rhs: T) {
+ *self = *self % rhs;
+ }
+}
+
+impl> iter::Sum for I256 {
+ #[track_caller]
+ fn sum(iter: I) -> Self
+ where
+ I: Iterator- ,
+ {
+ iter.fold(I256::zero(), |acc, x| acc + x)
+ }
+}
+
+impl> iter::Product for I256 {
+ #[track_caller]
+ fn product(iter: I) -> Self
+ where
+ I: Iterator
- ,
+ {
+ iter.fold(I256::one(), |acc, x| acc * x)
+ }
+}
+
+// bitwise ops - delegated to U256
impl ops::BitAnd for I256 {
type Output = Self;
+ #[inline(always)]
fn bitand(self, rhs: Self) -> Self::Output {
I256(self.0 & rhs.0)
}
}
impl ops::BitAndAssign for I256 {
+ #[inline(always)]
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs;
}
@@ -1168,12 +1488,14 @@ impl ops::BitAndAssign for I256 {
impl ops::BitOr for I256 {
type Output = Self;
+ #[inline(always)]
fn bitor(self, rhs: Self) -> Self::Output {
I256(self.0 | rhs.0)
}
}
impl ops::BitOrAssign for I256 {
+ #[inline(always)]
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;
}
@@ -1182,161 +1504,117 @@ impl ops::BitOrAssign for I256 {
impl ops::BitXor for I256 {
type Output = Self;
+ #[inline(always)]
fn bitxor(self, rhs: Self) -> Self::Output {
I256(self.0 ^ rhs.0)
}
}
impl ops::BitXorAssign for I256 {
+ #[inline(always)]
fn bitxor_assign(&mut self, rhs: Self) {
*self = *self ^ rhs;
}
}
+// Implement Shl and Shr only for types <= usize, since U256 uses .as_usize() which panics
macro_rules! impl_shift {
- ($( $t:ty $( [ $convert:ident ] )? ),*) => {
+ ($($t:ty),+) => {
+ // We are OK with wrapping behaviour here because it's how Rust behaves with the primitive
+ // integer types.
+
+ // $t <= usize: cast to usize
$(
- impl_shift!(__impl $t $([$convert])*);
- )*
- };
- (__impl $t:ty) => {
- impl_shift!(__impl $t [ from ]);
- };
- (__impl $t:ty [ $convert:ident ]) => {
- impl ops::Shl<$t> for I256 {
- type Output = Self;
+ impl ops::Shl<$t> for I256 {
+ type Output = Self;
- fn shl(self, rhs: $t) -> Self::Output {
- // NOTE: We are OK with wrapping behaviour here, that is how
- // Rust behaves with the primitive integer types.
- I256(self.0 << I256::$convert(rhs).0)
+ #[inline(always)]
+ fn shl(self, rhs: $t) -> Self::Output {
+ self.wrapping_shl(rhs as usize)
+ }
}
- }
- impl ops::ShlAssign<$t> for I256 {
- fn shl_assign(&mut self, rhs: $t) {
- *self = *self << rhs;
+ impl ops::ShlAssign<$t> for I256 {
+ #[inline(always)]
+ fn shl_assign(&mut self, rhs: $t) {
+ *self = *self << rhs;
+ }
}
- }
- /// Implements the logical shift right operation
- impl ops::Shr<$t> for I256 {
- type Output = Self;
+ impl ops::Shr<$t> for I256 {
+ type Output = Self;
- fn shr(self, rhs: $t) -> Self::Output {
- I256(self.0 >> I256::$convert(rhs).0)
+ #[inline(always)]
+ fn shr(self, rhs: $t) -> Self::Output {
+ self.wrapping_shr(rhs as usize)
+ }
}
- }
- impl ops::ShrAssign<$t> for I256 {
- fn shr_assign(&mut self, rhs: $t) {
- *self = *self >> rhs;
+ impl ops::ShrAssign<$t> for I256 {
+ #[inline(always)]
+ fn shr_assign(&mut self, rhs: $t) {
+ *self = *self >> rhs;
+ }
}
- }
+ )+
};
}
-impl_shift!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
-impl_shift!(I256, U256[from_raw]);
+#[cfg(target_pointer_width = "16")]
+impl_shift!(i8, u8, i16, u16, isize, usize);
-impl ops::Add for I256 {
- type Output = Self;
+#[cfg(target_pointer_width = "32")]
+impl_shift!(i8, u8, i16, u16, i32, u32, isize, usize);
- fn add(self, rhs: Self) -> Self::Output {
- handle_overflow(self.overflowing_add(rhs))
+#[cfg(target_pointer_width = "64")]
+impl_shift!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
+
+// unary ops
+impl ops::Neg for I256 {
+ type Output = I256;
+
+ #[inline(always)]
+ #[track_caller]
+ fn neg(self) -> Self::Output {
+ handle_overflow(self.overflowing_neg())
}
}
-impl ops::AddAssign for I256 {
- fn add_assign(&mut self, rhs: Self) {
- *self = *self + rhs
- }
-}
+impl ops::Not for I256 {
+ type Output = I256;
-impl ops::Sub for I256 {
- type Output = Self;
-
- fn sub(self, rhs: Self) -> Self::Output {
- handle_overflow(self.overflowing_sub(rhs))
- }
-}
-
-impl ops::SubAssign for I256 {
- fn sub_assign(&mut self, rhs: Self) {
- *self = *self - rhs;
- }
-}
-
-impl ops::Mul for I256 {
- type Output = Self;
-
- fn mul(self, rhs: Self) -> Self::Output {
- handle_overflow(self.overflowing_mul(rhs))
- }
-}
-
-impl ops::MulAssign for I256 {
- fn mul_assign(&mut self, rhs: Self) {
- *self = *self * rhs;
- }
-}
-
-impl ops::Div for I256 {
- type Output = Self;
-
- fn div(self, rhs: Self) -> Self::Output {
- handle_overflow(self.overflowing_div(rhs))
- }
-}
-
-impl ops::DivAssign for I256 {
- fn div_assign(&mut self, rhs: Self) {
- *self = *self / rhs;
- }
-}
-
-impl ops::Rem for I256 {
- type Output = Self;
-
- fn rem(self, rhs: Self) -> Self::Output {
- handle_overflow(self.overflowing_rem(rhs))
- }
-}
-
-impl ops::RemAssign for I256 {
- fn rem_assign(&mut self, rhs: Self) {
- *self = *self % rhs;
- }
-}
-
-impl iter::Sum for I256 {
- fn sum(iter: I) -> Self
- where
- I: Iterator
- ,
- {
- iter.fold(I256::zero(), |acc, x| acc + x)
- }
-}
-
-impl iter::Product for I256 {
- fn product(iter: I) -> Self
- where
- I: Iterator
- ,
- {
- iter.fold(I256::one(), |acc, x| acc * x)
+ #[inline(always)]
+ fn not(self) -> Self::Output {
+ I256(!self.0)
}
}
impl Tokenizable for I256 {
+ #[inline(always)]
fn from_token(token: Token) -> Result {
Ok(I256(U256::from_token(token)?))
}
+ #[inline(always)]
fn into_token(self) -> Token {
Token::Int(self.0)
}
}
+/// Compute the two's complement of a U256.
+#[inline(always)]
+fn twos_complement(u: U256) -> U256 {
+ (!u).overflowing_add(U256::one()).0
+}
+
+/// Panic if overflow on debug mode.
+#[inline(always)]
+#[track_caller]
+fn handle_overflow((result, overflow): (I256, bool)) -> I256 {
+ debug_assert!(!overflow, "overflow");
+ result
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -1366,7 +1644,7 @@ mod tests {
}
#[test]
- #[allow(clippy::cognitive_complexity)]
+ // #[allow(clippy::cognitive_complexity)]
fn std_num_conversion() {
let small_positive = I256::from(42);
let small_negative = I256::from(-42);
@@ -1592,71 +1870,71 @@ mod tests {
#[test]
fn arithmetic_shift_right() {
let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
- let expected_result = I256::from_raw(U256::MAX.sub(1u8));
- assert_eq!(value.asr(253u32), expected_result, "1011...1111 >> 253 was not 1111...1110");
+ let expected_result = I256::from_raw(U256::MAX - U256::one());
+ assert_eq!(value.asr(253), expected_result, "1011...1111 >> 253 was not 1111...1110");
let value = I256::minus_one();
let expected_result = I256::minus_one();
- assert_eq!(value.asr(250u32), expected_result, "-1 >> any_amount was not -1");
+ assert_eq!(value.asr(250), expected_result, "-1 >> any_amount was not -1");
let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
let expected_result = I256::minus_one();
- assert_eq!(value.asr(255u32), expected_result, "1011...1111 >> 255 was not -1");
+ assert_eq!(value.asr(255), expected_result, "1011...1111 >> 255 was not -1");
let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
let expected_result = I256::minus_one();
- assert_eq!(value.asr(1024u32), expected_result, "1011...1111 >> 1024 was not -1");
+ assert_eq!(value.asr(1024), expected_result, "1011...1111 >> 1024 was not -1");
let value = I256::from(1024i32);
let expected_result = I256::from(32i32);
- assert_eq!(value.asr(5u32), expected_result, "1024 >> 5 was not 32");
+ assert_eq!(value.asr(5), expected_result, "1024 >> 5 was not 32");
let value = I256::MAX;
let expected_result = I256::zero();
- assert_eq!(value.asr(255u32), expected_result, "I256::MAX >> 255 was not 0");
+ assert_eq!(value.asr(255), expected_result, "I256::MAX >> 255 was not 0");
let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
let expected_result = value;
- assert_eq!(value.asr(0u32), expected_result, "1011...1111 >> 0 was not 1011...111");
+ assert_eq!(value.asr(0), expected_result, "1011...1111 >> 0 was not 1011...111");
}
#[test]
fn arithmetic_shift_left() {
let value = I256::minus_one();
let expected_result = Some(value);
- assert_eq!(value.asl(0u32), expected_result, "-1 << 0 was not -1");
+ assert_eq!(value.asl(0), expected_result, "-1 << 0 was not -1");
let value = I256::minus_one();
let expected_result = None;
assert_eq!(
- value.asl(256u32),
+ value.asl(256),
expected_result,
"-1 << 256 did not overflow (result should be 0000...0000)"
);
let value = I256::minus_one();
let expected_result = Some(I256::from_raw(U256::from(2u8).pow(U256::from(255u8))));
- assert_eq!(value.asl(255u32), expected_result, "-1 << 255 was not 1000...0000");
+ assert_eq!(value.asl(255), expected_result, "-1 << 255 was not 1000...0000");
let value = I256::from(-1024i32);
let expected_result = Some(I256::from(-32768i32));
- assert_eq!(value.asl(5u32), expected_result, "-1024 << 5 was not -32768");
+ assert_eq!(value.asl(5), expected_result, "-1024 << 5 was not -32768");
let value = I256::from(1024i32);
let expected_result = Some(I256::from(32768i32));
- assert_eq!(value.asl(5u32), expected_result, "1024 << 5 was not 32768");
+ assert_eq!(value.asl(5), expected_result, "1024 << 5 was not 32768");
let value = I256::from(1024i32);
let expected_result = None;
assert_eq!(
- value.asl(245u32),
+ value.asl(245),
expected_result,
"1024 << 245 did not overflow (result should be 1000...0000)"
);
let value = I256::zero();
let expected_result = Some(value);
- assert_eq!(value.asl(1024u32), expected_result, "0 << anything was not 0");
+ assert_eq!(value.asl(1024), expected_result, "0 << anything was not 0");
}
#[test]
@@ -1677,7 +1955,6 @@ mod tests {
}
#[test]
- #[allow(clippy::eq_op)]
fn subtraction() {
assert_eq!(I256::MIN.overflowing_sub(I256::MAX), (I256::one(), true));
assert_eq!(I256::MAX.overflowing_sub(I256::MIN), (I256::minus_one(), true));
@@ -1748,16 +2025,16 @@ mod tests {
let b = I256::from(4);
assert_eq!(a.div_euclid(b), I256::one()); // 7 >= 4 * 1
- assert_eq!(a.div_euclid(-b), -I256::one()); // 7 >= -4 * -1
+ assert_eq!(a.div_euclid(-b), I256::minus_one()); // 7 >= -4 * -1
assert_eq!((-a).div_euclid(b), -I256::from(2)); // -7 >= 4 * -2
assert_eq!((-a).div_euclid(-b), I256::from(2)); // -7 >= -4 * 2
// Overflowing
- assert_eq!(I256::MIN.overflowing_div_euclid(-I256::one()), (I256::MIN, true));
+ assert_eq!(I256::MIN.overflowing_div_euclid(I256::minus_one()), (I256::MIN, true));
// Wrapping
- assert_eq!(I256::MIN.wrapping_div_euclid(-I256::one()), I256::MIN);
+ assert_eq!(I256::MIN.wrapping_div_euclid(I256::minus_one()), I256::MIN);
// // Checked
- assert_eq!(I256::MIN.checked_div_euclid(-I256::one()), None);
+ assert_eq!(I256::MIN.checked_div_euclid(I256::minus_one()), None);
assert_eq!(I256::one().checked_div_euclid(I256::zero()), None);
}
@@ -1773,29 +2050,32 @@ mod tests {
// Overflowing
assert_eq!(a.overflowing_rem_euclid(b), (I256::from(3), false));
- assert_eq!(I256::min_value().overflowing_rem_euclid(-I256::one()), (I256::zero(), true));
+ assert_eq!(
+ I256::min_value().overflowing_rem_euclid(I256::minus_one()),
+ (I256::zero(), true)
+ );
// Wrapping
assert_eq!(I256::from(100).wrapping_rem_euclid(I256::from(10)), I256::zero());
- assert_eq!(I256::min_value().wrapping_rem_euclid(-I256::one()), I256::zero());
+ assert_eq!(I256::min_value().wrapping_rem_euclid(I256::minus_one()), I256::zero());
// Checked
assert_eq!(a.checked_rem_euclid(b), Some(I256::from(3)));
assert_eq!(a.checked_rem_euclid(I256::zero()), None);
- assert_eq!(I256::min_value().checked_rem_euclid(-I256::one()), None);
+ assert_eq!(I256::min_value().checked_rem_euclid(I256::minus_one()), None);
}
#[test]
#[should_panic]
fn div_euclid_by_zero() {
let _ = I256::one().div_euclid(I256::zero());
- assert_eq!(I256::MIN.div_euclid(-I256::one()), I256::MAX);
+ assert_eq!(I256::MIN.div_euclid(I256::minus_one()), I256::MAX);
}
#[test]
- #[cfg_attr(debug_assertions, should_panic)]
+ #[should_panic]
fn div_euclid_overflow() {
- let _ = I256::MIN.div_euclid(-I256::one());
+ let _ = I256::MIN.div_euclid(I256::minus_one());
}
#[test]
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 6d29d9d0..542e543f 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -547,7 +547,7 @@ fn estimate_priority_fee(rewards: Vec>) -> U256 {
.map(|(a, b)| {
let a = I256::try_from(*a).expect("priority fee overflow");
let b = I256::try_from(*b).expect("priority fee overflow");
- ((b - a) * 100.into()) / a
+ ((b - a) * 100) / a
})
.collect();
percentage_change.pop();