#[cfg(all(feature = "u512", feature = "bigint"))]
compile_error!(
"features `u512` and `bigint` are mutually exclusive: pick exactly one backend. \
Rule B makes the value width a wire-format commitment, so a build with both \
would have an ambiguous canonical encoding."
);
#[cfg(not(any(feature = "u512", feature = "bigint")))]
compile_error!("exactly one of the `u512` or `bigint` features must be enabled");
pub const CANONICAL_BYTES: usize = 64;
pub const DOMAIN_BITS: u32 = 512;
pub trait TickInt:
Clone + PartialEq + Eq + PartialOrd + Ord + core::hash::Hash + core::fmt::Debug
{
type Wide: Clone + PartialEq + Eq + PartialOrd + Ord + core::fmt::Debug;
fn zero() -> Self;
fn one() -> Self;
fn domain_max() -> Self;
fn wide_mul(&self, other: &Self) -> Self::Wide;
fn wide_quot_rem(wide: &Self::Wide, divisor: &Self) -> (Self::Wide, Self);
fn narrow(wide: &Self::Wide) -> Option<Self>;
fn wide_is_zero(wide: &Self::Wide) -> bool;
fn pow2(exponent: u32) -> Option<Self> {
let mut acc = Self::one();
for _ in 0..exponent {
acc = acc.try_add(&acc)?;
}
Some(acc)
}
fn from_u64(v: u64) -> Self;
fn from_u128(v: u128) -> Option<Self> {
let hi = Self::from_u64((v >> 64) as u64);
let lo = Self::from_u64(v as u64);
let shift = Self::pow2(64)?;
hi.try_mul(&shift)?.try_add(&lo)
}
fn from_dec_str(s: &str) -> Option<Self>;
fn try_add(&self, other: &Self) -> Option<Self>;
fn try_sub(&self, other: &Self) -> Option<Self>;
fn try_mul(&self, other: &Self) -> Option<Self>;
fn quot_rem(&self, divisor: &Self) -> (Self, Self);
fn pow5(exponent: u32) -> Option<Self> {
let mut result = Self::one();
let mut base = Self::from_u64(5);
let mut e = exponent;
while e > 0 {
if e & 1 == 1 {
result = result.try_mul(&base)?;
}
e >>= 1;
if e > 0 {
base = base.try_mul(&base)?;
}
}
Some(result)
}
fn bit_len(&self) -> u32;
fn to_canonical_bytes(&self) -> [u8; CANONICAL_BYTES];
fn from_canonical_bytes(bytes: &[u8; CANONICAL_BYTES]) -> Option<Self>;
fn is_zero_ticks(&self) -> bool {
*self == Self::zero()
}
fn is_odd(&self) -> bool;
#[cfg(feature = "alloc")]
fn to_dec_string(&self) -> alloc::string::String;
#[cfg(feature = "alloc")]
fn to_radix_string(&self, radix: u32) -> alloc::string::String;
}
#[cfg(feature = "u512")]
mod imp {
use super::{TickInt, CANONICAL_BYTES, DOMAIN_BITS};
pub type Ticks = bnum::types::U512;
pub const fn konst(s: &str) -> Ticks {
match Ticks::from_str_radix(s, 10) {
Ok(v) => v,
Err(_) => panic!("invalid decimal literal in a const profile constant"),
}
}
pub type Wide = bnum::types::U1024;
fn widen(v: &Ticks) -> Wide {
let mut buf = [0u8; 2 * CANONICAL_BYTES];
buf[CANONICAL_BYTES..].copy_from_slice(&v.to_be_bytes());
Wide::from_be_bytes(buf)
}
impl TickInt for Ticks {
type Wide = Wide;
fn zero() -> Self {
Self::MIN
}
fn one() -> Self {
konst("1")
}
fn domain_max() -> Self {
Self::MAX
}
fn wide_mul(&self, other: &Self) -> Wide {
widen(self) * widen(other)
}
fn wide_quot_rem(wide: &Wide, divisor: &Self) -> (Wide, Self) {
assert!(
!TickInt::is_zero_ticks(divisor),
"internal invariant: division by zero"
);
let d = widen(divisor);
let q = *wide / d;
let r = *wide % d;
(
q,
Self::narrow(&r).expect("a remainder is smaller than its divisor"),
)
}
fn narrow(wide: &Wide) -> Option<Self> {
let bytes = wide.to_be_bytes();
if bytes[..CANONICAL_BYTES].iter().any(|b| *b != 0) {
return None;
}
let mut low = [0u8; CANONICAL_BYTES];
low.copy_from_slice(&bytes[CANONICAL_BYTES..]);
Some(Self::from_be_bytes(low))
}
fn wide_is_zero(wide: &Wide) -> bool {
*wide == Wide::MIN
}
fn from_u64(v: u64) -> Self {
let mut bytes = [0u8; CANONICAL_BYTES];
bytes[CANONICAL_BYTES - 8..].copy_from_slice(&v.to_be_bytes());
Self::from_be_bytes(bytes)
}
fn from_dec_str(s: &str) -> Option<Self> {
if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
Self::from_str_radix(s, 10).ok()
}
fn try_add(&self, other: &Self) -> Option<Self> {
bnum::types::U512::checked_add(*self, *other)
}
fn try_sub(&self, other: &Self) -> Option<Self> {
bnum::types::U512::checked_sub(*self, *other)
}
fn try_mul(&self, other: &Self) -> Option<Self> {
bnum::types::U512::checked_mul(*self, *other)
}
fn quot_rem(&self, divisor: &Self) -> (Self, Self) {
assert!(
!TickInt::is_zero_ticks(divisor),
"internal invariant: division by zero"
);
(*self / *divisor, *self % *divisor)
}
fn bit_len(&self) -> u32 {
let lz = self.leading_zeros();
if lz >= DOMAIN_BITS {
0
} else {
DOMAIN_BITS - lz
}
}
fn to_canonical_bytes(&self) -> [u8; CANONICAL_BYTES] {
self.to_be_bytes()
}
fn from_canonical_bytes(bytes: &[u8; CANONICAL_BYTES]) -> Option<Self> {
Some(Self::from_be_bytes(*bytes))
}
fn is_zero_ticks(&self) -> bool {
*self == Self::MIN
}
fn is_odd(&self) -> bool {
self.bit(0)
}
#[cfg(feature = "alloc")]
fn to_dec_string(&self) -> alloc::string::String {
self.to_str_radix(10)
}
#[cfg(feature = "alloc")]
fn to_radix_string(&self, radix: u32) -> alloc::string::String {
self.to_str_radix(radix)
}
}
}
#[cfg(feature = "bigint")]
mod imp {
use super::{TickInt, CANONICAL_BYTES, DOMAIN_BITS};
use alloc::string::String;
use alloc::vec;
use num_bigint::BigUint;
use num_integer::Integer;
use num_traits::{One, Zero};
pub type Ticks = BigUint;
fn ceiling() -> BigUint {
<BigUint as One>::one() << DOMAIN_BITS
}
fn within_domain(v: BigUint) -> Option<BigUint> {
if v < ceiling() {
Some(v)
} else {
None
}
}
impl TickInt for Ticks {
type Wide = BigUint;
fn zero() -> Self {
<BigUint as Zero>::zero()
}
fn one() -> Self {
<BigUint as One>::one()
}
fn domain_max() -> Self {
ceiling() - <BigUint as One>::one()
}
fn wide_mul(&self, other: &Self) -> BigUint {
self * other
}
fn wide_quot_rem(wide: &BigUint, divisor: &Self) -> (BigUint, Self) {
assert!(
!Zero::is_zero(divisor),
"internal invariant: division by zero"
);
Integer::div_rem(wide, divisor)
}
fn narrow(wide: &BigUint) -> Option<Self> {
within_domain(wide.clone())
}
fn wide_is_zero(wide: &BigUint) -> bool {
Zero::is_zero(wide)
}
fn from_u64(v: u64) -> Self {
BigUint::from(v)
}
fn from_dec_str(s: &str) -> Option<Self> {
if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
within_domain(BigUint::parse_bytes(s.as_bytes(), 10)?)
}
fn try_add(&self, other: &Self) -> Option<Self> {
within_domain(self + other)
}
fn try_sub(&self, other: &Self) -> Option<Self> {
if self < other {
None
} else {
Some(self - other)
}
}
fn try_mul(&self, other: &Self) -> Option<Self> {
within_domain(self * other)
}
fn quot_rem(&self, divisor: &Self) -> (Self, Self) {
assert!(
!Zero::is_zero(divisor),
"internal invariant: division by zero"
);
Integer::div_rem(self, divisor)
}
fn bit_len(&self) -> u32 {
self.bits() as u32
}
fn to_canonical_bytes(&self) -> [u8; CANONICAL_BYTES] {
let raw = self.to_bytes_be();
let raw: &[u8] = if Zero::is_zero(self) { &[] } else { &raw };
debug_assert!(raw.len() <= CANONICAL_BYTES, "Rule W violated");
let mut out = [0u8; CANONICAL_BYTES];
out[CANONICAL_BYTES - raw.len()..].copy_from_slice(raw);
out
}
fn from_canonical_bytes(bytes: &[u8; CANONICAL_BYTES]) -> Option<Self> {
within_domain(BigUint::from_bytes_be(bytes))
}
fn is_zero_ticks(&self) -> bool {
Zero::is_zero(self)
}
fn is_odd(&self) -> bool {
Integer::is_odd(self)
}
fn to_dec_string(&self) -> String {
self.to_str_radix(10)
}
fn to_radix_string(&self, radix: u32) -> String {
self.to_str_radix(radix)
}
}
pub fn konst(s: &str) -> Ticks {
<Ticks as TickInt>::from_dec_str(s)
.expect("invalid decimal literal in a profile constant")
}
#[allow(dead_code)]
fn _vec_used() -> alloc::vec::Vec<u8> {
vec![0u8; 0]
}
}
pub use imp::{konst, Ticks};
pub const TICKS_IS_COPY: bool = cfg!(feature = "u512");