use core::cmp::Ordering;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub(crate) struct U384(pub [u64; 6]);
impl U384 {
#[inline]
pub fn from_u128(v: u128) -> Self {
U384([v as u64, (v >> 64) as u64, 0, 0, 0, 0])
}
#[inline]
pub fn mul_u128(a: u128, b: u128) -> Self {
if (a >> 64) == 0 && (b >> 64) == 0 {
return Self::from_u128(a * b);
}
Self::from_u128(a).mul_small(b)
}
pub fn mul_small(&self, b: u128) -> Self {
let bl = [b as u64, (b >> 64) as u64];
let mut r = [0u64; 6];
for (j, &bj) in bl.iter().enumerate() {
if bj == 0 {
continue;
}
let mut carry: u128 = 0;
for i in 0..6 {
if i + j >= 6 {
break;
}
let t = self.0[i] as u128 * bj as u128 + r[i + j] as u128 + carry;
r[i + j] = t as u64;
carry = t >> 64;
}
}
U384(r)
}
pub fn to_f64(self) -> f64 {
let mut v = 0f64;
for i in (0..6).rev() {
v = v * 18446744073709551616.0 + self.0[i] as f64;
}
v
}
}
impl Ord for U384 {
fn cmp(&self, o: &Self) -> Ordering {
for i in (0..6).rev() {
match self.0[i].cmp(&o.0[i]) {
Ordering::Equal => {}
x => return x,
}
}
Ordering::Equal
}
}
impl PartialOrd for U384 {
fn partial_cmp(&self, o: &Self) -> Option<Ordering> {
Some(self.cmp(o))
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct I256Acc {
hi: i128,
lo: u128,
}
impl I256Acc {
#[inline]
pub fn add(&mut self, v: i128) {
let (lo, carry) = self.lo.overflowing_add(v as u128);
self.lo = lo;
self.hi = self
.hi
.wrapping_add(if v < 0 { -1 } else { 0 })
.wrapping_add(carry as i128);
}
pub fn to_f64(self) -> f64 {
match self.hi {
0 => self.lo as f64,
-1 if self.lo != 0 => -(self.lo.wrapping_neg() as f64),
_ => self.hi as f64 * 340282366920938463463374607431768211456.0 + self.lo as f64,
}
}
}
#[inline]
pub(crate) fn cmp_products(a: u128, b: u128, c: u128, d: u128) -> Ordering {
if let (Some(x), Some(y)) = (a.checked_mul(b), c.checked_mul(d)) {
return x.cmp(&y);
}
U384::mul_u128(a, b).cmp(&U384::mul_u128(c, d))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn products() {
let a = u128::MAX;
let p = U384::mul_u128(a, a);
assert_eq!(p.0, [1, 0, u64::MAX - 1, u64::MAX, 0, 0]);
assert_eq!(cmp_products(a, 2, 2, a), Ordering::Equal);
assert_eq!(cmp_products(a, 3, 2, a), Ordering::Greater);
let q = p.mul_small(1 << 100);
assert!(q > p);
assert_eq!(U384::from_u128(12345).to_f64(), 12345.0);
let mut acc = I256Acc::default();
acc.add(i128::MAX);
acc.add(i128::MAX);
acc.add(-5);
acc.add(i128::MIN);
assert_eq!(acc.to_f64(), (i128::MAX - 6) as f64);
let mut neg = I256Acc::default();
neg.add(-3);
assert_eq!(neg.to_f64(), -3.0);
}
}