use crate::kem::ntru_prime::constants::P;
pub struct R3Table {
mul_table: [[i8; 3]; 3],
}
impl R3Table {
pub const fn new() -> Self {
Self {
mul_table: [
[1, 0, -1],
[0, 0, 0],
[-1, 0, 1],
],
}
}
#[inline(always)]
pub const fn mul(&self, a: i8, b: i8) -> i8 {
let ai = (a + 1) as usize;
let bi = (b + 1) as usize;
self.mul_table[ai][bi]
}
}
pub const R3_MUL_TABLE: R3Table = R3Table::new();
pub struct SmallProductTable {
table: Vec<Vec<i8>>,
}
impl SmallProductTable {
pub fn new() -> Self {
let size = 81; let mut table = vec![vec![0i8; size * 4]; size];
for a in 0..size {
let a_poly = decode_4(a as u8);
for b in 0..size {
let b_poly = decode_4(b as u8);
let product = poly_mul_r3_small(&a_poly, &b_poly);
let offset = b * 4;
table[a][offset..offset + 4].copy_from_slice(&product[..4]);
}
}
Self { table }
}
#[inline(always)]
pub fn get(&self, a: usize, b: usize) -> &[i8] {
let offset = b * 4;
&self.table[a][offset..offset + 4]
}
}
#[inline(always)]
fn decode_4(mut x: u8) -> [i8; 4] {
let mut result = [0i8; 4];
for i in 0..4 {
result[i] = ((x % 3) as i8) - 1;
x /= 3;
}
result
}
#[inline(always)]
fn poly_mul_r3_small(a: &[i8; 4], b: &[i8; 4]) -> [i8; 7] {
let mut result = [0i8; 7];
for i in 0..4 {
for j in 0..4 {
result[i + j] = freeze_r3(result[i + j] as i32 + a[i] as i32 * b[j] as i32);
}
}
result
}
#[inline(always)]
const fn freeze_r3_const(a: i32) -> i8 {
let b = a - 3 * ((10_923 * a) >> 15);
let c = b - 3 * ((89_478_485 * b + 134_217_728) >> 28);
c as i8
}
#[inline(always)]
fn freeze_r3(a: i32) -> i8 {
freeze_r3_const(a)
}
pub struct WeightTable {
table: [i32; 256], }
impl WeightTable {
pub const fn new() -> Self {
let mut table = [0i32; 256];
let mut i = 0;
while i < 256 {
let val = (i as i8) as i32;
table[i] = val.abs();
i += 1;
}
Self { table }
}
#[inline(always)]
pub const fn weight(&self, a: i8) -> i32 {
self.table[(a as u8) as usize]
}
}
pub const WEIGHT_TABLE: WeightTable = WeightTable::new();
pub struct Mod3Table {
table: [i8; 128], }
impl Mod3Table {
pub const fn new() -> Self {
let mut table = [0i8; 128];
let mut i = 0;
while i < 128 {
let val = i as i32 - 64;
let reduced = freeze_r3_const(val);
table[i] = reduced;
i += 1;
}
Self { table }
}
#[inline(always)]
pub fn reduce(&self, a: i32) -> i8 {
if a >= -64 && a < 64 {
self.table[(a + 64) as usize]
} else {
freeze_r3(a)
}
}
}
pub const MOD3_TABLE: Mod3Table = Mod3Table::new();
pub struct ModQTable {
table: Vec<i16>,
}
impl ModQTable {
pub fn new() -> Self {
let mut table = vec![0i16; 16384];
for i in -8192i32..8192 {
table[(i + 8192) as usize] = freeze_rq(i);
}
Self { table }
}
#[inline(always)]
pub fn reduce(&self, a: i32) -> i16 {
if a >= -8192 && a < 8192 {
self.table[(a + 8192) as usize]
} else {
freeze_rq(a)
}
}
}
#[inline(always)]
fn freeze_rq(a: i32) -> i16 {
let b = a - 4_591 * ((228 * a) >> 20);
let c = b - 4_591 * ((58_470 * b + 134_217_728) >> 28);
c as i16
}
pub struct TwiddleTable {
factors: Vec<Vec<i32>>,
}
impl TwiddleTable {
pub fn new() -> Self {
const ROOT: i32 = 4; const Q_VAL: i32 = 4_591;
let mut factors = Vec::new();
let mut size_log = 1_usize; while size_log <= 10 {
let size = 1_usize << size_log;
let mut row = Vec::with_capacity(size / 2);
for j in 0..(size / 2) {
let exp = ((Q_VAL - 1) / size as i32 * j as i32) % Q_VAL;
let twiddle = mod_pow(ROOT, exp, Q_VAL);
row.push(twiddle);
}
factors.push(row);
size_log += 1;
}
Self { factors }
}
#[inline(always)]
pub fn get(&self, size: usize) -> Option<&[i32]> {
let idx = size.ilog2().saturating_sub(1) as usize;
self.factors.get(idx).map(|v| v.as_slice())
}
}
#[inline(always)]
fn mod_pow(mut base: i32, mut exp: i32, modulus: i32) -> i32 {
let mut result = 1;
base = ((base % modulus) + modulus) % modulus;
while exp > 0 {
if exp & 1 == 1 {
result = (result * base) % modulus;
}
exp >>= 1;
base = (base * base) % modulus;
}
result
}
#[cfg(feature = "pqc-simd")]
use once_cell::sync::Lazy;
#[cfg(feature = "pqc-simd")]
pub static SMALL_PRODUCT_TABLE: Lazy<SmallProductTable> = Lazy::new(|| SmallProductTable::new());
#[cfg(feature = "pqc-simd")]
pub static MOD_Q_TABLE: Lazy<ModQTable> = Lazy::new(|| ModQTable::new());
#[cfg(feature = "pqc-simd")]
pub static TWIDDLE_TABLE: Lazy<TwiddleTable> = Lazy::new(|| TwiddleTable::new());
#[inline(always)]
pub fn r3_mul_table(a: i8, b: i8) -> i8 {
R3_MUL_TABLE.mul(a, b)
}
#[inline(always)]
pub fn mod3_table(a: i32) -> i8 {
MOD3_TABLE.reduce(a)
}
#[inline(always)]
pub fn abs_table(a: i8) -> i32 {
WEIGHT_TABLE.weight(a)
}
pub fn count_weight_table(poly: &[i8]) -> i32 {
poly.iter().map(|&x| abs_table(x)).sum()
}
pub fn mod3_batch_table(values: &mut [i8]) {
for i in 0..values.len() {
values[i] = mod3_table(values[i] as i32);
}
}
pub fn r3_mult_table(h: &mut [i8], f: &[i8], g: &[i8]) {
let n = f.len();
let mut fg = [0i8; 1521];
for i in 0..n {
let mut r = 0i32;
for j in 0..=i {
let prod = r3_mul_table(f[j], g[i - j]);
r += prod as i32;
}
fg[i] = mod3_table(r);
}
for i in n..(2 * n - 1) {
let mut r = 0i32;
for j in (i - n + 1)..n {
let prod = r3_mul_table(f[j], g[i - j]);
r += prod as i32;
}
fg[i] = mod3_table(r);
}
for i in (n..(2 * n) - 1).rev() {
let tmp1 = mod3_table(fg[i - n] as i32 + fg[i] as i32);
fg[i - n] = tmp1;
let tmp2 = mod3_table(fg[i - n + 1] as i32 + fg[i] as i32);
fg[i - n + 1] = tmp2;
}
h[..n].copy_from_slice(&fg[..n]);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_r3_mul_table() {
assert_eq!(r3_mul_table(-1, -1), 1);
assert_eq!(r3_mul_table(-1, 0), 0);
assert_eq!(r3_mul_table(-1, 1), -1);
assert_eq!(r3_mul_table(0, 0), 0);
assert_eq!(r3_mul_table(1, 1), 1);
}
#[test]
fn test_mod3_table() {
assert_eq!(mod3_table(0), 0);
assert_eq!(mod3_table(3), 0);
assert_eq!(mod3_table(4), 1);
assert_eq!(mod3_table(-1), -1);
assert_eq!(mod3_table(-4), -1);
}
#[test]
fn test_abs_table() {
assert_eq!(abs_table(0), 0);
assert_eq!(abs_table(1), 1);
assert_eq!(abs_table(-1), 1);
assert_eq!(abs_table(5), 5);
assert_eq!(abs_table(-5), 5);
}
#[test]
fn test_count_weight_table() {
let poly = [1i8, 0, -1, 1, 1, 0, -1, -1];
assert_eq!(count_weight_table(&poly), 6);
}
#[test]
fn test_r3_mult_table_small() {
let mut h = [0i8; 5];
let f = [1i8, 1, 1, 1, 1];
let g = [1i8, 1, 1, 1, 1];
r3_mult_table(&mut h, &f, &g);
assert_eq!(h[0], 1);
assert_eq!(h[1], 0); assert_eq!(h[2], 0); }
#[test]
fn test_twiddle_table() {
#[cfg(feature = "pqc-simd")]
{
let table = &*TWIDDLE_TABLE;
if let Some(factors) = table.get(8) {
assert_eq!(factors.len(), 4);
assert_eq!(factors[0], 1);
}
}
}
#[test]
fn test_freeze_rq() {
assert_eq!(freeze_rq(0), 0);
assert_eq!(freeze_rq(4591), 0);
assert_eq!(freeze_rq(-4591), 0);
assert_eq!(freeze_rq(10000), 10000 - 4591 * 2);
}
}