File Coverage

semi_primes.c
Criterion Covered Total %
statement 182 204 89.2
branch 203 282 71.9
condition n/a
subroutine n/a
pod n/a
total 385 486 79.2


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4              
5             #include "ptypes.h"
6             #include "constants.h"
7             #define FUNC_isqrt 1
8             #define FUNC_icbrt 1
9             #define FUNC_ctz 1
10             #include "util.h"
11             #include "cache.h"
12             #include "sieve.h"
13             #include "lmo.h"
14             #include "semi_primes.h"
15              
16             #define SP_SIEVE_THRESH 100 /* When to sieve vs. iterate */
17              
18             /******************************************************************************/
19             /* SEMI PRIMES */
20             /******************************************************************************/
21              
22             static const unsigned char _semiprimelist[] =
23             {0,4,6,9,10,14,15,21,22,25,26,33,34,35,38,39,46,49,51,55,57,58,62,65,69,74,
24             77,82,85,86,87,91,93,94,95,106,111,115,118,119,121,122,123,129,133,134,141,
25             142,143,145,146,155,158,159,161,166,169,177,178,183,185,187,194,201,202,
26             203,205,206,209,213,214,215,217,218,219,221,226,235,237,247,249,253,254};
27             #define NSEMIPRIMELIST (sizeof(_semiprimelist)/sizeof(_semiprimelist[0]))
28              
29 113827           static UV _bs_count(UV n, UV const* const primes, UV lastidx)
30             {
31 113827           UV i = 0, j = lastidx; /* primes may not start at 0 */
32 113827 50         MPUassert(n >= primes[0] && n < primes[lastidx], "prime count via binary search out of range");
    50          
33 2464534 100         while (i < j) {
34 2350707           UV mid = i + (j-i)/2;
35 2350707 100         if (primes[mid] <= n) i = mid+1;
36 1502084           else j = mid;
37             }
38 113827           return i-1;
39             }
40              
41 2570           static UV _semiprime_count(UV n)
42             {
43 2570           UV pc = 0, sum = 0, sqrtn = prev_prime(isqrt(n)+1);
44 2570           UV xbeg = 0, xend = 0, xlim = 0, xoff = 0, xsize, *xarr = 0;
45 2570           UV const xmax = 200000000UL;
46              
47 2570 100         if (n > 1000000) { /* Upfront work to speed up the many small calls */
48 12           UV nprecalc = (UV) pow(n, .75);
49 12 100         if (nprecalc > _MPU_LMO_CROSSOVER) nprecalc = _MPU_LMO_CROSSOVER;
50 12           prime_precalc(nprecalc);
51             /* Make small calls even faster using binary search on a list */
52 12           xlim = (UV) pow(n, 0.70);
53             }
54              
55 2570 50         if (sqrtn >= 2) sum += LMO_prime_count(n/2) - pc++;
56 2570 50         if (sqrtn >= 3) sum += LMO_prime_count(n/3) - pc++;
57 2570 50         if (sqrtn >= 5) sum += LMO_prime_count(n/5) - pc++;
58 2570 50         if (sqrtn >= 7) {
59             unsigned char* segment;
60             UV seg_base, seg_low, seg_high, np, cnt;
61 2570           void* ctx = start_segment_primes(7, sqrtn, &segment);
62 5140 100         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
63 166471 50         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    100          
    50          
    100          
    100          
64 155985           np = n/p;
65 155985 100         if (np < xlim) {
66 113827 100         if (xarr == 0 || np < xbeg) {
    50          
67 12 50         if (xarr != 0) { Safefree(xarr); xarr = 0; }
68 12           xend = np;
69 12           xbeg = n/sqrtn;
70 12 50         if (xend - xbeg > xmax) xbeg = xend - xmax;
71 12           xbeg = prev_prime(xbeg);
72 12           xend = next_prime(xend);
73 12           xoff = LMO_prime_count(xbeg);
74 12           xarr = array_of_primes_in_range(&xsize, xbeg, xend);
75 12           xend = xarr[xsize-1];
76             }
77 113827           cnt = xoff + _bs_count(np, xarr, xsize-1);
78             } else {
79 42158           cnt = LMO_prime_count(np);
80             }
81 155985           sum += cnt - pc++;
82 7916           END_DO_FOR_EACH_SIEVE_PRIME
83             }
84 2570 100         if (xarr != 0) { Safefree(xarr); xarr = 0; }
85 2570           end_segment_primes(ctx);
86             }
87 2570           return sum;
88             }
89              
90             /* TODO: This overflows, see p=3037000507,lo=10739422018595509581.
91             * p2 = 9223372079518257049 => 9223372079518257049 + 9223372079518257049
92             * Also with lo=18446744073709551215,hi=18446744073709551515.
93             */
94             #define PGTLO(ip,p,lo) ((ip)>=(lo)) ? (ip) : ((p)*((lo)/(p)) + (((lo)%(p))?(p):0))
95             #define MARKSEMI(p,arr,lo,hi) \
96             do { UV i, p2=(p)*(p); \
97             for (i = PGTLO(p2, p, lo); i >= lo && i <= hi; i += p) arr[i-lo]++; \
98             for (i = PGTLO(2*p2, p2, lo); i >= lo && i <= hi; i += p2) arr[i-lo]++; \
99             } while (0);
100              
101 27           UV range_semiprime_sieve(UV** semis, UV lo, UV hi)
102             {
103 27           UV *S, i, count = 0;
104              
105 27 50         if (lo < 4) lo = 4;
106 27 50         if (hi > MPU_MAX_SEMI_PRIME) hi = MPU_MAX_SEMI_PRIME;
107              
108 27 100         if (hi <= _semiprimelist[NSEMIPRIMELIST-1]) {
109 16 100         if (semis == 0) {
110 11 50         for (i = 1; i < NSEMIPRIMELIST && _semiprimelist[i] <= hi; i++)
    100          
111 10 100         if (_semiprimelist[i] >= lo)
112 6           count++;
113             } else {
114 15           Newz(0, S, NSEMIPRIMELIST+1, UV);
115 123 50         for (i = 1; i < NSEMIPRIMELIST && _semiprimelist[i] <= hi; i++)
    100          
116 108 100         if (_semiprimelist[i] >= lo)
117 58           S[count++] = _semiprimelist[i];
118 16           *semis = S;
119             }
120             } else {
121             unsigned char* nfacs;
122 11           UV cutn, sqrtn = isqrt(hi);
123 11           Newz(0, nfacs, hi-lo+1, unsigned char);
124 11 50         if (sqrtn*sqrtn < hi && sqrtn < (UVCONST(1)<<(BITS_PER_WORD/2))-1) sqrtn++;
    50          
125 11           cutn = (sqrtn > 30000) ? 30000 : sqrtn;
126 15659 50         START_DO_FOR_EACH_PRIME(2, cutn) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
127 853550 100         MARKSEMI(p,nfacs,lo,hi);
    100          
    50          
    100          
    100          
    100          
    50          
    100          
128 15635           } END_DO_FOR_EACH_PRIME
129 11 100         if (cutn < sqrtn) {
130             unsigned char* segment;
131             UV seg_base, seg_low, seg_high;
132 2           void* ctx = start_segment_primes(cutn, sqrtn, &segment);
133 4 100         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
134 25176 50         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    100          
    50          
    100          
    100          
135 58489 50         MARKSEMI(p,nfacs,lo,hi);
    50          
    50          
    100          
    100          
    50          
    50          
    50          
136 1155           END_DO_FOR_EACH_SIEVE_PRIME
137             }
138 2           end_segment_primes(ctx);
139             }
140 11 100         if (semis == 0) {
141 34589 100         for (i = lo; i <= hi; i++)
142 34588 100         if (nfacs[i-lo] == 1)
143 5802           count++;
144             } else {
145 10           UV cn = 50 + 1.01 * (semiprime_count_approx(hi) - semiprime_count_approx(lo));
146 10 50         New(0, S, cn, UV);
147 242996 100         for (i = lo; i <= hi; i++) {
148 242986 100         if (nfacs[i-lo] == 1) {
149 34981 50         if (count >= cn)
150 0 0         Renew(S, cn += 4000, UV);
151 34981           S[count++] = i;
152             }
153             }
154 10           *semis = S;
155             }
156 11           Safefree(nfacs);
157             }
158 27           return count;
159             }
160              
161 0           static UV _range_semiprime_count_iterate(UV lo, UV hi)
162             {
163 0           UV sum = 0;
164 0 0         for (; lo < hi; lo++) /* TODO: We should walk composites */
165 0 0         if (is_semiprime(lo))
166 0           sum++;
167 0 0         if (is_semiprime(hi))
168 0           sum++;
169 0           return sum;
170             }
171              
172             #if 0
173             static UV _range_semiprime_selection(UV** semis, UV lo, UV hi)
174             {
175             UV *S = 0, *pr, cn = 0, count = 0;
176             UV i, xsize, lim = hi/2 + 1000, sqrtn = isqrt(hi);
177              
178             if (lo < 4) lo = 4;
179             if (hi > MPU_MAX_SEMI_PRIME) hi = MPU_MAX_SEMI_PRIME;
180              
181             if (semis != 0) {
182             cn = 50 + 1.01 * (semiprime_count_approx(hi) - semiprime_count_approx(lo));
183             New(0, S, cn, UV);
184             }
185              
186             pr = array_of_primes_in_range(&xsize, 0, lim);
187              
188             for (i = 0; pr[i] <= sqrtn; i++) {
189             UV const pi = pr[i], jlo = (lo+pi-1)/pi, jhi = hi/pi;
190             UV skip, j = i;
191             if (pr[j] < jlo)
192             for (skip = 2048; skip > 0; skip >>= 1)
193             while (j+skip-1 < xsize && pr[j+skip-1] < jlo)
194             j += skip;
195             if (semis == 0) {
196             while (pr[j++] <= jhi)
197             count++;
198             } else {
199             for (; pr[j] <= jhi; j++) {
200             if (count >= cn)
201             Renew(S, cn += 4000, UV);
202             S[count++] = pi * pr[j];
203             }
204             }
205             }
206             Safefree(pr);
207             if (semis != 0) {
208             qsort(S, count, sizeof(UV), _numcmp);
209             *semis = S;
210             }
211             return count;
212             }
213             #endif
214              
215              
216              
217 16           UV semiprime_count(UV lo, UV hi)
218             {
219 16 50         if (lo > hi || hi < 4)
    50          
220 0           return 0;
221              
222             /* tiny sizes fastest with the sieving code */
223 16 100         if (hi <= 400) return range_semiprime_sieve(0, lo, hi);
224             /* Large sizes best with the prime count method */
225 15 100         if (lo <= 4) return _semiprime_count(hi);
226              
227             /* Now it gets interesting. lo > 4, hi > 400. */
228              
229 1 50         if ((hi-lo+1) < hi / (isqrt(hi)*200)) {
230 0 0         MPUverbose(2, "semiprimes %"UVuf"-%"UVuf" via iteration\n", lo, hi);
231 0           return _range_semiprime_count_iterate(lo,hi);
232             }
233             /* TODO: Determine when _range_semiprime_selection(0,lo,hi) is better */
234 1 50         if ((hi-lo+1) < hi / (isqrt(hi)/4)) {
235 1 50         MPUverbose(2, "semiprimes %"UVuf"-%"UVuf" via sieving\n", lo, hi);
236 1           return range_semiprime_sieve(0, lo, hi);
237             }
238 0 0         MPUverbose(2, "semiprimes %"UVuf"-%"UVuf" via prime count\n", lo, hi);
239 0           return _semiprime_count(hi) - _semiprime_count(lo-1);
240             }
241              
242 24           UV semiprime_count_approx(UV n) {
243 24 100         if (n <= _semiprimelist[NSEMIPRIMELIST-1]) {
244 1           UV i = 0;
245 2 50         while (i < NSEMIPRIMELIST-1 && n >= _semiprimelist[i+1])
    100          
246 1           i++;
247 1           return i;
248             } else {
249             UV lo, hi;
250 23           double init, logn = log(n), loglogn = log(logn);
251             /* init = n * loglogn / logn; */
252             /* init = (n/logn) * (0.11147910114 + 0.00223801350*logn + 0.44233207922*loglogn + 1.65236647896*log(loglogn)); */
253 23           init = n * (loglogn + 0.302) / logn;
254 23 50         if (1.05*init >= (double)UV_MAX)
255 0           return init;
256              
257 23           lo = 0.90 * init - 5, hi = 1.05 * init;
258 563 100         while (lo < hi) {
259 540           UV mid = lo + (hi-lo)/2;
260 540 100         if (nth_semiprime_approx(mid) < n) lo = mid+1;
261 259           else hi = mid;
262             }
263 23           return lo;
264             }
265             }
266              
267 3115           UV nth_semiprime_approx(UV n) {
268             double logn,log2n,log3n,log4n, err_lo, err_md, err_hi, err_factor, est;
269              
270 3115 50         if (n < NSEMIPRIMELIST)
271 0           return _semiprimelist[n];
272              
273             /* Piecewise with blending. Hacky and maybe overkill, but it makes
274             * a big performance difference, especially at the high end.
275             * Interp Range Crossover to next
276             * lo 2^8 - 2^28 2^26 - 2^27
277             * md 2^25 - 2^48 2^46 - 2^47
278             * hi 2^45 - 2^64
279             */
280 3115           logn = log(n); log2n = log(logn); log3n = log(log2n); log4n=log(log3n);
281 3115           err_lo = 1.000 - 0.00018216088*logn + 0.18099609886*log2n - 0.51962474356*log3n - 0.01136143381*log4n;
282 3115           err_md = 0.968 - 0.00073297945*logn + 0.09731690314*log2n - 0.25212500749*log3n - 0.01366795346*log4n;
283 3115           err_hi = 0.968 - 0.00008034109*logn + 0.01522628393*log2n - 0.04020257367*log3n - 0.01266447175*log4n;
284              
285 3115 100         if (n <= (1UL<<26)) {
286 2807           err_factor = err_lo;
287 308 100         } else if (n < (1UL<<27)) { /* Linear interpolate the two in the blend area */
288 51           double x = (n - 67108864.0L) / 67108864.0L;
289 51           err_factor = ((1.0L-x) * err_lo) + (x * err_md);
290 257 100         } else if (logn <= 31.88477030575) {
291 198           err_factor = err_md;
292 59 50         } else if (logn < 32.57791748632) {
293 0           double x = (n - 70368744177664.0L) / 70368744177664.0L;
294 0           err_factor = ((1.0L-x) * err_md) + (x * err_hi);
295             } else {
296 59           err_factor = err_hi;
297             }
298 3115           est = 0.5 + err_factor * n * logn / log2n;
299 3115 50         if (est >= UV_MAX) return 0;
300 3115           return (UV)est;
301             }
302              
303 578           static UV _next_semiprime(UV n) {
304 2135 100         while (!is_semiprime(++n))
305             ;
306 578           return n;
307             }
308 14233           static UV _prev_semiprime(UV n) {
309 57943 100         while (!is_semiprime(--n))
310             ;
311 14233           return n;
312             }
313 2671           UV nth_semiprime(UV n)
314             {
315 2671           UV guess, spcnt, sptol, gn, ming = 0, maxg = UV_MAX;
316              
317 2671 100         if (n < NSEMIPRIMELIST)
318 116           return _semiprimelist[n];
319              
320 2555           guess = nth_semiprime_approx(n); /* Initial guess */
321 2555           sptol = 16*icbrt(n); /* Guess until within this many SPs */
322 2555 50         MPUverbose(2, " using exact counts until within %"UVuf"\n",sptol);
323              
324             /* Make successive interpolations until small enough difference */
325 2556 50         for (gn = 2; gn < 20; gn++) {
326             IV adjust;
327 9059 100         while (!is_semiprime(guess)) guess++; /* Guess is a semiprime */
328 2556 50         MPUverbose(2, " %"UVuf"-th semiprime is around %"UVuf" ... ", n, guess);
329             /* Compute exact count at our nth-semiprime guess */
330 2556           spcnt = _semiprime_count(guess);
331 2556 50         MPUverbose(2, "(%"IVdf")\n", (IV)(n-spcnt));
332             /* Stop guessing if within our tolerance */
333 2556 100         if (n==spcnt || (n>spcnt && n-spcnt < sptol) || (n
    100          
    100          
    100          
    50          
334             /* Determine how far off we think we are */
335 1           adjust = (IV) (nth_semiprime_approx(n) - nth_semiprime_approx(spcnt));
336             /* When computing new guess, ensure we don't overshoot. Rarely used. */
337 1 50         if (spcnt <= n && guess > ming) ming = guess; /* Previous guesses */
    50          
338 1 50         if (spcnt >= n && guess < maxg) maxg = guess;
    0          
339 1           guess += adjust;
340 1 50         if (guess <= ming || guess >= maxg) MPUverbose(2, " fix min/max for %"UVuf"\n",n);
    50          
    0          
341 1 50         if (guess <= ming) guess = ming + sptol - 1;
342 1 50         if (guess >= maxg) guess = maxg - sptol + 1;
343             }
344              
345             /* If we have far enough to go, sieve for semiprimes */
346 2557 100         if (n > spcnt && (n-spcnt) > SP_SIEVE_THRESH) { /* sieve forwards */
    100          
347             UV *S, count, i, range;
348 4 100         while (n > spcnt) {
349 2           range = nth_semiprime_approx(n) - nth_semiprime_approx(spcnt);
350 2           range = 1.10 * range + 100;
351 2 50         if (range > guess) range = guess; /* just in case */
352 2 50         if (range > 125000000) range = 125000000; /* Not too many at a time */
353             /* Get a bunch of semiprimes */
354 2 50         MPUverbose(2, " sieving forward %"UVuf"\n", range);
355 2           count = range_semiprime_sieve(&S, guess+1, guess+range);
356 2 50         if (spcnt+count <= n) {
357 0           guess = S[count-1];
358 0           spcnt += count;
359             } else { /* Walk forwards */
360 21087 50         for (i = 0; i < count && spcnt < n; i++) {
    100          
361 21085           guess = S[i];
362 21085           spcnt++;
363             }
364             }
365 2           Safefree(S);
366             }
367 2553 100         } else if (n < spcnt && (spcnt-n) > SP_SIEVE_THRESH) { /* sieve backwards */
    100          
368             UV *S, count, range;
369 10 100         while (n < spcnt) {
370 5           range = nth_semiprime_approx(spcnt) - nth_semiprime_approx(n);
371 5           range = 1.10 * range + 100;
372 5 50         if (range > guess) range = guess; /* just in case */
373 5 50         if (range > 125000000) range = 125000000; /* Not too many at a time */
374             /* Get a bunch of semiprimes */
375 5 50         MPUverbose(2, " sieving backward %"UVuf"\n", range);
376 5           count = range_semiprime_sieve(&S, guess-range, guess-1);
377 5 50         if (spcnt-count >= n) {
378 0           guess = S[0];
379 0           spcnt -= count;
380             } else { /* Walk backwards */
381 10260 50         while (count > 0 && n < spcnt) {
    100          
382 10255           guess = S[--count];
383 10255           spcnt--;
384             }
385             }
386 5           Safefree(S);
387             }
388             }
389              
390             /* Finally, iterate over semiprimes until we hit the exact spot */
391 16788 100         for (; spcnt > n; spcnt--)
392 14233           guess = _prev_semiprime(guess);
393 3133 100         for (; spcnt < n; spcnt++)
394 578           guess = _next_semiprime(guess);
395 2555           return guess;
396             }