Skip to main content

qfall_math/integer/mat_poly_over_z/
reduce.rs

1// Copyright © 2023 Marvin Beckmann
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//! This module allows to simulate functionality of matrices over the polynomial ring
10//! `Z[X]/f(x)` by entrywise reducing a [`MatPolyOverZ`] by a [`PolyOverZ`] that
11//! has a leading coefficient of `1`.
12
13use super::MatPolyOverZ;
14use crate::{
15    integer::{PolyOverZ, poly_over_z::fmpz_poly_helpers::reduce_fmpz_poly_by_poly_over_z},
16    traits::MatrixDimensions,
17};
18use flint_sys::fmpz_poly_mat::fmpz_poly_mat_entry;
19
20impl MatPolyOverZ {
21    /// Entrywise reduces a matrix of polynomials by a polynomial `modulus`.
22    /// The modulus must have a leading coefficient of `1`, else the function will panic.
23    ///
24    /// Parameters:
25    /// - `modulus`: Specifies the polynomial by which `self` is reduced
26    ///
27    /// # Examples
28    /// ```
29    /// use qfall_math::integer::{MatPolyOverZ, PolyOverZ};
30    /// use std::str::FromStr;
31    ///
32    /// let mut a = MatPolyOverZ::from_str("[[4  0 1 2 3, 3  0 1 1]]").unwrap();
33    /// let modulus = PolyOverZ::from_str("3  0 1 1").unwrap();
34    ///
35    /// a.reduce_by_poly(&modulus);
36    ///
37    /// assert_eq!(MatPolyOverZ::from_str("[[2  0 2, 0]]").unwrap(), a);
38    /// ```
39    ///
40    /// # Panics ...
41    /// - if the modulus does not have a leading coefficient of `1`.
42    pub fn reduce_by_poly(&mut self, modulus: &PolyOverZ) {
43        for i in 0..self.get_num_rows() {
44            for j in 0..self.get_num_columns() {
45                unsafe {
46                    let entry = fmpz_poly_mat_entry(&self.matrix, i, j);
47                    if (*entry).length > modulus.get_degree() {
48                        reduce_fmpz_poly_by_poly_over_z(&mut *entry, modulus);
49                    }
50                }
51            }
52        }
53    }
54}
55
56#[cfg(test)]
57mod test_reduce_by_poly {
58    use crate::integer::{MatPolyOverZ, PolyOverZ};
59    use std::str::FromStr;
60
61    /// Ensures that the reduction works properly for a fixed polynomial that has to be
62    /// reduce or more than one coefficient.
63    #[test]
64    fn reduce_more_than_once() {
65        let mut a = MatPolyOverZ::from_str("[[4  0 1 2 3, 3  0 1 1]]").unwrap();
66        let modulus = PolyOverZ::from_str("3  0 1 1").unwrap();
67
68        a.reduce_by_poly(&modulus);
69
70        assert_eq!(MatPolyOverZ::from_str("[[2  0 2, 0]]").unwrap(), a);
71    }
72
73    /// Ensures that the function panics, if the leading coefficient is not `1`.
74    #[test]
75    #[should_panic]
76    fn no_leading_zero() {
77        let mut a = MatPolyOverZ::from_str("[[3  1 2 3]]").unwrap();
78        let modulus = PolyOverZ::from_str("2  1 2").unwrap();
79
80        a.reduce_by_poly(&modulus);
81    }
82
83    /// Ensures that large coefficients are reduced properly.
84    #[test]
85    fn large_coefficients() {
86        let mut a = MatPolyOverZ::from_str(&format!(
87            "[[3  1 -{} -1, 1  1],[1  -1, 4  0 0 {} 1]]",
88            u64::MAX,
89            u64::MAX
90        ))
91        .unwrap();
92        let modulus = PolyOverZ::from_str(&format!("2  {} 1", u64::MAX)).unwrap();
93
94        a.reduce_by_poly(&modulus);
95
96        let cmp_mat = MatPolyOverZ::from_str("[[1  1, 1  1],[1  -1, 0]]").unwrap();
97        assert_eq!(cmp_mat, a);
98    }
99
100    /// Ensures that the zero polynomial does not cause problems.
101    #[test]
102    fn zero_polynomial() {
103        let mut a = MatPolyOverZ::from_str("[[0, 0]]").unwrap();
104        let modulus = PolyOverZ::from_str(&format!("2  {} 1", u64::MAX)).unwrap();
105
106        a.reduce_by_poly(&modulus);
107    }
108}