1use crate::num::arithmetic::traits::{CheckedSqrt, FloorSqrt, Square};
14use crate::num::basic::integers::PrimitiveInt;
15use crate::num::conversion::traits::{SplitInHalf, WrappingFrom};
16use crate::num::factorization::traits::IsSquare;
17
18const IS_SQUARE_MOD64: [bool; 64] = [
19 true, true, false, false, true, false, false, false, false, true, false, false, false, false,
20 false, false, true, true, false, false, false, false, false, false, false, true, false, false,
21 false, false, false, false, false, true, false, false, true, false, false, false, false, true,
22 false, false, false, false, false, false, false, true, false, false, false, false, false,
23 false, false, true, false, false, false, false, false, false,
24];
25
26const IS_SQUARE_MOD65: [bool; 65] = [
27 true, true, false, false, true, false, false, false, false, true, true, false, false, false,
28 true, false, true, false, false, false, false, false, false, false, false, true, true, false,
29 false, true, true, false, false, false, false, true, true, false, false, true, true, false,
30 false, false, false, false, false, false, false, true, false, true, false, false, false, true,
31 true, false, false, false, false, true, false, false, true,
32];
33
34const IS_SQUARE_MOD63: [bool; 63] = [
35 true, true, false, false, true, false, false, true, false, true, false, false, false, false,
36 true, false, true, false, true, false, false, false, true, false, false, true, false, false,
37 true, false, false, false, false, false, false, true, true, true, false, false, false, false,
38 false, true, false, false, true, false, false, true, false, false, false, false, false, false,
39 true, false, true, false, false, false, false,
40];
41
42fn is_square_u64(x: u64) -> bool {
44 IS_SQUARE_MOD64[(x % 64) as usize]
45 && IS_SQUARE_MOD63[(x % 63) as usize]
46 && IS_SQUARE_MOD65[(x % 65) as usize]
47 && x.floor_sqrt().square() == x
48}
49
50macro_rules! impl_unsigned {
51 ($t: ident) => {
52 impl IsSquare for $t {
53 #[inline]
63 fn is_square(&self) -> bool {
64 is_square_u64(u64::wrapping_from(*self))
65 }
66 }
67 };
68}
69impl_unsigned!(u8);
70impl_unsigned!(u16);
71impl_unsigned!(u32);
72impl_unsigned!(u64);
73impl_unsigned!(usize);
74
75const B1: u64 = u64::WIDTH >> 2;
77const B2: u64 = B1 * 2;
78const B3: u64 = B1 * 3;
79
80const M2: u64 = (1 << B2) - 1;
81const M3: u64 = (1 << B3) - 1;
82
83const fn low0(n: u64) -> u64 {
84 n & M3
85}
86
87const fn high0(n: u64) -> u64 {
88 n >> B3
89}
90
91const fn low1(n: u64) -> u64 {
92 (n & M2) << B1
93}
94
95const fn high1(n: u64) -> u64 {
96 n >> B2
97}
98
99const fn mod_34lsub1(x_hi: u64, x_lo: u64) -> u64 {
107 low0(x_lo) + high0(x_lo) + low1(x_hi) + high1(x_hi)
108}
109
110const MOD34_BITS: u64 = (u64::WIDTH >> 2) * 3;
111const MOD34_MASK: u64 = (1 << MOD34_BITS) - 1;
112
113const fn perfsqr_mod_34(x_hi: u64, x_lo: u64) -> u64 {
114 let r = mod_34lsub1(x_hi, x_lo);
115 (r & MOD34_MASK) + (r >> MOD34_BITS)
116}
117
118const SQR_MOD_BITS: u64 = MOD34_BITS + 1;
121const SQR_MOD_MASK: u64 = (1 << SQR_MOD_BITS) - 1;
122
123const fn perfsqr_mod_idx(r: u64, d: u64, inv: u64) -> u64 {
124 assert!(r <= SQR_MOD_MASK);
125 assert!(inv.wrapping_mul(d) & SQR_MOD_MASK == 1);
126 assert!(u64::MAX / d >= SQR_MOD_MASK);
127 let q = r.wrapping_mul(inv) & SQR_MOD_MASK;
128 assert!(r == (q.wrapping_mul(d) & SQR_MOD_MASK));
129 q.wrapping_mul(d) >> SQR_MOD_BITS
130}
131
132fn perfsqr_mod_1(r: u64, d: u64, inv: u64, mask: u64) -> bool {
134 assert!(d <= u64::WIDTH);
135 let idx = perfsqr_mod_idx(r, d, inv);
136 if (mask >> idx) & 1 == 0 {
137 return false;
139 }
140 true
141}
142
143fn perfsqr_mod_2(r: u64, d: u64, inv: u64, mhi: u64, mlo: u64) -> bool {
145 assert!(d <= const { 2 * u64::WIDTH });
146 let mut idx = perfsqr_mod_idx(r, d, inv);
147 let m = if idx < u64::WIDTH { mlo } else { mhi };
148 idx %= u64::WIDTH;
149 if (m >> idx) & 1 == 0 {
150 return false;
152 }
153 true
154}
155
156fn perfsqr_mod_test(x: u128) -> bool {
159 let (x_hi, x_lo) = x.split_in_half();
160 let r = perfsqr_mod_34(x_hi, x_lo);
161 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) }
166
167const SQR_MOD256: [u64; 4] =
170 [0x202021202030213, 0x202021202020213, 0x202021202030212, 0x202021202020212];
171
172impl IsSquare for u128 {
173 #[inline]
183 fn is_square(&self) -> bool {
184 let idx = self % 0x100; if (SQR_MOD256[(idx >> u64::LOG_WIDTH) as usize]
191 >> (idx & const { u64::WIDTH_MASK as Self }))
192 & 1
193 == 0
194 {
195 return false;
196 }
197 if !perfsqr_mod_test(*self) {
200 return false;
201 }
202 self.checked_sqrt().is_some()
205 }
206}
207
208macro_rules! impl_signed {
209 ($t: ident) => {
210 impl IsSquare for $t {
211 #[inline]
221 fn is_square(&self) -> bool {
222 if *self < 0 {
223 false
224 } else {
225 self.unsigned_abs().is_square()
226 }
227 }
228 }
229 };
230}
231apply_to_signeds!(impl_signed);