use crate::gaussian_integer::GaussianInteger;
use crate::integer::Integer;
use core::mem::take;
use malachite_base::num::arithmetic::traits::{Square, SquareAssign};
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::logic::traits::SignificantBits;
use crate::gaussian_integer::arithmetic::SIZE_BALANCE_BITS;
const THREE_SQUARES_THRESHOLD_BITS: u64 = 16 * 64;
enum SquareAlgorithm {
DoubleWord(i64, i64),
PurelyReal,
PurelyImaginary,
ThreeSquares,
General,
}
fn choose_algorithm(x: &GaussianInteger) -> SquareAlgorithm {
if let (Ok(a), Ok(b)) = (i64::try_from(&x.real), i64::try_from(&x.imaginary)) {
return SquareAlgorithm::DoubleWord(a, b);
}
if x.imaginary == 0u32 {
return SquareAlgorithm::PurelyReal;
}
if x.real == 0u32 {
return SquareAlgorithm::PurelyImaginary;
}
let a_bits = x.real.significant_bits();
if a_bits >= THREE_SQUARES_THRESHOLD_BITS {
let b_bits = x.imaginary.significant_bits();
if a_bits.abs_diff(b_bits) <= SIZE_BALANCE_BITS {
return SquareAlgorithm::ThreeSquares;
}
}
SquareAlgorithm::General
}
fn square_double_word(a: i64, b: i64) -> GaussianInteger {
let (a, b) = (i128::from(a), i128::from(b));
GaussianInteger {
real: Integer::from(a * a - b * b),
imaginary: Integer::from((a * b) << 1u64),
}
}
fn square_val(x: GaussianInteger) -> GaussianInteger {
match choose_algorithm(&x) {
SquareAlgorithm::DoubleWord(a, b) => square_double_word(a, b),
SquareAlgorithm::PurelyReal => GaussianInteger {
real: x.real.square(),
imaginary: Integer::ZERO,
},
SquareAlgorithm::PurelyImaginary => GaussianInteger {
real: -x.imaginary.square(),
imaginary: Integer::ZERO,
},
SquareAlgorithm::ThreeSquares => {
let mut u = (&x.real + &x.imaginary).square();
let t = x.real.square();
let v = x.imaginary.square();
u -= &t;
u -= &v;
GaussianInteger {
real: t - v,
imaginary: u,
}
}
SquareAlgorithm::General => {
let real = (&x.real).square() - (&x.imaginary).square();
GaussianInteger {
real,
imaginary: (x.real * x.imaginary) << 1u32,
}
}
}
}
fn square_ref(x: &GaussianInteger) -> GaussianInteger {
match choose_algorithm(x) {
SquareAlgorithm::DoubleWord(a, b) => square_double_word(a, b),
SquareAlgorithm::PurelyReal => GaussianInteger {
real: (&x.real).square(),
imaginary: Integer::ZERO,
},
SquareAlgorithm::PurelyImaginary => GaussianInteger {
real: -(&x.imaginary).square(),
imaginary: Integer::ZERO,
},
SquareAlgorithm::ThreeSquares => {
let mut u = (&x.real + &x.imaginary).square();
let t = (&x.real).square();
let v = (&x.imaginary).square();
u -= &t;
u -= &v;
GaussianInteger {
real: t - v,
imaginary: u,
}
}
SquareAlgorithm::General => GaussianInteger {
real: (&x.real).square() - (&x.imaginary).square(),
imaginary: (&x.real * &x.imaginary) << 1u32,
},
}
}
impl Square for GaussianInteger {
type Output = Self;
#[inline]
fn square(self) -> Self {
square_val(self)
}
}
impl Square for &GaussianInteger {
type Output = GaussianInteger;
#[inline]
fn square(self) -> GaussianInteger {
square_ref(self)
}
}
impl SquareAssign for GaussianInteger {
#[inline]
fn square_assign(&mut self) {
*self = square_val(take(self));
}
}