Skip to main content

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