use crate::params::Q;
pub type Fe = u16;
#[inline(always)]
pub fn barrett_reduce(a: i32) -> u16 {
const V: i64 = (1i64 << 26) / (Q as i64);
let t = ((V * a as i64) >> 26) as i32 * (Q as i32);
let r = a - t;
let r = r + ((r >> 31) & Q as i32);
let r = r - ((((Q as i32 - 1 - r) >> 31) & 1) * Q as i32);
r as u16
}
#[inline(always)]
pub fn fqadd(a: Fe, b: Fe) -> Fe {
let s = a as i32 + b as i32 - Q as i32;
(s + (((s >> 15) & 1) * Q as i32)) as u16
}
#[inline(always)]
pub fn fqsub(a: Fe, b: Fe) -> Fe {
let s = a as i32 - b as i32;
(s + (((s >> 15) & 1) * Q as i32)) as u16
}
#[inline(always)]
pub fn fqmul(a: Fe, b: Fe) -> Fe {
barrett_reduce(a as i32 * b as i32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn barrett_matches_naive() {
for a in 0..(Q as i32 * Q as i32) {
assert_eq!(barrett_reduce(a), (a % Q as i32) as u16);
if a > 100000 {
break;
} }
for a in [0, 1, Q as i32 - 1, Q as i32, Q as i32 * (Q as i32 - 1)] {
assert_eq!(barrett_reduce(a), (a % Q as i32) as u16);
}
}
#[test]
fn ops() {
assert_eq!(fqadd(3000, 500), 3500 - Q);
assert_eq!(fqsub(10, 20), (Q as i32 - 10) as u16);
assert_eq!(fqmul(17, 17), 289);
}
}