use crate::params::{Q, QINV};
#[inline]
#[must_use]
pub fn montgomery_reduce(a: i64) -> i32 {
let t = (a as i32).wrapping_mul(QINV as i32);
((a - (t as i64) * (Q as i64)) >> 32) as i32
}
#[inline]
#[must_use]
pub fn reduce32(a: i32) -> i32 {
let t = (a + (1 << 22)) >> 23;
a - t * Q
}
#[inline]
#[must_use]
pub fn caddq(a: i32) -> i32 {
a + ((a >> 31) & Q)
}
#[inline]
#[must_use]
pub fn freeze(a: i32) -> i32 {
caddq(reduce32(a))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_montgomery_reduce_zero() {
assert_eq!(montgomery_reduce(0), 0);
}
#[test]
fn test_reduce32_small() {
assert_eq!(reduce32(Q), 0);
assert_eq!(reduce32(0), 0);
}
#[test]
fn test_caddq_negative() {
assert_eq!(caddq(-1), Q - 1);
assert_eq!(caddq(0), 0);
assert_eq!(caddq(1), 1);
}
#[test]
fn test_freeze_identity() {
for x in [0, 1, 100, Q - 1] {
assert_eq!(freeze(x), x);
}
}
#[test]
fn test_freeze_negative() {
let r = freeze(-1);
assert_eq!(r, Q - 1);
}
#[test]
fn test_montgomery_round_trip() {
let a: i64 = 42;
let b: i64 = 123;
let ab = a * b;
let r = montgomery_reduce(ab);
assert!(r > -Q && r < Q);
}
}