use super::*;
const PRIME: [u64; 4] = [
0xffff_ffff_ffff_ffff,
0x0000_0000_ffff_ffff,
0x0000_0000_0000_0000,
0xffff_ffff_0000_0001,
];
fn lt<const N: usize>(a: &[u64; N], b: &[u64; N]) -> bool {
for i in (0..N).rev() {
if a[i] != b[i] {
return a[i] < b[i];
}
}
false
}
fn add_n<const N: usize>(a: [u64; N], b: [u64; N]) -> [u64; N] {
let mut r = [0u64; N];
let mut carry: u128 = 0;
for i in 0..N {
let t = a[i] as u128 + b[i] as u128 + carry;
r[i] = t as u64;
carry = t >> 64;
}
assert_eq!(carry, 0);
r
}
fn widen<const N: usize>(a: [u64; 4]) -> [u64; N] {
let mut r = [0u64; N];
r[..4].copy_from_slice(&a);
r
}
fn any_residue() -> [u64; 4] {
let a: [u64; 4] = kani::any();
kani::assume(lt(&a, &PRIME));
a
}
#[kani::proof]
fn add_is_a_plus_b_mod_p() {
let (a, b) = (any_residue(), any_residue());
let out = add(a, b);
let sum: [u64; 5] = add_n(widen(a), widen(b));
assert!(lt(&out, &PRIME));
assert!(sum == widen(out) || sum == add_n(widen(out), widen(PRIME)));
}
#[kani::proof]
fn sub_is_a_minus_b_mod_p() {
let (a, b) = (any_residue(), any_residue());
let out = sub(a, b);
assert!(lt(&out, &PRIME));
let back: [u64; 5] = add_n(widen(out), widen(b));
assert!(back == widen(a) || back == add_n(widen(a), widen(PRIME)));
}
#[kani::proof]
fn finish_is_hi_plus_w_mod_p() {
let hi: [u64; 4] = kani::any();
let w: [u64; 4] = kani::any();
kani::assume(lt(&hi, &PRIME));
kani::assume(!lt(&PRIME, &w));
let out = finish(hi, w);
let s: [u64; 5] = add_n(widen(hi), widen(w));
assert!(lt(&out, &PRIME));
assert!(s == widen(out) || s == add_n(widen(out), widen(PRIME)));
}