use core::cmp::Ordering;
use core::ops::{Div, Rem};
use primitive_types::U256;
#[allow(clippy::as_conversions)]
pub const USIZE_MAX: U256 = U256([usize::MAX as u64, 0, 0, 0]);
pub const U64_MAX: U256 = U256([u64::MAX, 0, 0, 0]);
pub const U256_ZERO: U256 = U256::zero();
pub const U256_ONE: U256 = U256::one();
pub const U256_VALUE_32: U256 = U256([32, 0, 0, 0]);
pub const U256_VALUE_256: U256 = U256([256, 0, 0, 0]);
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Sign {
Plus,
Minus,
Zero,
}
const SIGN_BIT_MASK: U256 = U256([
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0x7fff_ffff_ffff_ffff,
]);
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct I256(pub Sign, pub U256);
impl I256 {
#[must_use]
pub const fn zero() -> Self {
Self(Sign::Zero, U256_ZERO)
}
#[must_use]
pub fn min_value() -> Self {
Self(Sign::Minus, (U256::MAX & SIGN_BIT_MASK) + U256_ONE)
}
}
impl Ord for I256 {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0, other.0) {
(Sign::Zero, Sign::Zero) => Ordering::Equal,
(Sign::Zero | Sign::Minus, Sign::Plus) | (Sign::Minus, Sign::Zero) => Ordering::Less,
(Sign::Minus, Sign::Minus) => self.1.cmp(&other.1).reverse(),
(Sign::Zero | Sign::Plus, Sign::Minus) | (Sign::Plus, Sign::Zero) => Ordering::Greater,
(Sign::Plus, Sign::Plus) => self.1.cmp(&other.1),
}
}
}
impl PartialOrd for I256 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Default for I256 {
fn default() -> Self {
Self::zero()
}
}
impl From<U256> for I256 {
fn from(val: U256) -> Self {
if val == U256_ZERO {
Self::zero()
} else if val & SIGN_BIT_MASK == val {
Self(Sign::Plus, val)
} else {
Self(Sign::Minus, !val + U256_ONE)
}
}
}
impl From<I256> for U256 {
fn from(value: I256) -> Self {
let sign = value.0;
if sign == Sign::Zero {
Self::zero()
} else if sign == Sign::Plus {
value.1
} else {
!value.1 + Self::from(1u64)
}
}
}
impl Div for I256 {
type Output = Self;
fn div(self, other: Self) -> Self {
if other == Self::zero() {
return Self::zero();
}
if self == Self::min_value() && other.1 == U256_ONE {
return Self::min_value();
}
let d = (self.1 / other.1) & SIGN_BIT_MASK;
if d == U256_ZERO {
return Self::zero();
}
match (self.0, other.0) {
(Sign::Zero | Sign::Plus, Sign::Plus | Sign::Zero) | (Sign::Minus, Sign::Minus) => {
Self(Sign::Plus, d)
}
(Sign::Zero | Sign::Plus, Sign::Minus) | (Sign::Minus, Sign::Zero | Sign::Plus) => {
Self(Sign::Minus, d)
}
}
}
}
impl Rem for I256 {
type Output = Self;
fn rem(self, other: Self) -> Self {
let r = (self.1 % other.1) & SIGN_BIT_MASK;
if r == U256_ZERO {
return Self::zero();
}
Self(self.0, r)
}
}
#[cfg(test)]
mod tests {
use crate::utils::{Sign, I256, U256_ONE};
use primitive_types::U256;
use std::num::Wrapping;
#[test]
fn div_i256() {
assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN));
assert_eq!(100i8 / -1, -100i8);
assert_eq!(100i8 / 2, 50i8);
let one = I256(Sign::Zero, U256_ONE);
let one_hundred = I256(Sign::Zero, U256::from(100));
let fifty = I256(Sign::Plus, U256::from(50));
let two = I256(Sign::Zero, U256::from(2));
let neg_one_hundred = I256(Sign::Minus, U256::from(100));
let minus_one = I256(Sign::Minus, U256::from(1));
let max_value = I256(Sign::Plus, U256::from(2).pow(U256::from(255)) - 1);
let neg_max_value = I256(Sign::Minus, U256::from(2).pow(U256::from(255)) - 1);
assert_eq!(I256::min_value() / minus_one, I256::min_value());
assert_eq!(I256::min_value() / one, I256::min_value());
assert_eq!(max_value / one, max_value);
assert_eq!(max_value / minus_one, neg_max_value);
assert_eq!(one_hundred / minus_one, neg_one_hundred);
assert_eq!(one_hundred / two, fifty);
}
}