use crate::modular::{mod_add, mod_mul, mod_neg, mod_sub};
use crate::params::Params;
#[derive(Clone, Debug)]
pub struct Poly {
pub coeffs: Vec<u64>,
}
impl Poly {
pub fn zero(n: usize) -> Self {
Self {
coeffs: vec![0u64; n],
}
}
pub fn from_coeffs(coeffs: &[u64], n: usize) -> Self {
let mut c = vec![0u64; n];
let len = coeffs.len().min(n);
c[..len].copy_from_slice(&coeffs[..len]);
Self { coeffs: c }
}
pub fn constant(value: u64, n: usize) -> Self {
let mut c = vec![0u64; n];
c[0] = value;
Self { coeffs: c }
}
pub fn add(&self, other: &Poly, params: &Params) -> Poly {
debug_assert_eq!(self.coeffs.len(), other.coeffs.len());
let coeffs = self
.coeffs
.iter()
.zip(&other.coeffs)
.map(|(&a, &b)| mod_add(a, b, params.q))
.collect();
Poly { coeffs }
}
pub fn sub(&self, other: &Poly, params: &Params) -> Poly {
debug_assert_eq!(self.coeffs.len(), other.coeffs.len());
let coeffs = self
.coeffs
.iter()
.zip(&other.coeffs)
.map(|(&a, &b)| mod_sub(a, b, params.q))
.collect();
Poly { coeffs }
}
pub fn neg(&self, params: &Params) -> Poly {
let coeffs = self.coeffs.iter().map(|&a| mod_neg(a, params.q)).collect();
Poly { coeffs }
}
pub fn scalar_mul(&self, scalar: u64, params: &Params) -> Poly {
let coeffs = self
.coeffs
.iter()
.map(|&a| mod_mul(a, scalar, params.q))
.collect();
Poly { coeffs }
}
pub fn mul_schoolbook(&self, other: &Poly, params: &Params) -> Poly {
let n = params.n;
let q = params.q;
let mut result = vec![0u64; n];
for i in 0..n {
if self.coeffs[i] == 0 {
continue;
}
for j in 0..n {
if other.coeffs[j] == 0 {
continue;
}
let prod = mod_mul(self.coeffs[i], other.coeffs[j], q);
let idx = i + j;
if idx < n {
result[idx] = mod_add(result[idx], prod, q);
} else {
let idx = idx - n;
result[idx] = mod_sub(result[idx], prod, q);
}
}
}
Poly { coeffs: result }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tiny() -> Params {
Params::test_tiny()
}
#[test]
fn test_add_sub() {
let p = tiny();
let a = Poly::from_coeffs(&[1, 2, 3], p.n);
let b = Poly::from_coeffs(&[4, 5, 6], p.n);
let sum = a.add(&b, &p);
assert_eq!(sum.coeffs[0], 5);
assert_eq!(sum.coeffs[1], 7);
assert_eq!(sum.coeffs[2], 9);
let diff = sum.sub(&b, &p);
assert_eq!(diff.coeffs[0], 1);
assert_eq!(diff.coeffs[1], 2);
assert_eq!(diff.coeffs[2], 3);
}
#[test]
fn test_negacyclic_mul() {
let p = tiny();
let a = Poly::from_coeffs(&[1, 1], p.n);
let b = Poly::from_coeffs(&[1, 1], p.n);
let c = a.mul_schoolbook(&b, &p);
assert_eq!(c.coeffs[0], 1);
assert_eq!(c.coeffs[1], 2);
assert_eq!(c.coeffs[2], 1);
}
#[test]
fn test_negacyclic_wraparound() {
let p = tiny();
let n = p.n;
let mut a_coeffs = vec![0u64; n];
a_coeffs[n - 1] = 1;
let a = Poly { coeffs: a_coeffs };
let b = Poly::from_coeffs(&[0, 1], n);
let c = a.mul_schoolbook(&b, &p);
assert_eq!(c.coeffs[0], p.q - 1);
for i in 1..n {
assert_eq!(c.coeffs[i], 0);
}
}
#[test]
fn test_scalar_mul() {
let p = tiny();
let a = Poly::from_coeffs(&[3, 5, 7], p.n);
let b = a.scalar_mul(10, &p);
assert_eq!(b.coeffs[0], 30);
assert_eq!(b.coeffs[1], 50);
assert_eq!(b.coeffs[2], 70);
}
}