qfall_math/integer_mod_q/modulus_polynomial_ring_zq/
cmp.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//! Implementations to compare [`ModulusPolynomialRingZq`] with other values.
10//! This uses the traits from [`std::cmp`].
11
12use super::ModulusPolynomialRingZq;
13use crate::{
14    integer::Z, integer_mod_q::PolyOverZq, macros::for_others::implement_trait_reverse,
15    traits::GetCoefficient,
16};
17use flint_sys::{fmpz::fmpz_equal, fmpz_mod_poly::fmpz_mod_poly_equal};
18
19impl PartialEq for ModulusPolynomialRingZq {
20    /// Checks if two modulus objects of type over [`ModulusPolynomialRingZq`] are equal.
21    /// They are considered equal, if their representation as a
22    /// [`PolyOverZq`](crate::integer_mod_q::PolyOverZq) match: i.e. the modulus `q`
23    /// and the coefficients of the polynomial under modulus `q`.
24    /// Used by the `==` and `!=` operators.
25    ///
26    /// Parameters:
27    /// - `other`: the other value that is used to compare the elements
28    ///
29    /// Returns `true` if the elements are equal, otherwise `false`.
30    ///
31    /// # Examples
32    /// ```
33    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
34    /// use std::str::FromStr;
35    /// let a: ModulusPolynomialRingZq = ModulusPolynomialRingZq::from_str("2  24 1 mod 17").unwrap();
36    /// let b: ModulusPolynomialRingZq = ModulusPolynomialRingZq::from_str("2  42 1 mod 17").unwrap();
37    ///
38    /// // These are all equivalent and return false.
39    /// let compared: bool = (a == b);
40    /// # assert!(!compared);
41    /// let compared: bool = (&a == &b);
42    /// # assert!(!compared);
43    /// let compared: bool = (a.eq(&b));
44    /// # assert!(!compared);
45    /// let compared: bool = (ModulusPolynomialRingZq::eq(&a, &b));
46    /// # assert!(!compared);
47    /// ```
48    fn eq(&self, other: &Self) -> bool {
49        unsafe {
50            // compares the modulus `q`
51            1 == fmpz_equal(
52                &self.get_fq_ctx().ctxp[0].n[0],
53                &other.get_fq_ctx().ctxp[0].n[0],
54            ) &&
55            // compares the polynomial under `q`
56            1 == fmpz_mod_poly_equal(
57                    &self.get_fq_ctx().modulus[0],
58                    &other.get_fq_ctx().modulus[0],
59                    &self.get_fq_ctx().ctxp[0],
60                )
61        }
62    }
63}
64
65// With the [`Eq`] trait, `a == a` is always true.
66// This is not guaranteed by the [`PartialEq`] trait.
67impl Eq for ModulusPolynomialRingZq {}
68
69impl PartialEq<PolyOverZq> for ModulusPolynomialRingZq {
70    /// Checks if an integer matrix and a rational matrix are equal. Used by the `==` and `!=` operators.
71    /// [`PartialEq`] is also implemented for [`PolyOverZq`] using [`ModulusPolynomialRingZq`].
72    ///
73    /// Parameters:
74    /// - `other`: the other value that is used to compare the elements
75    ///
76    /// Returns `true` if the elements are equal, otherwise `false`.
77    ///
78    /// # Examples
79    /// ```
80    /// use qfall_math::integer_mod_q::{PolyOverZq, ModulusPolynomialRingZq};
81    /// use std::str::FromStr;
82    /// let a: ModulusPolynomialRingZq = ModulusPolynomialRingZq::from_str("3  1 2 3 mod 17").unwrap();
83    /// let b: PolyOverZq = PolyOverZq::from_str("3  1 2 3 mod 17").unwrap();
84    ///
85    /// // These are all equivalent and return true.
86    /// let compared: bool = (a == b);
87    /// # assert!(compared);
88    /// let compared: bool = (b == a);
89    /// # assert!(compared);
90    /// let compared: bool = (&a == &b);
91    /// # assert!(compared);
92    /// let compared: bool = (&b == &a);
93    /// # assert!(compared);
94    /// let compared: bool = (a.eq(&b));
95    /// # assert!(compared);
96    /// let compared: bool = (b.eq(&a));
97    /// # assert!(compared);
98    /// let compared: bool = (ModulusPolynomialRingZq::eq(&a, &b));
99    /// # assert!(compared);
100    /// let compared: bool = (PolyOverZq::eq(&b, &a));
101    /// # assert!(compared);
102    /// ```
103    fn eq(&self, other: &PolyOverZq) -> bool {
104        if self.get_q() != other.modulus {
105            return false;
106        }
107
108        let degree = self.get_degree();
109        if degree != other.get_degree() {
110            return false;
111        }
112
113        for i in 0..degree + 1 {
114            if unsafe { GetCoefficient::<Z>::get_coeff_unchecked(self, i) }
115                != unsafe { GetCoefficient::<Z>::get_coeff_unchecked(other, i) }
116            {
117                return false;
118            }
119        }
120
121        true
122    }
123}
124
125implement_trait_reverse!(PartialEq, eq, PolyOverZq, ModulusPolynomialRingZq, bool);
126
127/// Test that the [`PartialEq`] trait is correctly implemented.
128/// Consider that negative is turned positive due to the modulus being applied.
129/// Hence negative/small refers to the instantiation.
130#[cfg(test)]
131mod test_partial_eq {
132    // Test case structure:
133    // 1. Different ways to use equal and not equal.
134    // 2. Test different combinations of equal and not equal with different
135    //    parameter length combinations.
136    //    Not equal test are inverted equal tests.
137
138    use super::ModulusPolynomialRingZq;
139    use std::str::FromStr;
140
141    const LARGE_PRIME: u64 = u64::MAX - 58;
142
143    /// Demonstrate the different ways to use equal.
144    /// We assume that they behave the same in the other tests.
145    #[test]
146    #[allow(clippy::op_ref)]
147    fn equal_call_methods() {
148        let one_1 = ModulusPolynomialRingZq::from_str("2  42 -1 mod 17").unwrap();
149        let one_2 = ModulusPolynomialRingZq::from_str("2  42 -1 mod 17").unwrap();
150
151        assert!(one_1 == one_2);
152        assert!(&one_1 == &one_2);
153        assert!(one_1.eq(&one_2));
154        assert!(ModulusPolynomialRingZq::eq(&one_1, &one_2));
155        assert_eq!(one_1, one_2);
156    }
157
158    /// Demonstrate the different ways to use not equal.
159    /// We assume that they behave the same in the other tests.
160    #[test]
161    #[allow(clippy::op_ref)]
162    fn not_equal_call_methods_different_num_coeffs() {
163        let one = ModulusPolynomialRingZq::from_str("2  42 -1 mod 17").unwrap();
164        let two = ModulusPolynomialRingZq::from_str("3  42 -1 1 mod 17").unwrap();
165
166        assert!(one != two);
167        assert!(&one != &two);
168        assert!(one.ne(&two));
169        assert!(ModulusPolynomialRingZq::ne(&one, &two));
170        assert_ne!(one, two);
171    }
172
173    /// Test equal with small positive and negative constant polynomials.
174    #[test]
175    fn equal_small() {
176        let small_1 = ModulusPolynomialRingZq::from_str("2  1 10 mod 17").unwrap();
177        let small_2 = ModulusPolynomialRingZq::from_str("2  1 10 mod 17").unwrap();
178        let negative = ModulusPolynomialRingZq::from_str("2  1 -2 mod 17").unwrap();
179
180        assert!(small_1 == small_2);
181        assert!(small_2 == small_1);
182        assert!(small_1 == small_1);
183        assert!(!(small_1 == negative));
184        assert!(!(negative == small_1));
185    }
186
187    /// Test not equal with small positive and negative constant polynomials.
188    #[test]
189    fn not_equal_small() {
190        let small_1 = ModulusPolynomialRingZq::from_str("2  1 10 mod 17").unwrap();
191        let small_2 = ModulusPolynomialRingZq::from_str("2  1 10 mod 17").unwrap();
192        let negative = ModulusPolynomialRingZq::from_str("2  1 -1 mod 17").unwrap();
193
194        assert!(!(small_1 != small_2));
195        assert!(!(small_2 != small_1));
196        assert!(!(small_1 != small_1));
197        assert!(small_1 != negative);
198        assert!(negative != small_1);
199    }
200
201    /// Test equal with a large [`ModulusPolynomialRingZq`]
202    /// (uses FLINT's pointer representation)
203    #[test]
204    fn equal_large() {
205        let max_str = format!("2  1 {} mod {LARGE_PRIME}", u64::MAX);
206        let min_str = format!("2  1 {} mod {LARGE_PRIME}", i64::MIN);
207
208        let max_1 = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
209        let max_2 = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
210        let min = ModulusPolynomialRingZq::from_str(&min_str).unwrap();
211
212        assert!(max_1 == max_2);
213        assert!(max_2 == max_1);
214        assert!(max_1 == max_1);
215        assert!(min == min);
216        assert!(!(max_1 == min));
217        assert!(!(min == max_1));
218    }
219
220    /// Test not equal with a large [`ModulusPolynomialRingZq`]
221    /// (uses FLINT's pointer representation)
222    #[test]
223    fn not_equal_large() {
224        let max_str = format!("2  1 {} mod {LARGE_PRIME}", u64::MAX);
225        let min_str = format!("2  1 {} mod {LARGE_PRIME}", i64::MIN);
226
227        let max_1 = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
228        let max_2 = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
229        let min = ModulusPolynomialRingZq::from_str(&min_str).unwrap();
230
231        assert!(!(max_1 != max_2));
232        assert!(!(max_2 != max_1));
233        assert!(!(max_1 != max_1));
234        assert!(!(min != min));
235        assert!(max_1 != min);
236        assert!(min != max_1);
237    }
238
239    /// Test equal with a large polynomial with a large [`ModulusPolynomialRingZq`] (uses FLINT's pointer representation)
240    /// and small polynomial with a small [`ModulusPolynomialRingZq`] (no pointer representation).
241    #[test]
242    fn equal_large_small() {
243        let max_str = format!("2  1 {} mod {LARGE_PRIME}", u64::MAX);
244        let min_str = format!("2  1 {} mod {LARGE_PRIME}", i64::MIN);
245
246        let max = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
247        let min = ModulusPolynomialRingZq::from_str(&min_str).unwrap();
248
249        let small_positive = ModulusPolynomialRingZq::from_str("2  1 2 mod 17").unwrap();
250        let small_negative = ModulusPolynomialRingZq::from_str("2  1 -2 mod 17").unwrap();
251
252        assert!(!(max == small_negative));
253        assert!(!(small_negative == max));
254        assert!(!(max == small_positive));
255        assert!(!(small_positive == max));
256
257        assert!(!(min == small_negative));
258        assert!(!(small_negative == min));
259        assert!(!(min == small_positive));
260        assert!(!(small_positive == min));
261    }
262
263    /// Test not equal with a large [`ModulusPolynomialRingZq`] (uses FLINT's pointer representation)
264    /// and small [`ModulusPolynomialRingZq`] (no pointer representation).
265    #[test]
266    fn not_equal_large_small() {
267        let max_str = format!("2  1 {} mod {LARGE_PRIME}", u64::MAX);
268        let min_str = format!("2  1 {} mod {LARGE_PRIME}", i64::MIN);
269
270        let max = ModulusPolynomialRingZq::from_str(&max_str).unwrap();
271        let min = ModulusPolynomialRingZq::from_str(&min_str).unwrap();
272
273        let small_positive = ModulusPolynomialRingZq::from_str("2  1 2 mod 17").unwrap();
274        let small_negative = ModulusPolynomialRingZq::from_str("2  1 -2 mod 17").unwrap();
275
276        assert!(max != small_negative);
277        assert!(small_negative != max);
278        assert!(max != small_positive);
279        assert!(small_positive != max);
280
281        assert!(min != small_negative);
282        assert!(small_negative != min);
283        assert!(min != small_positive);
284        assert!(small_positive != min);
285    }
286
287    /// Test not equal for the same polynomial but with a different modulus
288    #[test]
289    fn different_modulus() {
290        let first_str = "2  1 2 mod 17";
291        let second_str = "2  1 2 mod 19";
292
293        let first = ModulusPolynomialRingZq::from_str(first_str).unwrap();
294        let second = ModulusPolynomialRingZq::from_str(second_str).unwrap();
295
296        assert_ne!(first, second);
297    }
298}
299
300/// Test that the [`PartialEq`] trait is correctly implemented.
301#[cfg(test)]
302mod test_partial_eq_q_other {
303    use crate::integer_mod_q::{ModulusPolynomialRingZq, PolyOverZq};
304    use std::str::FromStr;
305
306    /// Ensure that the function can be called with several types.
307    #[test]
308    #[allow(clippy::op_ref)]
309    fn availability() {
310        let q = ModulusPolynomialRingZq::from_str("4  1 2 3 4 mod 17").unwrap();
311        let z = PolyOverZq::from_str("4  1 2 3 4 mod 17").unwrap();
312
313        assert!(q == z);
314        assert!(z == q);
315        assert!(&q == &z);
316        assert!(&z == &q);
317    }
318
319    /// Ensure that equal values are compared correctly.
320    #[test]
321    fn equal() {
322        let q = ModulusPolynomialRingZq::from_str(&format!("3  1 2 {} mod {}", i64::MAX, u64::MAX))
323            .unwrap();
324        let z_1 = PolyOverZq::from_str(&format!("3  1 2 {} mod {}", i64::MAX, u64::MAX)).unwrap();
325        let z_2 = PolyOverZq::from_str(&format!("4  1 2 {} 0 mod {}", i64::MAX, u64::MAX)).unwrap();
326
327        assert!(q == z_1);
328        assert!(q == z_2);
329    }
330
331    /// Ensure that unequal values are compared correctly.
332    #[test]
333    fn unequal() {
334        let q = ModulusPolynomialRingZq::from_str(&format!("3  1 2 {} mod {}", i64::MAX, u64::MAX))
335            .unwrap();
336        let z_1 = PolyOverZq::from_str(&format!("3  1 3 {} mod {}", i64::MAX, u64::MAX)).unwrap();
337        let z_2 = PolyOverZq::from_str(&format!("4  1 2 {} 1 mod {}", i64::MAX, u64::MAX)).unwrap();
338        let z_3 =
339            PolyOverZq::from_str(&format!("3  1 2 {} mod {}", i64::MAX, u64::MAX - 1)).unwrap();
340
341        assert!(q != z_1);
342        assert!(q != z_2);
343        assert!(q != z_3);
344    }
345}