Skip to main content

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