use crate::{LIMBS, U256};
use core::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor,
BitXorAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl,
ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
#[inline(always)]
const fn mac(acc: u32, a: u32, b: u32, carry: u32) -> (u32, u32) {
let t = (a as u64) * (b as u64) + (acc as u64) + (carry as u64);
(t as u32, (t >> 32) as u32)
}
impl U256 {
#[inline]
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let mut out = [0u32; LIMBS];
let mut carry = 0u32;
let mut i = 0;
while i < LIMBS {
let t = (self.limbs[i] as u64)
+ (rhs.limbs[i] as u64)
+ (carry as u64);
out[i] = t as u32;
carry = (t >> 32) as u32;
i += 1;
}
(Self::from_limbs(out), carry != 0)
}
#[inline]
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let mut out = [0u32; LIMBS];
let mut borrow = 0u32;
let mut i = 0;
while i < LIMBS {
let t = (self.limbs[i] as u64)
.wrapping_sub(rhs.limbs[i] as u64)
.wrapping_sub(borrow as u64);
out[i] = t as u32;
borrow = ((t >> 32) & 1) as u32;
i += 1;
}
(Self::from_limbs(out), borrow != 0)
}
#[inline]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.overflowing_add(rhs) {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.overflowing_sub(rhs) {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn wrapping_add(self, rhs: Self) -> Self {
self.overflowing_add(rhs).0
}
#[inline]
pub const fn wrapping_sub(self, rhs: Self) -> Self {
self.overflowing_sub(rhs).0
}
#[inline]
pub const fn saturating_add(self, rhs: Self) -> Self {
match self.overflowing_add(rhs) {
(v, false) => v,
(_, true) => Self::MAX,
}
}
#[inline]
pub const fn saturating_sub(self, rhs: Self) -> Self {
match self.overflowing_sub(rhs) {
(v, false) => v,
(_, true) => Self::ZERO,
}
}
#[inline]
pub const fn overflowing_neg(self) -> (Self, bool) {
(Self::ZERO.wrapping_sub(self), !self.is_zero())
}
#[inline]
pub const fn checked_neg(self) -> Option<Self> {
match self.overflowing_neg() {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn wrapping_neg(self) -> Self {
Self::ZERO.wrapping_sub(self)
}
#[cfg_attr(target_arch = "riscv32", inline(never))]
pub const fn widening_mul_u128(a: u128, b: u128) -> Self {
let al = [
a as u32,
(a >> 32) as u32,
(a >> 64) as u32,
(a >> 96) as u32,
];
let bl = [
b as u32,
(b >> 32) as u32,
(b >> 64) as u32,
(b >> 96) as u32,
];
let mut out = [0u32; LIMBS];
let mut i = 0;
while i < 4 {
let mut carry = 0u32;
let mut j = 0;
while j < 4 {
let (lo, hi) = mac(out[i + j], al[i], bl[j], carry);
out[i + j] = lo;
carry = hi;
j += 1;
}
out[i + 4] = carry;
i += 1;
}
Self::from_limbs(out)
}
#[cfg_attr(target_arch = "riscv32", inline(never))]
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
let mut out = [0u32; 2 * LIMBS];
let mut i = 0;
while i < LIMBS {
let mut carry = 0u32;
let mut j = 0;
while j < LIMBS {
let (lo, hi) =
mac(out[i + j], self.limbs[i], rhs.limbs[j], carry);
out[i + j] = lo;
carry = hi;
j += 1;
}
out[i + LIMBS] = carry;
i += 1;
}
let mut lo = [0u32; LIMBS];
let mut hi = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
lo[i] = out[i];
hi[i] = out[i + LIMBS];
i += 1;
}
(Self::from_limbs(lo), Self::from_limbs(hi))
}
#[inline]
pub const fn overflowing_mul_u32(self, rhs: u32) -> (Self, bool) {
let mut out = [0u32; LIMBS];
let mut carry = 0u32;
let mut i = 0;
while i < LIMBS {
let (lo, hi) = mac(0, self.limbs[i], rhs, carry);
out[i] = lo;
carry = hi;
i += 1;
}
(Self::from_limbs(out), carry != 0)
}
#[cfg_attr(target_arch = "riscv32", inline(never))]
pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let mut out = [0u32; LIMBS];
let mut overflow = false;
let mut i = 0;
while i < LIMBS {
let mut carry = 0u32;
let mut j = 0;
while j < LIMBS - i {
let (lo, hi) =
mac(out[i + j], self.limbs[i], rhs.limbs[j], carry);
out[i + j] = lo;
carry = hi;
j += 1;
}
if carry != 0 {
overflow = true;
}
while j < LIMBS {
if self.limbs[i] != 0 && rhs.limbs[j] != 0 {
overflow = true;
}
j += 1;
}
i += 1;
}
(Self::from_limbs(out), overflow)
}
#[inline]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
match self.overflowing_mul(rhs) {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
self.overflowing_mul(rhs).0
}
#[inline]
pub const fn saturating_mul(self, rhs: Self) -> Self {
match self.overflowing_mul(rhs) {
(v, false) => v,
(_, true) => Self::MAX,
}
}
#[inline]
pub const fn const_bitand(self, rhs: Self) -> Self {
let mut out = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
out[i] = self.limbs[i] & rhs.limbs[i];
i += 1;
}
Self::from_limbs(out)
}
#[inline]
pub const fn const_bitor(self, rhs: Self) -> Self {
let mut out = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
out[i] = self.limbs[i] | rhs.limbs[i];
i += 1;
}
Self::from_limbs(out)
}
#[inline]
pub const fn const_bitxor(self, rhs: Self) -> Self {
let mut out = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
out[i] = self.limbs[i] ^ rhs.limbs[i];
i += 1;
}
Self::from_limbs(out)
}
#[inline]
pub const fn const_not(self) -> Self {
let mut out = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
out[i] = !self.limbs[i];
i += 1;
}
Self::from_limbs(out)
}
#[inline]
pub const fn overflowing_shl(self, n: u32) -> (Self, bool) {
(self.wrapping_shl(n), n >= Self::BITS)
}
#[inline]
pub const fn wrapping_shl(self, n: u32) -> Self {
let n = n & (Self::BITS - 1);
let limb_shift = (n / 32) as usize;
let bit_shift = n % 32;
let mut out = [0u32; LIMBS];
let mut i = LIMBS;
while i > limb_shift {
i -= 1;
let src = i - limb_shift;
let mut v = self.limbs[src] << bit_shift;
if bit_shift > 0 && src > 0 {
v |= self.limbs[src - 1] >> (32 - bit_shift);
}
out[i] = v;
}
Self::from_limbs(out)
}
#[inline]
pub const fn checked_shl(self, n: u32) -> Option<Self> {
if n >= Self::BITS {
None
} else {
Some(self.wrapping_shl(n))
}
}
#[inline]
pub const fn overflowing_shr(self, n: u32) -> (Self, bool) {
(self.wrapping_shr(n), n >= Self::BITS)
}
#[inline]
pub const fn wrapping_shr(self, n: u32) -> Self {
let n = n & (Self::BITS - 1);
let limb_shift = (n / 32) as usize;
let bit_shift = n % 32;
let mut out = [0u32; LIMBS];
let mut i = 0;
while i + limb_shift < LIMBS {
let src = i + limb_shift;
let mut v = self.limbs[src] >> bit_shift;
if bit_shift > 0 && src + 1 < LIMBS {
v |= self.limbs[src + 1] << (32 - bit_shift);
}
out[i] = v;
i += 1;
}
Self::from_limbs(out)
}
#[inline]
pub const fn checked_shr(self, n: u32) -> Option<Self> {
if n >= Self::BITS {
None
} else {
Some(self.wrapping_shr(n))
}
}
}
#[cfg(feature = "nightly")]
pub(crate) const OVERFLOW_CHECKS: bool = cfg!(overflow_checks);
#[cfg(not(feature = "nightly"))]
pub(crate) const OVERFLOW_CHECKS: bool = true;
impl Add for U256 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
let (v, overflow) = self.overflowing_add(rhs);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to add with overflow");
}
v
}
}
impl Sub for U256 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
let (v, overflow) = self.overflowing_sub(rhs);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to subtract with overflow");
}
v
}
}
impl Mul for U256 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let (v, overflow) = self.overflowing_mul(rhs);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to multiply with overflow");
}
v
}
}
impl Mul<u32> for U256 {
type Output = Self;
#[inline]
fn mul(self, rhs: u32) -> Self {
let (v, overflow) = self.overflowing_mul_u32(rhs);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to multiply with overflow");
}
v
}
}
impl Div for U256 {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_rem(rhs).0
}
}
impl Rem for U256 {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
self.div_rem(rhs).1
}
}
#[inline]
pub(crate) const fn shift_bits(wide: u128) -> (u32, bool) {
((wide as u32) & (U256::BITS - 1), wide >= U256::BITS as u128)
}
macro_rules! impl_assign {
($trait:ident, $method:ident, $base:ident, $base_method:ident, $rhs:ty) => {
impl $trait<$rhs> for U256 {
#[inline]
fn $method(&mut self, rhs: $rhs) {
*self = <Self as $base<$rhs>>::$base_method(*self, rhs);
}
}
};
}
macro_rules! impl_shift {
($($t:ty),* $(,)?) => {
$(
impl Shl<$t> for U256 {
type Output = Self;
#[inline]
fn shl(self, rhs: $t) -> Self {
let (bits, overflow) = shift_bits(rhs as u128);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to shift left with overflow");
}
self.wrapping_shl(bits)
}
}
impl Shr<$t> for U256 {
type Output = Self;
#[inline]
fn shr(self, rhs: $t) -> Self {
let (bits, overflow) = shift_bits(rhs as u128);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to shift right with overflow");
}
self.wrapping_shr(bits)
}
}
impl_assign!(ShlAssign, shl_assign, Shl, shl, $t);
impl_assign!(ShrAssign, shr_assign, Shr, shr, $t);
)*
};
}
impl_shift!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
impl Not for U256 {
type Output = Self;
#[inline]
fn not(self) -> Self {
self.const_not()
}
}
macro_rules! impl_bitop {
($trait:ident, $method:ident, $const_method:ident) => {
impl $trait for U256 {
type Output = Self;
#[inline]
fn $method(self, rhs: Self) -> Self {
self.$const_method(rhs)
}
}
};
}
impl_bitop!(BitAnd, bitand, const_bitand);
impl_bitop!(BitOr, bitor, const_bitor);
impl_bitop!(BitXor, bitxor, const_bitxor);
impl_assign!(AddAssign, add_assign, Add, add, U256);
impl_assign!(SubAssign, sub_assign, Sub, sub, U256);
impl_assign!(MulAssign, mul_assign, Mul, mul, U256);
impl_assign!(MulAssign, mul_assign, Mul, mul, u32);
impl_assign!(DivAssign, div_assign, Div, div, U256);
impl_assign!(RemAssign, rem_assign, Rem, rem, U256);
impl_assign!(BitAndAssign, bitand_assign, BitAnd, bitand, U256);
impl_assign!(BitOrAssign, bitor_assign, BitOr, bitor, U256);
impl_assign!(BitXorAssign, bitxor_assign, BitXor, bitxor, U256);
#[cfg(test)]
mod tests {
use super::shift_bits;
#[test]
fn out_of_range_shift_amounts_mask_rather_than_saturate() {
assert_eq!(shift_bits(0), (0, false));
assert_eq!(shift_bits(255), (255, false));
assert_eq!(shift_bits(256), (0, true));
assert_eq!(shift_bits(300), (44, true));
assert_eq!(shift_bits(1u128 << 32), (0, true));
assert_eq!(shift_bits((1u128 << 32) + 7), (7, true));
}
#[test]
fn negative_shift_amounts_mask_to_their_low_bits() {
assert_eq!(shift_bits(-1i32 as u128), (255, true));
assert_eq!(shift_bits(-2i32 as u128), (254, true));
assert_eq!(shift_bits(-256i64 as u128), (0, true));
}
}