qfall_math/rational/poly_over_q/
cmp.rs

1// Copyright © 2023 Marvin Beckmann, 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 compare [`PolyOverQ`] with other values.
10//! This uses the traits from [`std::cmp`].
11
12use super::PolyOverQ;
13use crate::{
14    integer::PolyOverZ,
15    macros::for_others::implement_trait_reverse,
16    rational::Q,
17    traits::{CompareBase, GetCoefficient},
18};
19use flint_sys::fmpq_poly::fmpq_poly_equal;
20
21impl PartialEq for PolyOverQ {
22    /// Checks if two polynomials over [`Q`](crate::rational::Q) are equal. Used by the `==` and `!=` operators.
23    ///
24    /// Parameters:
25    /// - `other`: the other value that is used to compare the elements
26    ///
27    /// Returns `true` if the elements are equal, otherwise `false`.
28    ///
29    /// # Examples
30    /// ```
31    /// use qfall_math::rational::PolyOverQ;
32    /// use std::str::FromStr;
33    /// let a: PolyOverQ = PolyOverQ::from_str("2  42/24 1").unwrap();
34    /// let b: PolyOverQ = PolyOverQ::from_str("2  24/42 1").unwrap();
35    ///
36    /// // These are all equivalent and return false.
37    /// let compared: bool = (a == b);
38    /// # assert!(!compared);
39    /// let compared: bool = (&a == &b);
40    /// # assert!(!compared);
41    /// let compared: bool = (a.eq(&b));
42    /// # assert!(!compared);
43    /// let compared: bool = (PolyOverQ::eq(&a, &b));
44    /// # assert!(!compared);
45    /// ```
46    fn eq(&self, other: &Self) -> bool {
47        unsafe { 1 == fmpq_poly_equal(&self.poly, &other.poly) }
48    }
49}
50
51// With the [`Eq`] trait, `a == a` is always true.
52// This is not guaranteed by the [`PartialEq`] trait.
53impl Eq for PolyOverQ {}
54
55impl PartialEq<PolyOverZ> for PolyOverQ {
56    /// Checks if an integer matrix and a rational matrix are equal. Used by the `==` and `!=` operators.
57    /// [`PartialEq`] is also implemented for [`PolyOverZ`] using [`PolyOverQ`].
58    ///
59    /// Parameters:
60    /// - `other`: the other value that is used to compare the elements
61    ///
62    /// Returns `true` if the elements are equal, otherwise `false`.
63    ///
64    /// # Examples
65    /// ```
66    /// use qfall_math::integer::PolyOverZ;
67    /// use qfall_math::rational::PolyOverQ;
68    /// use std::str::FromStr;
69    /// let a: PolyOverQ = PolyOverQ::from_str("3  1 2 3").unwrap();
70    /// let b: PolyOverZ = PolyOverZ::from_str("3  1 2 3").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 = (PolyOverQ::eq(&a, &b));
86    /// # assert!(compared);
87    /// let compared: bool = (PolyOverZ::eq(&b, &a));
88    /// # assert!(compared);
89    /// ```
90    fn eq(&self, other: &PolyOverZ) -> bool {
91        let degree = self.get_degree();
92
93        if degree != other.get_degree() {
94            return false;
95        }
96
97        for i in 0..degree + 1 {
98            if unsafe { self.get_coeff_unchecked(i) } != unsafe { other.get_coeff_unchecked(i) } {
99                return false;
100            }
101        }
102
103        true
104    }
105}
106
107implement_trait_reverse!(PartialEq, eq, PolyOverZ, PolyOverQ, bool);
108
109impl CompareBase<PolyOverQ> for PolyOverQ {}
110impl CompareBase<PolyOverZ> for PolyOverQ {}
111impl<Rational: Into<Q>> CompareBase<Rational> for PolyOverQ {}
112
113/// Test that the [`PartialEq`] trait is correctly implemented.
114#[cfg(test)]
115mod test_partial_eq {
116    // Test case structure:
117    // 1. Different ways to use equal and not equal.
118    // 2. Test different combinations of equal and not equal with different
119    //    parameter length combinations.
120    //    Not equal test are inverted equal tests.
121
122    use super::PolyOverQ;
123    use std::str::FromStr;
124
125    /// Demonstrate the different ways to use equal.
126    /// We assume that they behave the same in the other tests.
127    #[test]
128    #[allow(clippy::op_ref)]
129    fn equal_call_methods() {
130        let one_1 = PolyOverQ::from_str("2  24/42 1").unwrap();
131        let one_2 = PolyOverQ::from_str("2  24/42 1").unwrap();
132
133        assert!(one_1 == one_2);
134        assert!(&one_1 == &one_2);
135        assert!(one_1.eq(&one_2));
136        assert!(PolyOverQ::eq(&one_1, &one_2));
137        assert_eq!(one_1, one_2);
138    }
139
140    /// Demonstrate the different ways to use not equal.
141    /// We assume that they behave the same in the other tests.
142    #[test]
143    #[allow(clippy::op_ref)]
144    fn not_equal_call_methods() {
145        let one = PolyOverQ::from_str("2  24/42 1").unwrap();
146        let two = PolyOverQ::from_str("3  24/42 1 1").unwrap();
147
148        assert!(one != two);
149        assert!(&one != &two);
150        assert!(one.ne(&two));
151        assert!(PolyOverQ::ne(&one, &two));
152        assert_ne!(one, two);
153    }
154
155    /// Test equal with small positive and negative constant polynomials.
156    #[test]
157    fn equal_small() {
158        let small_1 = PolyOverQ::from_str("1  10/3").unwrap();
159        let small_2 = PolyOverQ::from_str("1  10/3").unwrap();
160        let negative = PolyOverQ::from_str("1  -1/5").unwrap();
161
162        assert!(small_1 == small_2);
163        assert!(small_2 == small_1);
164        assert!(small_1 == small_1);
165        assert!(!(small_1 == negative));
166        assert!(!(negative == small_1));
167    }
168
169    /// Test not equal with small positive and negative constant polynomials.
170    #[test]
171    fn not_equal_small() {
172        let small_1 = PolyOverQ::from_str("1  10/3").unwrap();
173        let small_2 = PolyOverQ::from_str("1  10/3").unwrap();
174        let negative = PolyOverQ::from_str("1  -1/5").unwrap();
175
176        assert!(!(small_1 != small_2));
177        assert!(!(small_2 != small_1));
178        assert!(!(small_1 != small_1));
179        assert!(small_1 != negative);
180        assert!(negative != small_1);
181    }
182
183    /// Test equal with a large [`PolyOverQ`]
184    /// (uses FLINT's pointer representation)
185    #[test]
186    fn equal_large() {
187        let max_str = format!("1  {}/{}", u64::MAX, 3);
188        let min_str = format!("1  {}/{}", i64::MIN, 4);
189
190        let max_1 = PolyOverQ::from_str(&max_str).unwrap();
191        let max_2 = PolyOverQ::from_str(&max_str).unwrap();
192        let min = PolyOverQ::from_str(&min_str).unwrap();
193
194        assert!(max_1 == max_2);
195        assert!(max_2 == max_1);
196        assert!(max_1 == max_1);
197        assert!(min == min);
198        assert!(!(max_1 == min));
199        assert!(!(min == max_1));
200    }
201
202    /// Test not equal with a large [`PolyOverQ`]
203    /// (uses FLINT's pointer representation)
204    #[test]
205    fn not_equal_large() {
206        let max_str = format!("1  {}/{}", u64::MAX, 3);
207        let min_str = format!("1  {}/{}", i64::MIN, 4);
208
209        let max_1 = PolyOverQ::from_str(&max_str).unwrap();
210        let max_2 = PolyOverQ::from_str(&max_str).unwrap();
211        let min = PolyOverQ::from_str(&min_str).unwrap();
212
213        assert!(!(max_1 != max_2));
214        assert!(!(max_2 != max_1));
215        assert!(!(max_1 != max_1));
216        assert!(!(min != min));
217        assert!(max_1 != min);
218        assert!(min != max_1);
219    }
220
221    /// Test equal with a large [`PolyOverQ`] (uses FLINT's pointer representation)
222    /// and small [`PolyOverQ`] (no pointer representation).
223    #[test]
224    fn equal_large_small() {
225        let max_str = format!("1  {}/{}", u64::MAX, 3);
226        let min_str = format!("1  {}/{}", i64::MIN, 4);
227
228        let max = PolyOverQ::from_str(&max_str).unwrap();
229        let min = PolyOverQ::from_str(&min_str).unwrap();
230
231        let small_positive = PolyOverQ::from_str("1  1/7").unwrap();
232        let small_negative = PolyOverQ::from_str("1  -1/7").unwrap();
233
234        assert!(!(max == small_negative));
235        assert!(!(small_negative == max));
236        assert!(!(max == small_positive));
237        assert!(!(small_positive == max));
238
239        assert!(!(min == small_negative));
240        assert!(!(small_negative == min));
241        assert!(!(min == small_positive));
242        assert!(!(small_positive == min));
243    }
244
245    /// Test not equal with a large [`PolyOverQ`] (uses FLINT's pointer representation)
246    /// and small [`PolyOverQ`] (no pointer representation).
247    #[test]
248    fn not_equal_large_small() {
249        let max_str = format!("1  {}/{}", u64::MAX, 3);
250        let min_str = format!("1  {}/{}", i64::MIN, 4);
251
252        let max = PolyOverQ::from_str(&max_str).unwrap();
253        let min = PolyOverQ::from_str(&min_str).unwrap();
254
255        let small_positive = PolyOverQ::from_str("1  1").unwrap();
256        let small_negative = PolyOverQ::from_str("1  -1").unwrap();
257
258        assert!(max != small_negative);
259        assert!(small_negative != max);
260        assert!(max != small_positive);
261        assert!(small_positive != max);
262
263        assert!(min != small_negative);
264        assert!(small_negative != min);
265        assert!(min != small_positive);
266        assert!(small_positive != min);
267    }
268
269    /// Test not equal with a large [`PolyOverQ`] (uses FLINT's pointer representation)
270    /// i.e. a large denominator and a large nominator
271    #[test]
272    #[allow(clippy::op_ref)]
273    fn large_nom_large_denom() {
274        let max_str = format!("1  {}/3{}", u64::MAX, u64::MAX);
275        let min_str = format!("1  {}/4{}", i64::MIN, u64::MAX);
276
277        let one = PolyOverQ::from_str(&min_str).unwrap();
278        let two = PolyOverQ::from_str(&max_str).unwrap();
279
280        assert!(one != two);
281        assert!(&one != &two);
282        assert!(one.ne(&two));
283        assert!(PolyOverQ::ne(&one, &two));
284        assert_ne!(one, two);
285    }
286}
287
288/// Test that the [`PartialEq`] trait is correctly implemented.
289#[cfg(test)]
290mod test_partial_eq_q_other {
291    use super::PolyOverQ;
292    use crate::integer::PolyOverZ;
293    use std::str::FromStr;
294
295    /// Ensure that the function can be called with several types.
296    #[test]
297    #[allow(clippy::op_ref)]
298    fn availability() {
299        let q = PolyOverQ::from_str("4  1 2 3 4").unwrap();
300        let z = PolyOverZ::from_str("4  1 2 3 4").unwrap();
301
302        assert!(q == z);
303        assert!(z == q);
304        assert!(&q == &z);
305        assert!(&z == &q);
306    }
307
308    /// Ensure that equal values are compared correctly.
309    #[test]
310    fn equal() {
311        let q = PolyOverQ::from_str(&format!("3  1 2 {}", u64::MAX)).unwrap();
312        let z_1 = PolyOverZ::from_str(&format!("3  1 2 {}", u64::MAX)).unwrap();
313        let z_2 = PolyOverZ::from_str(&format!("4  1 2 {} 0", u64::MAX)).unwrap();
314
315        assert!(q == z_1);
316        assert!(q == z_2);
317    }
318
319    /// Ensure that unequal polynomials are compared correctly.
320    #[test]
321    fn unequal() {
322        let q = PolyOverQ::from_str(&format!("3  1 2 {}", u64::MAX)).unwrap();
323        let z_1 = PolyOverZ::from_str(&format!("3  1 3 {}", u64::MAX)).unwrap();
324        let z_2 = PolyOverZ::from_str(&format!("4  1 2 {} 1", u64::MAX)).unwrap();
325
326        assert!(q != z_1);
327        assert!(q != z_2);
328    }
329}
330
331/// Test that the [`CompareBase`] trait uses the default implementation.
332#[cfg(test)]
333mod test_compare_base {
334    use crate::{
335        integer::{PolyOverZ, Z},
336        rational::{PolyOverQ, Q},
337        traits::CompareBase,
338    };
339    use std::str::FromStr;
340
341    /// Ensures that the [`CompareBase`] trait uses the default implementation
342    /// and is available for all types it would be checked against.
343    #[test]
344    fn availability() {
345        let one_1 = PolyOverQ::from_str("3  1/3 1 -7").unwrap();
346
347        assert!(one_1.compare_base(&Q::ONE));
348        assert!(one_1.compare_base(&Z::ONE));
349        assert!(one_1.compare_base(&PolyOverQ::from(1)));
350        assert!(one_1.compare_base(&PolyOverZ::from(1)));
351        assert!(one_1.compare_base(&0_i8));
352        assert!(one_1.compare_base(&0_i16));
353        assert!(one_1.compare_base(&0_i32));
354        assert!(one_1.compare_base(&0_i64));
355        assert!(one_1.compare_base(&0_u8));
356        assert!(one_1.compare_base(&0_u16));
357        assert!(one_1.compare_base(&0_u32));
358        assert!(one_1.compare_base(&0_u64));
359        assert!(one_1.compare_base(&0.5_f32));
360        assert!(one_1.compare_base(&0.5_f64));
361
362        assert!(one_1.call_compare_base_error(&PolyOverQ::from(1)).is_none());
363        assert!(one_1.call_compare_base_error(&PolyOverZ::from(1)).is_none());
364        assert!(one_1.call_compare_base_error(&Z::ONE).is_none());
365        assert!(one_1.call_compare_base_error(&Q::ONE).is_none());
366        assert!(one_1.call_compare_base_error(&0_i8).is_none());
367        assert!(one_1.call_compare_base_error(&0_i16).is_none());
368        assert!(one_1.call_compare_base_error(&0_i32).is_none());
369        assert!(one_1.call_compare_base_error(&0_i64).is_none());
370        assert!(one_1.call_compare_base_error(&0_u8).is_none());
371        assert!(one_1.call_compare_base_error(&0_u16).is_none());
372        assert!(one_1.call_compare_base_error(&0_u32).is_none());
373        assert!(one_1.call_compare_base_error(&0_u64).is_none());
374        assert!(one_1.call_compare_base_error(&0.5_f32).is_none());
375        assert!(one_1.call_compare_base_error(&0.5_f64).is_none());
376    }
377}