use crate::gaussian_integer::GaussianInteger;
use crate::integer::Integer;
use core::iter::Product;
use core::mem::take;
use core::ops::{Mul, MulAssign};
use malachite_base::iterators::balanced_fold;
use malachite_base::num::arithmetic::traits::{MulAddMul, MulSubMul, Square};
use malachite_base::num::basic::traits::One;
use malachite_base::num::logic::traits::SignificantBits;
use crate::gaussian_integer::arithmetic::SIZE_BALANCE_BITS;
const KARATSUBA_THRESHOLD_BITS: u64 = 13 * 64;
enum MulAlgorithm {
DoubleWord(i64, i64, i64, i64),
Karatsuba,
Fused,
}
fn choose_algorithm(x: &GaussianInteger, y: &GaussianInteger) -> MulAlgorithm {
if let (Ok(a), Ok(b), Ok(c), Ok(d)) = (
i64::try_from(&x.real),
i64::try_from(&x.imaginary),
i64::try_from(&y.real),
i64::try_from(&y.imaginary),
) {
return MulAlgorithm::DoubleWord(a, b, c, d);
}
let a_bits = x.real.significant_bits();
if a_bits >= KARATSUBA_THRESHOLD_BITS {
let b_bits = x.imaginary.significant_bits();
let c_bits = y.real.significant_bits();
let d_bits = y.imaginary.significant_bits();
if c_bits >= KARATSUBA_THRESHOLD_BITS
&& a_bits.abs_diff(b_bits) <= SIZE_BALANCE_BITS
&& c_bits.abs_diff(d_bits) <= SIZE_BALANCE_BITS
{
return MulAlgorithm::Karatsuba;
}
}
MulAlgorithm::Fused
}
fn mul_double_word(a: i64, b: i64, c: i64, d: i64) -> GaussianInteger {
let (a, b, c, d) = (i128::from(a), i128::from(b), i128::from(c), i128::from(d));
GaussianInteger {
real: Integer::from(a * c - b * d),
imaginary: Integer::from(a * d + b * c),
}
}
pub(super) fn mul_val_val(x: GaussianInteger, y: GaussianInteger) -> GaussianInteger {
match choose_algorithm(&x, &y) {
MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
MulAlgorithm::Karatsuba => {
let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
let t = x.real * y.real;
let v = x.imaginary * y.imaginary;
u -= &t;
u -= &v;
GaussianInteger {
real: t - v,
imaginary: u,
}
}
MulAlgorithm::Fused => {
let real = (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary);
GaussianInteger {
real,
imaginary: x.real.mul_add_mul(y.imaginary, x.imaginary, y.real),
}
}
}
}
pub(super) fn mul_val_ref(x: GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
match choose_algorithm(&x, y) {
MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
MulAlgorithm::Karatsuba => {
let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
let t = x.real * &y.real;
let v = x.imaginary * &y.imaginary;
u -= &t;
u -= &v;
GaussianInteger {
real: t - v,
imaginary: u,
}
}
MulAlgorithm::Fused => {
let real = (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary);
GaussianInteger {
real,
imaginary: x.real.mul_add_mul(&y.imaginary, x.imaginary, &y.real),
}
}
}
}
pub(super) fn mul_ref_ref(x: &GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
if core::ptr::eq(x, y) {
return x.square();
}
match choose_algorithm(x, y) {
MulAlgorithm::DoubleWord(a, b, c, d) => mul_double_word(a, b, c, d),
MulAlgorithm::Karatsuba => {
let mut u = (&x.real + &x.imaginary) * (&y.real + &y.imaginary);
let t = &x.real * &y.real;
let v = &x.imaginary * &y.imaginary;
u -= &t;
u -= &v;
GaussianInteger {
real: t - v,
imaginary: u,
}
}
MulAlgorithm::Fused => GaussianInteger {
real: (&x.real).mul_sub_mul(&y.real, &x.imaginary, &y.imaginary),
imaginary: (&x.real).mul_add_mul(&y.imaginary, &x.imaginary, &y.real),
},
}
}
impl Mul<Self> for GaussianInteger {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
mul_val_val(self, other)
}
}
impl Mul<&Self> for GaussianInteger {
type Output = Self;
#[inline]
fn mul(self, other: &Self) -> Self {
mul_val_ref(self, other)
}
}
impl Mul<GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn mul(self, other: GaussianInteger) -> GaussianInteger {
mul_val_ref(other, self)
}
}
impl Mul<&GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn mul(self, other: &GaussianInteger) -> GaussianInteger {
mul_ref_ref(self, other)
}
}
impl MulAssign<Self> for GaussianInteger {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = mul_val_val(take(self), other);
}
}
impl MulAssign<&Self> for GaussianInteger {
#[inline]
fn mul_assign(&mut self, other: &Self) {
*self = mul_val_ref(take(self), other);
}
}
impl Product for GaussianInteger {
#[inline]
fn product<I>(xs: I) -> Self
where
I: Iterator<Item = Self>,
{
balanced_fold(xs, |x| *x == 0u32, |a, b| *a *= b).unwrap_or(Self::ONE)
}
}
impl<'a> Product<&'a Self> for GaussianInteger {
#[inline]
fn product<I>(xs: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
balanced_fold(xs.cloned(), |x| *x == 0u32, |a, b| *a *= b).unwrap_or(Self::ONE)
}
}