qfall_math/integer/z/
cmp.rs

1// Copyright © 2023 Sven Moog, 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 [`Z`] with other values.
10//! This uses the traits from [`std::cmp`].
11
12use super::Z;
13use crate::{integer_mod_q::Modulus, macros::for_others::implement_for_others};
14use flint_sys::fmpz::{fmpz, fmpz_cmp, fmpz_equal};
15use std::cmp::Ordering;
16
17impl PartialEq for Z {
18    /// Checks if two integers are equal. Used by the `==` and `!=` operators.
19    ///
20    /// Parameters:
21    /// - `other`: the other value that is used to compare the elements
22    ///
23    /// Returns `true` if the elements are equal, otherwise `false`.
24    ///
25    /// # Examples
26    /// ```
27    /// use qfall_math::integer::Z;
28    /// let a: Z = Z::from(42);
29    /// let b: Z = Z::from(24);
30    ///
31    /// // These are all equivalent and return false.
32    /// let compared: bool = (a == b);
33    /// # assert!(!compared);
34    /// let compared: bool = (&a == &b);
35    /// # assert!(!compared);
36    /// let compared: bool = (a.eq(&b));
37    /// # assert!(!compared);
38    /// let compared: bool = (Z::eq(&a, &b));
39    /// # assert!(!compared);
40    /// ```
41    fn eq(&self, other: &Self) -> bool {
42        unsafe { 1 == fmpz_equal(&self.value, &other.value) }
43    }
44}
45
46// With the [`Eq`] trait, `a == a` is always true.
47// This is not guaranteed by the [`PartialEq`] trait.
48impl Eq for Z {}
49
50implement_for_others!(Z, Z, PartialEq for fmpz i8 i16 i32 i64 u8 u16 u32 u64);
51
52impl PartialOrd for Z {
53    /// Compares two [`Z`] values. Used by the `<`, `<=`, `>`, and `>=` operators.
54    ///
55    /// Parameters:
56    /// - `other`: the other value that is used to compare the elements
57    ///
58    /// Returns the [`Ordering`] of the elements.
59    ///
60    /// # Examples
61    /// ```
62    /// use qfall_math::integer::Z;
63    ///
64    /// let a: Z = Z::from(10);
65    /// let b: Z = Z::from(42);
66    ///
67    /// assert!(a < b);
68    /// assert!(a <= b);
69    /// assert!(b > a);
70    /// assert!(b >= a);
71    /// ```
72    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
73        Some(self.cmp(other))
74    }
75}
76
77impl Ord for Z {
78    //! Enables the usage of `max`, `min`, and `clamp`.
79    //!
80    //! # Examples
81    //! ```
82    //! use qfall_math::integer::Z;
83    //! use std::cmp::{max, min};
84    //!
85    //! let a: Z = Z::from(10);
86    //! let b: Z = Z::from(42);
87    //!
88    //! assert_eq!(b, max(a.clone(), b.clone()));
89    //! assert_eq!(a, min(a.clone(), b.clone()));
90    //! assert_eq!(a, Z::ZERO.clamp(a.clone(), b.clone()));
91    //! ```
92
93    /// Compares two [`Z`] values. Used by the `<`, `<=`, `>`, and `>=` operators.
94    ///
95    /// Parameters:
96    /// - `other`: the other value that is used to compare the elements
97    ///
98    /// Returns the [`Ordering`] of the elements.
99    ///
100    /// # Examples
101    /// ```
102    /// use qfall_math::integer::Z;
103    ///
104    /// let a: Z = Z::from(10);
105    /// let b: Z = Z::from(42);
106    ///
107    /// assert!(a < b);
108    /// assert!(a <= b);
109    /// assert!(b > a);
110    /// assert!(b >= a);
111    /// ```
112    fn cmp(&self, other: &Self) -> Ordering {
113        unsafe { fmpz_cmp(&self.value, &other.value).cmp(&0) }
114    }
115}
116
117impl PartialOrd<Modulus> for Z {
118    /// Compares a [`Z`] value with a [`Modulus`]. Used by the `<`, `<=`, `>`, and `>=` operators.
119    /// [`PartialOrd`] is also implemented for [`Z`] using [`Modulus`].
120    ///
121    /// Parameters:
122    /// - `other`: the other value that is used to compare the elements
123    ///
124    /// Returns the [`Ordering`] of the elements.
125    ///
126    /// # Examples
127    /// ```
128    /// use qfall_math::integer::Z;
129    /// use qfall_math::integer_mod_q::Modulus;
130    ///
131    /// let a: Modulus = Modulus::from(10);
132    /// let b: Z = Z::from(42);
133    ///
134    /// assert!(a < b);
135    /// assert!(a <= b);
136    /// assert!(b > a);
137    /// assert!(b >= a);
138    /// ```
139    fn partial_cmp(&self, other: &Modulus) -> Option<Ordering> {
140        Some(unsafe { fmpz_cmp(&self.value, &other.modulus.n[0]).cmp(&0) })
141    }
142}
143
144implement_for_others!(Z, Z, PartialOrd for fmpz i8 i16 i32 i64 u8 u16 u32 u64);
145
146/// Test that the [`PartialEq`] trait is correctly implemented.
147#[cfg(test)]
148mod test_partial_eq_z {
149    // Test case structure:
150    // 1. Different ways to use equal and not equal.
151    // 2. Test different combinations of equal and not equal with different
152    //    parameter length combinations.
153    //    Not equal test are inverted equal tests.
154
155    use super::Z;
156
157    // Ensure that the function can be called between owned and borrowed values
158    #[test]
159    #[allow(clippy::op_ref)]
160    fn availability() {
161        let z = Z::ONE;
162
163        assert!(z == z);
164        assert!(&z == &z);
165    }
166
167    /// Demonstrate the different ways to use equal.
168    /// We assume that they behave the same in the other tests.
169    #[test]
170    #[allow(clippy::op_ref)]
171    fn equal_call_methods() {
172        let one_1 = Z::from(1);
173        let one_2 = Z::from(1);
174
175        assert!(one_1 == one_2);
176        assert!(&one_1 == &one_2);
177        assert!(one_1.eq(&one_2));
178        assert!(Z::eq(&one_1, &one_2));
179        assert_eq!(one_1, one_2);
180    }
181
182    /// Demonstrate the different ways to use not equal.
183    /// We assume that they behave the same in the other tests.
184    #[test]
185    #[allow(clippy::op_ref)]
186    fn not_equal_call_methods() {
187        let one = Z::from(1);
188        let two = Z::from(2);
189
190        assert!(one != two);
191        assert!(&one != &two);
192        assert!(one.ne(&two));
193        assert!(Z::ne(&one, &two));
194        assert_ne!(one, two);
195    }
196
197    /// Test equal with small positive and negative numbers.
198    #[test]
199    fn equal_small() {
200        let small_1 = Z::from(10);
201        let small_2 = Z::from(10);
202
203        assert!(small_1 == small_2);
204        assert!(small_2 == small_1);
205        assert!(small_1 == small_1);
206    }
207
208    /// Test not equal with small positive and negative numbers.
209    #[test]
210    fn not_equal_small() {
211        let small_1 = Z::from(10);
212        let negative = Z::from(-1);
213
214        assert!(small_1 != negative);
215        assert!(negative != small_1);
216    }
217
218    /// Test equal with a large [`Z`]
219    /// (uses FLINT's pointer representation)
220    #[test]
221    fn equal_large() {
222        let max_1 = Z::from(u64::MAX);
223        let max_2 = Z::from(u64::MAX);
224        let min = Z::from(i64::MIN);
225
226        assert!(max_1 == max_2);
227        assert!(max_2 == max_1);
228        assert!(max_1 == max_1);
229        assert!(min == min);
230    }
231
232    /// Test not equal with a large [`Z`]
233    /// (uses FLINT's pointer representation)
234    #[test]
235    fn not_equal_large() {
236        let max_1 = Z::from(u64::MAX);
237        let min = Z::from(i64::MIN);
238
239        assert!(max_1 != min);
240        assert!(min != max_1);
241    }
242
243    /// Test not equal with a large [`Z`] (uses FLINT's pointer representation)
244    /// and small [`Z`] (no pointer representation).
245    #[test]
246    fn not_equal_large_small() {
247        let max = Z::from(u64::MAX);
248        let small_positive = Z::from(1);
249        let small_negative = Z::from(-1);
250        let min = Z::from(i64::MIN);
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
264/// Test that the [`PartialEq`] trait is correctly implemented.
265#[cfg(test)]
266mod test_partial_eq_z_other {
267    use super::Z;
268
269    // Ensure that the function can be called with several types
270    #[test]
271    #[allow(clippy::op_ref)]
272    fn availability() {
273        let z = Z::from(2);
274
275        assert!(z == z.value);
276        assert!(z == 2i8);
277        assert!(z == 2u8);
278        assert!(z == 2i16);
279        assert!(z == 2u16);
280        assert!(z == 2i32);
281        assert!(z == 2u32);
282        assert!(z == 2i64);
283        assert!(z == 2u64);
284
285        assert!(z.value == z);
286        assert!(2i8 == z);
287        assert!(2u8 == z);
288        assert!(2i16 == z);
289        assert!(2u16 == z);
290        assert!(2i32 == z);
291        assert!(2u32 == z);
292        assert!(2i64 == z);
293        assert!(2u64 == z);
294
295        assert!(&z == &2i8);
296        assert!(&2i8 == &z);
297    }
298}
299
300/// Test the [`PartialOrd`] trait implementation for [`Z`]
301#[cfg(test)]
302#[allow(clippy::neg_cmp_op_on_partial_ord)]
303mod test_partial_ord {
304    use super::Z;
305
306    /// Test less (<) comparison between small positive and negative [`Z`]
307    /// (FLINT is not using pointers)
308    #[test]
309    fn less_small() {
310        let small_positive_1 = Z::from(1);
311        let small_negative = Z::from(-1);
312
313        assert!(small_negative < small_positive_1);
314    }
315
316    /// Test less (<) comparison between large [`Z`] (FLINT uses pointers)
317    /// and small [`Z`] (not using pointers).
318    #[test]
319    fn less_large_small() {
320        let max = Z::from(u64::MAX);
321        let small_positive = Z::from(1);
322        let small_negative = Z::from(-1);
323        let max_negative = Z::from(i64::MIN);
324
325        // Comparisons with max
326        assert!(small_positive < max);
327        assert!(small_negative < max);
328
329        // Comparisons with max_negative
330        assert!(max_negative < small_positive);
331        assert!(max_negative < small_negative);
332    }
333
334    /// Test less (<) comparison between large positive and negative [`Z`]
335    /// (FLINT uses pointers)
336    #[test]
337    fn less_large() {
338        let max_1 = Z::from(u64::MAX);
339        let max_negative = Z::from(i64::MIN);
340
341        assert!(max_negative < max_1);
342    }
343
344    /// Test less or equal (<=) comparison between small positive and negative [`Z`]
345    /// (FLINT is not using pointers)
346    #[test]
347    fn less_equal_small() {
348        let small_positive_1 = Z::from(1);
349        let small_positive_2 = Z::from(1);
350        let small_negative = Z::from(-1);
351
352        assert!(small_positive_1 <= small_positive_2);
353        assert!(small_positive_2 <= small_positive_1);
354        assert!(small_positive_1 <= small_positive_1);
355
356        assert!(small_negative <= small_positive_1);
357        assert!(small_negative <= small_negative);
358    }
359
360    /// Test less or equal (<=) comparison between large [`Z`] (FLINT uses pointers)
361    /// and small [`Z`] (not using pointers).
362    #[test]
363    fn less_equal_large_small() {
364        let max = Z::from(u64::MAX);
365        let small_positive = Z::from(1);
366        let small_negative = Z::from(-1);
367        let max_negative = Z::from(i64::MIN);
368
369        // Comparisons with max
370        assert!(small_positive <= max);
371        assert!(small_negative <= max);
372
373        // Comparisons with max_negative
374        assert!(max_negative <= small_positive);
375        assert!(max_negative <= small_negative);
376    }
377
378    /// Test less or equal (<=) comparison between large positive and negative [`Z`]
379    /// (FLINT uses pointers)
380    #[test]
381    fn less_equal_large() {
382        let max_1 = Z::from(u64::MAX);
383        let max_2 = Z::from(u64::MAX);
384        let max_negative = Z::from(i64::MIN);
385
386        assert!(max_1 <= max_2);
387        assert!(max_2 <= max_1);
388        assert!(max_1 <= max_1);
389
390        assert!(max_negative <= max_1);
391        assert!(max_negative <= max_negative);
392    }
393
394    /// Test greater (>) comparison between small positive and negative [`Z`]
395    /// (FLINT is not using pointers)
396    #[test]
397    fn greater_small() {
398        let small_positive_1 = Z::from(1);
399        let small_negative = Z::from(-1);
400
401        assert!(small_positive_1 > small_negative);
402    }
403
404    /// Test greater (>) comparison between large [`Z`] (FLINT uses pointers)
405    /// and small [`Z`] (not using pointers).
406    #[test]
407    fn greater_large_small() {
408        let max = Z::from(u64::MAX);
409        let small_positive = Z::from(1);
410        let small_negative = Z::from(-1);
411        let max_negative = Z::from(i64::MIN);
412
413        // Comparisons with max
414        assert!(max > small_positive);
415        assert!(max > small_negative);
416
417        // Comparisons with max_negative
418        assert!(small_positive > max_negative);
419        assert!(small_negative > max_negative);
420    }
421
422    /// Test greater (>) comparison between large positive and negative [`Z`]
423    /// (FLINT uses pointers)
424    #[test]
425    fn greater_large() {
426        let max_1 = Z::from(u64::MAX);
427        let max_negative = Z::from(i64::MIN);
428
429        assert!(max_1 > max_negative);
430    }
431
432    /// Test greater or equal (>=) comparison between small positive and negative [`Z`]
433    /// (FLINT is not using pointers)
434    #[test]
435    fn greater_equal_small() {
436        let small_positive_1 = Z::from(1);
437        let small_positive_2 = Z::from(1);
438        let small_negative = Z::from(-1);
439
440        assert!(small_positive_1 >= small_positive_2);
441        assert!(small_positive_2 >= small_positive_1);
442        assert!(small_positive_1 >= small_positive_1);
443
444        assert!(small_positive_1 >= small_negative);
445        assert!(small_negative >= small_negative);
446    }
447
448    /// Test greater or equal (>=) comparison between large [`Z`] (FLINT uses pointers)
449    /// and small [`Z`] (not using pointers).
450    #[test]
451    fn greater_equal_large_small() {
452        let max = Z::from(u64::MAX);
453        let small_positive = Z::from(1);
454        let small_negative = Z::from(-1);
455        let max_negative = Z::from(i64::MIN);
456
457        // Comparisons with max
458        assert!(max >= small_positive);
459        assert!(max >= small_negative);
460
461        // Comparisons with max_negative
462        assert!(small_positive >= max_negative);
463        assert!(small_negative >= max_negative);
464    }
465
466    /// Test greater or equal (>=) comparison between large positive and negative [`Z`]
467    /// (FLINT uses pointers)
468    #[test]
469    fn greater_equal_large() {
470        let max_1 = Z::from(u64::MAX);
471        let max_2 = Z::from(u64::MAX);
472        let max_negative = Z::from(i64::MIN);
473
474        assert!(max_1 >= max_2);
475        assert!(max_2 >= max_1);
476        assert!(max_1 >= max_1);
477
478        assert!(max_1 >= max_negative);
479        assert!(max_negative >= max_negative);
480    }
481}
482
483/// Test that the [`PartialOrd`] trait is correctly implemented.
484#[cfg(test)]
485mod test_partial_ord_z_other {
486    use super::Z;
487    use crate::integer_mod_q::Modulus;
488
489    // Ensure that the function can be called with several types
490    #[test]
491    #[allow(clippy::op_ref)]
492    fn availability() {
493        let z = Z::from(2);
494        let modulus = Modulus::from(2);
495
496        assert!(z <= modulus);
497        assert!(z <= z.value);
498        assert!(z <= 2i8);
499        assert!(z <= 2u8);
500        assert!(z <= 2i16);
501        assert!(z <= 2u16);
502        assert!(z <= 2i32);
503        assert!(z <= 2u32);
504        assert!(z <= 2i64);
505        assert!(z <= 2u64);
506
507        assert!(z.value >= z);
508        assert!(2i8 >= z);
509        assert!(2u8 >= z);
510        assert!(2i16 >= z);
511        assert!(2u16 >= z);
512        assert!(2i32 >= z);
513        assert!(2u32 >= z);
514        assert!(2i64 >= z);
515        assert!(2u64 >= z);
516
517        assert!(&z <= &modulus);
518        assert!(&z <= &2i8);
519        assert!(&2i8 >= &z);
520    }
521}
522
523#[cfg(test)]
524mod test_ord {
525    use super::Z;
526    use std::cmp::{max, min};
527
528    // `cmp` is extensively tested in module `test_partial_eq`, hence omitted here
529
530    /// Check whether default implementations `max`, `min`, `clamp`
531    /// work properly for small numbers
532    #[test]
533    fn default_implementations_small() {
534        let a: Z = Z::from(10);
535        let b: Z = Z::from(42);
536
537        assert_eq!(b, max(a.clone(), b.clone()));
538        assert_eq!(a, min(a.clone(), b.clone()));
539
540        assert_eq!(a, Z::ZERO.clamp(a.clone(), b.clone()));
541        assert_eq!(a, a.clone().clamp(Z::ZERO, b.clone()));
542        assert_eq!(a, b.clamp(Z::ZERO, a.clone()));
543    }
544
545    /// Check whether default implementations `max`, `min`, `clamp`
546    /// work properly for large numbers
547    #[test]
548    fn default_implementations_large() {
549        let a: Z = Z::from(i64::MAX);
550        let b: Z = Z::from(u64::MAX);
551
552        assert_eq!(b, max(a.clone(), b.clone()));
553        assert_eq!(a, min(a.clone(), b.clone()));
554
555        assert_eq!(a, Z::ZERO.clamp(a.clone(), b.clone()));
556        assert_eq!(a, a.clone().clamp(Z::ZERO, b.clone()));
557        assert_eq!(a, b.clamp(Z::ZERO, a.clone()));
558    }
559}