//! This file implements the struct for an Tuple of Ring Rq elements and its //! operations, which are performed element-wise. use anyhow::Result; use itertools::zip_eq; use rand::{distributions::Distribution, Rng}; use rand_distr::{Normal, Uniform}; use std::iter::Sum; use std::{ array, ops::{Add, Mul, Neg, Sub}, }; use crate::Ring; /// Tuple of K Ring (Rq) elements. We use Vec to allocate it in the heap, /// since if using a fixed-size array it would overflow the stack. #[derive(Clone, Debug)] pub struct TR { pub k: usize, pub r: Vec, } // TODO rm pub from Vec, so that TR can not be created from a Vec with // invalid length, since it has to be created using the `new` method. impl TR { pub fn new(k: usize, r: Vec) -> Self { assert_eq!(r.len(), k); Self { k, r } } pub fn zero(k: usize, r_params: R::Params) -> Self { Self { k, r: (0..k).into_iter().map(|_| R::zero(r_params)).collect(), } } pub fn rand( mut rng: impl Rng, dist: impl Distribution, k: usize, r_params: R::Params, ) -> Self { Self { k, r: (0..k) .into_iter() .map(|_| R::rand(&mut rng, &dist, r_params)) .collect(), } } // returns the decomposition of each polynomial element pub fn decompose(&self, beta: u32, l: u32) -> Vec { unimplemented!() // self.0.iter().map(|r| r.decompose(beta, l)).collect() // this is Vec>> } } impl TR { pub fn mod_switch(&self, q2: u64) -> TR { TR:: { k: self.k, r: self.r.iter().map(|c_i| c_i.mod_switch(q2)).collect(), } } // pub fn mod_switch(&self, Q2: u64) -> TR { // TR(self.0.iter().map(|c_i| c_i.mod_switch(Q2)).collect()) // } } impl TR { pub fn left_rotate(&self, h: usize) -> Self { TR { k: self.k, r: self.r.iter().map(|c_i| c_i.left_rotate(h)).collect(), } } } impl TR { pub fn iter(&self) -> std::slice::Iter { self.r.iter() } } impl Add> for TR { type Output = Self; fn add(self, other: Self) -> Self { debug_assert_eq!(self.k, other.k); Self { k: self.k, r: zip_eq(self.r, other.r) .map(|(s, o)| s + o) .collect::>(), } } } impl Sub> for TR { type Output = Self; fn sub(self, other: Self) -> Self { debug_assert_eq!(self.k, other.k); Self { k: self.k, r: zip_eq(self.r, other.r).map(|(s, o)| s - o).collect(), } } } impl Neg for TR { type Output = Self; fn neg(self) -> Self::Output { Self { k: self.k, r: self.r.iter().map(|e_i| -*e_i).collect(), } } } /// for (TR,TR), the Mul operation is defined as the dot product: /// for A, B \in R^k, result = Σ A_i * B_i \in R impl Mul> for TR { type Output = R; fn mul(self, other: Self) -> R { debug_assert_eq!(self.k, other.k); zip_eq(self.r, other.r).map(|(s, o)| s * o).sum() } } impl Mul<&TR> for &TR { type Output = R; fn mul(self, other: &TR) -> R { debug_assert_eq!(self.k, other.k); zip_eq(self.r.clone(), other.r.clone()) .map(|(s, o)| s * o) .sum() } } /// for (TR, R), the Mul operation is defined as each element of TR is /// multiplied by R impl Mul for TR { type Output = TR; fn mul(self, other: R) -> TR { Self { k: self.k, r: self.r.iter().map(|s| s.clone() * other.clone()).collect(), } } } impl Mul<&R> for &TR { type Output = TR; fn mul(self, other: &R) -> TR { TR:: { k: self.k, r: self.r.iter().map(|s| s.clone() * other.clone()).collect(), } } }