Skip to main content

qfall_math/integer_mod_q/polynomial_ring_zq/
set.rs

1// Copyright 2023 Marcel Luca Schmidt
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 manipulate a [`PolynomialRingZq`] element.
10
11use super::PolynomialRingZq;
12use crate::{
13    integer::Z,
14    integer_mod_q::{Modulus, ModulusPolynomialRingZq, Zq},
15    macros::for_others::implement_for_owned,
16    traits::SetCoefficient,
17};
18use flint3_sys::fmpz_poly_set_coeff_fmpz;
19
20impl<Integer: Into<Z>> SetCoefficient<Integer> for PolynomialRingZq {
21    /// Sets the coefficient of a [`PolynomialRingZq`] element.
22    /// We advise to use small coefficients, since already 2^32 coefficients take space
23    /// of roughly 34 GB. If not careful, be prepared that memory problems can occur, if
24    /// the index is very high.
25    ///
26    /// Parameters:
27    /// - `index`: the index of the coefficient to set (has to be positive)
28    /// - `value`: the new value the index should have
29    ///
30    /// # Examples
31    /// ```
32    /// use crate::qfall_math::traits::SetCoefficient;
33    /// use qfall_math::integer::PolyOverZ;
34    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
35    /// use std::str::FromStr;
36    ///
37    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
38    /// let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
39    /// let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
40    ///
41    /// poly_ring.set_coeff(2, 16).unwrap();
42    /// unsafe{ poly_ring.set_coeff_unchecked(5, 5) };
43    /// ```
44    ///
45    /// # Safety
46    /// To use this function safely, make sure that the selected index
47    /// is greater or equal than `0` and that the provided value has
48    /// the same base so that they have a matching base.
49    unsafe fn set_coeff_unchecked(&mut self, index: i64, value: Integer) {
50        let value = value.into();
51
52        unsafe {
53            fmpz_poly_set_coeff_fmpz(&mut self.poly.poly, index, &value.value);
54        };
55        self.reduce();
56    }
57}
58
59impl SetCoefficient<&Zq> for PolynomialRingZq {
60    /// Sets the coefficient of a [`PolynomialRingZq`] element.
61    /// We advise to use small coefficients, since already 2^32 coefficients take space
62    /// of roughly 34 GB. If not careful, be prepared that memory problems can occur, if
63    /// the index is very high.
64    ///
65    /// This function does not check if the modulus of the polynomial and the value match.
66    ///
67    /// Parameters:
68    /// - `index`: the index of the coefficient to set (has to be positive)
69    /// - `value`: the new value the index should have
70    ///
71    /// # Examples
72    /// ```
73    /// use crate::qfall_math::traits::SetCoefficient;
74    /// use qfall_math::integer::PolyOverZ;
75    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
76    /// use qfall_math::integer_mod_q::Zq;
77    /// use std::str::FromStr;    
78    ///
79    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
80    /// let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
81    /// let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
82    /// let value = Zq::from((1000, 17));
83    ///
84    /// poly_ring.set_coeff(2, &value).unwrap();
85    /// unsafe{ poly_ring.set_coeff_unchecked(5, &value) };
86    /// ```
87    ///
88    /// # Safety
89    /// To use this function safely, make sure that the selected index
90    /// is greater or equal than `0` and that the provided value has
91    /// the same base so that they have a matching base.
92    unsafe fn set_coeff_unchecked(&mut self, index: i64, value: &Zq) {
93        unsafe { self.set_coeff_unchecked(index, &value.value) };
94    }
95}
96
97implement_for_owned!(Zq, PolynomialRingZq, SetCoefficient);
98
99impl PolynomialRingZq {
100    /// Changes the modulus of the given polynomial to the new modulus.
101    /// It takes the representation of each entry with coefficients in [0, q) as the new
102    /// coefficients and reduces them by the new [`ModulusPolynomialRingZq`].
103    ///
104    /// Parameters:
105    /// - `modulus`: the new modulus of the polynomial
106    ///
107    /// # Examples
108    /// ```
109    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
110    /// use std::str::FromStr;
111    /// let modulus0 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
112    /// let modulus1 = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 12").unwrap();
113    ///
114    /// let mut poly = PolynomialRingZq::from((13, modulus0));
115    ///
116    /// poly.change_modulus(modulus1);
117    ///
118    /// assert_eq!("1  1 / 3  1 0 1 mod 12", poly.to_string());
119    /// ```
120    ///
121    /// # Panics ...
122    /// - if `modulus` is smaller than `2`, or
123    /// - if the modulus polynomial is of degree smaller than `1`.
124    /// - if the leading coefficient is not `1.`
125    pub fn change_modulus(&mut self, modulus: impl Into<ModulusPolynomialRingZq>) {
126        self.modulus = modulus.into();
127        self.reduce();
128    }
129
130    /// Changes the modulus `q` of the given polynomial to the new modulus `q`.
131    /// It takes the representation of each entry with coefficients in `[0, q)` as the new
132    /// coefficients and reduces them by the new [`Modulus`].
133    ///
134    /// Parameters:
135    /// - `q`: the new modulus of the polynomial
136    ///
137    /// # Examples
138    /// ```
139    /// use qfall_math::integer_mod_q::PolynomialRingZq;
140    /// use std::str::FromStr;
141    ///
142    /// let mut poly = PolynomialRingZq::from_str("3  12 2 13 / 4  1 0 0 1 mod 17").unwrap();
143    ///
144    /// poly.change_q(12);
145    ///
146    /// assert_eq!("3  0 2 1 / 4  1 0 0 1 mod 12", poly.to_string());
147    /// ```
148    ///
149    /// # Panics ...
150    /// - if `modulus` is smaller than `2`.
151    pub fn change_q(&mut self, q: impl Into<Modulus>) {
152        self.modulus.change_q(q);
153        self.reduce();
154    }
155}
156
157#[cfg(test)]
158mod test_set_coeff {
159    use crate::{
160        integer::{PolyOverZ, Z},
161        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq, Zq},
162        traits::SetCoefficient,
163    };
164    use std::str::FromStr;
165
166    /// Ensure that setting is available for other types.
167    #[test]
168    fn availability() {
169        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
170        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
171        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
172
173        poly_ring.set_coeff(1, 3u64).unwrap();
174        poly_ring.set_coeff(1, 3u32).unwrap();
175        poly_ring.set_coeff(1, 3u16).unwrap();
176        poly_ring.set_coeff(1, 3u8).unwrap();
177        poly_ring.set_coeff(1, 3i64).unwrap();
178        poly_ring.set_coeff(1, 3i32).unwrap();
179        poly_ring.set_coeff(1, 3i16).unwrap();
180        poly_ring.set_coeff(1, 3i8).unwrap();
181        poly_ring.set_coeff(1, Z::from(3)).unwrap();
182        poly_ring.set_coeff(1, &Z::from(3)).unwrap();
183        poly_ring.set_coeff(1, &Zq::from((3, 17))).unwrap();
184    }
185
186    /// Ensure that large coefficients work.
187    #[test]
188    fn set_coeff_large() {
189        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
190        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
191        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
192
193        assert!(poly_ring.set_coeff(2, i32::MAX).is_ok());
194        assert!(poly_ring.set_coeff(2, i64::MAX).is_ok());
195    }
196
197    /// Ensure that the max of [`u8`] and [`u16`] works as an index.
198    #[test]
199    fn set_index_large() {
200        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
201        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
202        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
203
204        assert!(poly_ring.set_coeff(u8::MAX, 2).is_ok());
205        assert!(poly_ring.set_coeff(u16::MAX, 2).is_ok());
206    }
207
208    /// Ensure that a general case is working.
209    #[test]
210    fn set_coeff_working() {
211        let modulus = ModulusPolynomialRingZq::from_str("5  1 0 0 1 1 mod 17").unwrap();
212        let poly = PolyOverZ::from_str("3  0 2 1").unwrap();
213        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
214        let value = 10000;
215
216        poly_ring.set_coeff(0, value).unwrap();
217        poly_ring.set_coeff(2, value).unwrap();
218
219        let cmp_poly = PolyOverZ::from_str("3  10000 2 10000").unwrap();
220        let cmp_poly_ring = PolynomialRingZq::from((&cmp_poly, &modulus));
221        assert_eq!(cmp_poly_ring, poly_ring);
222    }
223
224    /// Ensure that the set polynomial is reduced correctly.
225    #[test]
226    fn set_reduce() {
227        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
228        let poly = PolyOverZ::from(1);
229        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
230
231        poly_ring.set_coeff(3, 1).unwrap();
232
233        let cmp_poly = PolyOverZ::from(0);
234        let cmp_poly_ring = PolynomialRingZq::from((&cmp_poly, &modulus));
235        assert_eq!(cmp_poly_ring, poly_ring);
236    }
237
238    /// Ensure that the correct coefficient is set and all others are set to `0`.
239    #[test]
240    fn set_coeff_rest_zero() {
241        let modulus = ModulusPolynomialRingZq::from_str("7  10 12 0 0 10 0 1 mod 17").unwrap();
242        let poly = PolyOverZ::from_str("2  0 2").unwrap();
243        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
244
245        poly_ring.set_coeff(5, 10).unwrap();
246
247        let cmp_poly = PolyOverZ::from_str("6  0 2 0 0 0 10").unwrap();
248        let cmp_poly_ring = PolynomialRingZq::from((&cmp_poly, &modulus));
249        assert_eq!(cmp_poly_ring, poly_ring);
250    }
251
252    /// Ensure that the negative indices return an error.
253    #[test]
254    fn set_negative_index() {
255        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
256        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
257        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
258
259        assert!(poly_ring.set_coeff(-1, 2).is_err());
260        assert!(poly_ring.set_coeff(i64::MIN, 2).is_err());
261        assert!(poly_ring.set_coeff(i32::MIN, 2).is_err());
262        assert!(poly_ring.set_coeff(i16::MIN, 2).is_err());
263        assert!(poly_ring.set_coeff(i8::MIN, 2).is_err());
264    }
265
266    /// Ensure that a different base returns an error.
267    #[test]
268    fn different_base() {
269        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
270        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
271        let mut poly_ring = PolynomialRingZq::from((&poly, &modulus));
272        let value = Zq::from((13, 18));
273
274        assert!(poly_ring.set_coeff(1, &value).is_err());
275    }
276}
277
278#[cfg(test)]
279mod test_change_modulus {
280    use super::PolynomialRingZq;
281    use crate::integer_mod_q::ModulusPolynomialRingZq;
282    use std::str::FromStr;
283
284    /// Ensures that the modulus is changed correctly.
285    #[test]
286    fn modulus_correct() {
287        let mut matrix = PolynomialRingZq::from_str("1  6 / 4  1 0 0 1 mod 7").unwrap();
288        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 5").unwrap();
289
290        matrix.change_modulus(&modulus);
291
292        assert_eq!("1  1 / 4  1 0 0 1 mod 5", matrix.to_string());
293    }
294
295    /// Ensures that the modulus is changed correctly, if the modulus is big.
296    #[test]
297    fn big_modulus_correct() {
298        let mut matrix =
299            PolynomialRingZq::from_str(&format!("1  6 / 4  1 0 0 1 mod {}", i64::MAX)).unwrap();
300        let modulus =
301            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
302
303        matrix.change_modulus(&modulus);
304
305        assert_eq!(
306            format!("1  6 / 4  1 0 0 1 mod {}", u64::MAX),
307            matrix.to_string()
308        );
309    }
310
311    /// Ensures that the matrix is reduced correctly.
312    #[test]
313    fn reduced_correct() {
314        let mut matrix =
315            PolynomialRingZq::from_str("4  1 0 0 1 / 8  1 0 0 0 0 0 0 1 mod 7").unwrap();
316        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 2").unwrap();
317
318        matrix.change_modulus(&modulus);
319
320        assert_eq!("0 / 4  1 0 0 1 mod 2", matrix.to_string());
321    }
322}
323
324#[cfg(test)]
325mod test_change_q {
326    use super::PolynomialRingZq;
327    use std::str::FromStr;
328
329    /// Ensures that the modulus `q` is changed correctly.
330    #[test]
331    fn q_correct() {
332        let mut matrix = PolynomialRingZq::from_str("1  6 / 4  1 0 0 1 mod 7").unwrap();
333
334        matrix.change_q(8);
335
336        assert_eq!("1  6 / 4  1 0 0 1 mod 8", matrix.to_string());
337    }
338
339    /// Ensures that the modulus `q` is changed correctly, if the modulus is big.
340    #[test]
341    fn big_q_correct() {
342        let mut matrix =
343            PolynomialRingZq::from_str(&format!("1  6 / 4  1 0 0 1 mod {}", i64::MAX)).unwrap();
344
345        matrix.change_q(u64::MAX);
346
347        assert_eq!(
348            format!("1  6 / 4  1 0 0 1 mod {}", u64::MAX),
349            matrix.to_string()
350        );
351    }
352
353    /// Ensures that the matrix is reduced correctly.
354    #[test]
355    fn reduced_correct() {
356        let mut matrix = PolynomialRingZq::from_str("2  5 4 / 4  1 0 0 1 mod 7").unwrap();
357
358        matrix.change_q(2);
359
360        assert_eq!("1  1 / 4  1 0 0 1 mod 2", matrix.to_string());
361    }
362}