malachite_nz/integer/comparison/cmp_products.rs
1// Copyright © 2026 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::integer::Integer;
10use crate::natural::Natural;
11use core::cmp::Ordering::{self, *};
12use malachite_base::num::comparison::traits::{OrdProducts, OrdProductsNeg};
13
14// Compares `a * b` with `c * d`, or with `-(c * d)` if `negate_right`. The signs settle every case
15// except when both products are nonzero with the same sign, where the absolute values are compared
16// by `Natural::cmp_products`.
17fn cmp_products_helper(
18 a: &Integer,
19 b: &Integer,
20 c: &Integer,
21 d: &Integer,
22 negate_right: bool,
23) -> Ordering {
24 let left_zero = a.abs == 0u32 || b.abs == 0u32;
25 let right_zero = c.abs == 0u32 || d.abs == 0u32;
26 let left_positive = a.sign == b.sign;
27 let right_positive = (c.sign == d.sign) != negate_right;
28 if left_zero {
29 if right_zero {
30 Equal
31 } else if right_positive {
32 Less
33 } else {
34 Greater
35 }
36 } else if left_positive {
37 if right_zero || !right_positive {
38 Greater
39 } else {
40 Natural::cmp_products(&a.abs, &b.abs, &c.abs, &d.abs)
41 }
42 } else if right_zero || right_positive {
43 Less
44 } else {
45 Natural::cmp_products(&c.abs, &d.abs, &a.abs, &b.abs)
46 }
47}
48
49impl OrdProducts for Integer {
50 /// Compares the products of two pairs of [`Integer`]s, computing the products only as a last
51 /// resort.
52 ///
53 /// $$
54 /// f(a, b, c, d) = \operatorname{cmp}(ab, cd).
55 /// $$
56 ///
57 /// The signs decide every case except when both products are nonzero and have the same sign,
58 /// where the absolute values are compared by
59 /// [`Natural::cmp_products`](OrdProducts::cmp_products), which multiplies only as a last
60 /// resort.
61 ///
62 /// # Worst-case complexity
63 /// $T(n) = O(n \log n \log\log n)$
64 ///
65 /// $M(n) = O(n \log n)$
66 ///
67 /// where $T$ is time, $M$ is additional memory, and $n$ is the largest number of significant
68 /// bits of `a`, `b`, `c`, and `d`.
69 ///
70 /// # Examples
71 /// ```
72 /// use core::cmp::Ordering::*;
73 /// use malachite_base::num::comparison::traits::OrdProducts;
74 /// use malachite_nz::integer::Integer;
75 ///
76 /// let z = |x: i32| Integer::from(x);
77 /// assert_eq!(Integer::cmp_products(&z(-3), &z(7), &z(-7), &z(3)), Equal);
78 /// assert_eq!(Integer::cmp_products(&z(-3), &z(7), &z(-4), &z(5)), Less);
79 /// assert_eq!(Integer::cmp_products(&z(3), &z(7), &z(-4), &z(5)), Greater);
80 /// ```
81 ///
82 /// This is equivalent to `compareProducts` from `AzInt/CompareProducts.lean`, Azurite.
83 #[inline]
84 fn cmp_products(a: &Self, b: &Self, c: &Self, d: &Self) -> Ordering {
85 cmp_products_helper(a, b, c, d, false)
86 }
87}
88
89impl OrdProductsNeg for Integer {
90 /// Compares the product of two [`Integer`]s with the negative of the product of two others,
91 /// computing the products only as a last resort and never computing the negative; equivalently,
92 /// the sign of $ab + cd$.
93 ///
94 /// $$
95 /// f(a, b, c, d) = \operatorname{cmp}(ab, -cd).
96 /// $$
97 ///
98 /// The negative is handled by flipping the sign of $cd$, which is read from the signs of $c$
99 /// and $d$, so no value is allocated for it.
100 ///
101 /// # Worst-case complexity
102 /// $T(n) = O(n \log n \log\log n)$
103 ///
104 /// $M(n) = O(n \log n)$
105 ///
106 /// where $T$ is time, $M$ is additional memory, and $n$ is the largest number of significant
107 /// bits of `a`, `b`, `c`, and `d`.
108 ///
109 /// # Examples
110 /// ```
111 /// use core::cmp::Ordering::*;
112 /// use malachite_base::num::comparison::traits::OrdProductsNeg;
113 /// use malachite_nz::integer::Integer;
114 ///
115 /// let z = |x: i32| Integer::from(x);
116 /// assert_eq!(
117 /// Integer::cmp_products_neg(&z(-3), &z(7), &z(7), &z(3)),
118 /// Equal
119 /// );
120 /// assert_eq!(Integer::cmp_products_neg(&z(-3), &z(7), &z(4), &z(5)), Less);
121 /// assert_eq!(
122 /// Integer::cmp_products_neg(&z(3), &z(7), &z(4), &z(5)),
123 /// Greater
124 /// );
125 /// ```
126 #[inline]
127 fn cmp_products_neg(a: &Self, b: &Self, c: &Self, d: &Self) -> Ordering {
128 cmp_products_helper(a, b, c, d, true)
129 }
130}