malachite_q/comparison/cmp.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;
12
13impl PartialOrd for Rational {
14 /// Compares two [`Rational`]s.
15 ///
16 /// See the documentation for the [`Ord`] implementation.
17 #[inline]
18 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
19 Some(self.cmp(other))
20 }
21}
22
23impl Ord for Rational {
24 /// Compares two [`Rational`]s.
25 ///
26 /// # Worst-case complexity
27 /// $T(n) = O(n \log n \log\log n)$
28 ///
29 /// $M(n) = O(n \log n)$
30 ///
31 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
32 /// other.significant_bits())`.
33 ///
34 /// # Examples
35 /// ```
36 /// use malachite_base::num::basic::traits::OneHalf;
37 /// use malachite_q::Rational;
38 /// use std::str::FromStr;
39 ///
40 /// assert!(Rational::from_str("2/3").unwrap() > Rational::ONE_HALF);
41 /// assert!(Rational::from_str("-2/3").unwrap() < Rational::ONE_HALF);
42 /// ```
43 fn cmp(&self, other: &Self) -> Ordering {
44 if core::ptr::eq(self, other) {
45 return Equal;
46 }
47 // First check signs
48 let self_sign = self.sign();
49 let other_sign = other.sign();
50 let sign_cmp = self_sign.cmp(&other_sign);
51 if sign_cmp != Equal || self_sign == Equal {
52 return sign_cmp;
53 }
54 // Then check if one is < 1 and the other is > 1
55 let self_cmp_one = self.numerator.cmp(&self.denominator);
56 let other_cmp_one = other.numerator.cmp(&other.denominator);
57 let one_cmp = self_cmp_one.cmp(&other_cmp_one);
58 if one_cmp != Equal {
59 return if self.sign {
60 one_cmp
61 } else {
62 one_cmp.reverse()
63 };
64 }
65 // Then compare numerators and denominators
66 let n_cmp = self.numerator.cmp(&other.numerator);
67 let d_cmp = self.denominator.cmp(&other.denominator);
68 if n_cmp == Equal && d_cmp == Equal {
69 return Equal;
70 }
71 let nd_cmp = n_cmp.cmp(&d_cmp);
72 if nd_cmp != Equal {
73 return if self.sign { nd_cmp } else { nd_cmp.reverse() };
74 }
75 // Then compare floor ∘ log_2 ∘ abs
76 let log_cmp = self
77 .floor_log_base_2_abs()
78 .cmp(&other.floor_log_base_2_abs());
79 if log_cmp != Equal {
80 return if self.sign {
81 log_cmp
82 } else {
83 log_cmp.reverse()
84 };
85 }
86 // Finally, cross-multiply.
87 let prod_cmp =
88 (&self.numerator * &other.denominator).cmp(&(&self.denominator * &other.numerator));
89 if self.sign {
90 prod_cmp
91 } else {
92 prod_cmp.reverse()
93 }
94 }
95}