use crate::gaussian_integer::GaussianInteger;
use crate::gaussian_integer::arithmetic::mul::{mul_val_ref, mul_val_val};
use crate::integer::Integer;
use core::mem::take;
use malachite_base::num::arithmetic::traits::{
AbsSquared, Conjugate, DivExact, DivExactAssign, DivI, Floor, PowerOf2,
};
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom, SciMantissaAndExponent};
use malachite_base::rounding_modes::RoundingMode::Down;
pub(super) const DOUBLE_QUOTIENT_BITS: u64 = 45;
const DOUBLE_SCALING_BITS: u64 = 500;
fn to_f64_truncated(x: &Integer) -> f64 {
f64::rounding_from(x, Down).0
}
fn to_f64_scaled(x: &Integer, shift: u64) -> f64 {
if *x == 0u32 {
return 0.0;
}
let (m, e): (f64, u64) = x.unsigned_abs_ref().sci_mantissa_and_exponent();
let v = m * f64::power_of_2((i64::exact_from(e) - i64::exact_from(shift)).max(-1024));
if *x < 0u32 { -v } else { v }
}
pub(super) fn nearest_quotient_double(
x: &GaussianInteger,
y: &GaussianInteger,
x_bits: u64,
) -> GaussianInteger {
let (a, b, c, d) = if x_bits < DOUBLE_SCALING_BITS {
(
to_f64_truncated(&x.real),
to_f64_truncated(&x.imaginary),
to_f64_truncated(&y.real),
to_f64_truncated(&y.imaginary),
)
} else {
(
to_f64_scaled(&x.real, x_bits),
to_f64_scaled(&x.imaginary, x_bits),
to_f64_scaled(&y.real, x_bits),
to_f64_scaled(&y.imaginary, x_bits),
)
};
let t = a * c + b * d;
let u = b * c - a * d;
let v = c * c + d * d;
let w = 0.5 / v;
let t = (2.0 * t + v) * w;
let u = (2.0 * u + v) * w;
GaussianInteger {
real: Integer::exact_from(Floor::floor(t)),
imaginary: Integer::exact_from(Floor::floor(u)),
}
}
fn div_exact_general(t: GaussianInteger, norm: Integer) -> GaussianInteger {
GaussianInteger {
real: t.real.div_exact(&norm),
imaginary: t.imaginary.div_exact(norm),
}
}
fn div_exact_val_ref(x: GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
if y.imaginary == 0u32 {
assert!(y.real != 0u32, "division by zero");
return GaussianInteger {
real: x.real.div_exact(&y.real),
imaginary: x.imaginary.div_exact(&y.real),
};
} else if y.real == 0u32 {
return GaussianInteger {
real: x.real.div_exact(&y.imaginary),
imaginary: x.imaginary.div_exact(&y.imaginary),
}
.div_i();
}
let x_bits = x.max_significant_bits();
if x_bits == 0 {
return GaussianInteger::ZERO;
}
let y_bits = y.max_significant_bits();
if x_bits < y_bits + DOUBLE_QUOTIENT_BITS {
nearest_quotient_double(&x, y, x_bits)
} else {
let norm = y.abs_squared();
div_exact_general(mul_val_val(x, y.conjugate()), norm)
}
}
fn div_exact_ref_ref(x: &GaussianInteger, y: &GaussianInteger) -> GaussianInteger {
if y.imaginary == 0u32 {
assert!(y.real != 0u32, "division by zero");
return GaussianInteger {
real: (&x.real).div_exact(&y.real),
imaginary: (&x.imaginary).div_exact(&y.real),
};
} else if y.real == 0u32 {
return GaussianInteger {
real: (&x.real).div_exact(&y.imaginary),
imaginary: (&x.imaginary).div_exact(&y.imaginary),
}
.div_i();
}
let x_bits = x.max_significant_bits();
if x_bits == 0 {
return GaussianInteger::ZERO;
}
let y_bits = y.max_significant_bits();
if x_bits < y_bits + DOUBLE_QUOTIENT_BITS {
nearest_quotient_double(x, y, x_bits)
} else {
let norm = y.abs_squared();
div_exact_general(mul_val_ref(y.conjugate(), x), norm)
}
}
impl DivExact<Self> for GaussianInteger {
type Output = Self;
#[inline]
fn div_exact(self, other: Self) -> Self {
div_exact_val_ref(self, &other)
}
}
impl DivExact<&Self> for GaussianInteger {
type Output = Self;
#[inline]
fn div_exact(self, other: &Self) -> Self {
div_exact_val_ref(self, other)
}
}
impl DivExact<GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn div_exact(self, other: GaussianInteger) -> GaussianInteger {
div_exact_ref_ref(self, &other)
}
}
impl DivExact<&GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn div_exact(self, other: &GaussianInteger) -> GaussianInteger {
div_exact_ref_ref(self, other)
}
}
impl DivExactAssign<Self> for GaussianInteger {
#[inline]
fn div_exact_assign(&mut self, other: Self) {
*self = div_exact_val_ref(take(self), &other);
}
}
impl DivExactAssign<&Self> for GaussianInteger {
#[inline]
fn div_exact_assign(&mut self, other: &Self) {
*self = div_exact_val_ref(take(self), other);
}
}