malachite_nz/gaussian_integer/arithmetic/square.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 crate::integer::Integer;
15use core::mem::take;
16use malachite_base::num::arithmetic::traits::{Square, SquareAssign};
17use malachite_base::num::basic::traits::Zero;
18use malachite_base::num::logic::traits::SignificantBits;
19
20use crate::gaussian_integer::arithmetic::SIZE_BALANCE_BITS;
21
22// This threshold is from `fmpzi_sqr` in FLINT 3.6.0, where it is a limb count (16 limbs, with
23// 64-bit limbs); it is expressed here in bits so that it does not shift when Malachite is built
24// with 32-bit limbs.
25const THREE_SQUARES_THRESHOLD_BITS: u64 = 16 * 64;
26
27enum SquareAlgorithm {
28 DoubleWord(i64, i64),
29 PurelyReal,
30 PurelyImaginary,
31 ThreeSquares,
32 General,
33}
34
35// The algorithm selection of fmpzi_sqr from fmpzi/sqr.c, FLINT 3.6.0. Every path uses three
36// multiplications, but arranges for as many of them as possible to be squarings, which are cheaper
37// than general multiplications.
38fn choose_algorithm(x: &GaussianInteger) -> SquareAlgorithm {
39 // If both parts fit in a signed word, three double-word products suffice.
40 if let (Ok(a), Ok(b)) = (i64::try_from(&x.real), i64::try_from(&x.imaginary)) {
41 return SquareAlgorithm::DoubleWord(a, b);
42 }
43 if x.imaginary == 0u32 {
44 return SquareAlgorithm::PurelyReal;
45 }
46 if x.real == 0u32 {
47 return SquareAlgorithm::PurelyImaginary;
48 }
49 // For large, balanced operands, three squarings: with $t = a^2$ and $v = b^2$, the real part is
50 // $t - v$ and the imaginary part is $(a + b)^2 - t - v$.
51 let a_bits = x.real.significant_bits();
52 if a_bits >= THREE_SQUARES_THRESHOLD_BITS {
53 let b_bits = x.imaginary.significant_bits();
54 if a_bits.abs_diff(b_bits) <= SIZE_BALANCE_BITS {
55 return SquareAlgorithm::ThreeSquares;
56 }
57 }
58 // Otherwise, two squarings and one general multiplication: $a^2 - b^2$ and $2ab$.
59 SquareAlgorithm::General
60}
61
62// The squares of two `i64`s and their sums and differences cannot overflow an `i128`.
63fn square_double_word(a: i64, b: i64) -> GaussianInteger {
64 let (a, b) = (i128::from(a), i128::from(b));
65 GaussianInteger {
66 real: Integer::from(a * a - b * b),
67 imaginary: Integer::from((a * b) << 1u64),
68 }
69}
70
71// Each part appears in exactly two products, so an owned part is borrowed by its first use and
72// consumed by its last, letting the products reuse the operand's storage.
73fn square_val(x: GaussianInteger) -> GaussianInteger {
74 match choose_algorithm(&x) {
75 SquareAlgorithm::DoubleWord(a, b) => square_double_word(a, b),
76 SquareAlgorithm::PurelyReal => GaussianInteger {
77 real: x.real.square(),
78 imaginary: Integer::ZERO,
79 },
80 SquareAlgorithm::PurelyImaginary => GaussianInteger {
81 real: -x.imaginary.square(),
82 imaginary: Integer::ZERO,
83 },
84 SquareAlgorithm::ThreeSquares => {
85 let mut u = (&x.real + &x.imaginary).square();
86 let t = x.real.square();
87 let v = x.imaginary.square();
88 u -= &t;
89 u -= &v;
90 GaussianInteger {
91 real: t - v,
92 imaginary: u,
93 }
94 }
95 SquareAlgorithm::General => {
96 let real = (&x.real).square() - (&x.imaginary).square();
97 GaussianInteger {
98 real,
99 imaginary: (x.real * x.imaginary) << 1u32,
100 }
101 }
102 }
103}
104
105fn square_ref(x: &GaussianInteger) -> GaussianInteger {
106 match choose_algorithm(x) {
107 SquareAlgorithm::DoubleWord(a, b) => square_double_word(a, b),
108 SquareAlgorithm::PurelyReal => GaussianInteger {
109 real: (&x.real).square(),
110 imaginary: Integer::ZERO,
111 },
112 SquareAlgorithm::PurelyImaginary => GaussianInteger {
113 real: -(&x.imaginary).square(),
114 imaginary: Integer::ZERO,
115 },
116 SquareAlgorithm::ThreeSquares => {
117 let mut u = (&x.real + &x.imaginary).square();
118 let t = (&x.real).square();
119 let v = (&x.imaginary).square();
120 u -= &t;
121 u -= &v;
122 GaussianInteger {
123 real: t - v,
124 imaginary: u,
125 }
126 }
127 SquareAlgorithm::General => GaussianInteger {
128 real: (&x.real).square() - (&x.imaginary).square(),
129 imaginary: (&x.real * &x.imaginary) << 1u32,
130 },
131 }
132}
133
134impl Square for GaussianInteger {
135 type Output = Self;
136
137 /// Squares a [`GaussianInteger`], taking it by value.
138 ///
139 /// $$
140 /// f(x) = x^2.
141 /// $$
142 ///
143 /// # Worst-case complexity
144 /// $T(n) = O(n \log n \log\log n)$
145 ///
146 /// $M(n) = O(n \log n)$
147 ///
148 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
149 /// bits of the real and imaginary parts.
150 ///
151 /// # Examples
152 /// ```
153 /// use malachite_base::num::arithmetic::traits::Square;
154 /// use malachite_base::num::basic::traits::I;
155 /// use malachite_nz::gaussian_integer::GaussianInteger;
156 /// use std::str::FromStr;
157 ///
158 /// assert_eq!(GaussianInteger::I.square().to_string(), "-1");
159 /// let x = GaussianInteger::from_str("2-3i").unwrap();
160 /// assert_eq!(x.square().to_string(), "-5-12i");
161 /// ```
162 #[inline]
163 fn square(self) -> Self {
164 square_val(self)
165 }
166}
167
168impl Square for &GaussianInteger {
169 type Output = GaussianInteger;
170
171 /// Squares a [`GaussianInteger`], taking it by reference.
172 ///
173 /// $$
174 /// f(x) = x^2.
175 /// $$
176 ///
177 /// # Worst-case complexity
178 /// $T(n) = O(n \log n \log\log n)$
179 ///
180 /// $M(n) = O(n \log n)$
181 ///
182 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
183 /// bits of the real and imaginary parts.
184 ///
185 /// # Examples
186 /// ```
187 /// use malachite_base::num::arithmetic::traits::Square;
188 /// use malachite_nz::gaussian_integer::GaussianInteger;
189 /// use std::str::FromStr;
190 ///
191 /// let x = GaussianInteger::from_str("2-3i").unwrap();
192 /// assert_eq!((&x).square().to_string(), "-5-12i");
193 /// ```
194 #[inline]
195 fn square(self) -> GaussianInteger {
196 square_ref(self)
197 }
198}
199
200impl SquareAssign for GaussianInteger {
201 /// Squares a [`GaussianInteger`] in place.
202 ///
203 /// $$
204 /// x \gets x^2.
205 /// $$
206 ///
207 /// # Worst-case complexity
208 /// $T(n) = O(n \log n \log\log n)$
209 ///
210 /// $M(n) = O(n \log n)$
211 ///
212 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
213 /// bits of the real and imaginary parts.
214 ///
215 /// # Examples
216 /// ```
217 /// use malachite_base::num::arithmetic::traits::SquareAssign;
218 /// use malachite_nz::gaussian_integer::GaussianInteger;
219 /// use std::str::FromStr;
220 ///
221 /// let mut x = GaussianInteger::from_str("2-3i").unwrap();
222 /// x.square_assign();
223 /// assert_eq!(x.to_string(), "-5-12i");
224 /// ```
225 #[inline]
226 fn square_assign(&mut self) {
227 *self = square_val(take(self));
228 }
229}