#[inline]
pub fn mod_reduce(a: i64, modulus: u64) -> u64 {
let m = modulus as i64;
let r = a % m;
if r < 0 { (r + m) as u64 } else { r as u64 }
}
#[inline]
pub fn center(a: u64, modulus: u64) -> i64 {
let a = a % modulus;
if a > modulus / 2 {
a as i64 - modulus as i64
} else {
a as i64
}
}
#[inline]
pub fn mod_add(a: u64, b: u64, modulus: u64) -> u64 {
((a as u128 + b as u128) % modulus as u128) as u64
}
#[inline]
pub fn mod_sub(a: u64, b: u64, modulus: u64) -> u64 {
if a >= b {
(a - b) % modulus
} else {
modulus - ((b - a) % modulus)
}
}
#[inline]
pub fn mod_mul(a: u64, b: u64, modulus: u64) -> u64 {
((a as u128 * b as u128) % modulus as u128) as u64
}
#[inline]
pub fn mod_neg(a: u64, modulus: u64) -> u64 {
if a == 0 { 0 } else { modulus - (a % modulus) }
}
pub fn mod_pow(mut base: u64, mut exp: u64, modulus: u64) -> u64 {
let mut result: u128 = 1;
let m = modulus as u128;
base %= modulus;
let mut b = base as u128;
while exp > 0 {
if exp & 1 == 1 {
result = (result * b) % m;
}
exp >>= 1;
b = (b * b) % m;
}
result as u64
}
pub fn mod_inv(a: u64, modulus: u64) -> u64 {
mod_pow(a, modulus - 2, modulus)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mod_reduce_positive() {
assert_eq!(mod_reduce(7, 5), 2);
}
#[test]
fn test_mod_reduce_negative() {
assert_eq!(mod_reduce(-3, 5), 2);
}
#[test]
fn test_center() {
assert_eq!(center(1, 7), 1);
assert_eq!(center(6, 7), -1);
assert_eq!(center(3, 7), 3); assert_eq!(center(4, 7), -3); }
#[test]
fn test_mod_arithmetic() {
let q = 97;
assert_eq!(mod_add(50, 60, q), 13);
assert_eq!(mod_sub(10, 30, q), 77);
assert_eq!(mod_mul(50, 50, q), (2500 % 97));
assert_eq!(mod_neg(10, q), 87);
}
#[test]
fn test_mod_pow() {
assert_eq!(mod_pow(2, 10, 1024), 0); assert_eq!(mod_pow(3, 4, 97), 81 % 97);
}
#[test]
fn test_mod_inv() {
let q = 97u64;
for a in 1..q {
let inv = mod_inv(a, q);
assert_eq!(mod_mul(a, inv, q), 1, "inverse failed for {a}");
}
}
}