use crate::gaussian_integer::GaussianInteger;
use crate::gaussian_integer::arithmetic::div_rem::div_rem_approx;
use crate::integer::Integer;
use core::mem::take;
use malachite_base::num::arithmetic::traits::{CanonicalizeUnit, Floor, Gcd, GcdAssign};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
const DOUBLE_GCD_BITS: u64 = 50;
fn fits_double(x: &GaussianInteger) -> bool {
x.real.significant_bits() <= DOUBLE_GCD_BITS
&& x.imaginary.significant_bits() <= DOUBLE_GCD_BITS
}
fn gcd_double(mut a: f64, mut b: f64, mut c: f64, mut d: f64) -> GaussianInteger {
while c != 0.0 || d != 0.0 {
let t = a * c + b * d;
let u = b * c - a * d;
let v = c * c + d * d;
let w = 0.5 / v;
let qa = Floor::floor((2.0 * t + v) * w);
let qb = Floor::floor((2.0 * u + v) * w);
let t = a - (qa * c - qb * d);
let u = b - (qb * c + qa * d);
a = c;
b = d;
c = t;
d = u;
}
GaussianInteger {
real: Integer::exact_from(a),
imaginary: Integer::exact_from(b),
}
.canonicalize_unit()
}
fn gcd_helper(mut x: GaussianInteger, mut y: GaussianInteger) -> GaussianInteger {
if x == 0u32 {
return y.canonicalize_unit();
} else if y == 0u32 {
return x.canonicalize_unit();
}
loop {
if fits_double(&x) && fits_double(&y) {
return gcd_double(
f64::exact_from(&x.real),
f64::exact_from(&x.imaginary),
f64::exact_from(&y.real),
f64::exact_from(&y.imaginary),
);
}
let r = div_rem_approx(&x, &y).1;
x = y;
y = r;
if y == 0u32 {
return x.canonicalize_unit();
}
}
}
impl Gcd<Self> for GaussianInteger {
type Output = Self;
#[inline]
fn gcd(self, other: Self) -> Self {
gcd_helper(self, other)
}
}
impl Gcd<&Self> for GaussianInteger {
type Output = Self;
#[inline]
fn gcd(self, other: &Self) -> Self {
gcd_helper(self, other.clone())
}
}
impl Gcd<GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn gcd(self, other: GaussianInteger) -> GaussianInteger {
gcd_helper(self.clone(), other)
}
}
impl Gcd<&GaussianInteger> for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn gcd(self, other: &GaussianInteger) -> GaussianInteger {
gcd_helper(self.clone(), other.clone())
}
}
impl GcdAssign<Self> for GaussianInteger {
#[inline]
fn gcd_assign(&mut self, other: Self) {
*self = gcd_helper(take(self), other);
}
}
impl GcdAssign<&Self> for GaussianInteger {
#[inline]
fn gcd_assign(&mut self, other: &Self) {
*self = gcd_helper(take(self), other.clone());
}
}