Skip to main content

malachite_nz/gaussian_integer/factorization/
remove_one_plus_i.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2022 Fredrik Johansson
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::gaussian_integer::GaussianInteger;
14use core::cmp::Ordering::*;
15use core::mem::take;
16use malachite_base::num::arithmetic::traits::{ModPowerOf2, MulIPowAssign};
17use malachite_base::num::basic::traits::Zero;
18
19// The largest power of 2 dividing both parts, and whether one more factor of 1 + i remains after
20// that power of 2 (which is (1 + i)^2 up to a unit) is removed. The input must be nonzero.
21fn one_plus_i_valuation(x: &GaussianInteger) -> (u64, bool) {
22    match (x.real.trailing_zeros(), x.imaginary.trailing_zeros()) {
23        (Some(s), None) | (None, Some(s)) => (s, false),
24        (Some(s), Some(t)) => match s.cmp(&t) {
25            // Both parts are odd after the shift, so their sum is even and 1 + i divides once more.
26            Equal => (s, true),
27            Less => (s, false),
28            Greater => (t, false),
29        },
30        (None, None) => unreachable!(),
31    }
32}
33
34// Given x with the common power of 2 already shifted out, fixes up the unit and removes the
35// remaining factor of 1 + i if there is one, returning the reduced number and the exponent.
36fn remove_one_plus_i_helper(mut x: GaussianInteger, s: u64, odd: bool) -> (GaussianInteger, u64) {
37    if s != 0 {
38        // Multiply by i^(-s), the unit left over when (1 + i)^(2s) = (2i)^s is removed by a shift.
39        x.mul_i_pow_assign(4 - s.mod_power_of_2(2));
40    }
41    if odd {
42        // (a + bi) / (1 + i) = ((a + b) + (b - a)i) / 2
43        let t = &x.real + &x.imaginary;
44        x.imaginary -= &x.real;
45        x.real = t >> 1u32;
46        x.imaginary >>= 1u32;
47    }
48    (x, (s << 1) | u64::from(odd))
49}
50
51impl GaussianInteger {
52    /// Removes the largest power of $1 + i$ from a [`GaussianInteger`], taking it by reference and
53    /// returning the reduced [`GaussianInteger`] together with the exponent of that power.
54    ///
55    /// $1 + i$ is the Gaussian prime above 2, with $(1 + i)^2 = 2i$. If $(1 + i)^k$ is the largest
56    /// power of $1 + i$ that divides `self`, this returns $(\text{self} / (1 + i)^k, k)$. The
57    /// exponent is twice the largest power of 2 dividing both parts, plus one more when the parts
58    /// have the same 2-adic valuation, since then both are odd after the shift and their sum is
59    /// even. Zero is left alone, with an exponent of 0, since every power of $1 + i$ divides it.
60    ///
61    /// # Worst-case complexity
62    /// $T(n) = O(n)$
63    ///
64    /// $M(n) = O(n)$
65    ///
66    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
67    ///
68    /// # Examples
69    /// ```
70    /// use malachite_base::num::basic::traits::Two;
71    /// use malachite_nz::gaussian_integer::GaussianInteger;
72    /// use std::str::FromStr;
73    ///
74    /// // 6+2i = (-1-2i)(1+i)^3
75    /// let (q, k) = GaussianInteger::from_str("6+2i")
76    ///     .unwrap()
77    ///     .remove_one_plus_i();
78    /// assert_eq!(q.to_string(), "-1-2i");
79    /// assert_eq!(k, 3);
80    ///
81    /// // 2 = (-i)(1+i)^2
82    /// let (q, k) = GaussianInteger::TWO.remove_one_plus_i();
83    /// assert_eq!(q.to_string(), "-i");
84    /// assert_eq!(k, 2);
85    ///
86    /// // 3+2i is not divisible by 1+i
87    /// let (q, k) = GaussianInteger::from_str("3+2i")
88    ///     .unwrap()
89    ///     .remove_one_plus_i();
90    /// assert_eq!(q.to_string(), "3+2i");
91    /// assert_eq!(k, 0);
92    /// ```
93    pub fn remove_one_plus_i(&self) -> (Self, u64) {
94        if *self == 0u32 {
95            return (Self::ZERO, 0);
96        }
97        let (s, odd) = one_plus_i_valuation(self);
98        let x = if s == 0 {
99            self.clone()
100        } else {
101            Self {
102                real: &self.real >> s,
103                imaginary: &self.imaginary >> s,
104            }
105        };
106        remove_one_plus_i_helper(x, s, odd)
107    }
108
109    /// Removes the largest power of $1 + i$ from a [`GaussianInteger`] in place, returning the
110    /// exponent of that power.
111    ///
112    /// $1 + i$ is the Gaussian prime above 2, with $(1 + i)^2 = 2i$. If $(1 + i)^k$ is the largest
113    /// power of $1 + i$ that divides `self`, this replaces `self` with $\text{self} / (1 + i)^k$
114    /// and returns $k$. Zero is left alone, with an exponent of 0, since every power of $1 + i$
115    /// divides it.
116    ///
117    /// # Worst-case complexity
118    /// $T(n) = O(n)$
119    ///
120    /// $M(n) = O(n)$
121    ///
122    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
123    ///
124    /// # Examples
125    /// ```
126    /// use malachite_nz::gaussian_integer::GaussianInteger;
127    /// use std::str::FromStr;
128    ///
129    /// // 6+2i = (-1-2i)(1+i)^3
130    /// let mut x = GaussianInteger::from_str("6+2i").unwrap();
131    /// assert_eq!(x.remove_one_plus_i_assign(), 3);
132    /// assert_eq!(x.to_string(), "-1-2i");
133    ///
134    /// // 1000000000000 = 244140625 (1+i)^24
135    /// let mut x = GaussianInteger::from(1000000000000u64);
136    /// assert_eq!(x.remove_one_plus_i_assign(), 24);
137    /// assert_eq!(x.to_string(), "244140625");
138    /// ```
139    pub fn remove_one_plus_i_assign(&mut self) -> u64 {
140        if *self == 0u32 {
141            return 0;
142        }
143        let (s, odd) = one_plus_i_valuation(self);
144        if s != 0 {
145            self.real >>= s;
146            self.imaginary >>= s;
147        }
148        let (x, k) = remove_one_plus_i_helper(take(self), s, odd);
149        *self = x;
150        k
151    }
152}