#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(feature = "nightly", feature(cfg_overflow_checks))]
mod arith;
mod div;
mod fmt;
mod integration;
mod math;
mod signed;
mod signed_arith;
mod signed_fmt;
mod signed_math;
mod sqrt;
pub use div::{checked_mul_div_u128, mul_div_u128};
pub use math::Rounding;
pub use signed::I256;
#[cfg(feature = "fmt")]
pub use fmt::ParseU256Error;
#[cfg(feature = "fmt")]
pub use signed_fmt::ParseI256Error;
pub(crate) const LIMBS: usize = 8;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct U256 {
pub(crate) limbs: [u32; LIMBS],
}
impl U256 {
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 MAX: Self = Self::from_limbs([u32::MAX; LIMBS]);
pub const BITS: u32 = 256;
#[inline]
pub const fn from_limbs(limbs: [u32; LIMBS]) -> Self {
Self { limbs }
}
#[inline]
pub const fn from(n: u128) -> Self {
Self::from_u128(n)
}
#[inline]
pub const fn from_u128(n: u128) -> Self {
Self::from_limbs([
n as u32,
(n >> 32) as u32,
(n >> 64) as u32,
(n >> 96) as u32,
0,
0,
0,
0,
])
}
#[inline]
pub const fn from_u32(n: u32) -> Self {
Self::from_limbs([n, 0, 0, 0, 0, 0, 0, 0])
}
#[inline]
pub const fn from_u64(n: u64) -> Self {
Self::from_limbs([n as u32, (n >> 32) as u32, 0, 0, 0, 0, 0, 0])
}
#[inline]
pub const fn to_limbs(self) -> [u32; LIMBS] {
self.limbs
}
#[inline]
pub const fn is_zero(self) -> bool {
let mut i = 0;
while i < LIMBS {
if self.limbs[i] != 0 {
return false;
}
i += 1;
}
true
}
#[inline]
pub const fn bit_len(self) -> u32 {
let mut i = LIMBS;
while i > 0 {
i -= 1;
if self.limbs[i] != 0 {
return (i as u32) * 32 + (32 - self.limbs[i].leading_zeros());
}
}
0
}
#[inline]
pub const fn leading_zeros(self) -> u32 {
Self::BITS - self.bit_len()
}
#[inline]
pub(crate) const fn limb_len(self) -> usize {
let mut i = LIMBS;
while i > 0 {
i -= 1;
if self.limbs[i] != 0 {
return i + 1;
}
}
0
}
#[inline]
pub const fn bit(self, i: u32) -> bool {
if i >= Self::BITS {
return false;
}
(self.limbs[(i / 32) as usize] >> (i % 32)) & 1 == 1
}
#[inline]
pub const fn const_cmp(self, other: Self) -> core::cmp::Ordering {
let mut i = LIMBS;
while i > 0 {
i -= 1;
if self.limbs[i] < other.limbs[i] {
return core::cmp::Ordering::Less;
}
if self.limbs[i] > other.limbs[i] {
return core::cmp::Ordering::Greater;
}
}
core::cmp::Ordering::Equal
}
#[inline]
pub(crate) const fn const_eq(self, other: Self) -> bool {
matches!(self.const_cmp(other), core::cmp::Ordering::Equal)
}
#[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(crate) const fn const_ge(self, other: Self) -> bool {
!self.const_lt(other)
}
#[cfg_attr(target_arch = "riscv32", inline(never))]
pub const fn to_be_bytes(self) -> [u8; 32] {
let mut out = [0u8; 32];
let mut i = 0;
while i < LIMBS {
let bytes = self.limbs[LIMBS - 1 - i].to_be_bytes();
out[i * 4] = bytes[0];
out[i * 4 + 1] = bytes[1];
out[i * 4 + 2] = bytes[2];
out[i * 4 + 3] = bytes[3];
i += 1;
}
out
}
#[cfg_attr(target_arch = "riscv32", inline(never))]
pub const fn from_be_bytes(bytes: [u8; 32]) -> Self {
let mut limbs = [0u32; LIMBS];
let mut i = 0;
while i < LIMBS {
limbs[LIMBS - 1 - i] = u32::from_be_bytes([
bytes[i * 4],
bytes[i * 4 + 1],
bytes[i * 4 + 2],
bytes[i * 4 + 3],
]);
i += 1;
}
Self::from_limbs(limbs)
}
}
macro_rules! impl_from_small {
($($t:ty),* $(,)?) => {
$(
impl From<$t> for U256 {
#[inline]
fn from(n: $t) -> Self {
Self::from_u32(n as u32)
}
}
)*
};
}
impl_from_small!(u8, u16, u32);
impl From<u64> for U256 {
#[inline]
fn from(n: u64) -> Self {
Self::from_u64(n)
}
}
impl From<u128> for U256 {
#[inline]
fn from(n: u128) -> Self {
Self::from_u128(n)
}
}
impl From<bool> for U256 {
#[inline]
fn from(b: bool) -> Self {
if b { Self::ONE } else { Self::ZERO }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TryFromIntError(pub(crate) ());
#[cfg(feature = "fmt")]
impl core::fmt::Display for TryFromIntError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("value does not fit in the target type")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromIntError {}
macro_rules! impl_try_into {
($($t:ty => $limbs:expr),* $(,)?) => {
$(
impl TryFrom<U256> for $t {
type Error = TryFromIntError;
#[inline]
fn try_from(n: U256) -> Result<Self, Self::Error> {
if n.limb_len() > $limbs {
return Err(TryFromIntError(()));
}
let mut out: $t = 0;
let mut i = $limbs;
while i > 0 {
i -= 1;
out = (out << 16 << 16) | (n.limbs[i] as $t);
}
Ok(out)
}
}
)*
};
}
impl_try_into!(u32 => 1, u64 => 2, u128 => 4);
macro_rules! impl_try_into_narrow {
($($t:ty),* $(,)?) => {
$(
impl TryFrom<U256> for $t {
type Error = TryFromIntError;
#[inline]
fn try_from(n: U256) -> Result<Self, Self::Error> {
let narrowed = u32::try_from(n)?;
<$t>::try_from(narrowed)
.map_err(|_| TryFromIntError(()))
}
}
)*
};
}
impl_try_into_narrow!(u8, u16);
impl PartialOrd for U256 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for U256 {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let mut i = LIMBS;
while i > 0 {
i -= 1;
if self.limbs[i] != other.limbs[i] {
return self.limbs[i].cmp(&other.limbs[i]);
}
}
core::cmp::Ordering::Equal
}
}