wip
@@ -150,8 +150,9 @@ debug_assert!(*a < self.four_q, "a:{} q:{}", a, self.four_q);
debug_assert!(*b < self.four_q, "b:{} q:{}", b, self.four_q); a.reduce_once_assign(self.two_q); let bt: u64 = self.prime.barrett.mul_external::<NONE>(t, *b); - *b = a.wrapping_add(self.two_q-bt); - *a = a.wrapping_add(bt); + debug_assert!(bt < self.two_q, "bt:{} two_q:{}", bt, self.two_q); + *b = *a + self.two_q-bt; + *a += bt; if !LAZY { a.reduce_once_assign(self.two_q); b.reduce_once_assign(self.two_q);@@ -223,10 +224,10 @@ }
#[inline(always)] fn dif_inplace<const LAZY: bool>(&self, a: &mut u64, b: &mut u64, t: Barrett<u64>) { - debug_assert!(*a < self.two_q, "a:{} q:{}", a, self.four_q); - debug_assert!(*b < self.two_q, "b:{} q:{}", b, self.four_q); + debug_assert!(*a < self.two_q, "a:{} q:{}", a, self.two_q); + debug_assert!(*b < self.two_q, "b:{} q:{}", b, self.two_q); let d: u64 = self.prime.barrett.mul_external::<NONE>(t, *a + self.two_q - *b); - *a = a.wrapping_add(*b); + *a = *a + *b; a.reduce_once_assign(self.two_q); *b = d; if !LAZY {
@@ -14,6 +14,8 @@ &self.1
} } +pub struct BarrettRNS<O>(pub Vec<Barrett<O>>); + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BarrettPrecomp<O>{ pub q: O,
@@ -21,10 +21,6 @@ pub fn one(&self) -> Barrett<u64> {
self.one } - /// Applies a modular reduction on x based on REDUCE: - /// - LAZY: no modular reduction. - /// - ONCE: subtracts q if x >= q. - /// - FULL: maps x to x mod q using Barrett reduction. #[inline(always)] pub fn reduce_assign<const REDUCE:REDUCEMOD>(&self, x: &mut u64){ match REDUCE {@@ -43,6 +39,13 @@ *x = *x - mhi.wrapping_mul(self.q)
}, _ => unreachable!("invalid REDUCE argument") } + } + + #[inline(always)] + pub fn reduce<const REDUCE:REDUCEMOD>(&self, x: &u64) -> u64{ + let mut r = *x; + self.reduce_assign::<REDUCE>(&mut r); + r } #[inline(always)]
@@ -20,12 +20,12 @@ assert!(buf.len() >= n, "invalid buffer: buf.len()={} < n={}", buf.len(), n);
self.0 = Vec::from(&buf[..n]); } - pub fn n(&self) -> usize{ - (usize::BITS - self.0.len().leading_zeros()) as usize + pub fn log_n(&self) -> usize{ + (usize::BITS - (self.n()-1).leading_zeros()) as usize } - pub fn log_n(&self) -> usize{ - self.0.len()-1 + pub fn n(&self) -> usize{ + self.0.len() } pub fn resize(&mut self, n:usize){
@@ -2,7 +2,6 @@ pub mod impl_u64;
use crate::modulus::prime::Prime; use crate::poly::{Poly, PolyRNS}; -use num_bigint::BigInt; use crate::dft::DFT;@@ -37,7 +36,7 @@
pub fn max_level(&self) -> usize{ self.0.len()-1 } - + pub fn level(&self) -> usize{ self.0.len()-1 }
@@ -1,6 +1,6 @@
use crate::ring::RingRNS; use crate::poly::{Poly, PolyRNS}; -use crate::modulus::barrett::Barrett; +use crate::modulus::barrett::BarrettRNS; use crate::modulus::ONCE; extern crate test;@@ -11,12 +11,12 @@ /// Expects a and b to be in the NTT domain.
pub fn div_floor_by_last_modulus_ntt(&self, a: &PolyRNS<u64>, buf: &mut PolyRNS<u64>, b: &mut PolyRNS<u64>){ assert!(b.level() >= a.level()-1, "invalid input b: b.level()={} < a.level()-1={}", b.level(), a.level()-1); let level = self.level(); - self.0[level].intt::<true>(a.at(level), buf.at_mut(0)); - let rescaling_constants: Vec<Barrett<u64>> = self.rescaling_constant(); + self.0[level].intt::<false>(a.at(level), buf.at_mut(0)); + let rescaling_constants: BarrettRNS<u64> = self.rescaling_constant(); let (buf_ntt_q_scaling, buf_ntt_qi_scaling) = buf.0.split_at_mut(1); for (i, r) in self.0[0..level].iter().enumerate(){ - r.ntt::<true>(&buf_ntt_q_scaling[0], &mut buf_ntt_qi_scaling[0]); - r.sum_aqqmb_prod_c_scalar_barrett::<ONCE>(&buf_ntt_qi_scaling[0], a.at(i), &rescaling_constants[i], b.at_mut(i)); + r.ntt::<false>(&buf_ntt_q_scaling[0], &mut buf_ntt_qi_scaling[0]); + r.sum_aqqmb_prod_c_scalar_barrett::<ONCE>(&buf_ntt_qi_scaling[0], a.at(i), &rescaling_constants.0[i], b.at_mut(i)); } }@@ -25,11 +25,11 @@ /// Expects b to be in the NTT domain.
pub fn div_floor_by_last_modulus_ntt_inplace(&self, buf: &mut PolyRNS<u64>, b: &mut PolyRNS<u64>){ let level = self.level(); self.0[level].intt::<true>(b.at(level), buf.at_mut(0)); - let rescaling_constants: Vec<Barrett<u64>> = self.rescaling_constant(); + let rescaling_constants: BarrettRNS<u64> = self.rescaling_constant(); let (buf_ntt_q_scaling, buf_ntt_qi_scaling) = buf.0.split_at_mut(1); for (i, r) in self.0[0..level].iter().enumerate(){ r.ntt::<true>(&buf_ntt_q_scaling[0], &mut buf_ntt_qi_scaling[0]); - r.sum_aqqmb_prod_c_scalar_barrett_inplace::<ONCE>(&buf_ntt_qi_scaling[0], &rescaling_constants[i], b.at_mut(i)); + r.sum_aqqmb_prod_c_scalar_barrett_inplace::<ONCE>(&buf_ntt_qi_scaling[0], &rescaling_constants.0[i], b.at_mut(i)); } }@@ -37,19 +37,19 @@ /// Updates b to floor(a / q[b.level()]).
pub fn div_floor_by_last_modulus(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ assert!(b.level() >= a.level()-1, "invalid input b: b.level()={} < a.level()-1={}", b.level(), a.level()-1); let level = self.level(); - let rescaling_constants: Vec<Barrett<u64>> = self.rescaling_constant(); + let rescaling_constants:crate::modulus::barrett::BarrettRNS<u64> = self.rescaling_constant(); for (i, r) in self.0[0..level].iter().enumerate(){ - r.sum_aqqmb_prod_c_scalar_barrett::<ONCE>(a.at(level), a.at(i), &rescaling_constants[i], b.at_mut(i)); + r.sum_aqqmb_prod_c_scalar_barrett::<ONCE>(a.at(level), a.at(i), &rescaling_constants.0[i], b.at_mut(i)); } } /// Updates a to floor(b / q[b.level()]). pub fn div_floor_by_last_modulus_inplace(&self, a: &mut PolyRNS<u64>){ let level = self.level(); - let rescaling_constants: Vec<Barrett<u64>> = self.rescaling_constant(); + let rescaling_constants: BarrettRNS<u64> = self.rescaling_constant(); let (a_i, a_level) = a.split_at_mut(level); for (i, r) in self.0[0..level].iter().enumerate(){ - r.sum_aqqmb_prod_c_scalar_barrett_inplace::<ONCE>(&a_level[0], &rescaling_constants[i], &mut a_i[i]); + r.sum_aqqmb_prod_c_scalar_barrett_inplace::<ONCE>(&a_level[0], &rescaling_constants.0[i], &mut a_i[i]); } }@@ -76,6 +76,7 @@
#[cfg(test)] mod tests { + use num_bigint::BigInt; use crate::ring::Ring; use crate::ring::impl_u64::ring_rns::new_rings; use super::*;@@ -83,16 +84,36 @@
#[test] fn test_div_floor_by_last_modulus_ntt() { let n = 1<<10; - let moduli: Vec<u64> = vec![0x1fffffffffe00001u64, 0x1fffffffffc80001u64]; + let moduli: Vec<u64> = vec![0x1fffffffffc80001u64, 0x1fffffffffe00001u64]; let rings: Vec<Ring<u64>> = new_rings(n, moduli); let ring_rns = RingRNS::new(&rings); - let a: PolyRNS<u64> = ring_rns.new_polyrns(); + let mut a: PolyRNS<u64> = ring_rns.new_polyrns(); let mut b: PolyRNS<u64> = ring_rns.new_polyrns(); - let mut c: PolyRNS<u64> = ring_rns.new_polyrns(); + let mut c: PolyRNS<u64> = ring_rns.at_level(ring_rns.level()-1).new_polyrns(); + + // Allocates an rns poly with values [0..n] + let mut coeffs_a: Vec<BigInt> = (0..n).map(|i|{BigInt::from(i)}).collect(); + ring_rns.from_bigint_inplace(&coeffs_a, 1, &mut a); + + // Scales by q_level both a and coeffs_a + let scalar: u64 = ring_rns.0[ring_rns.level()].modulus.q; + ring_rns.mul_scalar_inplace::<ONCE>(&scalar, &mut a); + let scalar_big = BigInt::from(scalar); + coeffs_a.iter_mut().for_each(|a|{*a *= &scalar_big}); + // Performs c = intt(ntt(a) / q_level) + ring_rns.ntt_inplace::<false>(&mut a); ring_rns.div_floor_by_last_modulus_ntt(&a, &mut b, &mut c); + ring_rns.at_level(c.level()).intt_inplace::<false>(&mut c); - //assert!(m_precomp.mul_external::<ONCE>(y_mont, x) == (x as u128 * y as u128 % q as u128) as u64); + // Exports c to coeffs_c + let mut coeffs_c = vec![BigInt::from(0);c.n()]; + ring_rns.at_level(c.level()).to_bigint_inplace(&c, 1, &mut coeffs_c); + + // Performs floor division on a + coeffs_a.iter_mut().for_each(|a|{*a /= &scalar_big}); + + assert!(coeffs_a == coeffs_c); } }
@@ -4,7 +4,7 @@ use crate::modulus::prime::Prime;
use crate::modulus::montgomery::Montgomery; use crate::modulus::barrett::Barrett; use crate::poly::Poly; -use crate::modulus::REDUCEMOD; +use crate::modulus::{REDUCEMOD, BARRETT}; use crate::modulus::VecOperations; use num_bigint::BigInt; use num_traits::ToPrimitive;@@ -38,16 +38,16 @@ }
pub fn intt_inplace<const LAZY:bool>(&self, poly: &mut Poly<u64>){ match LAZY{ - true => self.dft.forward_inplace_lazy(&mut poly.0), - false => self.dft.forward_inplace(&mut poly.0) + true => self.dft.backward_inplace_lazy(&mut poly.0), + false => self.dft.backward_inplace(&mut poly.0) } } pub fn ntt<const LAZY:bool>(&self, poly_in: &Poly<u64>, poly_out: &mut Poly<u64>){ poly_out.0.copy_from_slice(&poly_in.0); match LAZY{ - true => self.dft.backward_inplace_lazy(&mut poly_out.0), - false => self.dft.backward_inplace(&mut poly_out.0) + true => self.dft.forward_inplace_lazy(&mut poly_out.0), + false => self.dft.forward_inplace(&mut poly_out.0) } }@@ -118,6 +118,19 @@ pub fn mul_montgomery_external_inplace<const REDUCE:REDUCEMOD>(&self, a:&Poly<Montgomery<u64>>, b:&mut Poly<u64>){
debug_assert!(a.n() == self.n(), "a.n()={} != n={}", a.n(), self.n()); debug_assert!(b.n() == self.n(), "b.n()={} != n={}", b.n(), self.n()); self.modulus.vec_mul_montgomery_external_unary_assign::<CHUNK, REDUCE>(&a.0, &mut b.0); + } + + #[inline(always)] + pub fn mul_scalar<const REDUCE:REDUCEMOD>(&self, a:&Poly<u64>, b: &u64, c:&mut Poly<u64>){ + debug_assert!(a.n() == self.n(), "b.n()={} != n={}", a.n(), self.n()); + debug_assert!(c.n() == self.n(), "c.n()={} != n={}", c.n(), self.n()); + self.modulus.vec_mul_scalar_barrett_external_binary_assign::<CHUNK, REDUCE>(&self.modulus.barrett.prepare(*b), &a.0, &mut c.0); + } + + #[inline(always)] + pub fn mul_scalar_inplace<const REDUCE:REDUCEMOD>(&self, a:&u64, b:&mut Poly<u64>){ + debug_assert!(b.n() == self.n(), "b.n()={} != n={}", b.n(), self.n()); + self.modulus.vec_mul_scalar_barrett_external_unary_assign::<CHUNK, REDUCE>(&self.modulus.barrett.prepare(self.modulus.barrett.reduce::<BARRETT>(a)), &mut b.0); } #[inline(always)]
@@ -1,7 +1,7 @@
use crate::ring::{Ring, RingRNS}; use crate::poly::PolyRNS; use crate::modulus::montgomery::Montgomery; -use crate::modulus::barrett::Barrett; +use crate::modulus::barrett::BarrettRNS; use crate::modulus::REDUCEMOD; use num_bigint::BigInt;@@ -25,19 +25,19 @@ self.0.iter().enumerate().for_each(|(_, r)|modulus *= BigInt::from(r.modulus.q));
modulus } - pub fn rescaling_constant(&self) -> Vec<Barrett<u64>> { + pub fn rescaling_constant(&self) -> BarrettRNS<u64> { let level = self.level(); let q_scale: u64 = self.0[level].modulus.q; - (0..level).map(|i| {self.0[i].modulus.barrett.prepare(self.0[i].modulus.q - self.0[i].modulus.inv(q_scale))}).collect() + BarrettRNS((0..level).map(|i| {self.0[i].modulus.barrett.prepare(self.0[i].modulus.q - self.0[i].modulus.inv(q_scale))}).collect()) } - pub fn set_poly_from_bigint(&self, coeffs: &[BigInt], step:usize, a: &mut PolyRNS<u64>){ + pub fn from_bigint_inplace(&self, coeffs: &[BigInt], step:usize, a: &mut PolyRNS<u64>){ let level = self.level(); assert!(level <= a.level(), "invalid level: level={} > a.level()={}", level, a.level()); (0..level).for_each(|i|{self.0[i].from_bigint(coeffs, step, a.at_mut(i))}); } - pub fn set_bigint_from_poly(&self, a: &PolyRNS<u64>, step: usize, coeffs: &mut [BigInt]){ + pub fn to_bigint_inplace(&self, a: &PolyRNS<u64>, step: usize, coeffs: &mut [BigInt]){ assert!(step <= a.n(), "invalid step: step={} > a.n()={}", step, a.n()); assert!(coeffs.len() <= a.n() / step, "invalid coeffs: coeffs.len()={} > a.n()/step={}", coeffs.len(), a.n()/step);@@ -47,7 +47,7 @@ let q_big_half: BigInt = &q_big>>1;
inv_crt.iter_mut().enumerate().for_each(|(i, a)|{ let qi_big = BigInt::from(self.0[i].modulus.q); - *a = (&q_big / &qi_big); + *a = &q_big / &qi_big; *a *= a.modinv(&qi_big).unwrap(); });@@ -65,78 +65,93 @@ }
} impl RingRNS<'_, u64>{ + pub fn ntt_inplace<const LAZY:bool>(&self, a: &mut PolyRNS<u64>){ + self.0.iter().enumerate().for_each(|(i, ring)| ring.ntt_inplace::<LAZY>(&mut a.0[i])); + } + + pub fn intt_inplace<const LAZY:bool>(&self, a: &mut PolyRNS<u64>){ + self.0.iter().enumerate().for_each(|(i, ring)| ring.intt_inplace::<LAZY>(&mut a.0[i])); + } + + pub fn ntt<const LAZY:bool>(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ + self.0.iter().enumerate().for_each(|(i, ring)| ring.ntt::<LAZY>(&a.0[i], &mut b.0[i])); + } + + pub fn intt<const LAZY:bool>(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ + self.0.iter().enumerate().for_each(|(i, ring)| ring.intt::<LAZY>(&a.0[i], &mut b.0[i])); + } +} + +impl RingRNS<'_, u64>{ #[inline(always)] pub fn add<const REDUCE: REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &PolyRNS<u64>, c: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - debug_assert!(c.level() >= level, "c.level()={} < level={}", c.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.add::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + debug_assert!(c.level() >= self.level(), "c.level()={} < self.level()={}", c.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.add::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); } #[inline(always)] pub fn add_inplace<const REDUCE: REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.add_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.add_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); } #[inline(always)] pub fn sub<const REDUCE: REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &PolyRNS<u64>, c: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - debug_assert!(c.level() >= level, "c.level()={} < level={}", c.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.sub::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + debug_assert!(c.level() >= self.level(), "c.level()={} < self.level()={}", c.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.sub::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); } #[inline(always)] pub fn sub_inplace<const REDUCE: REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.sub_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.sub_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); } #[inline(always)] pub fn neg<const REDUCE: REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.neg::<REDUCE>(&a.0[i], &mut b.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.neg::<REDUCE>(&a.0[i], &mut b.0[i])); } #[inline(always)] pub fn neg_inplace<const REDUCE: REDUCEMOD>(&self, a: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.neg_inplace::<REDUCE>(&mut a.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.neg_inplace::<REDUCE>(&mut a.0[i])); } #[inline(always)] pub fn mul_montgomery_external<const REDUCE:REDUCEMOD>(&self, a:&PolyRNS<Montgomery<u64>>, b:&PolyRNS<u64>, c: &mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - debug_assert!(c.level() >= level, "c.level()={} < level={}", c.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.mul_montgomery_external::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + debug_assert!(c.level() >= self.level(), "c.level()={} < self.level()={}", c.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.mul_montgomery_external::<REDUCE>(&a.0[i], &b.0[i], &mut c.0[i])); } #[inline(always)] pub fn mul_montgomery_external_inplace<const REDUCE:REDUCEMOD>(&self, a:&PolyRNS<Montgomery<u64>>, b:&mut PolyRNS<u64>){ - let level: usize = self.level(); - debug_assert!(self.max_level() <= level, "max_level={} < level={}", self.max_level(), level); - debug_assert!(a.level() >= level, "a.level()={} < level={}", a.level(), level); - debug_assert!(b.level() >= level, "b.level()={} < level={}", b.level(), level); - self.0.iter().take(level + 1).enumerate().for_each(|(i, ring)| ring.mul_montgomery_external_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.mul_montgomery_external_inplace::<REDUCE>(&a.0[i], &mut b.0[i])); + } + + #[inline(always)] + pub fn mul_scalar<const REDUCE:REDUCEMOD>(&self, a: &PolyRNS<u64>, b: &u64, c: &mut PolyRNS<u64>){ + debug_assert!(a.level() >= self.level(), "a.level()={} < self.level()={}", a.level(), self.level()); + debug_assert!(c.level() >= self.level(), "b.level()={} < self.level()={}", c.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.mul_scalar::<REDUCE>(&a.0[i], b, &mut c.0[i])); + } + + #[inline(always)] + pub fn mul_scalar_inplace<const REDUCE:REDUCEMOD>(&self, a: &u64, b: &mut PolyRNS<u64>){ + debug_assert!(b.level() >= self.level(), "b.level()={} < self.level()={}", b.level(), self.level()); + self.0.iter().enumerate().for_each(|(i, ring)| ring.mul_scalar_inplace::<REDUCE>(a, &mut b.0[i])); } }