#[inline(always)]
const fn addhilo(x_hi: u64, x_lo: u64, y_hi: u64, y_lo: u64) -> (u64, u64) {
let res = (((x_hi as u128) << u64::BITS) | (x_lo as u128))
+ (((y_hi as u128) << u64::BITS) | (y_lo as u128));
((res >> u64::BITS) as u64, res as u64)
}
#[inline(always)]
const fn mulhilo(x: u64, y: u64) -> (u64, u64) {
let res = (x as u128) * (y as u128);
((res >> u64::BITS) as u64, res as u64)
}
const fn reciprocal(d: u64) -> u64 {
debug_assert!(d >= (1 << (u64::BITS - 1)));
let d0 = d & 1;
let d9 = d >> 55;
let d40 = (d >> 24) + 1;
let d63 = (d >> 1) + d0;
let v0 = short_div((1 << 19) - 3 * (1 << 8), 19, d9 as u32, 9) as u64;
let v1 = (v0 << 11) - ((v0 * v0 * d40) >> 40) - 1;
let v2 = (v1 << 13) + ((v1 * ((1 << 60) - v1 * d40)) >> 47);
debug_assert!(mulhilo(v2, d63).0 == (1 << 32) - 1);
let e = u64::MAX - v2.wrapping_mul(d63) + 1 + (v2 >> 1) * d0;
let (hi, _lo) = mulhilo(v2, e);
let v3 = (v2 << 31).wrapping_add(hi >> 1);
let x = v3.wrapping_add(1);
let (hi, _lo) = mulhilo(x, d);
let hi = if x > 0 { hi } else { d };
v3.wrapping_sub(hi).wrapping_sub(d)
}
#[inline(always)]
const fn short_div(dividend: u32, dividend_bits: u32, divisor: u32, divisor_bits: u32) -> u32 {
let mut dividend = dividend;
let mut divisor = divisor << (dividend_bits - divisor_bits);
let mut quotient: u32 = 0;
let mut i = dividend_bits - divisor_bits + 1;
while i > 0 {
i -= 1;
if dividend >= divisor {
dividend = dividend.wrapping_sub(divisor);
quotient |= 1 << i;
}
divisor >>= 1;
}
quotient
}
#[derive(Copy, Clone, Debug)]
pub struct Reciprocal {
divisor_normalized: u64,
shift: u32,
reciprocal: u64,
}
impl Reciprocal {
pub const fn new(divisor: u64) -> Self {
let shift = divisor.leading_zeros();
let divisor_normalized = divisor << shift;
Self {
divisor_normalized,
shift,
reciprocal: reciprocal(divisor_normalized),
}
}
}
#[inline(always)]
pub(crate) fn div2by1(u1: u64, u0: u64, reciprocal: &Reciprocal) -> (u64, u64) {
let d = reciprocal.divisor_normalized;
let rec = reciprocal.reciprocal;
debug_assert!(d >= (1 << (u64::BITS - 1)));
debug_assert!(u1 < d);
let (q1, q0) = mulhilo(rec, u1);
let (q1, q0) = addhilo(q1, q0, u1, u0);
let mut q1 = q1.wrapping_add(1);
let mut r = u0.wrapping_sub(q1.wrapping_mul(d));
if r > q0 {
q1 = q1.wrapping_sub(1);
r = r.wrapping_add(d);
}
debug_assert!(r < d || q1 < u64::MAX);
if r >= d {
q1 += 1;
r -= d;
}
(q1, r)
}
#[inline(always)]
fn rem_with_reciprocal(hi: u64, lo: u64, reciprocal: &Reciprocal) -> u64 {
let mut hi = hi << reciprocal.shift;
if reciprocal.shift > 0 {
hi |= lo >> (u64::BITS - reciprocal.shift);
}
let lo = lo << reciprocal.shift;
let (_q, r) = div2by1(hi, lo, reciprocal);
r >> reciprocal.shift
}
#[inline(always)]
pub fn rem_wide_with_reciprocal(x: u128, reciprocal: &Reciprocal) -> u64 {
let hi = (x >> u64::BITS) as u64;
let lo = x as u64;
rem_with_reciprocal(hi, lo, reciprocal)
}
#[cfg(test)]
mod tests {
use super::{rem_wide_with_reciprocal, Reciprocal};
use proptest::prelude::*;
proptest! {
#[test]
fn rem(x in any::<u128>(), m in any::<u64>()) {
let m = if m == 0 {
1
}
else {
m
};
let t = x % ((m as u128) * (m as u128));
let expected = (t % (m as u128)) as u64;
let test = rem_wide_with_reciprocal(t, &Reciprocal::new(m));
assert_eq!(test, expected);
}
}
}