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}