#![doc = include_str!("../README.md")]
#![doc(issue_tracker_base_url = "https://github.com/recmo/uint/issues/")]
#![cfg_attr(test, allow(clippy::wildcard_imports, clippy::cognitive_complexity))]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(
feature = "nightly",
feature(core_intrinsics, const_unsigned_bigint_helpers)
)]
#![cfg_attr(
feature = "nightly",
allow(internal_features, clippy::incompatible_msrv)
)]
#![cfg_attr(
feature = "generic_const_exprs",
feature(generic_const_exprs),
allow(incomplete_features)
)]
#[cfg(feature = "alloc")]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
#[macro_use]
mod macros;
mod add;
pub mod algorithms;
pub mod aliases;
mod base_convert;
mod bit_arr;
mod bits;
mod bytes;
mod cmp;
mod const_for;
mod div;
mod fmt;
mod from;
mod gcd;
mod log;
mod modular;
mod mul;
mod pow;
mod root;
mod special;
mod string;
mod utils;
pub mod support;
#[doc(inline)]
pub use bit_arr::Bits;
#[doc(inline)]
pub use self::{
base_convert::BaseConvertError,
bytes::nbytes,
from::{FromUintError, ToFieldError, ToUintError, UintTryFrom, UintTryTo},
string::ParseError,
};
#[cfg(doc)]
#[doc(inline)]
pub use ruint_macro::uint;
#[cfg(feature = "generic_const_exprs")]
pub mod nightly {
pub type Uint<const BITS: usize> = crate::Uint<BITS, { crate::nlimbs(BITS) }>;
pub type Bits<const BITS: usize> = crate::Bits<BITS, { crate::nlimbs(BITS) }>;
}
#[derive(Clone, Copy)]
#[allow(non_camel_case_types)]
pub(crate) struct pu128 {
inner: [u64; 2],
}
impl pu128 {
#[inline]
pub(crate) const fn get(self) -> u128 {
let arr = self.inner;
#[cfg(target_endian = "little")]
{
unsafe { core::mem::transmute(arr) }
}
#[cfg(target_endian = "big")]
{
arr[0] as u128 | (arr[1] as u128) << 64
}
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct Uint<const BITS: usize, const LIMBS: usize> {
limbs: [u64; LIMBS],
}
impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
pub const LIMBS: usize = {
let limbs = nlimbs(BITS);
assert!(
LIMBS == limbs,
"Can not construct Uint<BITS, LIMBS> with incorrect LIMBS"
);
limbs
};
pub const MASK: u64 = mask(BITS);
const SHOULD_MASK: bool = BITS > 0 && Self::MASK != u64::MAX;
pub const BITS: usize = BITS;
pub const ZERO: Self = Self::from_limbs([0; LIMBS]);
pub const ONE: Self = Self::const_from_u64(1);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Self::from_limbs_unmasked([u64::MAX; LIMBS]);
#[inline(always)]
#[must_use]
pub const fn as_limbs(&self) -> &[u64; LIMBS] {
&self.limbs
}
#[inline(always)]
#[must_use]
pub const unsafe fn as_limbs_mut(&mut self) -> &mut [u64; LIMBS] {
&mut self.limbs
}
#[inline(always)]
#[must_use]
pub const fn into_limbs(self) -> [u64; LIMBS] {
self.limbs
}
#[inline]
pub(crate) const fn as_double_words(&self) -> &[pu128] {
assert!(LIMBS >= 2);
let (ptr, len) = (self.limbs.as_ptr(), self.limbs.len());
unsafe { core::slice::from_raw_parts(ptr.cast(), len / 2) }
}
#[inline(always)]
#[must_use]
#[track_caller]
pub const fn from_limbs(limbs: [u64; LIMBS]) -> Self {
if Self::SHOULD_MASK {
assert!(
limbs[LIMBS - 1] <= Self::MASK,
"Value too large for this Uint"
);
}
let _ = Self::LIMBS; Self { limbs }
}
#[inline(always)]
#[must_use]
const fn from_limbs_unmasked(limbs: [u64; LIMBS]) -> Self {
let _ = Self::LIMBS; Self { limbs }.masked()
}
#[inline]
#[must_use]
#[track_caller]
pub fn from_limbs_slice(slice: &[u64]) -> Self {
match Self::overflowing_from_limbs_slice(slice) {
(n, false) => n,
(_, true) => panic!("Value too large for this Uint"),
}
}
#[inline]
#[must_use]
pub fn checked_from_limbs_slice(slice: &[u64]) -> Option<Self> {
match Self::overflowing_from_limbs_slice(slice) {
(n, false) => Some(n),
(_, true) => None,
}
}
#[inline]
#[must_use]
pub fn wrapping_from_limbs_slice(slice: &[u64]) -> Self {
Self::overflowing_from_limbs_slice(slice).0
}
#[inline]
#[must_use]
pub fn overflowing_from_limbs_slice(slice: &[u64]) -> (Self, bool) {
if slice.len() < LIMBS {
let mut limbs = [0; LIMBS];
limbs[..slice.len()].copy_from_slice(slice);
(Self::from_limbs(limbs), false)
} else {
let (head, tail) = slice.split_at(LIMBS);
let mut limbs = [0; LIMBS];
limbs.copy_from_slice(head);
let mut overflow = tail.iter().any(|&limb| limb != 0);
if LIMBS > 0 {
overflow |= limbs[LIMBS - 1] > Self::MASK;
limbs[LIMBS - 1] &= Self::MASK;
}
(Self::from_limbs(limbs), overflow)
}
}
#[inline]
#[must_use]
pub fn saturating_from_limbs_slice(slice: &[u64]) -> Self {
match Self::overflowing_from_limbs_slice(slice) {
(n, false) => n,
(_, true) => Self::MAX,
}
}
#[inline(always)]
const fn apply_mask(&mut self) {
if Self::SHOULD_MASK {
self.limbs[LIMBS - 1] &= Self::MASK;
}
}
#[inline(always)]
const fn masked(mut self) -> Self {
self.apply_mask();
self
}
}
impl<const BITS: usize, const LIMBS: usize> Default for Uint<BITS, LIMBS> {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
#[inline]
#[must_use]
pub const fn nlimbs(bits: usize) -> usize {
bits.div_ceil(64)
}
#[inline]
#[must_use]
pub const fn mask(bits: usize) -> u64 {
if bits == 0 {
return 0;
}
let bits = bits % 64;
if bits == 0 { u64::MAX } else { (1 << bits) - 1 }
}
#[doc(hidden)]
pub mod __private {
pub use ruint_macro;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_mask() {
assert_eq!(mask(0), 0);
assert_eq!(mask(1), 1);
assert_eq!(mask(5), 0x1f);
assert_eq!(mask(63), u64::MAX >> 1);
assert_eq!(mask(64), u64::MAX);
}
#[test]
fn test_max() {
assert_eq!(Uint::<0, 0>::MAX, Uint::ZERO);
assert_eq!(Uint::<1, 1>::MAX, Uint::from_limbs([1]));
assert_eq!(Uint::<7, 1>::MAX, Uint::from_limbs([127]));
assert_eq!(Uint::<64, 1>::MAX, Uint::from_limbs([u64::MAX]));
assert_eq!(
Uint::<100, 2>::MAX,
Uint::from_limbs([u64::MAX, u64::MAX >> 28])
);
}
#[test]
fn test_constants() {
const_for!(BITS in SIZES {
const LIMBS: usize = nlimbs(BITS);
assert_eq!(Uint::<BITS, LIMBS>::MIN, Uint::<BITS, LIMBS>::ZERO);
let _ = Uint::<BITS, LIMBS>::MAX;
});
}
}