use crate::{LIMBS, TryFromIntError, U256};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct I256 {
pub(crate) limbs: [u32; LIMBS],
}
impl I256 {
pub const ZERO: Self = Self::from_limbs([0; LIMBS]);
pub const ONE: Self = Self::from_limbs([1, 0, 0, 0, 0, 0, 0, 0]);
pub const MINUS_ONE: Self = Self::from_limbs([u32::MAX; LIMBS]);
pub const MAX: Self = U256::MAX.wrapping_shr(1).cast_signed();
pub const MIN: Self = U256::ONE.wrapping_shl(255).cast_signed();
pub const BITS: u32 = 256;
#[inline]
pub const fn from_limbs(limbs: [u32; LIMBS]) -> Self {
Self { limbs }
}
#[inline]
pub const fn to_limbs(self) -> [u32; LIMBS] {
self.limbs
}
#[inline]
pub const fn from(n: i128) -> Self {
Self::from_i128(n)
}
#[inline]
pub const fn from_i128(n: i128) -> Self {
let fill = if n < 0 { u32::MAX } else { 0 };
Self::from_limbs([
n as u32,
(n >> 32) as u32,
(n >> 64) as u32,
(n >> 96) as u32,
fill,
fill,
fill,
fill,
])
}
#[inline]
pub const fn from_i32(n: i32) -> Self {
let fill = if n < 0 { u32::MAX } else { 0 };
Self::from_limbs([n as u32, fill, fill, fill, fill, fill, fill, fill])
}
#[inline]
pub const fn from_i64(n: i64) -> Self {
let fill = if n < 0 { u32::MAX } else { 0 };
Self::from_limbs([
n as u32,
(n >> 32) as u32,
fill,
fill,
fill,
fill,
fill,
fill,
])
}
#[inline]
pub const fn cast_unsigned(self) -> U256 {
U256::from_limbs(self.limbs)
}
#[inline]
pub const fn is_zero(self) -> bool {
self.cast_unsigned().is_zero()
}
#[inline]
pub const fn is_negative(self) -> bool {
self.limbs[LIMBS - 1] >> 31 == 1
}
#[inline]
pub const fn is_positive(self) -> bool {
!self.is_negative() && !self.is_zero()
}
#[inline]
pub const fn signum(self) -> Self {
if self.is_zero() {
Self::ZERO
} else if self.is_negative() {
Self::MINUS_ONE
} else {
Self::ONE
}
}
#[inline]
pub const fn unsigned_abs(self) -> U256 {
if self.is_negative() {
self.cast_unsigned().wrapping_neg()
} else {
self.cast_unsigned()
}
}
#[inline]
pub const fn bit(self, i: u32) -> bool {
self.cast_unsigned().bit(i)
}
#[inline]
pub const fn const_cmp(self, other: Self) -> core::cmp::Ordering {
let flip = U256::ONE.wrapping_shl(255);
self.cast_unsigned()
.const_bitxor(flip)
.const_cmp(other.cast_unsigned().const_bitxor(flip))
}
#[inline]
pub(crate) const fn const_eq(self, other: Self) -> bool {
self.cast_unsigned().const_eq(other.cast_unsigned())
}
#[inline]
pub(crate) const fn const_lt(self, other: Self) -> bool {
matches!(self.const_cmp(other), core::cmp::Ordering::Less)
}
#[inline]
pub(crate) const fn const_gt(self, other: Self) -> bool {
matches!(self.const_cmp(other), core::cmp::Ordering::Greater)
}
#[inline]
pub const fn to_be_bytes(self) -> [u8; 32] {
self.cast_unsigned().to_be_bytes()
}
#[inline]
pub const fn from_be_bytes(bytes: [u8; 32]) -> Self {
U256::from_be_bytes(bytes).cast_signed()
}
}
impl U256 {
#[inline]
pub const fn cast_signed(self) -> I256 {
I256::from_limbs(self.limbs)
}
}
macro_rules! impl_from_signed {
($($t:ty),* $(,)?) => {
$(
impl From<$t> for I256 {
#[inline]
fn from(n: $t) -> Self {
Self::from_i32(n as i32)
}
}
)*
};
}
impl_from_signed!(i8, i16, i32);
impl From<i64> for I256 {
#[inline]
fn from(n: i64) -> Self {
Self::from_i64(n)
}
}
impl From<i128> for I256 {
#[inline]
fn from(n: i128) -> Self {
Self::from_i128(n)
}
}
macro_rules! impl_from_unsigned {
($($t:ty),* $(,)?) => {
$(
impl From<$t> for I256 {
#[inline]
fn from(n: $t) -> Self {
U256::from_u128(n as u128).cast_signed()
}
}
)*
};
}
impl_from_unsigned!(u8, u16, u32, u64, u128);
impl From<bool> for I256 {
#[inline]
fn from(b: bool) -> Self {
if b { Self::ONE } else { Self::ZERO }
}
}
impl TryFrom<U256> for I256 {
type Error = TryFromIntError;
#[inline]
fn try_from(n: U256) -> Result<Self, Self::Error> {
let v = n.cast_signed();
if v.is_negative() {
Err(TryFromIntError(()))
} else {
Ok(v)
}
}
}
impl TryFrom<I256> for U256 {
type Error = TryFromIntError;
#[inline]
fn try_from(n: I256) -> Result<Self, Self::Error> {
if n.is_negative() {
Err(TryFromIntError(()))
} else {
Ok(n.cast_unsigned())
}
}
}
macro_rules! impl_try_into_signed {
($($signed:ty => $unsigned:ty),* $(,)?) => {
$(
impl TryFrom<I256> for $signed {
type Error = TryFromIntError;
#[inline]
fn try_from(n: I256) -> Result<Self, Self::Error> {
let mag = <$unsigned>::try_from(n.unsigned_abs())
.map_err(|_| TryFromIntError(()))?;
if n.is_negative() {
if mag > (1 as $unsigned) << (<$signed>::BITS - 1) {
return Err(TryFromIntError(()));
}
Ok((mag as $signed).wrapping_neg())
} else {
<$signed>::try_from(mag)
.map_err(|_| TryFromIntError(()))
}
}
}
)*
};
}
impl_try_into_signed!(
i8 => u8,
i16 => u16,
i32 => u32,
i64 => u64,
i128 => u128,
);
macro_rules! impl_try_into_unsigned {
($($t:ty),* $(,)?) => {
$(
impl TryFrom<I256> for $t {
type Error = TryFromIntError;
#[inline]
fn try_from(n: I256) -> Result<Self, Self::Error> {
if n.is_negative() {
return Err(TryFromIntError(()));
}
<$t>::try_from(n.cast_unsigned())
.map_err(|_| TryFromIntError(()))
}
}
)*
};
}
impl_try_into_unsigned!(u8, u16, u32, u64, u128);
impl PartialOrd for I256 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for I256 {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.const_cmp(*other)
}
}