Skip to main content

malachite_nz/gaussian_integer/arithmetic/
canonical_unit_i_pow.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 core::cmp::Ordering::*;
11use malachite_base::num::arithmetic::traits::CanonicalUnitIPow;
12use malachite_base::num::comparison::traits::PartialOrdAbs;
13
14impl CanonicalUnitIPow for GaussianInteger {
15    /// Finds the power of $i$ that brings a [`GaussianInteger`] into canonical unit form.
16    ///
17    /// A nonzero value has four associates, $x$, $ix$, $-x$, and $-ix$; the canonical one is the
18    /// associate whose argument lies in $(-\pi/4, \pi/4]$, that is, whose real part $a$ is positive
19    /// and whose imaginary part $b$ satisfies $-a < b \leq a$. The result is the $k \in \\{0, 1, 2,
20    /// 3\\}$ such that $x i^k$ is canonical, and 0 for zero. The choice of associate, including the
21    /// tie on the diagonals, matches FLINT's `fmpzi_canonical_unit_i_pow`.
22    ///
23    /// # Worst-case complexity
24    /// $T(n) = O(n)$
25    ///
26    /// $M(n) = O(1)$
27    ///
28    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
29    /// bits of the real and imaginary parts.
30    ///
31    /// # Examples
32    /// ```
33    /// use malachite_base::num::arithmetic::traits::CanonicalUnitIPow;
34    /// use malachite_nz::gaussian_integer::GaussianInteger;
35    /// use std::str::FromStr;
36    ///
37    /// assert_eq!(
38    ///     GaussianInteger::from_str("2+i")
39    ///         .unwrap()
40    ///         .canonical_unit_i_pow(),
41    ///     0
42    /// );
43    /// assert_eq!(
44    ///     GaussianInteger::from_str("-1+2i")
45    ///         .unwrap()
46    ///         .canonical_unit_i_pow(),
47    ///     3
48    /// );
49    /// assert_eq!(
50    ///     GaussianInteger::from_str("-2-i")
51    ///         .unwrap()
52    ///         .canonical_unit_i_pow(),
53    ///     2
54    /// );
55    /// assert_eq!(
56    ///     GaussianInteger::from_str("1-2i")
57    ///         .unwrap()
58    ///         .canonical_unit_i_pow(),
59    ///     1
60    /// );
61    /// assert_eq!(
62    ///     GaussianInteger::from_str("1+i")
63    ///         .unwrap()
64    ///         .canonical_unit_i_pow(),
65    ///     0
66    /// );
67    /// assert_eq!(
68    ///     GaussianInteger::from_str("1-i")
69    ///         .unwrap()
70    ///         .canonical_unit_i_pow(),
71    ///     1
72    /// );
73    /// assert_eq!(
74    ///     GaussianInteger::from_str("0")
75    ///         .unwrap()
76    ///         .canonical_unit_i_pow(),
77    ///     0
78    /// );
79    /// ```
80    fn canonical_unit_i_pow(&self) -> u64 {
81        match self.real.cmp(&self.imaginary) {
82            Equal => u64::from(self.real < 0u32) << 1,
83            Greater => u64::from(self.real.le_abs(&self.imaginary)),
84            Less => {
85                if self.real.le_abs(&self.imaginary) {
86                    3
87                } else {
88                    2
89                }
90            }
91        }
92    }
93}