arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/utils.rs
src/utils.rsRaw
  1use std::{fmt::Debug, usize, vec};
  2
  3use itertools::{izip, Itertools};
  4use num_traits::{FromPrimitive, One, PrimInt, Signed};
  5
  6use crate::{
  7    backend::Modulus,
  8    random::{RandomElementInModulus, RandomFill},
  9    Matrix, Row, RowEntity, RowMut,
 10};
 11pub trait WithLocal {
 12    fn with_local<F, R>(func: F) -> R
 13    where
 14        F: Fn(&Self) -> R;
 15
 16    fn with_local_mut<F, R>(func: F) -> R
 17    where
 18        F: Fn(&mut Self) -> R;
 19
 20    fn with_local_mut_mut<F, R>(func: &mut F) -> R
 21    where
 22        F: FnMut(&mut Self) -> R;
 23}
 24
 25pub trait Global {
 26    fn global() -> &'static Self;
 27}
 28
 29pub(crate) trait ShoupMul {
 30    fn representation(value: Self, q: Self) -> Self;
 31    fn mul(a: Self, b: Self, b_shoup: Self, q: Self) -> Self;
 32}
 33
 34impl ShoupMul for u64 {
 35    #[inline]
 36    fn representation(value: Self, q: Self) -> Self {
 37        ((value as u128 * (1u128 << 64)) / q as u128) as u64
 38    }
 39
 40    #[inline]
 41    /// Returns a * b % q
 42    fn mul(a: Self, b: Self, b_shoup: Self, q: Self) -> Self {
 43        (b.wrapping_mul(a))
 44            .wrapping_sub(q.wrapping_mul(((b_shoup as u128 * a as u128) >> 64) as u64))
 45    }
 46}
 47
 48pub(crate) trait ToShoup {
 49    type Modulus;
 50    fn to_shoup(value: &Self, modulus: Self::Modulus) -> Self;
 51}
 52
 53impl ToShoup for u64 {
 54    type Modulus = u64;
 55    fn to_shoup(value: &Self, modulus: Self) -> Self {
 56        ((*value as u128 * (1u128 << 64)) / modulus as u128) as u64
 57    }
 58}
 59
 60impl ToShoup for Vec<Vec<u64>> {
 61    type Modulus = u64;
 62    fn to_shoup(value: &Self, modulus: Self::Modulus) -> Self {
 63        let (row, col) = value.dimension();
 64        let mut shoup_value = vec![vec![0u64; col]; row];
 65        izip!(shoup_value.iter_mut(), value.iter()).for_each(|(shoup_r, r)| {
 66            izip!(shoup_r.iter_mut(), r.iter()).for_each(|(s, e)| {
 67                *s = u64::to_shoup(e, modulus);
 68            })
 69        });
 70        shoup_value
 71    }
 72}
 73
 74pub fn fill_random_ternary_secret_with_hamming_weight<
 75    T: Signed,
 76    R: RandomFill<[u8]> + RandomElementInModulus<usize, usize>,
 77>(
 78    out: &mut [T],
 79    hamming_weight: usize,
 80    rng: &mut R,
 81) {
 82    let mut bytes = vec![0u8; hamming_weight.div_ceil(8)];
 83    RandomFill::<[u8]>::random_fill(rng, &mut bytes);
 84
 85    let size = out.len();
 86    let mut secret_indices = (0..size).into_iter().map(|i| i).collect_vec();
 87    let mut bit_index = 0;
 88    let mut byte_index = 0;
 89    for i in 0..hamming_weight {
 90        let s_index = RandomElementInModulus::<usize, usize>::random(rng, &secret_indices.len());
 91
 92        let curr_bit = (bytes[byte_index] >> bit_index) & 1;
 93        if curr_bit == 1 {
 94            out[secret_indices[s_index]] = T::one();
 95        } else {
 96            out[secret_indices[s_index]] = -T::one();
 97        }
 98
 99        secret_indices[s_index] = *secret_indices.last().unwrap();
100        secret_indices.truncate(secret_indices.len() - 1);
101
102        if bit_index == 7 {
103            bit_index = 0;
104            byte_index += 1;
105        } else {
106            bit_index += 1;
107        }
108    }
109}
110
111// TODO (Jay): this is only a workaround. Add a propoer way to perform primality
112// tests.
113fn is_probably_prime(candidate: u64) -> bool {
114    num_bigint_dig::prime::probably_prime(&num_bigint_dig::BigUint::from(candidate), 0)
115}
116
117/// Finds prime that satisfy
118/// - $prime \lt upper_bound$
119/// - $\log{prime} = num_bits$
120/// - `prime % modulo == 1`
121pub(crate) fn generate_prime(num_bits: usize, modulo: u64, upper_bound: u64) -> Option<u64> {
122    let leading_zeros = (64 - num_bits) as u32;
123
124    let mut tentative_prime = upper_bound - 1;
125    while tentative_prime % modulo != 1 && tentative_prime.leading_zeros() == leading_zeros {
126        tentative_prime -= 1;
127    }
128
129    while !is_probably_prime(tentative_prime)
130        && tentative_prime.leading_zeros() == leading_zeros
131        && tentative_prime >= modulo
132    {
133        tentative_prime -= modulo;
134    }
135
136    if is_probably_prime(tentative_prime) && tentative_prime.leading_zeros() == leading_zeros {
137        Some(tentative_prime)
138    } else {
139        None
140    }
141}
142
143/// Returns a^b mod q
144pub fn mod_exponent(a: u64, mut b: u64, q: u64) -> u64 {
145    let mod_mul = |v1: &u64, v2: &u64| {
146        let tmp = *v1 as u128 * *v2 as u128;
147        (tmp % q as u128) as u64
148    };
149
150    let mut acc = a;
151    let mut out = 1;
152    while b != 0 {
153        let flag = b & 1;
154
155        if flag == 1 {
156            out = mod_mul(&acc, &out);
157        }
158        acc = mod_mul(&acc, &acc);
159
160        b >>= 1;
161    }
162
163    out
164}
165
166pub fn mod_inverse(a: u64, q: u64) -> u64 {
167    mod_exponent(a, q - 2, q)
168}
169
170pub fn negacyclic_mul<T: PrimInt, F: Fn(&T, &T) -> T>(
171    a: &[T],
172    b: &[T],
173    mul: F,
174    modulus: T,
175) -> Vec<T> {
176    let mut r = vec![T::zero(); a.len()];
177    for i in 0..a.len() {
178        for j in 0..i + 1 {
179            // println!("i: {j} {}", i - j);
180            r[i] = (r[i] + mul(&a[j], &b[i - j])) % modulus;
181        }
182
183        for j in i + 1..a.len() {
184            // println!("i: {j} {}", a.len() - j + i);
185            r[i] = (r[i] + modulus - mul(&a[j], &b[a.len() - j + i])) % modulus;
186        }
187        // println!("")
188    }
189
190    return r;
191}
192
193/// Returns a polynomial X^{emebedding_factor * si} \mod {Z_Q / X^{N}+1}
194pub(crate) fn encode_x_pow_si_with_emebedding_factor<
195    R: RowEntity + RowMut,
196    M: Modulus<Element = R::Element>,
197>(
198    si: i32,
199    embedding_factor: usize,
200    ring_size: usize,
201    modulus: &M,
202) -> R
203where
204    R::Element: One,
205{
206    assert!((si.abs() as usize) < ring_size);
207    let mut m = R::zeros(ring_size);
208    let si = si * (embedding_factor as i32);
209    if si < 0 {
210        // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N
211        m.as_mut()[ring_size - (si.abs() as usize)] = modulus.neg_one();
212    } else {
213        m.as_mut()[si as usize] = R::Element::one();
214    }
215    m
216}
217
218pub(crate) fn puncture_p_rng<S: Default + Copy, R: RandomFill<S>>(
219    p_rng: &mut R,
220    times: usize,
221) -> S {
222    let mut out = S::default();
223    for _ in 0..times {
224        RandomFill::<S>::random_fill(p_rng, &mut out);
225    }
226    return out;
227}
228
229pub trait TryConvertFrom1<T: ?Sized, P> {
230    fn try_convert_from(value: &T, parameters: &P) -> Self;
231}
232
233impl<P: Modulus<Element = u64>> TryConvertFrom1<[i64], P> for Vec<u64> {
234    fn try_convert_from(value: &[i64], parameters: &P) -> Self {
235        value
236            .iter()
237            .map(|v| parameters.map_element_from_i64(*v))
238            .collect_vec()
239    }
240}
241
242impl<P: Modulus<Element = u64>> TryConvertFrom1<[i32], P> for Vec<u64> {
243    fn try_convert_from(value: &[i32], parameters: &P) -> Self {
244        value
245            .iter()
246            .map(|v| parameters.map_element_from_i64(*v as i64))
247            .collect_vec()
248    }
249}
250
251impl<P: Modulus> TryConvertFrom1<[P::Element], P> for Vec<i64> {
252    fn try_convert_from(value: &[P::Element], parameters: &P) -> Self {
253        value
254            .iter()
255            .map(|v| parameters.map_element_to_i64(v))
256            .collect_vec()
257    }
258}
259
260#[cfg(test)]
261pub(crate) mod tests {
262    use std::fmt::Debug;
263
264    use num_traits::{FromPrimitive, PrimInt};
265    use rand::thread_rng;
266
267    use crate::random::DefaultSecureRng;
268
269    use super::fill_random_ternary_secret_with_hamming_weight;
270
271    #[derive(Clone)]
272    pub(crate) struct Stats<T> {
273        pub(crate) samples: Vec<T>,
274    }
275
276    impl<T: PrimInt + FromPrimitive + Debug> Stats<T>
277    where
278        // T: for<'a> Sum<&'a T>,
279        T: for<'a> std::iter::Sum<&'a T> + std::iter::Sum<T>,
280    {
281        pub(crate) fn new() -> Self {
282            Self { samples: vec![] }
283        }
284
285        pub(crate) fn mean(&self) -> f64 {
286            self.samples.iter().sum::<T>().to_f64().unwrap() / (self.samples.len() as f64)
287        }
288
289        pub(crate) fn std_dev(&self) -> f64 {
290            let mean = self.mean();
291
292            // diff
293            let diff_sq = self
294                .samples
295                .iter()
296                .map(|v| {
297                    let t = v.to_f64().unwrap() - mean;
298                    t * t
299                })
300                .into_iter()
301                .sum::<f64>();
302
303            (diff_sq / (self.samples.len() as f64)).sqrt()
304        }
305
306        pub(crate) fn add_more(&mut self, values: &[T]) {
307            self.samples.extend(values.iter());
308        }
309    }
310
311    #[test]
312    fn ternary_secret_has_correct_hw() {
313        let mut rng = DefaultSecureRng::new();
314        for n in 4..15 {
315            let ring_size = 1 << n;
316            let mut out = vec![0i32; ring_size];
317            fill_random_ternary_secret_with_hamming_weight(&mut out, ring_size >> 1, &mut rng);
318
319            // check hamming weight of out equals ring_size/2
320            let mut non_zeros = 0;
321            out.iter().for_each(|i| {
322                if *i != 0 {
323                    non_zeros += 1;
324                }
325            });
326
327            assert_eq!(ring_size >> 1, non_zeros);
328        }
329    }
330}