malachite_nz/gaussian_integer/comparison/cmp_abs_primitive_float.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::gaussian_integer::GaussianInteger;
10use crate::natural::Natural;
11use core::cmp::Ordering::{self, Greater, Less};
12use malachite_base::num::arithmetic::traits::{AbsSquared, UnsignedAbs};
13use malachite_base::num::comparison::traits::PartialOrdAbs;
14use malachite_base::num::conversion::traits::IntegerMantissaAndExponent;
15
16macro_rules! impl_float {
17 ($t: ident) => {
18 impl PartialOrdAbs<$t> for GaussianInteger {
19 /// Compares the absolute values of a [`GaussianInteger`] and a primitive float.
20 ///
21 /// NaN is not comparable to any [`GaussianInteger`]. $\infty$ and $-\infty$ are greater
22 /// in absolute value than any [`GaussianInteger`]. When the squared absolute values
23 /// must be compared, the float's square is represented exactly as an odd square times a
24 /// power of two, so the comparison is exact.
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 the maximum number of
32 /// significant bits of the real and imaginary parts of `self` and
33 /// `other.sci_exponent().abs()`.
34 ///
35 /// # Examples
36 /// See [here](super::cmp_abs_primitive_float#partial_cmp_abs).
37 fn partial_cmp_abs(&self, other: &$t) -> Option<Ordering> {
38 if other.is_nan() {
39 None
40 } else if !other.is_finite() {
41 Some(Less)
42 } else if self.imaginary == 0u32 {
43 self.real.partial_cmp_abs(other)
44 } else if self.real == 0u32 {
45 self.imaginary.partial_cmp_abs(other)
46 } else if !self.real.lt_abs(other) || !self.imaginary.lt_abs(other) {
47 // This also covers a zero float, whose absolute value cannot exceed either
48 // nonzero component.
49 Some(Greater)
50 } else {
51 // |other| = m * 2^e with m odd, so |other|^2 = m^2 * 2^(2e), compared exactly
52 // against |self|^2 by shifting whichever side has the nonnegative exponent.
53 let (m, e) = other.abs().integer_mantissa_and_exponent();
54 let m_squared = Natural::from(u128::from(m) * u128::from(m));
55 let abs_squared = self.abs_squared().unsigned_abs();
56 let shift = e.unsigned_abs() << 1;
57 Some(if e >= 0 {
58 abs_squared.cmp(&(m_squared << shift))
59 } else {
60 (abs_squared << shift).cmp(&m_squared)
61 })
62 }
63 }
64 }
65
66 impl PartialOrdAbs<GaussianInteger> for $t {
67 /// Compares the absolute values of a primitive float and a [`GaussianInteger`].
68 ///
69 /// NaN is not comparable to any [`GaussianInteger`]. $\infty$ and $-\infty$ are greater
70 /// in absolute value than any [`GaussianInteger`].
71 ///
72 /// # Worst-case complexity
73 /// $T(n) = O(n \log n \log\log n)$
74 ///
75 /// $M(n) = O(n \log n)$
76 ///
77 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of
78 /// significant bits of the real and imaginary parts of `other` and
79 /// `self.sci_exponent().abs()`.
80 ///
81 /// # Examples
82 /// See [here](super::cmp_abs_primitive_float#partial_cmp_abs).
83 #[inline]
84 fn partial_cmp_abs(&self, other: &GaussianInteger) -> Option<Ordering> {
85 other.partial_cmp_abs(self).map(Ordering::reverse)
86 }
87 }
88 };
89}
90apply_to_primitive_floats!(impl_float);