arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/arith/src/torus.rs
arith/src/torus.rsRaw
  1use rand::{distributions::Distribution, Rng};
  2use std::{
  3    iter::Sum,
  4    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
  5};
  6
  7use crate::ring::Ring;
  8
  9/// Let 𝕋 = ℝ/ℤ, where 𝕋 is a ℤ-module, with homogeneous external product.
 10/// Let 𝕋q
 11/// T64 is 𝕋q with q=2^Ω, with Ω=64. We identify 𝕋q=(1/q)ℤ/ℤ ≈ ℤq.
 12#[derive(Debug, Clone, Copy, PartialEq)]
 13pub struct T64(pub u64);
 14
 15// implement the `Ring` trait for T64, so that it can be used where we would use
 16// `Tn<1>`.
 17impl Ring for T64 {
 18    type C = T64;
 19    // type Param = ();
 20
 21    // const Q: u64 = u64::MAX; // WIP
 22    // const N: usize = 1;
 23
 24    fn param(&self) -> Self::Param {
 25        ()
 26    }
 27    fn coeffs(&self) -> Vec<T64> {
 28        vec![self.clone()]
 29    }
 30    fn zero(_: ()) -> Self {
 31        Self(0u64)
 32    }
 33    fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, _: ()) -> Self {
 34        let r: f64 = dist.sample(&mut rng);
 35        Self(r.round() as u64)
 36    }
 37    fn from_vec(_n: (), coeffs: Vec<Self::C>) -> Self {
 38        assert_eq!(coeffs.len(), 1);
 39        coeffs[0]
 40    }
 41
 42    // TODO rm beta & l from inputs, make it always beta=2,l=64.
 43    /// Note: only beta=2 and l=64 is supported.
 44    fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {
 45        assert_eq!(beta, 2u32); // only beta=2 supported
 46                                // assert_eq!(l, 64u32); // only l=64 supported
 47
 48        // (0..64)
 49        (0..l)
 50            .rev()
 51            .map(|i| T64(((self.0 >> i) & 1) as u64))
 52            .collect()
 53    }
 54    fn remodule(&self, p: u64) -> T64 {
 55        todo!()
 56    }
 57
 58    // modulus switch from Q to Q2: self * Q2/Q
 59    fn mod_switch(&self, q2: u64) -> T64 {
 60        // for the moment we assume Q|Q2, since Q=2^64, check that Q2 is a power
 61        // of two:
 62        assert!(q2.is_power_of_two());
 63        // since Q=2^64, dividing Q2/Q is equivalent to dividing 2^log2(Q2)/2^64
 64        // which would be like right-shifting 64-log2(Q2).
 65        let log2_q2 = 63 - q2.leading_zeros();
 66        T64(self.0 >> (64 - log2_q2))
 67    }
 68
 69    fn mul_div_round(&self, num: u64, den: u64) -> Self {
 70        T64(((num as f64 * self.0 as f64) / den as f64).round() as u64)
 71    }
 72}
 73
 74impl T64 {
 75    pub fn rand_u64(mut rng: impl Rng, dist: impl Distribution<u64>) -> Self {
 76        let r: u64 = dist.sample(&mut rng);
 77        Self(r)
 78    }
 79}
 80
 81impl Add<T64> for T64 {
 82    type Output = Self;
 83
 84    fn add(self, rhs: Self) -> Self::Output {
 85        Self(self.0.wrapping_add(rhs.0))
 86    }
 87}
 88impl AddAssign for T64 {
 89    fn add_assign(&mut self, rhs: Self) {
 90        self.0 = self.0.wrapping_add(rhs.0)
 91    }
 92}
 93
 94impl Sub<T64> for T64 {
 95    type Output = Self;
 96
 97    fn sub(self, rhs: Self) -> Self::Output {
 98        Self(self.0.wrapping_sub(rhs.0))
 99    }
100}
101impl SubAssign for T64 {
102    fn sub_assign(&mut self, rhs: Self) {
103        self.0 = self.0.wrapping_sub(rhs.0)
104    }
105}
106
107impl Neg for T64 {
108    type Output = Self;
109
110    fn neg(self) -> Self::Output {
111        Self(self.0.wrapping_neg())
112    }
113}
114
115impl Sum for T64 {
116    fn sum<I>(iter: I) -> Self
117    where
118        I: Iterator<Item = Self>,
119    {
120        iter.fold(Self(0), |acc, x| acc + x)
121    }
122}
123
124impl Mul<T64> for T64 {
125    type Output = Self;
126
127    fn mul(self, rhs: Self) -> Self::Output {
128        Self(self.0.wrapping_mul(rhs.0))
129    }
130}
131impl MulAssign for T64 {
132    fn mul_assign(&mut self, rhs: Self) {
133        self.0 = self.0.wrapping_mul(rhs.0)
134    }
135}
136
137// mul by u64
138impl Mul<u64> for T64 {
139    type Output = Self;
140
141    fn mul(self, s: u64) -> Self {
142        if self.0 == 0 {
143            return Self(0);
144        }
145        Self(self.0.wrapping_mul(s))
146    }
147}
148impl Mul<&u64> for &T64 {
149    type Output = T64;
150
151    fn mul(self, s: &u64) -> Self::Output {
152        T64(self.0.wrapping_mul(*s))
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use rand::distributions::Standard;
160
161    fn recompose(d: Vec<T64>) -> T64 {
162        T64(d.iter().fold(0u64, |acc, &b| (acc << 1) | b.0))
163    }
164    #[test]
165    fn test_decompose() {
166        let beta: u32 = 2;
167        let l: u32 = 64;
168
169        let x = T64(12345);
170        let d = x.decompose(beta, l);
171        assert_eq!(recompose(d), T64(12345));
172
173        let x = T64(0);
174        let d = x.decompose(beta, l);
175        assert_eq!(recompose(d), T64(0));
176
177        let x = T64(u64::MAX - 1);
178        let d = x.decompose(beta, l);
179        assert_eq!(recompose(d), T64(u64::MAX - 1));
180
181        let mut rng = rand::thread_rng();
182        for _ in 0..1000 {
183            let x = T64::rand(&mut rng, Standard, ());
184            let d = x.decompose(beta, l);
185            assert_eq!(recompose(d), x);
186        }
187    }
188}