use crate::num::arithmetic::traits::{CheckedSqrt, FloorSqrt, Square};
use crate::num::basic::integers::PrimitiveInt;
use crate::num::conversion::traits::{SplitInHalf, WrappingFrom};
use crate::num::factorization::traits::IsSquare;
const IS_SQUARE_MOD64: [bool; 64] = [
true, true, false, false, true, false, false, false, false, true, false, false, false, false,
false, false, true, true, false, false, false, false, false, false, false, true, false, false,
false, false, false, false, false, true, false, false, true, false, false, false, false, true,
false, false, false, false, false, false, false, true, false, false, false, false, false,
false, false, true, false, false, false, false, false, false,
];
const IS_SQUARE_MOD65: [bool; 65] = [
true, true, false, false, true, false, false, false, false, true, true, false, false, false,
true, false, true, false, false, false, false, false, false, false, false, true, true, false,
false, true, true, false, false, false, false, true, true, false, false, true, true, false,
false, false, false, false, false, false, false, true, false, true, false, false, false, true,
true, false, false, false, false, true, false, false, true,
];
const IS_SQUARE_MOD63: [bool; 63] = [
true, true, false, false, true, false, false, true, false, true, false, false, false, false,
true, false, true, false, true, false, false, false, true, false, false, true, false, false,
true, false, false, false, false, false, false, true, true, true, false, false, false, false,
false, true, false, false, true, false, false, true, false, false, false, false, false, false,
true, false, true, false, false, false, false,
];
fn is_square_u64(x: u64) -> bool {
IS_SQUARE_MOD64[(x % 64) as usize]
&& IS_SQUARE_MOD63[(x % 63) as usize]
&& IS_SQUARE_MOD65[(x % 65) as usize]
&& x.floor_sqrt().square() == x
}
macro_rules! impl_unsigned {
($t: ident) => {
impl IsSquare for $t {
#[inline]
fn is_square(&self) -> bool {
is_square_u64(u64::wrapping_from(*self))
}
}
};
}
impl_unsigned!(u8);
impl_unsigned!(u16);
impl_unsigned!(u32);
impl_unsigned!(u64);
impl_unsigned!(usize);
const B1: u64 = u64::WIDTH >> 2;
const B2: u64 = B1 * 2;
const B3: u64 = B1 * 3;
const M2: u64 = (1 << B2) - 1;
const M3: u64 = (1 << B3) - 1;
const fn low0(n: u64) -> u64 {
n & M3
}
const fn high0(n: u64) -> u64 {
n >> B3
}
const fn low1(n: u64) -> u64 {
(n & M2) << B1
}
const fn high1(n: u64) -> u64 {
n >> B2
}
const fn mod_34lsub1(x_hi: u64, x_lo: u64) -> u64 {
low0(x_lo) + high0(x_lo) + low1(x_hi) + high1(x_hi)
}
const MOD34_BITS: u64 = (u64::WIDTH >> 2) * 3;
const MOD34_MASK: u64 = (1 << MOD34_BITS) - 1;
const fn perfsqr_mod_34(x_hi: u64, x_lo: u64) -> u64 {
let r = mod_34lsub1(x_hi, x_lo);
(r & MOD34_MASK) + (r >> MOD34_BITS)
}
const SQR_MOD_BITS: u64 = MOD34_BITS + 1;
const SQR_MOD_MASK: u64 = (1 << SQR_MOD_BITS) - 1;
const fn perfsqr_mod_idx(r: u64, d: u64, inv: u64) -> u64 {
assert!(r <= SQR_MOD_MASK);
assert!(inv.wrapping_mul(d) & SQR_MOD_MASK == 1);
assert!(u64::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: u64, d: u64, inv: u64, mask: u64) -> bool {
assert!(d <= u64::WIDTH);
let idx = perfsqr_mod_idx(r, d, inv);
if (mask >> idx) & 1 == 0 {
return false;
}
true
}
fn perfsqr_mod_2(r: u64, d: u64, inv: u64, mhi: u64, mlo: u64) -> bool {
assert!(d <= const { 2 * u64::WIDTH });
let mut idx = perfsqr_mod_idx(r, d, inv);
let m = if idx < u64::WIDTH { mlo } else { mhi };
idx %= u64::WIDTH;
if (m >> idx) & 1 == 0 {
return false;
}
true
}
fn perfsqr_mod_test(x: u128) -> bool {
let (x_hi, x_lo) = x.split_in_half();
let r = perfsqr_mod_34(x_hi, x_lo);
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) }
const SQR_MOD256: [u64; 4] =
[0x202021202030213, 0x202021202020213, 0x202021202030212, 0x202021202020212];
impl IsSquare for u128 {
#[inline]
fn is_square(&self) -> bool {
let idx = self % 0x100;
if (SQR_MOD256[(idx >> u64::LOG_WIDTH) as usize]
>> (idx & const { u64::WIDTH_MASK as Self }))
& 1
== 0
{
return false;
}
if !perfsqr_mod_test(*self) {
return false;
}
self.checked_sqrt().is_some()
}
}
macro_rules! impl_signed {
($t: ident) => {
impl IsSquare for $t {
#[inline]
fn is_square(&self) -> bool {
if *self < 0 {
false
} else {
self.unsigned_abs().is_square()
}
}
}
};
}
apply_to_signeds!(impl_signed);