Skip to content

Commit f078611

Browse files
committed
Removed support or 64-bit large sieve. Was just too big.
1 parent f7cef19 commit f078611

File tree

5 files changed

+90
-204
lines changed

5 files changed

+90
-204
lines changed

demo/test.c

+14-37
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ static int test_mp_prime_rand(void)
811811

812812
/* test for size */
813813
for (ix = 10; ix < 128; ix++) {
814-
printf("Testing (not safe-prime): %9d bits \n", ix);
814+
printf("\rTesting (not safe-prime): %9d bits ", ix);
815815
fflush(stdout);
816816
DO(mp_prime_rand(&a, 8, ix, (rand_int() & 1) ? 0 : MP_PRIME_2MSB_ON));
817817
EXPECT(mp_count_bits(&a) == ix);
@@ -1623,31 +1623,13 @@ static int test_mp_next_small_prime(void)
16231623
1019879761, 72282701, 2048787577, 2058368113
16241624
};
16251625

1626-
/* TODO: No, remove 64-bit version, way too big and a bit unwieldy, too! */
1627-
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
1628-
/* primesum up to 2^64
1629-
$ time /home/czurnieden/GITHUB/primesum/primesum 2^64
1630-
3879578600671960388666457126750869198
1631-
1632-
real 37m6,448s
1633-
user 107m17,056s
1634-
sys 0m12,152s
1635-
I think we should use something smaller.
1636-
*/
1637-
/* const char *primesum_64 = "3879578600671960388666457126750869198"; */
1638-
1639-
const char *primesum_64 = "208139436659661";
1640-
#else
16411626
#ifdef BENCHMARK_SIEVE
16421627
const char *primesum_32 = "425649736193687430";
16431628
#else
16441629
const char *primesum_32 = "202259606268580";
16451630
#endif
1646-
#endif
1647-
16481631
mp_sieve_init(&sieve);
16491632

1650-
16511633
test_size = (int)(sizeof(to_test)/sizeof(mp_sieve_prime));
16521634

16531635
for (i = 0; i < test_size; i++) {
@@ -1666,7 +1648,7 @@ static int test_mp_next_small_prime(void)
16661648
}
16671649
#ifdef BENCHMARK_SIEVE
16681650
stopgt = gettime();
1669-
printf("Single random access for prime: %*u: ", 10, ret);
1651+
printf("Single random access for prime: %*"MP_SIEVE_PR_UINT": ", 10, ret);
16701652
print_timer("",startgt, stopgt);
16711653
#endif
16721654
}
@@ -1675,37 +1657,32 @@ static int test_mp_next_small_prime(void)
16751657
#ifdef BENCHMARK_SIEVE
16761658
startgt = gettime();
16771659
#endif
1678-
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
1679-
for (p = 4293918720lu; ret < (mp_sieve_prime)4294997297lu;) {
1680-
DO(mp_next_small_prime(p, &ret, &sieve));
1681-
p = ret + 1;
1682-
mp_set_u64(&t, ret);
1683-
DO(mp_add(&primesum, &t, &primesum));
1684-
}
1685-
#else
1660+
16861661
#ifdef BENCHMARK_SIEVE
16871662
for (p = 0ul; ret < (mp_sieve_prime)MP_SIEVE_BIGGEST_PRIME;) {
16881663
#else
16891664
for (p = 4293918720lu; ret < (mp_sieve_prime)MP_SIEVE_BIGGEST_PRIME;) {
16901665
#endif
16911666
DO(mp_next_small_prime(p, &ret, &sieve));
1692-
p = ret + 1;
1667+
p = ret + 1u;
1668+
#ifdef MP_64BIT
1669+
mp_set_u64(&t, ret);
1670+
#else
16931671
mp_set_u32(&t, ret);
1672+
#endif
16941673
DO(mp_add(&primesum, &t, &primesum));
16951674
}
1696-
#endif
1675+
16971676
#ifdef BENCHMARK_SIEVE
16981677
stopgt = gettime();
1699-
printf("Sum of all primes between 0 and %u = ", MP_SIEVE_BIGGEST_PRIME);
1678+
printf("Sum of all primes between 0 and %"MP_SIEVE_PR_UINT" = ", (mp_sieve_prime)MP_SIEVE_BIGGEST_PRIME);
17001679
DO(mp_fwrite(&primesum, 10, stdout));
1701-
print_timer(", computed in ",startgt, stopgt);
1680+
print_timer(", computed in \n",startgt, stopgt);
17021681
#endif
1703-
1704-
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
1705-
DO(mp_read_radix(&t, primesum_64, 10));
1706-
#else
1682+
printf("Primesum computed: ");
1683+
DO(mp_fwrite(&primesum, 10, stdout));
1684+
puts("");
17071685
DO(mp_read_radix(&t, primesum_32, 10));
1708-
#endif
17091686
EXPECT(mp_cmp(&primesum, &t) == MP_EQ);
17101687

17111688
mp_sieve_clear(&sieve);

doc/bn.tex

+43-128
Original file line numberDiff line numberDiff line change
@@ -2349,9 +2349,44 @@ \section{Random Primes}
23492349

23502350

23512351
\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.
23552390

23562391
\subsection{Initialization and Clearing}
23572392
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}
24022437

24032438
\subsection{Useful Constants}
24042439
\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}.
24092442

24102443
\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}..
24192446

24202447
\item[\texttt{MP\_SIEVE\_PR\_UINT}] Choses the correct macro from \texttt{inttypes.h} to print a\\
24212448
\texttt{mp\_sieve\_prime}. The header \texttt{inttypes.h} must be included before\\
@@ -2453,148 +2480,36 @@ \subsubsection{Primality Test}
24532480
int main(int argc, char **argv)
24542481
{
24552482
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;
24592484
int e;
2460-
24612485
/* variable holding the result of mp_is_small_prime */
24622486
mp_sieve_prime result;
24632487
24642488
if (argc != 2) {
24652489
fprintf(stderr,"Usage %s number\textbackslash{}n", argv[0]);
24662490
exit(EXIT_FAILURE);
24672491
}
2468-
24692492
number = (mp_sieve_prime) strtoul(argv[1],NULL, 10);
24702493
if (errno == ERANGE) {
24712494
fprintf(stderr,"strtoul(l) failed: input out of range\textbackslash{}n");
24722495
goto LTM_ERR;
24732496
}
2474-
24752497
mp_sieve_init(&sieve);
2476-
24772498
if ((e = mp_is_small_prime(number, &result, &sieve)) != MP_OKAY) {
24782499
fprintf(stderr,"mp_is_small_prime failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
24792500
mp_error_to_string(e));
24802501
goto LTM_ERR;
24812502
}
2482-
24832503
printf("The number %" LTM_SIEVE_PR_UINT " is %s prime\textbackslash{}n",
24842504
number,(result)?"":"not");
24852505
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);
25892506
mp_sieve_clear(&sieve);
25902507
exit(EXIT_SUCCESS);
25912508
LTM_ERR:
2592-
mp_clear_multi(&total, &t, NULL);
25932509
mp_sieve_clear(&sieve);
25942510
exit(EXIT_FAILURE);
25952511
}
25962512
\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).
25982513

25992514
\chapter{Random Number Generation}
26002515
\section{PRNG}

0 commit comments

Comments
 (0)