@@ -2349,9 +2349,44 @@ \section{Random Primes}
2349
2349
2350
2350
2351
2351
\section {Prime Sieve }
2352
- The prime sieve is implemented as a simple segmented Sieve of Eratosthenes. It is only moderately optimized for
2353
- space and runtime but should be small enough and also fast enough for almost all use-cases; quite quick for
2354
- sequential access but relatively slow for random access.
2352
+ The prime sieve is implemented as a simple segmented Sieve of Eratosthenes.
2353
+
2354
+ This library needs some small sequential amounts for divisibility tests starting at two and
2355
+ some random small primes for the Miller--Rabin test. That means we need a small base sieve
2356
+ for quick results with a cold start and small segments to get random small primes fast.
2357
+
2358
+ The base sieve holds odd values only and even with a size of \texttt {4096 } bytes it is
2359
+ small enough to get build quickly, in about 50 $ \mu $ sec on the author's old machine.
2360
+
2361
+ The choice of the size for the individual segments varies more in the results. See table
2362
+ \ref {table:sievebenchmarks } for some data. Size must be of the form $ -1 + 2 ^n$ .
2363
+ Default size is \texttt {0xFFF }. It might be of interest that the biggest prime gap
2364
+ below $ 2 ^{32}$ is $ 335 $ .
2365
+
2366
+ \begin {table }[h]
2367
+ \begin {center }
2368
+ \begin {tabular }{r c l}
2369
+ \textbf {Segment Size (bits) } & \textbf {Random Access in $ \mu $ sec } & \textbf {Full Primesum } \\
2370
+ \texttt { 0x1F } & (60) & - \\
2371
+ \texttt { 0x3F } & (75) & - \\
2372
+ \texttt { 0x7F } & 63 & - \\
2373
+ \texttt { 0xFF } & 70 & 26 min \\
2374
+ \texttt { 0x1FF } & 73 & 13 min \\
2375
+ \texttt { 0x3FF } & 75 & 7 min 17 sec \\
2376
+ \texttt { 0x7FF } & 85 & 4 min 10 sec \\
2377
+ \texttt { 0xFFF } & 100 & 2 min 52 sec \\
2378
+ \texttt { 0x1FFF } & 120 & 1 min 45 sec \\
2379
+ \texttt { 0x3FFF } & 145 & 1 min 19 sec \\
2380
+ \texttt { 0x7FFF } & 200 & 1 min 04 sec \\
2381
+ \texttt { 0xFFFF } & 350 & 0 min 57 sec \\
2382
+ \texttt { 0xFFFFFFFF } & 300 & 0 min 35 sec
2383
+ \end {tabular }
2384
+ \caption {Average access times (warm) with the default base sieve (\texttt {MP-64BIT })} \label {table:sievebenchmarks }
2385
+ \end {center }
2386
+ \end {table }
2387
+
2388
+ The default sizes are in \texttt {tommath\_ private.h }: \texttt {MP\_ SIEVE\_ PRIME\_ MAX\_ SQRT } is
2389
+ the size of the base sieve and \texttt {MP\_ SIEVE\_ RANGE\_ A\_ B } is the size of the segment.
2355
2390
2356
2391
\subsection {Initialization and Clearing }
2357
2392
Initializing. It cannot fail because it only sets some default values. Memory is allocated later according to needs.
@@ -2402,20 +2437,12 @@ \subsection{Find Adjacent Primes}
2402
2437
2403
2438
\subsection {Useful Constants }
2404
2439
\begin {description }
2405
- \item [\texttt {MP\_ SIEVE\_ BIGGEST\_ PRIME }] \texttt {read-only } The biggest prime the sieve can offer. It is
2406
- $ 4 \, 294 \, 967 \, 291 $ for \texttt {MP\_ 16BIT }, \texttt {MP\_ 32BIT } and \texttt {MP\_ 64BIT }; and
2407
- $ 18 \, 446 \, 744 \, 073 \, 709 \, 551 \, 557 $ for \texttt {MP\_ 64BIT } if the macro\\
2408
- \texttt {LTM\_ SIEVE\_ USE\_ LARGE\_ SIEVE } is defined.
2440
+ \item [\texttt {MP\_ SIEVE\_ BIGGEST\_ PRIME }] \texttt {read-only } The biggest prime the sieve can offer.
2441
+ It is $ 4 \, 294 \, 967 \, 291 $ for all \texttt {MP\_ xBIT }.
2409
2442
2410
2443
\item [\texttt {mp\_ sieve\_ prime }] \texttt {read-only } The basic type for the numbers in the sieve. It
2411
- is \texttt {uint32\_ t } for \texttt {MP\_ 16BIT }, \texttt {MP\_ 32BIT } and \texttt {MP\_ 64BIT }; and
2412
- \texttt {uint64\_ t } for \texttt {MP\_ 64BIT } if the macro \texttt {LTM\_ SIEVE\_ USE\_ LARGE\_ SIEVE } is defined.
2413
-
2414
- \item [\texttt {LTM\_ SIEVE\_ USE\_ LARGE\_ SIEVE }] \texttt {read-only } A compile time flag to make a large sieve.
2415
- No advantage has been seen in using 64-bit integers if available except the ability to get a sieve up
2416
- to $ 2 ^64 $ . But in this case the base sieve gets 0.25 Gibibytes large and the segments 0.5 Gibibytes
2417
- (although you can change \texttt {LTM\_ SIEVE\_ RANGE\_ A\_ B } in \texttt {bn\_ mp\_ sieve.c } to get smaller segments)
2418
- and it needs a long time to fill.
2444
+ is \texttt {uint32\_ t } for \texttt {MP\_ 16BIT }, \texttt {MP\_ 32BIT }; and
2445
+ \texttt {uint64\_ t } for \texttt {MP\_ 64BIT }..
2419
2446
2420
2447
\item [\texttt {MP\_ SIEVE\_ PR\_ UINT }] Choses the correct macro from \texttt {inttypes.h } to print a\\
2421
2448
\texttt {mp\_ sieve\_ prime }. The header \texttt {inttypes.h } must be included before\\
@@ -2453,148 +2480,36 @@ \subsubsection{Primality Test}
2453
2480
int main(int argc, char **argv)
2454
2481
{
2455
2482
mp_sieve_prime number;
2456
- mp_sieve *base = NULL;
2457
- mp_sieve *segment = NULL;
2458
- mp_sieve_prime single_segment_a = 0;
2483
+ mp_sieve sieve;
2459
2484
int e;
2460
-
2461
2485
/* variable holding the result of mp_is_small_prime */
2462
2486
mp_sieve_prime result;
2463
2487
2464
2488
if (argc != 2) {
2465
2489
fprintf(stderr,"Usage %s number\textbackslash{}n", argv[0]);
2466
2490
exit(EXIT_FAILURE);
2467
2491
}
2468
-
2469
2492
number = (mp_sieve_prime) strtoul(argv[1],NULL, 10);
2470
2493
if (errno == ERANGE) {
2471
2494
fprintf(stderr,"strtoul(l) failed: input out of range\textbackslash{}n");
2472
2495
goto LTM_ERR;
2473
2496
}
2474
-
2475
2497
mp_sieve_init(&sieve);
2476
-
2477
2498
if ((e = mp_is_small_prime(number, &result, &sieve)) != MP_OKAY) {
2478
2499
fprintf(stderr,"mp_is_small_prime failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2479
2500
mp_error_to_string(e));
2480
2501
goto LTM_ERR;
2481
2502
}
2482
-
2483
2503
printf("The number %" LTM_SIEVE_PR_UINT " is %s prime\textbackslash{}n",
2484
2504
number,(result)?"":"not");
2485
2505
2486
-
2487
- mp_sieve_clear(&sieve);
2488
- exit(EXIT_SUCCESS);
2489
- LTM_ERR:
2490
- mp_sieve_clear(&sieve);
2491
- exit(EXIT_FAILURE);
2492
- }
2493
- \end {alltt }
2494
- \subsubsection {Find Adjacent Primes }
2495
- To sum up all primes up to and including \texttt {MP\_ SIEVE\_ BIGGEST\_ PRIME } you might do something like:
2496
- \begin {alltt }
2497
- #include <stdlib.h>
2498
- #include <stdio.h>
2499
- #include <errno.h>
2500
- #include <tommath.h>
2501
- int main(int argc, char **argv)
2502
- {
2503
- mp_sieve_prime number;
2504
- mp_sieve sieve;
2505
- mp_sieve_prime k, ret;
2506
- mp_int total, t;
2507
- int e;
2508
-
2509
- if (argc != 2) {
2510
- fprintf(stderr,"Usage %s integer\textbackslash{}n", argv[0]);
2511
- exit(EXIT_FAILURE);
2512
- }
2513
-
2514
- if ((e = mp_init_multi(&total, &t, NULL)) != MP_OKAY) {
2515
- fprintf(stderr,"mp_init_multi(segment): \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2516
- mp_error_to_string(e));
2517
- goto LTM_ERR_1;
2518
- }
2519
- errno = 0;
2520
- #if ( (defined MP_64BIT) && (defined LTM_SIEVE_USE_LARGE_SIEVE) )
2521
- number = (mp_sieve_prime) strtoull(argv[1],NULL, 10);
2522
- #else
2523
- number = (mp_sieve_prime) strtoul(argv[1],NULL, 10);
2524
- #endif
2525
- if (errno == ERANGE) {
2526
- fprintf(stderr,"strtoul(l) failed: input out of range\textbackslash{}n");
2527
- return EXIT_FAILURE
2528
- }
2529
-
2530
- mp_sieve_init(&sieve);
2531
-
2532
- for (k = 0, ret = 0; ret < number; k = ret) {
2533
- if ((e = mp_next_small_prime(k + 1, &ret, &sieve)) != MP_OKAY) {
2534
- if (e == LTM_SIEVE_MAX_REACHED) {
2535
- #ifdef MP_64BIT
2536
- if ((e = mp_add_d(&total, (mp_digit) k, &total)) != MP_OKAY) {
2537
- fprintf(stderr,"mp_add_d (1) failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2538
- mp_error_to_string(e));
2539
- goto LTM_ERR;
2540
- }
2541
- #else
2542
- if ((e = mp_set_long(&t, k)) != MP_OKAY) {
2543
- fprintf(stderr,"mp_set_long (1) failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2544
- mp_error_to_string(e));
2545
- goto LTM_ERR;
2546
- }
2547
- if ((e = mp_add(&total, &t, &total)) != MP_OKAY) {
2548
- fprintf(stderr,"mp_add (1) failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2549
- mp_error_to_string(e));
2550
- goto LTM_ERR;
2551
- }
2552
- #endif
2553
- break;
2554
- }
2555
- fprintf(stderr,"mp_next_small_prime failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2556
- mp_error_to_string(e));
2557
- goto LTM_ERR;
2558
- }
2559
- /* The check if the prime is below the given limit
2560
- * cannot be done in the for-loop conditions because if
2561
- * it could we wouldn't need the sieve in the first place.
2562
- */
2563
- if (ret <= number) {
2564
- #ifdef MP_64BIT
2565
- if ((e = mp_add_d(&total, (mp_digit) k, &total)) != MP_OKAY) {
2566
- fprintf(stderr,"mp_add_d failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2567
- mp_error_to_string(e));
2568
- goto LTM_ERR;
2569
- }
2570
- #else
2571
- if ((e = mp_set_long(&t, k)) != MP_OKAY) {
2572
- fprintf(stderr,"mp_set_long failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2573
- mp_error_to_string(e));
2574
- goto LTM_ERR;
2575
- }
2576
- if ((e = mp_add(&total, &t, &total)) != MP_OKAY) {
2577
- fprintf(stderr,"mp_add failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2578
- mp_error_to_string(e));
2579
- goto LTM_ERR;
2580
- }
2581
- #endif
2582
- }
2583
- }
2584
- printf("total: ");
2585
- mp_fwrite(&total,10,stdout);
2586
- putchar('\textbackslash{}n');
2587
-
2588
- mp_clear_multi(&total, &t, NULL);
2589
2506
mp_sieve_clear(&sieve);
2590
2507
exit(EXIT_SUCCESS);
2591
2508
LTM_ERR:
2592
- mp_clear_multi(&total, &t, NULL);
2593
2509
mp_sieve_clear(&sieve);
2594
2510
exit(EXIT_FAILURE);
2595
2511
}
2596
2512
\end {alltt }
2597
- It took about a minute on the authors machine from 2015 to get the expected $ 425 \, 649 \, 736 \, 193 \, 687 \, 430 $ for the sum of all primes up to $ 2 ^{32}$ , about the same runtime as Pari/GP version 2.9.4 (with a GMP-5.1.3 kernel).
2598
2513
2599
2514
\chapter {Random Number Generation }
2600
2515
\section {PRNG }
0 commit comments