malachite_q/comparison/
cmp_abs.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::Rational;
10use core::cmp::Ordering::{self, *};
11use malachite_base::num::arithmetic::traits::Sign;
12use malachite_base::num::comparison::traits::{OrdAbs, PartialOrdAbs};
13
14impl PartialOrdAbs for Rational {
15    /// Compares the absolute values of two [`Rational`]s.
16    ///
17    /// See the documentation for the [`OrdAbs`] implementation.
18    #[inline]
19    fn partial_cmp_abs(&self, other: &Self) -> Option<Ordering> {
20        Some(self.cmp_abs(other))
21    }
22}
23
24impl OrdAbs for Rational {
25    /// Compares the absolute values of two [`Rational`]s.
26    ///
27    /// # Worst-case complexity
28    /// $T(n) = O(n \log n \log\log n)$
29    ///
30    /// $M(n) = O(n \log n)$
31    ///
32    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
33    /// other.significant_bits())`.
34    ///
35    /// # Examples
36    /// ```
37    /// use malachite_base::num::basic::traits::OneHalf;
38    /// use malachite_base::num::comparison::traits::OrdAbs;
39    /// use malachite_q::Rational;
40    /// use std::cmp::Ordering::*;
41    /// use std::str::FromStr;
42    ///
43    /// assert_eq!(
44    ///     Rational::from_str("2/3")
45    ///         .unwrap()
46    ///         .cmp_abs(&Rational::ONE_HALF),
47    ///     Greater
48    /// );
49    /// assert_eq!(
50    ///     Rational::from_str("-2/3")
51    ///         .unwrap()
52    ///         .cmp_abs(&Rational::ONE_HALF),
53    ///     Greater
54    /// );
55    /// ```
56    fn cmp_abs(&self, other: &Self) -> Ordering {
57        if core::ptr::eq(self, other) {
58            return Equal;
59        }
60        // First check if either value is zero
61        let self_sign = self.numerator_ref().sign();
62        let other_sign = other.numerator_ref().sign();
63        let sign_cmp = self_sign.cmp(&other_sign);
64        if sign_cmp != Equal || self_sign == Equal {
65            return sign_cmp;
66        }
67        // Then check if one is < 1 and the other is > 1
68        let self_cmp_one = self.numerator.cmp(&self.denominator);
69        let other_cmp_one = other.numerator.cmp(&other.denominator);
70        let one_cmp = self_cmp_one.cmp(&other_cmp_one);
71        if one_cmp != Equal {
72            return one_cmp;
73        }
74        // Then compare numerators and denominators
75        let n_cmp = self.numerator.cmp(&other.numerator);
76        let d_cmp = self.denominator.cmp(&other.denominator);
77        if n_cmp == Equal && d_cmp == Equal {
78            return Equal;
79        }
80        let nd_cmp = n_cmp.cmp(&d_cmp);
81        if nd_cmp != Equal {
82            return nd_cmp;
83        }
84        // Then compare floor ∘ log_2 ∘ abs
85        let log_cmp = self
86            .floor_log_base_2_abs()
87            .cmp(&other.floor_log_base_2_abs());
88        if log_cmp != Equal {
89            return log_cmp;
90        }
91        // Finally, cross-multiply.
92        (&self.numerator * &other.denominator).cmp(&(&self.denominator * &other.numerator))
93    }
94}