use crate::arith::OVERFLOW_CHECKS;
use crate::{I256, U256};
use core::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor,
BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign,
Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
impl I256 {
#[inline]
pub const fn overflowing_neg(self) -> (Self, bool) {
(
self.cast_unsigned().wrapping_neg().cast_signed(),
self.const_eq(Self::MIN),
)
}
#[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.overflowing_neg().0
}
#[inline]
pub const fn saturating_neg(self) -> Self {
match self.overflowing_neg() {
(v, false) => v,
(_, true) => Self::MAX,
}
}
#[inline]
pub const fn overflowing_abs(self) -> (Self, bool) {
if self.is_negative() {
self.overflowing_neg()
} else {
(self, false)
}
}
#[inline]
pub const fn checked_abs(self) -> Option<Self> {
match self.overflowing_abs() {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn wrapping_abs(self) -> Self {
self.overflowing_abs().0
}
#[inline]
pub const fn saturating_abs(self) -> Self {
match self.overflowing_abs() {
(v, false) => v,
(_, true) => Self::MAX,
}
}
#[inline]
pub const fn abs(self) -> Self {
let (v, overflow) = self.overflowing_abs();
if OVERFLOW_CHECKS && overflow {
panic!("attempt to negate with overflow");
}
v
}
#[inline]
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let v = self
.cast_unsigned()
.wrapping_add(rhs.cast_unsigned())
.cast_signed();
let overflow = self.is_negative() == rhs.is_negative()
&& v.is_negative() != self.is_negative();
(v, overflow)
}
#[inline]
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let v = self
.cast_unsigned()
.wrapping_sub(rhs.cast_unsigned())
.cast_signed();
let overflow = self.is_negative() != rhs.is_negative()
&& v.is_negative() != self.is_negative();
(v, overflow)
}
#[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.saturation_bound(),
}
}
#[inline]
pub const fn saturating_sub(self, rhs: Self) -> Self {
match self.overflowing_sub(rhs) {
(v, false) => v,
(_, true) => self.saturation_bound(),
}
}
#[inline]
const fn saturation_bound(self) -> Self {
if self.is_negative() {
Self::MIN
} else {
Self::MAX
}
}
#[inline]
pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let (mag, wide) =
self.unsigned_abs().overflowing_mul(rhs.unsigned_abs());
let negative = self.is_negative() != rhs.is_negative();
let v = if negative { mag.wrapping_neg() } else { mag };
let limit = if negative {
U256::ONE.wrapping_shl(255)
} else {
Self::MAX.cast_unsigned()
};
(v.cast_signed(), wide || mag.const_gt(limit))
}
#[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) => {
if self.is_negative() == rhs.is_negative() {
Self::MAX
} else {
Self::MIN
}
}
}
}
#[inline]
pub const fn checked_div_rem(self, rhs: Self) -> Option<(Self, Self)> {
if rhs.is_zero()
|| (self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE))
{
return None;
}
Some(self.div_rem_unchecked(rhs))
}
#[inline]
const fn div_rem_unchecked(self, rhs: Self) -> (Self, Self) {
let (q, r) =
match self.unsigned_abs().checked_div_rem(rhs.unsigned_abs()) {
Some(v) => v,
None => panic!("attempt to divide by zero"),
};
let q = if self.is_negative() != rhs.is_negative() {
q.wrapping_neg()
} else {
q
};
let r = if self.is_negative() {
r.wrapping_neg()
} else {
r
};
(q.cast_signed(), r.cast_signed())
}
#[inline]
pub const fn div_rem(self, rhs: Self) -> (Self, Self) {
if rhs.is_zero() {
panic!("attempt to divide by zero");
}
if self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE) {
panic!("attempt to divide with overflow");
}
self.div_rem_unchecked(rhs)
}
#[inline]
pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) {
if self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE) {
return (Self::MIN, true);
}
(self.wrapping_div(rhs), false)
}
#[inline]
pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
if self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE) {
return (Self::ZERO, true);
}
(self.wrapping_rem(rhs), false)
}
#[inline]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
match self.checked_div_rem(rhs) {
Some((q, _)) => Some(q),
None => None,
}
}
#[inline]
pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
match self.checked_div_rem(rhs) {
Some((_, r)) => Some(r),
None => None,
}
}
#[inline]
pub const fn wrapping_div(self, rhs: Self) -> Self {
if self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE) {
return Self::MIN;
}
self.div_rem_unchecked(rhs).0
}
#[inline]
pub const fn wrapping_rem(self, rhs: Self) -> Self {
if self.const_eq(Self::MIN) && rhs.const_eq(Self::MINUS_ONE) {
return Self::ZERO;
}
self.div_rem_unchecked(rhs).1
}
#[inline]
pub const fn div_euclid(self, rhs: Self) -> Self {
let (q, r) = self.div_rem(rhs);
if r.is_negative() {
if rhs.is_negative() {
return q.wrapping_add(Self::ONE);
}
return q.wrapping_sub(Self::ONE);
}
q
}
#[inline]
pub const fn rem_euclid(self, rhs: Self) -> Self {
let (_, r) = self.div_rem(rhs);
if r.is_negative() {
return r.wrapping_add(rhs.unsigned_abs().cast_signed());
}
r
}
#[inline]
pub const fn wrapping_shl(self, n: u32) -> Self {
self.cast_unsigned().wrapping_shl(n).cast_signed()
}
#[inline]
pub const fn wrapping_shr(self, n: u32) -> Self {
let n = n & (Self::BITS - 1);
let shifted = self.cast_unsigned().wrapping_shr(n);
if self.is_negative() {
let sign_fill = U256::MAX.wrapping_shr(n).const_not();
shifted.const_bitor(sign_fill).cast_signed()
} else {
shifted.cast_signed()
}
}
#[inline]
pub const fn overflowing_shl(self, n: u32) -> (Self, bool) {
(self.wrapping_shl(n), n >= Self::BITS)
}
#[inline]
pub const fn overflowing_shr(self, n: u32) -> (Self, bool) {
(self.wrapping_shr(n), n >= Self::BITS)
}
#[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 checked_shr(self, n: u32) -> Option<Self> {
if n >= Self::BITS {
None
} else {
Some(self.wrapping_shr(n))
}
}
#[inline]
pub const fn const_bitand(self, rhs: Self) -> Self {
self.cast_unsigned()
.const_bitand(rhs.cast_unsigned())
.cast_signed()
}
#[inline]
pub const fn const_bitor(self, rhs: Self) -> Self {
self.cast_unsigned()
.const_bitor(rhs.cast_unsigned())
.cast_signed()
}
#[inline]
pub const fn const_bitxor(self, rhs: Self) -> Self {
self.cast_unsigned()
.const_bitxor(rhs.cast_unsigned())
.cast_signed()
}
#[inline]
pub const fn const_not(self) -> Self {
self.cast_unsigned().const_not().cast_signed()
}
pub const fn overflowing_pow(self, exp: u32) -> (Self, bool) {
let mut base = self;
let mut exp = exp;
let mut acc = Self::ONE;
let mut overflow = false;
while exp > 0 {
if exp & 1 == 1 {
let (v, o) = acc.overflowing_mul(base);
acc = v;
overflow |= o;
}
exp >>= 1;
if exp > 0 {
let (v, o) = base.overflowing_mul(base);
base = v;
overflow |= o;
}
}
(acc, overflow)
}
#[inline]
pub const fn checked_pow(self, exp: u32) -> Option<Self> {
match self.overflowing_pow(exp) {
(v, false) => Some(v),
(_, true) => None,
}
}
#[inline]
pub const fn wrapping_pow(self, exp: u32) -> Self {
self.overflowing_pow(exp).0
}
#[inline]
pub const fn saturating_pow(self, exp: u32) -> Self {
match self.overflowing_pow(exp) {
(v, false) => v,
(_, true) => {
if self.is_negative() && exp % 2 == 1 {
Self::MIN
} else {
Self::MAX
}
}
}
}
#[inline]
pub const fn pow(self, exp: u32) -> Self {
let (v, overflow) = self.overflowing_pow(exp);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to multiply with overflow");
}
v
}
}
impl Add for I256 {
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 I256 {
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 I256 {
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 Div for I256 {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_rem(rhs).0
}
}
impl Rem for I256 {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
self.div_rem(rhs).1
}
}
impl Neg for I256 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
let (v, overflow) = self.overflowing_neg();
if OVERFLOW_CHECKS && overflow {
panic!("attempt to negate with overflow");
}
v
}
}
impl Not for I256 {
type Output = Self;
#[inline]
fn not(self) -> Self {
self.const_not()
}
}
macro_rules! impl_assign {
($trait:ident, $method:ident, $base:ident, $base_method:ident, $rhs:ty) => {
impl $trait<$rhs> for I256 {
#[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 I256 {
type Output = Self;
#[inline]
fn shl(self, rhs: $t) -> Self {
let (bits, overflow) = crate::arith::shift_bits(rhs as u128);
if OVERFLOW_CHECKS && overflow {
panic!("attempt to shift left with overflow");
}
self.wrapping_shl(bits)
}
}
impl Shr<$t> for I256 {
type Output = Self;
#[inline]
fn shr(self, rhs: $t) -> Self {
let (bits, overflow) = crate::arith::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
);
macro_rules! impl_bitop {
($trait:ident, $method:ident, $const_method:ident) => {
impl $trait for I256 {
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, I256);
impl_assign!(SubAssign, sub_assign, Sub, sub, I256);
impl_assign!(MulAssign, mul_assign, Mul, mul, I256);
impl_assign!(DivAssign, div_assign, Div, div, I256);
impl_assign!(RemAssign, rem_assign, Rem, rem, I256);
impl_assign!(BitAndAssign, bitand_assign, BitAnd, bitand, I256);
impl_assign!(BitOrAssign, bitor_assign, BitOr, bitor, I256);
impl_assign!(BitXorAssign, bitxor_assign, BitXor, bitxor, I256);