malachite_nz/natural/factorization/
is_square.rs1use crate::natural::InnerNatural::{Large, Small};
12use crate::natural::Natural;
13use crate::natural::arithmetic::sqrt::limbs_checked_sqrt;
14use crate::platform::Limb;
15use malachite_base::num::basic::integers::PrimitiveInt;
16use malachite_base::num::basic::traits::Zero;
17use malachite_base::num::conversion::traits::ExactFrom;
18use malachite_base::num::factorization::traits::IsSquare;
19
20const MOD34_BITS: Limb = ((Limb::WIDTH as Limb) / 4) * 3;
21const MOD34_MASK: Limb = (1 << MOD34_BITS) - 1;
22
23const SQR_MOD_BITS: Limb = MOD34_BITS + 1;
26const SQR_MOD_MASK: Limb = (1 << SQR_MOD_BITS) - 1;
27
28const B1: Limb = (Limb::WIDTH as Limb) / 4;
30const B2: Limb = B1 * 2;
31const B3: Limb = B1 * 3;
32
33const M1: Limb = (1 << B1) - 1;
34const M2: Limb = (1 << B2) - 1;
35const M3: Limb = (1 << B3) - 1;
36
37const fn low0(n: Limb) -> Limb {
38 n & M3
39}
40const fn high0(n: Limb) -> Limb {
41 n >> B3
42}
43
44const fn low1(n: Limb) -> Limb {
45 (n & M2) << B1
46}
47const fn high1(n: Limb) -> Limb {
48 n >> B2
49}
50
51const fn low2(n: Limb) -> Limb {
52 (n & M1) << B2
53}
54const fn high2(n: Limb) -> Limb {
55 n >> B1
56}
57
58const fn parts0(n: Limb) -> Limb {
59 low0(n) + high0(n)
60}
61const fn parts1(n: Limb) -> Limb {
62 low1(n) + high1(n)
63}
64const fn parts2(n: Limb) -> Limb {
65 low2(n) + high2(n)
66}
67
68pub(crate) fn mod_34lsub1(limbs: &[Limb]) -> Limb {
76 let (sums, carries) = limbs.chunks(3).fold(
78 ([Limb::ZERO; 3], [Limb::ZERO; 3]),
79 |(mut sums, mut carries), chunk| {
80 for (i, &limb) in chunk.iter().enumerate() {
81 let (sum, overflow) = sums[i].overflowing_add(limb);
82 sums[i] = sum;
83 if overflow {
84 carries[i] += 1;
85 }
86 }
87 (sums, carries)
88 },
89 );
90 parts0(sums[0])
91 + parts1(sums[1])
92 + parts2(sums[2])
93 + parts1(carries[0])
94 + parts2(carries[1])
95 + parts0(carries[2])
96}
97
98fn perfsqr_mod_34(limbs: &[Limb]) -> Limb {
100 let r = mod_34lsub1(limbs);
101 (r & MOD34_MASK) + (r >> MOD34_BITS)
102}
103
104const fn perfsqr_mod_idx(r: Limb, d: Limb, inv: Limb) -> Limb {
105 assert!(r <= SQR_MOD_MASK);
106 assert!(inv.wrapping_mul(d) & SQR_MOD_MASK == 1);
107 assert!(Limb::MAX / d >= SQR_MOD_MASK);
108 let q = r.wrapping_mul(inv) & SQR_MOD_MASK;
109 assert!(r == (q.wrapping_mul(d) & SQR_MOD_MASK));
110 q.wrapping_mul(d) >> SQR_MOD_BITS
111}
112
113fn perfsqr_mod_1(r: Limb, d: Limb, inv: Limb, mask: Limb) -> bool {
115 assert!(d <= Limb::WIDTH as Limb);
117 let idx = perfsqr_mod_idx(r, d, inv);
118 if (mask >> idx) & 1 == 0 {
119 return false;
121 }
122 true
123}
124
125fn perfsqr_mod_2(r: Limb, d: Limb, inv: Limb, mhi: Limb, mlo: Limb) -> bool {
127 assert!(d <= 2 * Limb::WIDTH as Limb);
128 let mut idx = perfsqr_mod_idx(r, d, inv);
129 let m = if idx < Limb::WIDTH as Limb { mlo } else { mhi };
130 idx %= Limb::WIDTH as Limb;
131 if (m >> idx) & 1 == 0 {
132 return false;
134 }
135 true
136}
137
138#[cfg(not(feature = "32_bit_limbs"))]
141fn perfsqr_mod_test(limbs: &[Limb]) -> bool {
142 let r = perfsqr_mod_34(limbs);
143
144 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) }
149
150#[cfg(feature = "32_bit_limbs")]
153fn perfsqr_mod_test(limbs: &[Limb]) -> bool {
154 let r = perfsqr_mod_34(limbs);
155
156 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) }
161
162#[cfg(not(feature = "32_bit_limbs"))]
165const SQR_MOD256: [u64; 4] =
166 [0x202021202030213, 0x202021202020213, 0x202021202030212, 0x202021202020212];
167
168#[cfg(feature = "32_bit_limbs")]
171const SQR_MOD256: [u32; 8] =
172 [0x2030213, 0x2020212, 0x2020213, 0x2020212, 0x2030212, 0x2020212, 0x2020212, 0x2020212];
173
174fn limbs_is_square(limbs: &[Limb]) -> bool {
175 assert!(!limbs.is_empty());
176 let idx = limbs[0] % 0x100; if (SQR_MOD256[usize::exact_from(idx >> Limb::LOG_WIDTH)]
183 >> (idx & const { Limb::WIDTH_MASK as Limb }))
184 & 1
185 == 0
186 {
187 return false;
188 }
189 if !perfsqr_mod_test(limbs) {
192 return false;
193 }
194 limbs_checked_sqrt(limbs).is_some()
197}
198
199impl IsSquare for Natural {
200 fn is_square(&self) -> bool {
226 match self {
227 Self(Small(small)) => small.is_square(),
230 Self(Large(limbs)) => limbs_is_square(limbs),
231 }
232 }
233}