malachite_nz/gaussian_integer/arithmetic/mul.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::iter::Product;
16use core::mem::take;
17use core::ops::{Mul, MulAssign};
18use malachite_base::iterators::balanced_fold;
19use malachite_base::num::arithmetic::traits::{MulAddMul, MulSubMul, Square};
20use malachite_base::num::basic::traits::One;
21use malachite_base::num::logic::traits::SignificantBits;
22
23use crate::gaussian_integer::arithmetic::SIZE_BALANCE_BITS;
24
25// This threshold is from `fmpzi_mul` in FLINT 3.6.0, where it is a limb count (13 limbs, with
26// 64-bit limbs); it is expressed here in bits so that it does not shift when Malachite is built
27// with 32-bit limbs.
28const KARATSUBA_THRESHOLD_BITS: u64 = 13 * 64;
29
30enum MulAlgorithm {
31 DoubleWord(i64, i64, i64, i64),
32 Karatsuba,
33 Fused,
34}
35
36// The algorithm selection of fmpzi_mul from fmpzi/mul.c, FLINT 3.6.0, except that the squaring
37// fallback for aliased operands is omitted: Rust's ownership rules make the aliasing detectable at
38// the call site, and a dedicated squaring implementation may be added later.
39fn choose_algorithm(x: &GaussianInteger, y: &GaussianInteger) -> MulAlgorithm {
40 // If all four parts fit in a signed word, two double-word products per output part suffice.
41 if let (Ok(a), Ok(b), Ok(c), Ok(d)) = (
42 i64::try_from(&x.real),
43 i64::try_from(&x.imaginary),
44 i64::try_from(&y.real),
45 i64::try_from(&y.imaginary),
46 ) {
47 return MulAlgorithm::DoubleWord(a, b, c, d);
48 }
49 // For large, balanced operands, a Karatsuba-style scheme computes the product with three
50 // multiplications instead of four: with $t = ac$ and $v = bd$, the real part is $t - v$ and the
51 // imaginary part is $(a + b)(c + d) - t - v$.
52 let a_bits = x.real.significant_bits();
53 if a_bits >= KARATSUBA_THRESHOLD_BITS {
54 let b_bits = x.imaginary.significant_bits();
55 let c_bits = y.real.significant_bits();
56 let d_bits = y.imaginary.significant_bits();
57 if c_bits >= KARATSUBA_THRESHOLD_BITS
58 && a_bits.abs_diff(b_bits) <= SIZE_BALANCE_BITS
59 && c_bits.abs_diff(d_bits) <= SIZE_BALANCE_BITS
60 {
61 return MulAlgorithm::Karatsuba;
62 }
63 }
64 // Otherwise, the four products are computed with the fused kernels.
65 MulAlgorithm::Fused
66}
67
68// The products of two `i64`s and their sums and differences cannot overflow an `i128`.
69fn mul_double_word(a: i64, b: i64, c: i64, d: i64) -> GaussianInteger {
70 let (a, b, c, d) = (i128::from(a), i128::from(b), i128::from(c), i128::from(d));
71 GaussianInteger {
72 real: Integer::from(a * c - b * d),
73 imaginary: Integer::from(a * d + b * c),
74 }
75}
76
77// Each part of each operand appears in exactly two products, so an owned part is borrowed by its
78// first use and consumed by its last, letting the products reuse the operands' storage.
79pub(super) fn mul_val_val(x: GaussianInteger, y: GaussianInteger) -> GaussianInteger {
80 match choose_algorithm(&x, &y) {
81 MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
82 MulAlgorithm::Karatsuba => {
83 let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
84 let t = x.real * y.real;
85 let v = x.imaginary * y.imaginary;
86 u -= &t;
87 u -= &v;
88 GaussianInteger {
89 real: t - v,
90 imaginary: u,
91 }
92 }
93 MulAlgorithm::Fused => {
94 let real = (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary);
95 GaussianInteger {
96 real,
97 imaginary: x.real.mul_add_mul(y.imaginary, x.imaginary, y.real),
98 }
99 }
100 }
101}
102
103pub(super) fn mul_val_ref(x: GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
104 match choose_algorithm(&x, y) {
105 MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
106 MulAlgorithm::Karatsuba => {
107 let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
108 let t = x.real * &y.real;
109 let v = x.imaginary * &y.imaginary;
110 u -= &t;
111 u -= &v;
112 GaussianInteger {
113 real: t - v,
114 imaginary: u,
115 }
116 }
117 MulAlgorithm::Fused => {
118 let real = (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary);
119 GaussianInteger {
120 real,
121 imaginary: x.real.mul_add_mul(&y.imaginary, x.imaginary, &y.real),
122 }
123 }
124 }
125}
126
127pub(super) fn mul_ref_ref(x: &GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
128 // As in fmpzi_mul, aliased operands are detected by address and routed to the squaring
129 // algorithm, which replaces general multiplications with cheaper squarings. Only this variant
130 // checks: two owned operands are always distinct objects.
131 if core::ptr::eq(x, y) {
132 return x.square();
133 }
134 match choose_algorithm(x, y) {
135 MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
136 MulAlgorithm::Karatsuba => {
137 let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
138 let t = &x.real * &y.real;
139 let v = &x.imaginary * &y.imaginary;
140 u -= &t;
141 u -= &v;
142 GaussianInteger {
143 real: t - v,
144 imaginary: u,
145 }
146 }
147 MulAlgorithm::Fused => GaussianInteger {
148 real: (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary),
149 imaginary: (&x.real).mul_add_mul(&y.imaginary, &x.imaginary, &y.real),
150 },
151 }
152}
153
154impl Mul<Self> for GaussianInteger {
155 type Output = Self;
156
157 /// Multiplies two [`GaussianInteger`]s, taking both by value.
158 ///
159 /// $$
160 /// f(x, y) = xy.
161 /// $$
162 ///
163 /// # Worst-case complexity
164 /// $T(n) = O(n \log n \log\log n)$
165 ///
166 /// $M(n) = O(n \log n)$
167 ///
168 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
169 /// bits of the real and imaginary parts of `self` and `other`.
170 ///
171 /// # Examples
172 /// ```
173 /// use malachite_base::num::basic::traits::{I, One};
174 /// use malachite_nz::gaussian_integer::GaussianInteger;
175 /// use std::str::FromStr;
176 ///
177 /// assert_eq!(
178 /// GaussianInteger::I * -GaussianInteger::I,
179 /// GaussianInteger::ONE
180 /// );
181 /// let x = GaussianInteger::from_str("2-3i").unwrap();
182 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
183 /// assert_eq!((x * y).to_string(), "10+11i");
184 /// ```
185 #[inline]
186 fn mul(self, other: Self) -> Self {
187 mul_val_val(self, other)
188 }
189}
190
191impl Mul<&Self> for GaussianInteger {
192 type Output = Self;
193
194 /// Multiplies two [`GaussianInteger`]s, taking the first by value and the second by reference.
195 ///
196 /// $$
197 /// f(x, y) = xy.
198 /// $$
199 ///
200 /// # Worst-case complexity
201 /// $T(n) = O(n \log n \log\log n)$
202 ///
203 /// $M(n) = O(n \log n)$
204 ///
205 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
206 /// bits of the real and imaginary parts of `self` and `other`.
207 ///
208 /// # Examples
209 /// ```
210 /// use malachite_nz::gaussian_integer::GaussianInteger;
211 /// use std::str::FromStr;
212 ///
213 /// let x = GaussianInteger::from_str("2-3i").unwrap();
214 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
215 /// assert_eq!((x * &y).to_string(), "10+11i");
216 /// ```
217 #[inline]
218 fn mul(self, other: &Self) -> Self {
219 mul_val_ref(self, other)
220 }
221}
222
223impl Mul<GaussianInteger> for &GaussianInteger {
224 type Output = GaussianInteger;
225
226 /// Multiplies two [`GaussianInteger`]s, taking the first by reference and the second by value.
227 ///
228 /// $$
229 /// f(x, y) = xy.
230 /// $$
231 ///
232 /// # Worst-case complexity
233 /// $T(n) = O(n \log n \log\log n)$
234 ///
235 /// $M(n) = O(n \log n)$
236 ///
237 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
238 /// bits of the real and imaginary parts of `self` and `other`.
239 ///
240 /// # Examples
241 /// ```
242 /// use malachite_nz::gaussian_integer::GaussianInteger;
243 /// use std::str::FromStr;
244 ///
245 /// let x = GaussianInteger::from_str("2-3i").unwrap();
246 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
247 /// assert_eq!((&x * y).to_string(), "10+11i");
248 /// ```
249 #[inline]
250 fn mul(self, other: GaussianInteger) -> GaussianInteger {
251 // Multiplication is commutative, so the operands can be swapped to consume `other`.
252 mul_val_ref(other, self)
253 }
254}
255
256impl Mul<&GaussianInteger> for &GaussianInteger {
257 type Output = GaussianInteger;
258
259 /// Multiplies two [`GaussianInteger`]s, taking both by reference.
260 ///
261 /// $$
262 /// f(x, y) = xy.
263 /// $$
264 ///
265 /// # Worst-case complexity
266 /// $T(n) = O(n \log n \log\log n)$
267 ///
268 /// $M(n) = O(n \log n)$
269 ///
270 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
271 /// bits of the real and imaginary parts of `self` and `other`.
272 ///
273 /// # Examples
274 /// ```
275 /// use malachite_nz::gaussian_integer::GaussianInteger;
276 /// use std::str::FromStr;
277 ///
278 /// let x = GaussianInteger::from_str("2-3i").unwrap();
279 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
280 /// assert_eq!((&x * &y).to_string(), "10+11i");
281 /// ```
282 #[inline]
283 fn mul(self, other: &GaussianInteger) -> GaussianInteger {
284 mul_ref_ref(self, other)
285 }
286}
287
288impl MulAssign<Self> for GaussianInteger {
289 /// Multiplies a [`GaussianInteger`] by a [`GaussianInteger`] in place, taking the
290 /// [`GaussianInteger`] on the right-hand side by value.
291 ///
292 /// $$
293 /// x \gets xy.
294 /// $$
295 ///
296 /// # Worst-case complexity
297 /// $T(n) = O(n \log n \log\log n)$
298 ///
299 /// $M(n) = O(n \log n)$
300 ///
301 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
302 /// bits of the real and imaginary parts of `self` and `other`.
303 ///
304 /// # Examples
305 /// ```
306 /// use malachite_nz::gaussian_integer::GaussianInteger;
307 /// use std::str::FromStr;
308 ///
309 /// let x = GaussianInteger::from_str("2-3i").unwrap();
310 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
311 /// let mut product = x;
312 /// product *= y;
313 /// assert_eq!(product.to_string(), "10+11i");
314 /// ```
315 #[inline]
316 fn mul_assign(&mut self, other: Self) {
317 *self = mul_val_val(take(self), other);
318 }
319}
320
321impl MulAssign<&Self> for GaussianInteger {
322 /// Multiplies a [`GaussianInteger`] by a [`GaussianInteger`] in place, taking the
323 /// [`GaussianInteger`] on the right-hand side by reference.
324 ///
325 /// $$
326 /// x \gets xy.
327 /// $$
328 ///
329 /// # Worst-case complexity
330 /// $T(n) = O(n \log n \log\log n)$
331 ///
332 /// $M(n) = O(n \log n)$
333 ///
334 /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
335 /// bits of the real and imaginary parts of `self` and `other`.
336 ///
337 /// # Examples
338 /// ```
339 /// use malachite_nz::gaussian_integer::GaussianInteger;
340 /// use std::str::FromStr;
341 ///
342 /// let x = GaussianInteger::from_str("2-3i").unwrap();
343 /// let y = GaussianInteger::from_str("-1+4i").unwrap();
344 /// let mut product = x;
345 /// product *= &y;
346 /// assert_eq!(product.to_string(), "10+11i");
347 /// ```
348 #[inline]
349 fn mul_assign(&mut self, other: &Self) {
350 *self = mul_val_ref(take(self), other);
351 }
352}
353
354impl Product for GaussianInteger {
355 /// Multiplies together all the [`GaussianInteger`]s in an iterator.
356 ///
357 /// $$
358 /// f((x_i)_ {i=0}^{n-1}) = \prod_ {i=0}^{n-1} x_i.
359 /// $$
360 ///
361 /// # Worst-case complexity
362 /// $T(n) = O(n (\log n)^2 \log\log n)$
363 ///
364 /// $M(n) = O(n \log n)$
365 ///
366 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of significant bits
367 /// of the real and imaginary parts of the [`GaussianInteger`]s.
368 ///
369 /// # Examples
370 /// ```
371 /// use core::iter::Product;
372 /// use malachite_base::vecs::vec_from_str;
373 /// use malachite_nz::gaussian_integer::GaussianInteger;
374 ///
375 /// assert_eq!(
376 /// GaussianInteger::product(
377 /// vec_from_str::<GaussianInteger>("[2, -3i, 5+i, 7-2i]")
378 /// .unwrap()
379 /// .into_iter()
380 /// )
381 /// .to_string(),
382 /// "-18-222i"
383 /// );
384 /// ```
385 #[inline]
386 fn product<I>(xs: I) -> Self
387 where
388 I: Iterator<Item = Self>,
389 {
390 balanced_fold(xs, |x| *x == 0u32, |a, b| *a *= b).unwrap_or(Self::ONE)
391 }
392}
393
394impl<'a> Product<&'a Self> for GaussianInteger {
395 /// Multiplies together all the [`GaussianInteger`]s in an iterator of [`GaussianInteger`]
396 /// references.
397 ///
398 /// $$
399 /// f((x_i)_ {i=0}^{n-1}) = \prod_ {i=0}^{n-1} x_i.
400 /// $$
401 ///
402 /// # Worst-case complexity
403 /// $T(n) = O(n (\log n)^2 \log\log n)$
404 ///
405 /// $M(n) = O(n \log n)$
406 ///
407 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of significant bits
408 /// of the real and imaginary parts of the [`GaussianInteger`]s.
409 ///
410 /// # Examples
411 /// ```
412 /// use core::iter::Product;
413 /// use malachite_base::vecs::vec_from_str;
414 /// use malachite_nz::gaussian_integer::GaussianInteger;
415 ///
416 /// assert_eq!(
417 /// GaussianInteger::product(
418 /// vec_from_str::<GaussianInteger>("[2, -3i, 5+i, 7-2i]")
419 /// .unwrap()
420 /// .iter()
421 /// )
422 /// .to_string(),
423 /// "-18-222i"
424 /// );
425 /// ```
426 #[inline]
427 fn product<I>(xs: I) -> Self
428 where
429 I: Iterator<Item = &'a Self>,
430 {
431 balanced_fold(xs.cloned(), |x| *x == 0u32, |a, b| *a *= b).unwrap_or(Self::ONE)
432 }
433}