plat-core 0.1.1

Core types and traits for the plat FHE compute engine
Documentation
//! Modular arithmetic utilities for lattice-based cryptography.

/// Reduce `a` into [0, modulus).
#[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 }
}

/// Centered representative: maps value in [0, q) to (-q/2, q/2].
#[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
    }
}

/// Modular addition: (a + b) mod q.
#[inline]
pub fn mod_add(a: u64, b: u64, modulus: u64) -> u64 {
    ((a as u128 + b as u128) % modulus as u128) as u64
}

/// Modular subtraction: (a - b) mod q.
#[inline]
pub fn mod_sub(a: u64, b: u64, modulus: u64) -> u64 {
    if a >= b {
        (a - b) % modulus
    } else {
        modulus - ((b - a) % modulus)
    }
}

/// Modular multiplication: (a * b) mod q.
#[inline]
pub fn mod_mul(a: u64, b: u64, modulus: u64) -> u64 {
    ((a as u128 * b as u128) % modulus as u128) as u64
}

/// Modular negation: (-a) mod q.
#[inline]
pub fn mod_neg(a: u64, modulus: u64) -> u64 {
    if a == 0 { 0 } else { modulus - (a % modulus) }
}

/// Modular exponentiation: a^exp mod q (for NTT root computation).
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
}

/// Modular inverse via Fermat's little theorem (modulus must be prime).
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); // 3 == 7/2 = 3, not > 3
        assert_eq!(center(4, 7), -3); // 4 > 3 → 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); // 1024 mod 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}");
        }
    }
}