Skip to main content

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}