use crate::gaussian_integer::GaussianInteger;
use core::cmp::Ordering::*;
use core::mem::take;
use malachite_base::num::arithmetic::traits::{ModPowerOf2, MulIPowAssign};
use malachite_base::num::basic::traits::Zero;
fn one_plus_i_valuation(x: &GaussianInteger) -> (u64, bool) {
match (x.real.trailing_zeros(), x.imaginary.trailing_zeros()) {
(Some(s), None) | (None, Some(s)) => (s, false),
(Some(s), Some(t)) => match s.cmp(&t) {
Equal => (s, true),
Less => (s, false),
Greater => (t, false),
},
(None, None) => unreachable!(),
}
}
fn remove_one_plus_i_helper(mut x: GaussianInteger, s: u64, odd: bool) -> (GaussianInteger, u64) {
if s != 0 {
x.mul_i_pow_assign(4 - s.mod_power_of_2(2));
}
if odd {
let t = &x.real + &x.imaginary;
x.imaginary -= &x.real;
x.real = t >> 1u32;
x.imaginary >>= 1u32;
}
(x, (s << 1) | u64::from(odd))
}
impl GaussianInteger {
pub fn remove_one_plus_i(&self) -> (Self, u64) {
if *self == 0u32 {
return (Self::ZERO, 0);
}
let (s, odd) = one_plus_i_valuation(self);
let x = if s == 0 {
self.clone()
} else {
Self {
real: &self.real >> s,
imaginary: &self.imaginary >> s,
}
};
remove_one_plus_i_helper(x, s, odd)
}
pub fn remove_one_plus_i_assign(&mut self) -> u64 {
if *self == 0u32 {
return 0;
}
let (s, odd) = one_plus_i_valuation(self);
if s != 0 {
self.real >>= s;
self.imaginary >>= s;
}
let (x, k) = remove_one_plus_i_helper(take(self), s, odd);
*self = x;
k
}
}