1use crate::num::arithmetic::traits::{CheckedSqrt, FloorSqrt, Parity, Square};
14use crate::num::basic::integers::PrimitiveInt;
15use crate::num::conversion::traits::{SplitInHalf, WrappingFrom};
16use crate::num::factorization::TWICE_U64_WIDTH;
17use crate::num::factorization::traits::IsSquare;
18
19const IS_SQUARE_MOD64: [bool; 64] = [
20 true, true, false, false, true, false, false, false, false, true, false, false, false, false,
21 false, false, true, true, false, false, false, false, false, false, false, true, false, false,
22 false, false, false, false, false, true, false, false, true, false, false, false, false, true,
23 false, false, false, false, false, false, false, true, false, false, false, false, false,
24 false, false, true, false, false, false, false, false, false,
25];
26
27const IS_SQUARE_MOD65: [bool; 65] = [
28 true, true, false, false, true, false, false, false, false, true, true, false, false, false,
29 true, false, true, false, false, false, false, false, false, false, false, true, true, false,
30 false, true, true, false, false, false, false, true, true, false, false, true, true, false,
31 false, false, false, false, false, false, false, true, false, true, false, false, false, true,
32 true, false, false, false, false, true, false, false, true,
33];
34
35const IS_SQUARE_MOD63: [bool; 63] = [
36 true, true, false, false, true, false, false, true, false, true, false, false, false, false,
37 true, false, true, false, true, false, false, false, true, false, false, true, false, false,
38 true, false, false, false, false, false, false, true, true, true, false, false, false, false,
39 false, true, false, false, true, false, false, true, false, false, false, false, false, false,
40 true, false, true, false, false, false, false,
41];
42
43fn is_square_u64(x: u64) -> bool {
45 IS_SQUARE_MOD64[(x % 64) as usize]
46 && IS_SQUARE_MOD63[(x % 63) as usize]
47 && IS_SQUARE_MOD65[(x % 65) as usize]
48 && x.floor_sqrt().square() == x
49}
50
51macro_rules! impl_unsigned {
52 ($t: ident) => {
53 impl IsSquare for $t {
54 #[inline]
64 fn is_square(&self) -> bool {
65 is_square_u64(u64::wrapping_from(*self))
66 }
67 }
68 };
69}
70impl_unsigned!(u8);
71impl_unsigned!(u16);
72impl_unsigned!(u32);
73impl_unsigned!(u64);
74impl_unsigned!(usize);
75
76const B1: u64 = u64::WIDTH >> 2;
78const B2: u64 = B1 << 1;
79const B3: u64 = B1 * 3;
80
81const M2: u64 = (1 << B2) - 1;
82const M3: u64 = (1 << B3) - 1;
83
84const fn low0(n: u64) -> u64 {
85 n & M3
86}
87
88const fn high0(n: u64) -> u64 {
89 n >> B3
90}
91
92const fn low1(n: u64) -> u64 {
93 (n & M2) << B1
94}
95
96const fn high1(n: u64) -> u64 {
97 n >> B2
98}
99
100const fn mod_34lsub1(x_hi: u64, x_lo: u64) -> u64 {
108 low0(x_lo) + high0(x_lo) + low1(x_hi) + high1(x_hi)
109}
110
111const MOD34_BITS: u64 = (u64::WIDTH >> 2) * 3;
112const MOD34_MASK: u64 = (1 << MOD34_BITS) - 1;
113
114const fn perfsqr_mod_34(x_hi: u64, x_lo: u64) -> u64 {
115 let r = mod_34lsub1(x_hi, x_lo);
116 (r & MOD34_MASK) + (r >> MOD34_BITS)
117}
118
119const SQR_MOD_BITS: u64 = MOD34_BITS + 1;
122const SQR_MOD_MASK: u64 = (1 << SQR_MOD_BITS) - 1;
123
124const fn perfsqr_mod_idx(r: u64, d: u64, inv: u64) -> u64 {
125 assert!(r <= SQR_MOD_MASK);
126 assert!(inv.wrapping_mul(d) & SQR_MOD_MASK == 1);
127 assert!(u64::MAX / d >= SQR_MOD_MASK);
128 let q = r.wrapping_mul(inv) & SQR_MOD_MASK;
129 assert!(r == (q.wrapping_mul(d) & SQR_MOD_MASK));
130 q.wrapping_mul(d) >> SQR_MOD_BITS
131}
132
133fn perfsqr_mod_1(r: u64, d: u64, inv: u64, mask: u64) -> bool {
135 assert!(d <= u64::WIDTH);
136 let idx = perfsqr_mod_idx(r, d, inv);
137 if (mask >> idx).even() {
138 return false;
140 }
141 true
142}
143
144fn perfsqr_mod_2(r: u64, d: u64, inv: u64, mhi: u64, mlo: u64) -> bool {
146 assert!(d <= TWICE_U64_WIDTH);
147 let mut idx = perfsqr_mod_idx(r, d, inv);
148 let m = if idx < u64::WIDTH { mlo } else { mhi };
149 idx %= u64::WIDTH;
150 if (m >> idx).even() {
151 return false;
153 }
154 true
155}
156
157fn perfsqr_mod_test(x: u128) -> bool {
160 let (x_hi, x_lo) = x.split_in_half();
161 let r = perfsqr_mod_34(x_hi, x_lo);
162 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) }
167
168const SQR_MOD256: [u64; 4] =
171 [0x202021202030213, 0x202021202020213, 0x202021202030212, 0x202021202020212];
172
173impl IsSquare for u128 {
174 #[cfg_attr(dylint_lib = "malachite_lints", allow(collapse_adjacent_ifs))]
192 #[inline]
193 fn is_square(&self) -> bool {
194 let idx = self % 0x100; if (SQR_MOD256[(idx >> u64::LOG_WIDTH) as usize]
201 >> (idx & const { u64::WIDTH_MASK as Self }))
202 .even()
203 {
204 return false;
205 }
206 if !perfsqr_mod_test(*self) {
209 return false;
210 }
211 self.checked_sqrt().is_some()
214 }
215}
216
217macro_rules! impl_signed {
218 ($t: ident) => {
219 impl IsSquare for $t {
220 #[inline]
230 fn is_square(&self) -> bool {
231 if *self < 0 {
232 false
233 } else {
234 self.unsigned_abs().is_square()
235 }
236 }
237 }
238 };
239}
240apply_to_signeds!(impl_signed);