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