use crate::u256::U256;
use crate::u512::U512;
use super::KnuthD;
impl KnuthD {
pub(crate) fn rem(&self, dividend: &U512) -> U256 {
let u_le = dividend.to_le_limbs();
let m = 8 - self.n;
let mut un = [0u64; 9];
self.normalize_dividend(&u_le, &mut un);
let mut q_arr = [0u64; 8];
self.knuth_loop(&mut un[..m + self.n + 1], m, &mut q_arr[..m + 1]);
let rem_le = self.unnormalize(&un);
U256(rem_le)
}
}
#[cfg(test)]
mod ai_tests {
use crate::u256::U256;
use crate::u512::U512;
const P: U256 = U256::from_be_limbs([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2F]);
use super::*;
fn knuth_rem(dividend: &U512, divisor: &U256) -> U256 {
let n = divisor.sig_limbs();
let v_le = divisor.to_le_limbs();
KnuthD::new(&v_le, n).rem(dividend)
}
#[test]
fn small_dividend() {
let dividend = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 42]);
assert_eq!(knuth_rem(÷nd, &P), U256::from_be_limbs([0, 0, 0, 42]));
}
#[test]
fn p_minus_one_squared() {
let p_minus_1 = U256::from_be_limbs([
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFEFFFFFC2E,
]);
let product = p_minus_1 * p_minus_1;
assert_eq!(knuth_rem(&product, &P), U256::ONE);
}
#[test]
fn multiple_of_p() {
let small = U256::from_be_limbs([0, 0, 0, 42]);
let product = small * P;
assert_eq!(knuth_rem(&product, &P), U256::ZERO);
}
#[test]
fn two_limb_divisor() {
let dividend = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 1, 0]);
let divisor = U256::from_be_limbs([0, 0, 1, 1]);
assert_eq!(
knuth_rem(÷nd, &divisor),
U256::from_be_limbs([0, 0, 1, 0])
);
}
#[test]
fn max_squared() {
let max = U256::from_be_limbs([u64::MAX; 4]);
let product = max * max;
let reduced = U256::from_be_limbs([0, 0, 0, 0x1000003D0]);
let expected = reduced.mul_mod(&reduced, &P);
assert_eq!(knuth_rem(&product, &P), expected);
}
}