qfall_math/integer_mod_q/mat_ntt_polynomial_ring_zq/
from.rs

1// Copyright © 2025 Niklas Siemer
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! Implementations to create a [`MatNTTPolynomialRingZq`] value from other types.
10
11use super::MatNTTPolynomialRingZq;
12use crate::{
13    integer::Z,
14    integer_mod_q::{MatPolynomialRingZq, NTTPolynomialRingZq, PolynomialRingZq},
15    traits::{MatrixDimensions, MatrixGetEntry, MatrixSetEntry},
16};
17
18impl From<&MatPolynomialRingZq> for MatNTTPolynomialRingZq {
19    /// Computes the NTT representation of `matrix`.
20    ///
21    /// Parameters:
22    /// - `matrix`: the matrix that's going to be represented in NTT format.
23    ///
24    /// Returns the NTT representation as a [`MatNTTPolynomialRingZq`] of `matrix`.
25    ///
26    /// # Examples
27    /// ```
28    /// use qfall_math::integer_mod_q::{MatNTTPolynomialRingZq, MatPolynomialRingZq, ModulusPolynomialRingZq};
29    /// use std::str::FromStr;
30    /// let mut modulus = ModulusPolynomialRingZq::from_str("5  1 0 0 0 1 mod 257").unwrap();
31    /// modulus.set_ntt_unchecked(64);
32    ///
33    /// let mat_poly_ring = MatPolynomialRingZq::sample_uniform(2, 3, &modulus);
34    ///
35    /// let mat_ntt_poly_ring = MatNTTPolynomialRingZq::from(&mat_poly_ring);
36    /// ```
37    ///
38    /// # Panics ...
39    /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq),
40    ///   which is part of the [`ModulusPolynomialRingZq`](crate::integer_mod_q::ModulusPolynomialRingZq) in `matrix` is not set.
41    fn from(matrix: &MatPolynomialRingZq) -> Self {
42        let degree = matrix.get_mod().get_degree();
43        let nr_rows = matrix.get_num_rows();
44        let nr_columns = matrix.get_num_columns();
45
46        let mut res = Vec::with_capacity((degree * nr_rows * nr_columns) as usize);
47
48        for col in 0..nr_columns {
49            for row in 0..nr_rows {
50                let entry = unsafe { matrix.get_entry_unchecked(row, col) };
51                let mut ntt_poly = NTTPolynomialRingZq::from(&entry);
52                res.append(&mut ntt_poly.poly);
53            }
54        }
55
56        MatNTTPolynomialRingZq {
57            matrix: res,
58            nr_rows: nr_rows as usize,
59            nr_columns: nr_columns as usize,
60            modulus: matrix.modulus.clone(),
61        }
62    }
63}
64
65impl MatNTTPolynomialRingZq {
66    /// Computes the inverse NTT of `self` with respect to the given `modulus`.
67    ///
68    /// Returns a new [`MatPolynomialRingZq`] with the entries from `self`.
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, MatNTTPolynomialRingZq, ModulusPolynomialRingZq};
73    /// use std::str::FromStr;
74    /// let mut modulus = ModulusPolynomialRingZq::from_str("5  1 0 0 0 1 mod 257").unwrap();
75    /// modulus.set_ntt_unchecked(64);
76    /// let ntt_mat = MatNTTPolynomialRingZq::sample_uniform(1, 1, &modulus);
77    ///
78    /// let poly_ring_mat = ntt_mat.inv_ntt();
79    /// ```
80    ///
81    /// # Panics ...
82    /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq) in `modulus`
83    ///   is not set.
84    pub fn inv_ntt(mut self) -> MatPolynomialRingZq {
85        let height = self.nr_rows;
86        let width = self.nr_columns;
87
88        let mut res = MatPolynomialRingZq::new(height, width, &self.modulus);
89        for column in (0..width).rev() {
90            for row in (0..height).rev() {
91                let index = self.modulus.get_degree() as usize * row
92                    + self.modulus.get_degree() as usize * self.nr_rows * column;
93                let entry = PolynomialRingZq::from(NTTPolynomialRingZq {
94                    poly: self.matrix.split_off(index).iter().map(Z::from).collect(),
95                    modulus: self.modulus.clone(),
96                });
97                unsafe { res.set_entry_unchecked(row as i64, column as i64, entry) };
98            }
99        }
100        res
101    }
102}
103
104#[cfg(test)]
105mod test_from {
106    use super::MatNTTPolynomialRingZq;
107    use crate::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
108    use std::str::FromStr;
109
110    /// Ensures that the transform to NTT representation and back works properly.
111    #[test]
112    fn round_trip() {
113        let mut modulus = ModulusPolynomialRingZq::from_str("5  1 0 0 0 1 mod 257").unwrap();
114        modulus.set_ntt_unchecked(64);
115        let matrix = MatPolynomialRingZq::sample_uniform(3, 5, &modulus);
116
117        let ntt_matrix = MatNTTPolynomialRingZq::from(&matrix);
118
119        let cmp_matrix = MatPolynomialRingZq::from(ntt_matrix);
120
121        assert_eq!(matrix, cmp_matrix);
122    }
123}