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}