use crate::natural::InnerNatural::{Large, Small};
use crate::natural::Natural;
use crate::natural::arithmetic::sqrt::limbs_checked_sqrt;
use crate::platform::Limb;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::factorization::traits::IsSquare;
const MOD34_BITS: Limb = ((Limb::WIDTH as Limb) / 4) * 3;
const MOD34_MASK: Limb = (1 << MOD34_BITS) - 1;
const SQR_MOD_BITS: Limb = MOD34_BITS + 1;
const SQR_MOD_MASK: Limb = (1 << SQR_MOD_BITS) - 1;
const B1: Limb = (Limb::WIDTH as Limb) / 4;
const B2: Limb = B1 * 2;
const B3: Limb = B1 * 3;
const M1: Limb = (1 << B1) - 1;
const M2: Limb = (1 << B2) - 1;
const M3: Limb = (1 << B3) - 1;
const fn low0(n: Limb) -> Limb {
n & M3
}
const fn high0(n: Limb) -> Limb {
n >> B3
}
const fn low1(n: Limb) -> Limb {
(n & M2) << B1
}
const fn high1(n: Limb) -> Limb {
n >> B2
}
const fn low2(n: Limb) -> Limb {
(n & M1) << B2
}
const fn high2(n: Limb) -> Limb {
n >> B1
}
const fn parts0(n: Limb) -> Limb {
low0(n) + high0(n)
}
const fn parts1(n: Limb) -> Limb {
low1(n) + high1(n)
}
const fn parts2(n: Limb) -> Limb {
low2(n) + high2(n)
}
pub(crate) fn mod_34lsub1(limbs: &[Limb]) -> Limb {
let (sums, carries) = limbs.chunks(3).fold(
([Limb::ZERO; 3], [Limb::ZERO; 3]),
|(mut sums, mut carries), chunk| {
for (i, &limb) in chunk.iter().enumerate() {
let (sum, overflow) = sums[i].overflowing_add(limb);
sums[i] = sum;
if overflow {
carries[i] += 1;
}
}
(sums, carries)
},
);
parts0(sums[0])
+ parts1(sums[1])
+ parts2(sums[2])
+ parts1(carries[0])
+ parts2(carries[1])
+ parts0(carries[2])
}
fn perfsqr_mod_34(limbs: &[Limb]) -> Limb {
let r = mod_34lsub1(limbs);
(r & MOD34_MASK) + (r >> MOD34_BITS)
}
const fn perfsqr_mod_idx(r: Limb, d: Limb, inv: Limb) -> Limb {
assert!(r <= SQR_MOD_MASK);
assert!(inv.wrapping_mul(d) & SQR_MOD_MASK == 1);
assert!(Limb::MAX / d >= SQR_MOD_MASK);
let q = r.wrapping_mul(inv) & SQR_MOD_MASK;
assert!(r == (q.wrapping_mul(d) & SQR_MOD_MASK));
q.wrapping_mul(d) >> SQR_MOD_BITS
}
fn perfsqr_mod_1(r: Limb, d: Limb, inv: Limb, mask: Limb) -> bool {
assert!(d <= Limb::WIDTH as Limb);
let idx = perfsqr_mod_idx(r, d, inv);
if (mask >> idx) & 1 == 0 {
return false;
}
true
}
fn perfsqr_mod_2(r: Limb, d: Limb, inv: Limb, mhi: Limb, mlo: Limb) -> bool {
assert!(d <= 2 * Limb::WIDTH as Limb);
let mut idx = perfsqr_mod_idx(r, d, inv);
let m = if idx < Limb::WIDTH as Limb { mlo } else { mhi };
idx %= Limb::WIDTH as Limb;
if (m >> idx) & 1 == 0 {
return false;
}
true
}
#[cfg(not(feature = "32_bit_limbs"))]
fn perfsqr_mod_test(limbs: &[Limb]) -> bool {
let r = perfsqr_mod_34(limbs);
perfsqr_mod_2(r, 91, 0xfd2fd2fd2fd3, 0x2191240, 0x8850a206953820e1) && perfsqr_mod_2(r, 85, 0xfcfcfcfcfcfd, 0x82158, 0x10b48c4b4206a105) && perfsqr_mod_1(r, 9, 0xe38e38e38e39, 0x93) && perfsqr_mod_2(r, 97, 0xfd5c5f02a3a1, 0x1eb628b47, 0x6067981b8b451b5f) }
#[cfg(feature = "32_bit_limbs")]
fn perfsqr_mod_test(limbs: &[Limb]) -> bool {
let r = perfsqr_mod_34(limbs);
perfsqr_mod_2(r, 45, 0xfa4fa5, 0x920, 0x1a442481) && perfsqr_mod_1(r, 17, 0xf0f0f1, 0x1a317) && perfsqr_mod_1(r, 13, 0xec4ec5, 0x9e5) && perfsqr_mod_1(r, 7, 0xdb6db7, 0x69) }
#[cfg(not(feature = "32_bit_limbs"))]
const SQR_MOD256: [u64; 4] =
[0x202021202030213, 0x202021202020213, 0x202021202030212, 0x202021202020212];
#[cfg(feature = "32_bit_limbs")]
const SQR_MOD256: [u32; 8] =
[0x2030213, 0x2020212, 0x2020213, 0x2020212, 0x2030212, 0x2020212, 0x2020212, 0x2020212];
fn limbs_is_square(limbs: &[Limb]) -> bool {
assert!(!limbs.is_empty());
let idx = limbs[0] % 0x100;
if (SQR_MOD256[usize::exact_from(idx >> Limb::LOG_WIDTH)]
>> (idx & const { Limb::WIDTH_MASK as Limb }))
& 1
== 0
{
return false;
}
if !perfsqr_mod_test(limbs) {
return false;
}
limbs_checked_sqrt(limbs).is_some()
}
impl IsSquare for Natural {
fn is_square(&self) -> bool {
match self {
Self(Small(small)) => small.is_square(),
Self(Large(limbs)) => limbs_is_square(limbs),
}
}
}