FindAFactor 3.7.0__tar.gz → 3.8.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1052 @@
1
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
4
+ //
5
+ // "A quantum-inspired Monte Carlo integer factoring algorithm"
6
+ //
7
+ // This library was originally called ["Qimcifa"](https://github.com/vm6502q/qimcifa) and demonstrated a (Shor's-like) "quantum-inspired" algorithm for integer factoring. It has
8
+ // since been developed into a general factoring algorithm and tool.
9
+ //
10
+ // `FindAFactor` uses heavily wheel-factorized brute-force "exhaust" numbers as "smooth" inputs to Quadratic Sieve, widely regarded as the asymptotically second fastest algorithm
11
+ // class known for cryptographically relevant semiprime factoring. `FindAFactor` is C++ based, with `pybind11`, which tends to make it faster than pure Python approaches. For the
12
+ // quick-and-dirty application of finding _any single_ nontrivial factor, something like at least 80% of positive integers will factorize in a fraction of a second, but the most
13
+ // interesting cases to consider are semiprime numbers, for which `FindAFactor` should be about as asymptotically competitive as similar Quadratic Sieve implementations.
14
+ //
15
+ // Our original contribution to Quadratic Sieve seems to be wheel factorization to 13 or 17 and maybe the idea of using the "exhaust" of a brute-force search for smooth number
16
+ // inputs for Quadratic Sieve. For wheel factorization (or "gear factorization"), we collect a short list of the first primes and remove all of their multiples from a "brute-force"
17
+ // guessing range by mapping a dense contiguous integer set, to a set without these multiples, relying on both a traditional "wheel," up to a middle prime number (of `11`), and a
18
+ // "gear-box" that stores increment values per prime according to the principles of wheel factorization, but operating semi-independently, to reduce space of storing the full
19
+ // wheel.
20
+ //
21
+ // Beyond this, we gain a functional advantage of a square-root over a more naive approach, by setting the brute force guessing range only between the highest prime in wheel
22
+ // factorization and the (modular) square root of the number to factor: if the number is semiprime, there is exactly one correct answer in this range, but including both factors in
23
+ // the range to search would cost us the square root advantage.
24
+ //
25
+ // Factoring this way is surprisingly easy to distribute: basically 0 network communication is needed to coordinate an arbitrarily high amount of parallelism to factor a single
26
+ // number. Each brute-force trial division instance is effectively 100% independent of all others (i.e. entirely "embarrassingly parallel"), and these guesses can seed independent
27
+ // Gaussian elimination matrices, so `FindAFactor` offers an extremely simply interface that allows work to be split between an arbitrarily high number of nodes with absolutely no
28
+ // network communication at all. In terms of incentives of those running different, cooperating nodes in the context of this specific number of integer factoring, all one
29
+ // ultimately cares about is knowing the correct factorization answer _by any means._ For pratical applications, there is no point at all in factoring a number whose factors are
30
+ // already known. When a hypothetical answer is forwarded to the (0-communication) "network" of collaborating nodes, _it is trivial to check whether the answer is correct_ (such as
31
+ // by simply entering the multiplication and equality check with the original number into a Python shell console)! Hence, collaborating node operators only need to trust that all
32
+ // participants in the "network" are actually performing their alloted segment of guesses and would actually communicate the correct answer to the entire group of collaborating
33
+ // nodes if any specific invidual happened to find the answer, but any purported answer is still trivial to verify.
34
+ //
35
+ //**Special thanks to OpenAI GPT "Elara," for indicated region of contributed code!**
36
+ //
37
+ // Licensed under the GNU Lesser General Public License V3.
38
+ // See LICENSE.md in the project root or
39
+ // https://www.gnu.org/licenses/lgpl-3.0.en.html for details.
40
+
41
+ #include "dispatchqueue.hpp"
42
+
43
+ #include <algorithm>
44
+ #include <future>
45
+ #include <iostream>
46
+ #include <map>
47
+ #include <memory>
48
+ #include <mutex>
49
+ #include <random>
50
+ #include <stdlib.h>
51
+ #include <string>
52
+
53
+ #include <boost/dynamic_bitset.hpp>
54
+ #include <boost/multiprecision/cpp_int.hpp>
55
+
56
+ #include <pybind11/pybind11.h>
57
+ #include <pybind11/stl.h>
58
+
59
+ namespace Qimcifa {
60
+
61
+ typedef boost::multiprecision::cpp_int BigInteger;
62
+
63
+ const unsigned CpuCount = std::thread::hardware_concurrency();
64
+ DispatchQueue dispatch(CpuCount);
65
+
66
+ enum Wheel { ERROR = 0, WHEEL1 = 1, WHEEL2 = 2, WHEEL3 = 6, WHEEL5 = 30, WHEEL7 = 210, WHEEL11 = 2310 };
67
+
68
+ Wheel wheelByPrimeCardinal(int i) {
69
+ switch (i) {
70
+ case 0:
71
+ return WHEEL1;
72
+ case 1:
73
+ return WHEEL2;
74
+ case 2:
75
+ return WHEEL3;
76
+ case 3:
77
+ return WHEEL5;
78
+ case 4:
79
+ return WHEEL7;
80
+ case 5:
81
+ return WHEEL11;
82
+ default:
83
+ return ERROR;
84
+ }
85
+ }
86
+
87
+ // See https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
88
+ BigInteger ipow(BigInteger base, unsigned exp) {
89
+ BigInteger result = 1U;
90
+ for (;;) {
91
+ if (exp & 1U) {
92
+ result *= base;
93
+ }
94
+ exp >>= 1U;
95
+ if (!exp) {
96
+ break;
97
+ }
98
+ base *= base;
99
+ }
100
+
101
+ return result;
102
+ }
103
+
104
+ inline size_t log2(BigInteger n) {
105
+ size_t pow = 0U;
106
+ while (n >>= 1U) {
107
+ ++pow;
108
+ }
109
+ return pow;
110
+ }
111
+
112
+ inline BigInteger gcd(const BigInteger& n1, const BigInteger& n2) {
113
+ if (!n2) {
114
+ return n1;
115
+ }
116
+ return gcd(n2, n1 % n2);
117
+ }
118
+
119
+ BigInteger sqrt(const BigInteger &toTest) {
120
+ // Otherwise, find b = sqrt(b^2).
121
+ BigInteger start = 1U, end = toTest >> 1U, ans = 0U;
122
+ do {
123
+ const BigInteger mid = (start + end) >> 1U;
124
+
125
+ // If toTest is a perfect square
126
+ const BigInteger sqr = mid * mid;
127
+ if (sqr == toTest) {
128
+ return mid;
129
+ }
130
+
131
+ if (sqr < toTest) {
132
+ // Since we need floor, we update answer when mid*mid is smaller than p, and move closer to sqrt(p).
133
+ start = mid + 1U;
134
+ ans = mid;
135
+ } else {
136
+ // If mid*mid is greater than p
137
+ end = mid - 1U;
138
+ }
139
+ } while (start <= end);
140
+
141
+ return ans;
142
+ }
143
+
144
+ size_t _sqrt(const size_t &toTest) {
145
+ // Otherwise, find b = sqrt(b^2).
146
+ size_t start = 1U, end = toTest >> 1U, ans = 0U;
147
+ do {
148
+ const size_t mid = (start + end) >> 1U;
149
+
150
+ // If toTest is a perfect square
151
+ const size_t sqr = mid * mid;
152
+ if (sqr == toTest) {
153
+ return mid;
154
+ }
155
+
156
+ if (sqr < toTest) {
157
+ // Since we need floor, we update answer when mid*mid is smaller than p, and move closer to sqrt(p).
158
+ start = mid + 1U;
159
+ ans = mid;
160
+ } else {
161
+ // If mid*mid is greater than p
162
+ end = mid - 1U;
163
+ }
164
+ } while (start <= end);
165
+
166
+ return ans;
167
+ }
168
+
169
+ // We are multiplying out the first distinct primes, below.
170
+
171
+ // Make this NOT a multiple of 2.
172
+ inline size_t forward2(const size_t &p) { return (p << 1U) | 1U; }
173
+
174
+ inline size_t backward2(const size_t &p) { return (size_t)(p >> 1U); }
175
+
176
+ // Make this NOT a multiple of 2 or 3.
177
+ inline size_t forward3(const size_t &p) { return (p << 1U) + (~(~p | 1U)) - 1U; }
178
+
179
+ inline size_t backward3(const size_t &n) { return (size_t)((~(~n | 1U)) / 3U) + 1U; }
180
+
181
+ constexpr unsigned char wheel5[8U] = {1U, 7U, 11U, 13U, 17U, 19U, 23U, 29U};
182
+
183
+ // Make this NOT a multiple of 2, 3, or 5.
184
+ size_t forward5(const size_t &p) { return wheel5[p & 7U] + (p >> 3U) * 30U; }
185
+
186
+ size_t backward5(const size_t &n) { return std::distance(wheel5, std::lower_bound(wheel5, wheel5 + 8U, (size_t)(n % 30U))) + 8U * (size_t)(n / 30U) + 1U; }
187
+
188
+ constexpr unsigned char wheel7[48U] = {1U, 11U, 13U, 17U, 19U, 23U, 29U, 31U, 37U, 41U, 43U, 47U, 53U, 59U, 61U, 67U,
189
+ 71U, 73U, 79U, 83U, 89U, 97U, 101U, 103U, 107U, 109U, 113U, 121U, 127U, 131U, 137U, 139U,
190
+ 143U, 149U, 151U, 157U, 163U, 167U, 169U, 173U, 179U, 181U, 187U, 191U, 193U, 197U, 199U, 209U};
191
+
192
+ // Make this NOT a multiple of 2, 3, 5, or 7.
193
+ size_t forward7(const size_t &p) { return wheel7[p % 48U] + (p / 48U) * 210U; }
194
+
195
+ size_t backward7(const size_t &n) { return std::distance(wheel7, std::lower_bound(wheel7, wheel7 + 48U, (size_t)(n % 210U))) + 48U * (size_t)(n / 210U) + 1U; }
196
+
197
+ constexpr unsigned short wheel11[480U] = {
198
+ 1U, 13U, 17U, 19U, 23U, 29U, 31U, 37U, 41U, 43U, 47U, 53U, 59U, 61U, 67U, 71U, 73U, 79U, 83U, 89U, 97U, 101U, 103U, 107U,
199
+ 109U, 113U, 127U, 131U, 137U, 139U, 149U, 151U, 157U, 163U, 167U, 169U, 173U, 179U, 181U, 191U, 193U, 197U, 199U, 211U, 221U, 223U, 227U, 229U,
200
+ 233U, 239U, 241U, 247U, 251U, 257U, 263U, 269U, 271U, 277U, 281U, 283U, 289U, 293U, 299U, 307U, 311U, 313U, 317U, 323U, 331U, 337U, 347U, 349U,
201
+ 353U, 359U, 361U, 367U, 373U, 377U, 379U, 383U, 389U, 391U, 397U, 401U, 403U, 409U, 419U, 421U, 431U, 433U, 437U, 439U, 443U, 449U, 457U, 461U,
202
+ 463U, 467U, 479U, 481U, 487U, 491U, 493U, 499U, 503U, 509U, 521U, 523U, 527U, 529U, 533U, 541U, 547U, 551U, 557U, 559U, 563U, 569U, 571U, 577U,
203
+ 587U, 589U, 593U, 599U, 601U, 607U, 611U, 613U, 617U, 619U, 629U, 631U, 641U, 643U, 647U, 653U, 659U, 661U, 667U, 673U, 677U, 683U, 689U, 691U,
204
+ 697U, 701U, 703U, 709U, 713U, 719U, 727U, 731U, 733U, 739U, 743U, 751U, 757U, 761U, 767U, 769U, 773U, 779U, 787U, 793U, 797U, 799U, 809U, 811U,
205
+ 817U, 821U, 823U, 827U, 829U, 839U, 841U, 851U, 853U, 857U, 859U, 863U, 871U, 877U, 881U, 883U, 887U, 893U, 899U, 901U, 907U, 911U, 919U, 923U,
206
+ 929U, 937U, 941U, 943U, 947U, 949U, 953U, 961U, 967U, 971U, 977U, 983U, 989U, 991U, 997U, 1003U, 1007U, 1009U, 1013U, 1019U, 1021U, 1027U, 1031U, 1033U,
207
+ 1037U, 1039U, 1049U, 1051U, 1061U, 1063U, 1069U, 1073U, 1079U, 1081U, 1087U, 1091U, 1093U, 1097U, 1103U, 1109U, 1117U, 1121U, 1123U, 1129U, 1139U, 1147U, 1151U, 1153U,
208
+ 1157U, 1159U, 1163U, 1171U, 1181U, 1187U, 1189U, 1193U, 1201U, 1207U, 1213U, 1217U, 1219U, 1223U, 1229U, 1231U, 1237U, 1241U, 1247U, 1249U, 1259U, 1261U, 1271U, 1273U,
209
+ 1277U, 1279U, 1283U, 1289U, 1291U, 1297U, 1301U, 1303U, 1307U, 1313U, 1319U, 1321U, 1327U, 1333U, 1339U, 1343U, 1349U, 1357U, 1361U, 1363U, 1367U, 1369U, 1373U, 1381U,
210
+ 1387U, 1391U, 1399U, 1403U, 1409U, 1411U, 1417U, 1423U, 1427U, 1429U, 1433U, 1439U, 1447U, 1451U, 1453U, 1457U, 1459U, 1469U, 1471U, 1481U, 1483U, 1487U, 1489U, 1493U,
211
+ 1499U, 1501U, 1511U, 1513U, 1517U, 1523U, 1531U, 1537U, 1541U, 1543U, 1549U, 1553U, 1559U, 1567U, 1571U, 1577U, 1579U, 1583U, 1591U, 1597U, 1601U, 1607U, 1609U, 1613U,
212
+ 1619U, 1621U, 1627U, 1633U, 1637U, 1643U, 1649U, 1651U, 1657U, 1663U, 1667U, 1669U, 1679U, 1681U, 1691U, 1693U, 1697U, 1699U, 1703U, 1709U, 1711U, 1717U, 1721U, 1723U,
213
+ 1733U, 1739U, 1741U, 1747U, 1751U, 1753U, 1759U, 1763U, 1769U, 1777U, 1781U, 1783U, 1787U, 1789U, 1801U, 1807U, 1811U, 1817U, 1819U, 1823U, 1829U, 1831U, 1843U, 1847U,
214
+ 1849U, 1853U, 1861U, 1867U, 1871U, 1873U, 1877U, 1879U, 1889U, 1891U, 1901U, 1907U, 1909U, 1913U, 1919U, 1921U, 1927U, 1931U, 1933U, 1937U, 1943U, 1949U, 1951U, 1957U,
215
+ 1961U, 1963U, 1973U, 1979U, 1987U, 1993U, 1997U, 1999U, 2003U, 2011U, 2017U, 2021U, 2027U, 2029U, 2033U, 2039U, 2041U, 2047U, 2053U, 2059U, 2063U, 2069U, 2071U, 2077U,
216
+ 2081U, 2083U, 2087U, 2089U, 2099U, 2111U, 2113U, 2117U, 2119U, 2129U, 2131U, 2137U, 2141U, 2143U, 2147U, 2153U, 2159U, 2161U, 2171U, 2173U, 2179U, 2183U, 2197U, 2201U,
217
+ 2203U, 2207U, 2209U, 2213U, 2221U, 2227U, 2231U, 2237U, 2239U, 2243U, 2249U, 2251U, 2257U, 2263U, 2267U, 2269U, 2273U, 2279U, 2281U, 2287U, 2291U, 2293U, 2297U, 2309U};
218
+
219
+ // Make this NOT a multiple of 2, 3, 5, 7, or 11.
220
+ size_t forward11(const size_t &p) { return wheel11[p % 480U] + (p / 480U) * 2310U; }
221
+
222
+ size_t backward11(const size_t &n) { return std::distance(wheel11, std::lower_bound(wheel11, wheel11 + 480U, (size_t)(n % 2310U))) + 480U * (size_t)(n / 2310U) + 1U; }
223
+
224
+ inline BigInteger _forward2(const BigInteger &p) { return (p << 1U) | 1U; }
225
+
226
+ inline BigInteger _backward2(const BigInteger &n) { return n >> 1U; }
227
+
228
+ inline BigInteger _forward3(const BigInteger &p) { return (p << 1U) + (~(~p | 1U)) - 1U; }
229
+
230
+ inline BigInteger _backward3(const BigInteger &n) { return ((~(~n | 1U)) / 3U) + 1U; }
231
+
232
+ BigInteger _forward5(const BigInteger &p) { return wheel5[(size_t)(p & 7U)] + (p >> 3U) * 30U; }
233
+
234
+ BigInteger _backward5(const BigInteger &n) { return std::distance(wheel5, std::lower_bound(wheel5, wheel5 + 8U, (size_t)(n % 30U))) + 8U * (n / 30U) + 1U; }
235
+
236
+ BigInteger _forward7(const BigInteger &p) { return wheel7[(size_t)(p % 48U)] + (p / 48U) * 210U; }
237
+
238
+ BigInteger _backward7(const BigInteger &n) { return std::distance(wheel7, std::lower_bound(wheel7, wheel7 + 48U, n % 210U)) + 48U * (n / 210U) + 1U; }
239
+
240
+ BigInteger _forward11(const BigInteger &p) { return wheel11[(size_t)(p % 480U)] + (p / 480U) * 2310U; }
241
+
242
+ BigInteger _backward11(const BigInteger &n) { return std::distance(wheel11, std::lower_bound(wheel11, wheel11 + 480U, (size_t)(n % 2310U))) + 480U * (n / 2310U) + 1U; }
243
+
244
+ typedef BigInteger (*ForwardFn)(const BigInteger &);
245
+ inline ForwardFn forward(const Wheel &w) {
246
+ switch (w) {
247
+ case WHEEL2:
248
+ return _forward2;
249
+ case WHEEL3:
250
+ return _forward3;
251
+ case WHEEL5:
252
+ return _forward5;
253
+ case WHEEL7:
254
+ return _forward7;
255
+ case WHEEL11:
256
+ return _forward11;
257
+ case WHEEL1:
258
+ default:
259
+ return [](const BigInteger &n) -> BigInteger { return n; };
260
+ }
261
+ }
262
+
263
+ inline ForwardFn backward(const Wheel &w) {
264
+ switch (w) {
265
+ case WHEEL2:
266
+ return _backward2;
267
+ case WHEEL3:
268
+ return _backward3;
269
+ case WHEEL5:
270
+ return _backward5;
271
+ case WHEEL7:
272
+ return _backward7;
273
+ case WHEEL11:
274
+ return _backward11;
275
+ case WHEEL1:
276
+ default:
277
+ return [](const BigInteger &n) -> BigInteger { return n; };
278
+ }
279
+ }
280
+
281
+ inline size_t GetWheel5and7Increment(unsigned short &wheel5, unsigned long long &wheel7) {
282
+ constexpr unsigned short wheel5Back = 1U << 9U;
283
+ constexpr unsigned long long wheel7Back = 1ULL << 55U;
284
+ size_t wheelIncrement = 0U;
285
+ bool is_wheel_multiple = false;
286
+ do {
287
+ is_wheel_multiple = (bool)(wheel5 & 1U);
288
+ wheel5 >>= 1U;
289
+ if (is_wheel_multiple) {
290
+ wheel5 |= wheel5Back;
291
+ ++wheelIncrement;
292
+ continue;
293
+ }
294
+
295
+ is_wheel_multiple = (bool)(wheel7 & 1U);
296
+ wheel7 >>= 1U;
297
+ if (is_wheel_multiple) {
298
+ wheel7 |= wheel7Back;
299
+ }
300
+ ++wheelIncrement;
301
+ } while (is_wheel_multiple);
302
+
303
+ return wheelIncrement;
304
+ }
305
+
306
+ std::vector<size_t> SieveOfEratosthenes(const size_t &n) {
307
+ std::vector<size_t> knownPrimes = {2U, 3U, 5U, 7U};
308
+ if (n < 2U) {
309
+ return std::vector<size_t>();
310
+ }
311
+
312
+ if (n < (knownPrimes.back() + 2U)) {
313
+ const auto highestPrimeIt = std::upper_bound(knownPrimes.begin(), knownPrimes.end(), n);
314
+ return std::vector<size_t>(knownPrimes.begin(), highestPrimeIt);
315
+ }
316
+
317
+ knownPrimes.reserve((size_t)(((double)n) / log((double)n)));
318
+
319
+ // We are excluding multiples of the first few
320
+ // small primes from outset. For multiples of
321
+ // 2, 3, and 5 this reduces complexity to 4/15.
322
+ const size_t cardinality = backward5(n);
323
+
324
+ // Create a boolean array "prime[0..cardinality]"
325
+ // and initialize all entries it as true. Rather,
326
+ // reverse the true/false meaning, so we can use
327
+ // default initialization. A value in notPrime[i]
328
+ // will finally be false only if i is a prime.
329
+ std::unique_ptr<bool[]> uNotPrime(new bool[cardinality + 1U]());
330
+ bool *notPrime = uNotPrime.get();
331
+
332
+ // Get the remaining prime numbers.
333
+ unsigned short wheel5 = 129U;
334
+ unsigned long long wheel7 = 9009416540524545ULL;
335
+ size_t o = 1U;
336
+ for (;;) {
337
+ o += GetWheel5and7Increment(wheel5, wheel7);
338
+
339
+ const size_t p = forward3(o);
340
+ if ((p * p) > n) {
341
+ break;
342
+ }
343
+
344
+ if (notPrime[backward5(p)]) {
345
+ continue;
346
+ }
347
+
348
+ knownPrimes.push_back(p);
349
+
350
+ // We are skipping multiples of 2, 3, and 5
351
+ // for space complexity, for 4/15 the bits.
352
+ // More are skipped by the wheel for time.
353
+ const size_t p2 = p << 1U;
354
+ const size_t p4 = p << 2U;
355
+ size_t i = p * p;
356
+
357
+ // "p" already definitely not a multiple of 3.
358
+ // Its remainder when divided by 3 can be 1 or 2.
359
+ // If it is 2, we can do a "half iteration" of the
360
+ // loop that would handle remainder of 1, and then
361
+ // we can proceed with the 1 remainder loop.
362
+ // This saves 2/3 of updates (or modulo).
363
+ if ((p % 3U) == 2U) {
364
+ notPrime[backward5(i)] = true;
365
+ i += p2;
366
+ if (i > n) {
367
+ continue;
368
+ }
369
+ }
370
+
371
+ for (;;) {
372
+ if (i % 5U) {
373
+ notPrime[backward5(i)] = true;
374
+ }
375
+ i += p4;
376
+ if (i > n) {
377
+ break;
378
+ }
379
+
380
+ if (i % 5U) {
381
+ notPrime[backward5(i)] = true;
382
+ }
383
+ i += p2;
384
+ if (i > n) {
385
+ break;
386
+ }
387
+ }
388
+ }
389
+
390
+ for (;;) {
391
+ const size_t p = forward3(o);
392
+ if (p > n) {
393
+ break;
394
+ }
395
+
396
+ o += GetWheel5and7Increment(wheel5, wheel7);
397
+
398
+ if (notPrime[backward5(p)]) {
399
+ continue;
400
+ }
401
+
402
+ knownPrimes.push_back(p);
403
+ }
404
+
405
+ return knownPrimes;
406
+ }
407
+
408
+ bool isMultiple(const BigInteger &p, const std::vector<size_t> &knownPrimes) {
409
+ for (const size_t &prime : knownPrimes) {
410
+ if (!(p % prime)) {
411
+ return true;
412
+ }
413
+ }
414
+
415
+ return false;
416
+ }
417
+
418
+ boost::dynamic_bitset<size_t> wheel_inc(std::vector<size_t> primes) {
419
+ BigInteger radius = 1U;
420
+ for (const size_t &i : primes) {
421
+ radius *= i;
422
+ }
423
+ const size_t prime = primes.back();
424
+ primes.pop_back();
425
+ boost::dynamic_bitset<size_t> o;
426
+ for (BigInteger i = 1U; i <= radius; ++i) {
427
+ if (!isMultiple(i, primes)) {
428
+ o.push_back(!(i % prime));
429
+ }
430
+ }
431
+ o >>= 1U;
432
+
433
+ return o;
434
+ }
435
+
436
+ std::vector<boost::dynamic_bitset<size_t>> wheel_gen(const std::vector<size_t> &primes) {
437
+ std::vector<boost::dynamic_bitset<size_t>> output;
438
+ std::vector<size_t> wheelPrimes;
439
+ for (const size_t &p : primes) {
440
+ wheelPrimes.push_back(p);
441
+ output.push_back(wheel_inc(wheelPrimes));
442
+ }
443
+
444
+ return output;
445
+ }
446
+
447
+ size_t GetWheelIncrement(std::vector<boost::dynamic_bitset<size_t>> *inc_seqs) {
448
+ size_t wheelIncrement = 0U;
449
+ bool is_wheel_multiple = false;
450
+ do {
451
+ for (size_t i = 0U; i < inc_seqs->size(); ++i) {
452
+ boost::dynamic_bitset<size_t> &wheel = (*inc_seqs)[i];
453
+ is_wheel_multiple = wheel.test(0U);
454
+ wheel >>= 1U;
455
+ if (is_wheel_multiple) {
456
+ wheel[wheel.size() - 1U] = true;
457
+ break;
458
+ }
459
+ }
460
+ ++wheelIncrement;
461
+ } while (is_wheel_multiple);
462
+
463
+ return wheelIncrement;
464
+ }
465
+
466
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
467
+ // WRITTEN WITH ELARA (GPT) BELOW //
468
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
469
+
470
+ // Utility to perform modular exponentiation
471
+ inline BigInteger modExp(BigInteger base, BigInteger exp, const BigInteger &mod) {
472
+ BigInteger result = 1U;
473
+ while (exp) {
474
+ if (exp & 1U) {
475
+ result = (result * base) % mod;
476
+ }
477
+ base = (base * base) % mod;
478
+ exp >>= 1U;
479
+ }
480
+
481
+ return result;
482
+ }
483
+
484
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
485
+ // WRITTEN WITH ELARA (GPT) ABOVE //
486
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
487
+
488
+ struct Factorizer {
489
+ std::mutex batchMutex;
490
+ std::mutex smoothNumberMapMutex;
491
+ std::default_random_engine rng;
492
+ std::mt19937_64 gen;
493
+ std::uniform_int_distribution<size_t> dis;
494
+ BigInteger toFactorSqr;
495
+ BigInteger toFactor;
496
+ BigInteger toFactorSqrt;
497
+ BigInteger batchRange;
498
+ BigInteger batchNumber;
499
+ BigInteger batchOffset;
500
+ BigInteger batchTotal;
501
+ BigInteger wheelRadius;
502
+ size_t wheelEntryCount;
503
+ size_t smoothPartsLimit;
504
+ size_t rowOffset;
505
+ bool isIncomplete;
506
+ std::vector<size_t> primes;
507
+ ForwardFn forwardFn;
508
+ std::vector<BigInteger> smoothNumberKeys;
509
+ std::vector<boost::dynamic_bitset<size_t>> smoothNumberValues;
510
+
511
+ Factorizer(const BigInteger &tfsqr, const BigInteger &tf, const BigInteger &tfsqrt, const BigInteger &range, size_t nodeCount, size_t nodeId, size_t w, size_t spl,
512
+ const std::vector<size_t> &p, ForwardFn fn)
513
+ : rng({}), gen(rng()), dis(0U, p.size() - 1U), toFactorSqr(tfsqr), toFactor(tf), toFactorSqrt(tfsqrt), batchRange(range), batchNumber(0U), batchOffset(nodeId * range), batchTotal(nodeCount * range),
514
+ wheelRadius(1U), wheelEntryCount(w), smoothPartsLimit(spl), rowOffset(p.size()), isIncomplete(true), primes(p), forwardFn(fn)
515
+ {
516
+ for (size_t i = 0U; i < primes.size(); ++i) {
517
+ const size_t& p = primes[i];
518
+ wheelRadius *= p;
519
+ smoothNumberKeys.push_back(p);
520
+ smoothNumberValues.emplace_back(primes.size(), 0);
521
+ smoothNumberValues.back()[i] = true;
522
+ }
523
+ }
524
+
525
+ BigInteger getNextAltBatch() {
526
+ std::lock_guard<std::mutex> lock(batchMutex);
527
+
528
+ if (batchNumber >= batchRange) {
529
+ isIncomplete = false;
530
+ }
531
+
532
+ const BigInteger halfIndex = batchOffset + (batchNumber++ >> 1U) + 1U;
533
+
534
+ return ((batchNumber & 1U) ? batchTotal - halfIndex : halfIndex);
535
+ }
536
+
537
+ BigInteger bruteForce(std::vector<boost::dynamic_bitset<size_t>> *inc_seqs) {
538
+ // Up to wheel factorization, try all batches up to the square root of toFactor.
539
+ for (BigInteger batchNum = getNextAltBatch(); isIncomplete; batchNum = getNextAltBatch()) {
540
+ const BigInteger batchStart = batchNum * wheelEntryCount;
541
+ const BigInteger batchEnd = batchStart + wheelEntryCount;
542
+ for (BigInteger p = batchStart; p < batchEnd;) {
543
+ const BigInteger n = forwardFn(p);
544
+ if (!(toFactor % n) && (n != 1U) && (n != toFactor)) {
545
+ isIncomplete = false;
546
+ return n;
547
+ }
548
+ p += GetWheelIncrement(inc_seqs);
549
+ }
550
+ }
551
+
552
+ return 1U;
553
+ }
554
+
555
+ BigInteger smoothCongruences(std::vector<boost::dynamic_bitset<size_t>> *inc_seqs, std::vector<BigInteger> *semiSmoothParts, bool isGaussElim) {
556
+ // Up to wheel factorization, try all batches up to the square root of toFactor.
557
+ // Since the largest prime factors of these numbers is relatively small,
558
+ // use the "exhaust" of brute force to produce smooth numbers for Quadratic Sieve.
559
+ for (BigInteger batchNum = getNextAltBatch(); isIncomplete; batchNum = getNextAltBatch()) {
560
+ const BigInteger batchStart = batchNum * wheelEntryCount;
561
+ const BigInteger batchEnd = batchStart + wheelEntryCount;
562
+ for (BigInteger p = batchStart; p < batchEnd;) {
563
+ // Brute-force check if the sequential number is a factor.
564
+ const BigInteger n = forwardFn(p);
565
+ // If so, terminate this node and return the answer.
566
+ if (!(toFactor % n) && (n != 1U) && (n != toFactor)) {
567
+ isIncomplete = false;
568
+ return n;
569
+ }
570
+ // Use the "exhaust" to produce smoother numbers.
571
+ semiSmoothParts->push_back(n);
572
+ // Skip increments on the "wheels" (or "gears").
573
+ p += GetWheelIncrement(inc_seqs);
574
+ }
575
+
576
+ // Batch this work, to reduce contention.
577
+ if (semiSmoothParts->size() >= smoothPartsLimit) {
578
+ makeSmoothNumbers(semiSmoothParts, isGaussElim);
579
+
580
+ return 1U;
581
+ }
582
+ }
583
+
584
+ return 1U;
585
+ }
586
+
587
+ // Compute the prime factorization modulo 2
588
+ boost::dynamic_bitset<size_t> factorizationVector(BigInteger num) {
589
+ boost::dynamic_bitset<size_t> vec(primes.size(), 0);
590
+ while (true) {
591
+ BigInteger factor = gcd(num, wheelRadius);
592
+ if (factor == 1U) {
593
+ break;
594
+ }
595
+ num /= factor;
596
+ // Remove smooth primes from factor
597
+ for (size_t pi = 0U; pi < primes.size(); ++pi) {
598
+ const size_t& p = primes[pi];
599
+ if (factor % p) {
600
+ continue;
601
+ }
602
+ factor /= p;
603
+ vec.flip(pi);
604
+ if (factor == 1U) {
605
+ break;
606
+ }
607
+ }
608
+ if (num == 1U) {
609
+ return vec;
610
+ }
611
+ }
612
+ if (num != 1U) {
613
+ return boost::dynamic_bitset<size_t>();
614
+ }
615
+
616
+ return vec;
617
+ }
618
+
619
+ void makeSmoothNumbers(std::vector<BigInteger> *semiSmoothParts, bool isGaussElim) {
620
+ // Factorize all "smooth parts."
621
+ std::vector<BigInteger> smoothParts;
622
+ std::map<BigInteger, boost::dynamic_bitset<size_t>> smoothPartsMap;
623
+ for (const BigInteger &n : (*semiSmoothParts)) {
624
+ const boost::dynamic_bitset<size_t> fv = factorizationVector(n);
625
+ if (fv.size()) {
626
+ smoothPartsMap[n] = fv;
627
+ smoothParts.push_back(n);
628
+ }
629
+ }
630
+ // We can clear the thread's buffer vector.
631
+ semiSmoothParts->clear();
632
+
633
+ // This is the only nondeterminism in the algorithm.
634
+ std::shuffle(smoothParts.begin(), smoothParts.end(), rng);
635
+
636
+ const BigInteger limit = isGaussElim ? toFactor : toFactorSqrt;
637
+
638
+ // Now that smooth parts have been shuffled, just multiply down the list until they are larger than square root of toFactor.
639
+ BigInteger smoothNumber = 1U;
640
+ boost::dynamic_bitset<size_t> fv(primes.size(), 0);
641
+ for (size_t spi = 0U; spi < smoothParts.size(); ++spi) {
642
+ const BigInteger &sp = smoothParts[spi];
643
+ // This multiplies together the factorizations of the smooth parts
644
+ // (producing the overall factorization of their multiplication)
645
+ fv ^= smoothPartsMap[sp];
646
+ smoothNumber *= sp;
647
+ // Check if the number is big enough
648
+ if (smoothNumber <= limit) {
649
+ continue;
650
+ }
651
+ if (true) {
652
+ std::lock_guard<std::mutex> lock(smoothNumberMapMutex);
653
+ smoothNumberValues.emplace_back(fv);
654
+ smoothNumberKeys.push_back(smoothNumber);
655
+ }
656
+ // Reset "smoothNumber" and its factorization vector.
657
+ smoothNumber = 1U;
658
+ fv = boost::dynamic_bitset<size_t>(primes.size(), 0);
659
+ }
660
+ }
661
+
662
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
663
+ // WRITTEN WITH ELARA (GPT) BELOW //
664
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
665
+
666
+ // Perform Gaussian elimination on a binary matrix
667
+ void gaussianElimination() {
668
+ const unsigned cpuCount = CpuCount;
669
+ auto mColIt = smoothNumberValues.begin();
670
+ auto nColIt = smoothNumberKeys.begin();
671
+ const size_t rows = smoothNumberValues.size();
672
+ for (size_t col = 0U; col < primes.size(); ++col) {
673
+ auto mRowIt = mColIt;
674
+ auto nRowIt = nColIt;
675
+
676
+ int64_t pivot = -1;
677
+ for (size_t row = col; row < rows; ++row) {
678
+ if ((*mRowIt)[col]) {
679
+ // Swapping matrix rows corresponds
680
+ // with swapping factorized numbers.
681
+ if (row != col) {
682
+ std::swap(*mColIt, *mRowIt);
683
+ std::swap(*nColIt, *nRowIt);
684
+ }
685
+ pivot = row;
686
+ break;
687
+ }
688
+ ++nRowIt;
689
+ ++mRowIt;
690
+ }
691
+
692
+ if (pivot != -1) {
693
+ const boost::dynamic_bitset<size_t> &cm = *mColIt;
694
+ const BigInteger &cn = *nColIt;
695
+ mRowIt = smoothNumberValues.begin();
696
+ nRowIt = smoothNumberKeys.begin();
697
+ for (unsigned cpu = 0U; (cpu < CpuCount) && (cpu < rows); ++cpu) {
698
+ dispatch.dispatch([cpu, &cpuCount, &col, &rows, &cm, &cn, nRowIt, mRowIt]() -> bool {
699
+ auto mrIt = mRowIt;
700
+ auto nrIt = nRowIt;
701
+ for (size_t row = cpu; ; row += cpuCount) {
702
+ boost::dynamic_bitset<size_t> &rm = *mrIt;
703
+ BigInteger &rn = *nrIt;
704
+ if ((row != col) && rm[col]) {
705
+ // XOR-ing factorization rows
706
+ // is like multiplying the numbers.
707
+ rm ^= cm;
708
+ rn *= cn;
709
+ }
710
+ if ((row + cpuCount) >= rows) {
711
+ return false;
712
+ }
713
+ std::advance(nrIt, cpuCount);
714
+ std::advance(mrIt, cpuCount);
715
+ }
716
+
717
+ return false;
718
+ });
719
+ ++mRowIt;
720
+ ++nRowIt;
721
+ }
722
+ dispatch.finish();
723
+ }
724
+
725
+ ++mColIt;
726
+ ++nColIt;
727
+ }
728
+ }
729
+
730
+ BigInteger checkPerfectSquare(BigInteger perfectSquare) {
731
+ // Compute x and y
732
+ const BigInteger x = perfectSquare % toFactor;
733
+ const BigInteger y = modExp(x, toFactor >> 1U, toFactor);
734
+
735
+ // Check congruence of squares
736
+ BigInteger factor = gcd(toFactor, x + y);
737
+ if ((factor != 1U) && (factor != toFactor)) {
738
+ return factor;
739
+ }
740
+
741
+ if (x == y) {
742
+ return 1U;
743
+ }
744
+
745
+ // Try x - y as well
746
+ factor = gcd(toFactor, x - y);
747
+ if ((factor != 1U) && (factor != toFactor)) {
748
+ return factor;
749
+ }
750
+
751
+ return 1U;
752
+ }
753
+
754
+ // Find duplicate rows
755
+ BigInteger findDuplicateRows(const BigInteger &target) {
756
+ // Check for linear dependencies and find a congruence of squares
757
+ std::mutex rowMutex;
758
+ BigInteger result = 1U;
759
+ std::set<size_t> toStrike;
760
+ auto iIt = smoothNumberValues.begin();
761
+ const size_t rowCount = smoothNumberValues.size();
762
+ const size_t rowCountMin1 = rowCount - 1U;
763
+ for (size_t i = primes.size(); (i < rowCountMin1) && (result == 1U); ++i) {
764
+ dispatch.dispatch([this, &target, i, iIt, &rowCount, &result, &rowMutex, &toStrike]() -> bool {
765
+ boost::dynamic_bitset<size_t> &iRow = *iIt;
766
+ const BigInteger& iInt = this->smoothNumberKeys[i];
767
+
768
+ const size_t startJ = std::max(this->rowOffset, i + 1U);
769
+ auto jIt = this->smoothNumberValues.begin();
770
+ std::advance(jIt, (startJ - 1U));
771
+ for (size_t j = startJ; j < rowCount; ++j) {
772
+ ++jIt;
773
+
774
+ boost::dynamic_bitset<size_t> &jRow = *jIt;
775
+ if (iRow != jRow) {
776
+ continue;
777
+ }
778
+
779
+ const BigInteger& jInt = this->smoothNumberKeys[j];
780
+ if (iInt < jInt) {
781
+ std::lock_guard<std::mutex> lock(rowMutex);
782
+ toStrike.insert(j);
783
+ } else {
784
+ std::lock_guard<std::mutex> lock(rowMutex);
785
+ toStrike.insert(i);
786
+ }
787
+
788
+ const BigInteger factor = checkPerfectSquare(this->smoothNumberKeys[i]);
789
+ if ((factor != 1U) && (factor != target)) {
790
+ std::lock_guard<std::mutex> lock(rowMutex);
791
+ result = factor;
792
+
793
+ return true;
794
+ }
795
+ }
796
+
797
+ return false;
798
+ });
799
+ ++iIt;
800
+ }
801
+ dispatch.finish();
802
+
803
+ if (result != 1U) {
804
+ return result;
805
+ }
806
+
807
+ // These numbers have been tried already:
808
+ for (const size_t& i : toStrike) {
809
+ smoothNumberKeys.erase(smoothNumberKeys.begin() + i);
810
+ smoothNumberValues.erase(smoothNumberValues.begin() + i);
811
+ }
812
+
813
+ rowOffset = smoothNumberKeys.size();
814
+
815
+ return 1U; // No factor found
816
+ }
817
+
818
+ // Use Gaussian elimination
819
+ BigInteger findFactor(const BigInteger &target) {
820
+ // Gaussian elimination multiplies these numbers
821
+ // with small primes, to produce squares
822
+ gaussianElimination();
823
+
824
+ // Check for linear dependencies and find a congruence of squares
825
+ std::mutex rowMutex;
826
+ BigInteger result = 1U;
827
+ const size_t rowCount = smoothNumberKeys.size();
828
+ for (size_t i = primes.size(); (i < rowCount) && (result == 1U); ++i) {
829
+ dispatch.dispatch([this, &target, i, &result, &rowMutex]() -> bool {
830
+ const BigInteger factor = checkPerfectSquare(this->smoothNumberKeys[i]);
831
+
832
+ if ((factor != 1U) && (factor != target)) {
833
+ std::lock_guard<std::mutex> lock(rowMutex);
834
+ result = factor;
835
+
836
+ return true;
837
+ }
838
+
839
+ return false;
840
+ });
841
+ }
842
+ dispatch.finish();
843
+
844
+ if (result != 1U) {
845
+ return result;
846
+ }
847
+
848
+ // These numbers have been tried already:
849
+ smoothNumberKeys.resize(primes.size());
850
+ smoothNumberValues.resize(primes.size());
851
+
852
+ return 1U; // No factor found
853
+ }
854
+
855
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
856
+ // WRITTEN WITH ELARA (GPT) ABOVE //
857
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
858
+ };
859
+
860
+ std::string find_a_factor(const std::string &toFactorStr, const bool &isConOfSqr, const bool &isGaussElim, const size_t &nodeCount, const size_t &nodeId,
861
+ size_t trialDivisionLevel, size_t gearFactorizationLevel, size_t wheelFactorizationLevel, double smoothnessBoundMultiplier, double batchSizeMultiplier) {
862
+ // (At least) level 11 wheel factorization is baked into basic functions.
863
+ if (!wheelFactorizationLevel) {
864
+ wheelFactorizationLevel = 1U;
865
+ } else if (wheelFactorizationLevel > 11U) {
866
+ wheelFactorizationLevel = 11U;
867
+ std::cout << "Warning: Wheel factorization limit is 11. (Parameter will be ignored and default to 11.)" << std::endl;
868
+ }
869
+ if (!gearFactorizationLevel) {
870
+ gearFactorizationLevel = 1U;
871
+ } else if (gearFactorizationLevel < wheelFactorizationLevel) {
872
+ gearFactorizationLevel = wheelFactorizationLevel;
873
+ std::cout << "Warning: Gear factorization level must be at least as high as wheel level. (Parameter will be ignored and default to wheel level.)" << std::endl;
874
+ }
875
+
876
+ // Convert from string.
877
+ const BigInteger toFactor(toFactorStr);
878
+
879
+ // The largest possible discrete factor of "toFactor" is its square root (as with any integer).
880
+ const BigInteger fullMaxBase = sqrt(toFactor);
881
+ if (fullMaxBase * fullMaxBase == toFactor) {
882
+ return boost::lexical_cast<std::string>(fullMaxBase);
883
+ }
884
+
885
+ // We only need to try trial division about as high as would be necessary for 4096 bits of semiprime.
886
+ const size_t primeCeiling = (trialDivisionLevel < fullMaxBase) ? trialDivisionLevel : (size_t)fullMaxBase;
887
+ BigInteger result = 1U;
888
+ // This uses very little memory and time, to find primes.
889
+ std::vector<size_t> primes = SieveOfEratosthenes(primeCeiling);
890
+ // "it" is the end-of-list iterator for a list up-to-and-including wheelFactorizationLevel.
891
+ const auto itw = std::upper_bound(primes.begin(), primes.end(), wheelFactorizationLevel);
892
+ const auto itg = std::upper_bound(primes.begin(), primes.end(), gearFactorizationLevel);
893
+ const size_t wgDiff = std::distance(itw, itg);
894
+
895
+ // This is simply trial division up to the ceiling.
896
+ std::mutex trialDivisionMutex;
897
+ for (size_t primeIndex = 0U; (primeIndex < primes.size()) && (result == 1U); primeIndex += 64U) {
898
+ dispatch.dispatch([&toFactor, &primes, &result, &trialDivisionMutex, primeIndex]() -> bool {
899
+ const size_t maxLcv = std::min(primeIndex + 64U, primes.size());
900
+ for (size_t pi = primeIndex; pi < maxLcv; ++pi) {
901
+ const size_t& currentPrime = primes[pi];
902
+ if (!(toFactor % currentPrime)) {
903
+ std::lock_guard<std::mutex> lock(trialDivisionMutex);
904
+ result = currentPrime;
905
+ return true;
906
+ }
907
+ }
908
+ return false;
909
+ });
910
+ }
911
+ dispatch.finish();
912
+ // If we've checked all primes below the square root of toFactor, then it's prime.
913
+ if ((result != 1U) || (toFactor <= (primeCeiling * primeCeiling))) {
914
+ return boost::lexical_cast<std::string>(result);
915
+ }
916
+
917
+ // Set up wheel factorization (or "gear" factorization)
918
+ std::vector<size_t> gearFactorizationPrimes(primes.begin(), itg);
919
+ std::vector<size_t> wheelFactorizationPrimes(primes.begin(), itw);
920
+ // Keep as many "smooth" primes as bits in number to factor.
921
+ const size_t toFactorBits = (size_t)log2(toFactor);
922
+ size_t smoothPrimeCount = (size_t)(smoothnessBoundMultiplier * toFactorBits);
923
+ if (!smoothPrimeCount) {
924
+ smoothPrimeCount = 1U;
925
+ std::cout << "Warning: smoothness bound multiplier would retain no primes, but it must retain at least 1. (Defaulting to retaining 1 prime.)" << std::endl;
926
+ }
927
+ // Primes are only present in range above wheel factorization level
928
+ primes.erase(primes.begin(), itg);
929
+ const size_t maxPrimeCount = std::min(primes.size(), smoothPrimeCount);
930
+ std::vector<size_t> smoothPrimes;
931
+ for (size_t primeId = 0U; (primeId < primes.size()) && (smoothPrimes.size() < maxPrimeCount); ++primeId) {
932
+ const size_t p = primes[primeId];
933
+ const size_t residue = (size_t)(toFactor % p);
934
+ const size_t sr = _sqrt(residue);
935
+ if ((sr * sr) == residue) {
936
+ smoothPrimes.push_back(p);
937
+ }
938
+ }
939
+ if (isConOfSqr && (smoothPrimes.size() < maxPrimeCount)) {
940
+ std::cout << "Warning: Factor base truncated to " << smoothPrimes.size() << " factors. If you don't want to truncate, set the trial division level option higher." << std::endl;
941
+ }
942
+ // From 1, this is a period for wheel factorization
943
+ size_t biggestWheel = 1ULL;
944
+ for (const size_t &wp : gearFactorizationPrimes) {
945
+ biggestWheel *= (size_t)wp;
946
+ }
947
+ // Wheel entry count per largest "gear" scales our brute-force range.
948
+ size_t wheelEntryCount = 0U;
949
+ for (size_t i = 0U; i < biggestWheel; ++i) {
950
+ if (!isMultiple(i, wheelFactorizationPrimes)) {
951
+ ++wheelEntryCount;
952
+ }
953
+ }
954
+ wheelFactorizationPrimes.clear();
955
+ // These are "gears," for wheel factorization (with a "wheel" already in place up to 11).
956
+ std::vector<boost::dynamic_bitset<size_t>> inc_seqs = wheel_gen(gearFactorizationPrimes);
957
+ // We're done with the lowest primes.
958
+ const size_t MIN_RTD_LEVEL = gearFactorizationPrimes.size() - wgDiff;
959
+ const Wheel SMALLEST_WHEEL = wheelByPrimeCardinal(MIN_RTD_LEVEL);
960
+ // Skip multiples removed by wheel factorization.
961
+ inc_seqs.erase(inc_seqs.begin(), inc_seqs.end() - wgDiff);
962
+ gearFactorizationPrimes.clear();
963
+
964
+ // Range per parallel node
965
+ const BigInteger nodeRange = (((backward(SMALLEST_WHEEL)(fullMaxBase) + nodeCount - 1U) / nodeCount) + wheelEntryCount - 1U) / wheelEntryCount;
966
+ // This manages the work of all threads.
967
+ Factorizer worker(toFactor * toFactor, toFactor, fullMaxBase,
968
+ nodeRange, nodeCount, nodeId,
969
+ wheelEntryCount, (size_t)((wheelEntryCount << 1U) * batchSizeMultiplier),
970
+ smoothPrimes, forward(SMALLEST_WHEEL));
971
+
972
+ if (!isConOfSqr) {
973
+ const auto workerFn = [&inc_seqs, &worker] {
974
+ // inc_seq needs to be independent per thread.
975
+ std::vector<boost::dynamic_bitset<size_t>> inc_seqs_clone;
976
+ inc_seqs_clone.reserve(inc_seqs.size());
977
+ for (const boost::dynamic_bitset<size_t> &b : inc_seqs) {
978
+ inc_seqs_clone.emplace_back(b);
979
+ }
980
+
981
+ // "Brute force" includes extensive wheel multiplication and can be faster.
982
+ return worker.bruteForce(&inc_seqs_clone);
983
+ };
984
+
985
+ std::vector<std::future<BigInteger>> futures;
986
+ futures.reserve(CpuCount);
987
+
988
+ for (unsigned cpu = 0U; cpu < CpuCount; ++cpu) {
989
+ futures.push_back(std::async(std::launch::async, workerFn));
990
+ }
991
+
992
+ for (unsigned cpu = 0U; cpu < futures.size(); ++cpu) {
993
+ const BigInteger r = futures[cpu].get();
994
+ if ((r > result) && (r != toFactor)) {
995
+ result = r;
996
+ }
997
+ }
998
+
999
+ return boost::lexical_cast<std::string>(result);
1000
+ }
1001
+
1002
+ const auto smoothNumberFn = [&inc_seqs, &wheelEntryCount, &batchSizeMultiplier, &worker, &isGaussElim] {
1003
+ // inc_seq needs to be independent per thread.
1004
+ std::vector<boost::dynamic_bitset<size_t>> inc_seqs_clone;
1005
+ inc_seqs_clone.reserve(inc_seqs.size());
1006
+ for (const boost::dynamic_bitset<size_t> &b : inc_seqs) {
1007
+ inc_seqs_clone.emplace_back(b);
1008
+ }
1009
+
1010
+ // Different collections per thread;
1011
+ std::vector<BigInteger> semiSmoothParts;
1012
+ semiSmoothParts.reserve((size_t)((wheelEntryCount << 1U) * batchSizeMultiplier));
1013
+
1014
+ // While brute-forcing, use the "exhaust" to feed "smooth" number generation and check conguence of squares.
1015
+ return worker.smoothCongruences(&inc_seqs_clone, &semiSmoothParts, isGaussElim);
1016
+ };
1017
+
1018
+ std::vector<std::future<BigInteger>> futures;
1019
+ futures.reserve(CpuCount);
1020
+
1021
+ do {
1022
+ for (unsigned cpu = 0U; cpu < CpuCount; ++cpu) {
1023
+ futures.push_back(std::async(std::launch::async, smoothNumberFn));
1024
+ }
1025
+
1026
+ for (unsigned cpu = 0U; cpu < futures.size(); ++cpu) {
1027
+ const BigInteger r = futures[cpu].get();
1028
+ if ((r > result) && (r != toFactor)) {
1029
+ result = r;
1030
+ }
1031
+ }
1032
+
1033
+ if ((result != 1U) && (result != toFactor)) {
1034
+ return boost::lexical_cast<std::string>(result);
1035
+ }
1036
+
1037
+ futures.clear();
1038
+
1039
+ // This next section is for (Quadratic Sieve) Gaussian elimination.
1040
+ result = isGaussElim ? worker.findFactor(toFactor) : worker.findDuplicateRows(toFactor);
1041
+ } while ((result == 1U) || (result == toFactor));
1042
+
1043
+ return boost::lexical_cast<std::string>(result);
1044
+ }
1045
+ } // namespace Qimcifa
1046
+
1047
+ using namespace Qimcifa;
1048
+
1049
+ PYBIND11_MODULE(_find_a_factor, m) {
1050
+ m.doc() = "pybind11 plugin to find any factor of input";
1051
+ m.def("_find_a_factor", &find_a_factor, "Finds any nontrivial factor of input (or returns 1 if prime)");
1052
+ }