qfall_math/rational/mat_q/
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//! This module contains implementations for comparison of [`MatQ`].
10
11use super::MatQ;
12use crate::{
13    integer::MatZ,
14    macros::{compare_base::compare_base_default, for_others::implement_trait_reverse},
15    rational::Q,
16    traits::{CompareBase, MatrixDimensions, MatrixGetEntry},
17};
18use flint_sys::{
19    fmpq_mat::{fmpq_mat_equal, fmpq_mat_set_fmpz_mat_div_fmpz},
20    fmpz::fmpz,
21};
22
23impl PartialEq for MatQ {
24    /// Checks if two [`MatQ`] instances are equal. Used by the `==` and `!=` operators.
25    ///
26    /// Parameters:
27    /// - `other`: the other value that is compare against `self`
28    ///
29    /// Returns `true` if the elements are equal, otherwise `false`.
30    ///
31    /// # Examples
32    /// ```
33    /// use qfall_math::rational::MatQ;
34    /// use std::str::FromStr;
35    ///
36    /// let a_1 = MatQ::from_str("[[1/2, 2],[3/2, 4]]").unwrap();
37    /// let a_2 = MatQ::from_str("[[2/4, 2],[3/2, 4]]").unwrap();
38    /// assert!(a_1 == a_2);
39    ///
40    /// let b = MatQ::from_str("[[1, 2],[2, 4]]").unwrap();
41    ///
42    /// // These are all equivalent and return false.
43    /// let compared: bool = (a_1 == b);
44    /// # assert!(!compared);
45    /// let compared: bool = (&a_1 == &b);
46    /// # assert!(!compared);
47    /// let compared: bool = (a_1.eq(&b));
48    /// # assert!(!compared);
49    /// let compared: bool = (MatQ::eq(&a_1, &b));
50    /// # assert!(!compared);
51    /// ```
52    fn eq(&self, other: &Self) -> bool {
53        unsafe { fmpq_mat_equal(&self.matrix, &other.matrix) != 0 }
54    }
55}
56
57// With the [`Eq`] trait, `a == a` is always true.
58// This is not guaranteed by the [`PartialEq`] trait.
59impl Eq for MatQ {}
60
61impl PartialEq<MatZ> for MatQ {
62    /// Checks if an integer matrix and a rational matrix are equal. Used by the `==` and `!=` operators.
63    /// [`PartialEq`] is also implemented for [`MatZ`] using [`MatQ`].
64    ///
65    /// Parameters:
66    /// - `other`: the other value that is used to compare the elements
67    ///
68    /// Returns `true` if the elements are equal, otherwise `false`.
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer::MatZ;
73    /// use qfall_math::rational::MatQ;
74    /// use std::str::FromStr;
75    /// let a: MatQ = MatQ::from_str("[[42, 2],[3, 4]]").unwrap();
76    /// let b: MatZ = MatZ::from_str("[[42, 2],[3, 4]]").unwrap();
77    ///
78    /// // These are all equivalent and return true.
79    /// let compared: bool = (a == b);
80    /// # assert!(compared);
81    /// let compared: bool = (b == a);
82    /// # assert!(compared);
83    /// let compared: bool = (&a == &b);
84    /// # assert!(compared);
85    /// let compared: bool = (&b == &a);
86    /// # assert!(compared);
87    /// let compared: bool = (a.eq(&b));
88    /// # assert!(compared);
89    /// let compared: bool = (b.eq(&a));
90    /// # assert!(compared);
91    /// let compared: bool = (MatQ::eq(&a, &b));
92    /// # assert!(compared);
93    /// let compared: bool = (MatZ::eq(&b, &a));
94    /// # assert!(compared);
95    /// ```
96    fn eq(&self, other: &MatZ) -> bool {
97        let num_rows = self.get_num_rows();
98        let num_cols = self.get_num_columns();
99
100        if num_rows != other.get_num_rows() || num_cols != other.get_num_columns() {
101            return false;
102        }
103
104        for i in 0..num_rows {
105            for j in 0..num_cols {
106                if unsafe { self.get_entry_unchecked(i, j) != other.get_entry_unchecked(i, j) } {
107                    return false;
108                }
109            }
110        }
111
112        true
113    }
114}
115
116impl MatQ {
117    pub fn equal(self, other: MatZ) -> bool {
118        let mut other_matq = MatQ::new(other.get_num_rows(), other.get_num_columns());
119        unsafe { fmpq_mat_set_fmpz_mat_div_fmpz(&mut other_matq.matrix, &other.matrix, &fmpz(1)) };
120        1 != unsafe { fmpq_mat_equal(&other_matq.matrix, &self.matrix) }
121    }
122}
123
124implement_trait_reverse!(PartialEq, eq, MatZ, MatQ, bool);
125
126compare_base_default!(MatQ for MatQ MatZ);
127impl<Rational: Into<Q>> CompareBase<Rational> for MatQ {}
128
129/// Test that the [`PartialEq`] trait is correctly implemented.
130#[cfg(test)]
131mod test_partial_eq {
132    use super::MatQ;
133    use crate::{rational::Q, traits::MatrixSetEntry};
134    use std::str::FromStr;
135
136    /// Ensures that different instantiations do not break the equality between matrices
137    #[test]
138    fn equality_between_instantiations() {
139        let a = MatQ::from_str("[[0, 1/2],[0/2, 0]]").unwrap();
140        let mut b = MatQ::new(2, 2);
141        b.set_entry(0, 1, Q::from((2, 4))).unwrap();
142
143        assert_eq!(a, b);
144    }
145
146    /// Checks that large and small entries (and different points in storage) do not break equality
147    #[test]
148    fn equality_for_large_and_small_entries() {
149        let mat_str_1 = &format!(
150            "[[{}/{}, {}/{}, 1],[-10, 10, 0],[0, 1, -10]]",
151            i64::MIN,
152            i64::MIN + 1,
153            i64::MAX,
154            i64::MAX - 1
155        );
156
157        // like mat_str_1 but also 2nd row is expanded by 2
158        let mat_str_2 = &format!(
159            "[[{}/{}, {}/{}, 1],[-20/2, 20/2, 0/2],[0, 1, -10]]",
160            i64::MIN,
161            i64::MIN + 1,
162            i64::MAX,
163            i64::MAX - 1
164        );
165
166        let a = MatQ::from_str(mat_str_1).unwrap();
167        let b = MatQ::from_str(mat_str_1).unwrap();
168        let c = MatQ::from_str(mat_str_2).unwrap();
169
170        assert_eq!(&a, &b);
171        assert_eq!(&a, &c);
172    }
173
174    /// Checks that different unequal matrices are unequal
175    #[test]
176    fn not_equal() {
177        let a = MatQ::from_str(&format!("[[{}, {}],[-10, 10]]", i64::MIN, i64::MAX)).unwrap();
178        let b = MatQ::from_str(&format!("[[0, {}],[-10, 10]]", i64::MAX)).unwrap();
179        let c =
180            MatQ::from_str(&format!("[[{}, {}],[-10, 10],[0, 0]]", i64::MIN, i64::MAX)).unwrap();
181        let d = MatQ::from_str(&format!("[[{}, {}]]", i64::MIN, i64::MAX)).unwrap();
182        let e = MatQ::from_str("[[0]]").unwrap();
183
184        assert_ne!(&a, &b);
185        assert_ne!(&a, &c);
186        assert_ne!(&a, &d);
187        assert_ne!(&a, &e);
188        assert_ne!(&b, &c);
189        assert_ne!(&b, &d);
190        assert_ne!(&b, &e);
191        assert_ne!(&c, &d);
192        assert_ne!(&c, &e);
193        assert_ne!(&d, &e);
194    }
195}
196
197/// Test that the [`PartialEq`] trait is correctly implemented.
198#[cfg(test)]
199mod test_partial_eq_q_other {
200    use super::MatQ;
201    use crate::integer::MatZ;
202    use std::str::FromStr;
203
204    /// Ensure that the function can be called with several types.
205    #[test]
206    #[allow(clippy::op_ref)]
207    fn availability() {
208        let q = MatQ::from_str("[[1, 2],[3, 4]]").unwrap();
209        let z = MatZ::from_str("[[1, 2],[3, 4]]").unwrap();
210
211        assert!(q == z);
212        assert!(z == q);
213        assert!(&q == &z);
214        assert!(&z == &q);
215    }
216
217    /// Ensure that large values are compared correctly.
218    #[test]
219    fn equal_large() {
220        let q = MatQ::from_str(&format!("[[1,2],[3,{}]]", u64::MAX)).unwrap();
221        let z = MatZ::from_str(&format!("[[1,2],[3,{}]]", u64::MAX)).unwrap();
222
223        assert!(q == z);
224    }
225}
226
227/// Test that the [`CompareBase`] trait uses the default implementation.
228#[cfg(test)]
229mod test_compare_base {
230    use crate::{
231        integer::{MatZ, Z},
232        rational::{MatQ, Q},
233        traits::CompareBase,
234    };
235    use std::str::FromStr;
236
237    /// Ensures that the [`CompareBase`] trait uses the default implementation
238    /// and is available for all types it would be checked against.
239    #[test]
240    fn availability() {
241        let one_1 = MatQ::from_str("[[2,5,-7/3],[2,3,4]]").unwrap();
242
243        assert!(one_1.compare_base(&MatZ::new(1, 1)));
244        assert!(one_1.compare_base(&MatQ::new(1, 1)));
245        assert!(one_1.compare_base(&Z::ONE));
246        assert!(one_1.compare_base(&Q::ONE));
247        assert!(one_1.compare_base(&0_i8));
248        assert!(one_1.compare_base(&0_i16));
249        assert!(one_1.compare_base(&0_i32));
250        assert!(one_1.compare_base(&0_i64));
251        assert!(one_1.compare_base(&0_u8));
252        assert!(one_1.compare_base(&0_u16));
253        assert!(one_1.compare_base(&0_u32));
254        assert!(one_1.compare_base(&0_u64));
255        assert!(one_1.compare_base(&0.5_f32));
256        assert!(one_1.compare_base(&0.5_f64));
257
258        assert!(one_1.call_compare_base_error(&MatZ::new(1, 1)).is_none());
259        assert!(one_1.call_compare_base_error(&MatQ::new(1, 1)).is_none());
260        assert!(one_1.call_compare_base_error(&Z::ONE).is_none());
261        assert!(one_1.call_compare_base_error(&Q::ONE).is_none());
262        assert!(one_1.call_compare_base_error(&0_i8).is_none());
263        assert!(one_1.call_compare_base_error(&0_i16).is_none());
264        assert!(one_1.call_compare_base_error(&0_i32).is_none());
265        assert!(one_1.call_compare_base_error(&0_i64).is_none());
266        assert!(one_1.call_compare_base_error(&0_u8).is_none());
267        assert!(one_1.call_compare_base_error(&0_u16).is_none());
268        assert!(one_1.call_compare_base_error(&0_u32).is_none());
269        assert!(one_1.call_compare_base_error(&0_u64).is_none());
270        assert!(one_1.call_compare_base_error(&0.5_f32).is_none());
271        assert!(one_1.call_compare_base_error(&0.5_f64).is_none());
272    }
273}