Skip to content

Commit 85d2d71

Browse files
committed
Added documentation that went missing while rebasing
1 parent 5715bba commit 85d2d71

File tree

3 files changed

+277
-17
lines changed

3 files changed

+277
-17
lines changed

demo/test.c

+23-17
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ static int test_mp_is_small_prime(void)
15371537
mp_sieve_prime to_test[] = {
15381538
52, 137, 153, 179, 6, 153, 53, 132, 150, 65,
15391539
27414, 36339, 36155, 11067, 52060, 5741,
1540-
29755, 2698, 52572, 13053, 9375, 47241 ,39626,
1540+
29755, 2698, 52572, 13053, 9375, 47241,39626,
15411541
207423, 128857, 37419, 141696, 189465,
15421542
41503, 127370, 91673, 8473, 479142414, 465566339,
15431543
961126169, 1057886067, 1222702060, 1017450741,
@@ -1601,23 +1601,21 @@ static int test_mp_next_small_prime(void)
16011601
};
16021602

16031603
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
1604-
/* primesum up to 2^64
1604+
/* primesum up to 2^64
16051605
$ time /home/czurnieden/GITHUB/primesum/primesum 2^64
16061606
3879578600671960388666457126750869198
16071607
1608-
real 37m6,448s
1609-
user 107m17,056s
1610-
sys 0m12,152s
1608+
real 37m6,448s
1609+
user 107m17,056s
1610+
sys 0m12,152s
16111611
I think we should use something smaller.
16121612
*/
16131613
/* const char *primesum_64 = "3879578600671960388666457126750869198"; */
16141614

1615-
/* primesum up to 2^33. */
1616-
const char *primesum_64 = "1649816561794735645";
1615+
const char *primesum_64 = "208139436659661";
16171616
#else
1618-
/* TODO: Needs a minute here. Might cause a time-out in Github CI tests: check */
1619-
const char *primesum_32 = "425649736193687430";
1620-
#endif
1617+
const char *primesum_32 = "202259606268580";
1618+
#endif
16211619

16221620
mp_sieve_init(&sieve);
16231621

@@ -1639,25 +1637,33 @@ static int test_mp_next_small_prime(void)
16391637

16401638
DOR(mp_init_multi(&primesum, &t, NULL));
16411639
start = clock();
1642-
for (p = 0;ret < (mp_sieve_prime)MP_SIEVE_BIGGEST_PRIME;) {
1640+
/* Define -DMP_SIEVE_PRIME_MAX_SQRT=0x1ffffffllu to make it feasable */
1641+
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
1642+
for (p = 4293918720lu; ret < (mp_sieve_prime)4294997297lu;) {
16431643
DO(mp_next_small_prime(p, &ret, &sieve));
16441644
p = ret + 1;
1645-
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
16461645
mp_set_u64(&t, ret);
1646+
DO(mp_add(&primesum, &t, &primesum));
1647+
}
16471648
#else
1648-
mp_set_u32(&t, ret);
1649-
#endif
1650-
printf(" %"MP_SIEVE_PR_UINT"\n", ret);
1649+
/* We build the whole sieve instead of just the last segment. Too slow for valgrind */
1650+
for (p = 4293918720lu; ret < (mp_sieve_prime)MP_SIEVE_BIGGEST_PRIME;) {
1651+
DO(mp_next_small_prime(p, &ret, &sieve));
1652+
p = ret + 1;
1653+
mp_set_u32(&t, ret);
16511654
DO(mp_add(&primesum, &t, &primesum));
16521655
}
1656+
#endif
16531657
stop = clock();
1654-
printf("Time for primesum up to %"MP_SIEVE_PR_UINT" was %.2f seconds\n", MP_SIEVE_BIGGEST_PRIME, (double)(stop-start)/(double)CLOCKS_PER_SEC);
1658+
(void)mp_fwrite(&primesum, 10, stdout);puts("");
1659+
printf("Time for primesum was %.2f seconds\n",
1660+
(double)(stop-start)/(double)CLOCKS_PER_SEC);
16551661

16561662
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
16571663
DO(mp_read_radix(&t, primesum_64, 10));
16581664
#else
16591665
DO(mp_read_radix(&t, primesum_32, 10));
1660-
#endif
1666+
#endif
16611667
EXPECT(mp_cmp(&primesum, &t) == MP_EQ);
16621668

16631669
mp_sieve_clear(&sieve);

doc/bn.tex

+252
Original file line numberDiff line numberDiff line change
@@ -2347,6 +2347,255 @@ \section{Random Primes}
23472347
\label{fig:primeopts}
23482348
\end{figure}
23492349

2350+
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.
2355+
2356+
\subsection{Initialization and Clearing}
2357+
Initializing. It cannot fail because it only sets some default values. Memory is allocated later according to needs.
2358+
\index{mp\_sieve\_init}
2359+
\begin{alltt}
2360+
void mp_sieve_init(mp_sieve *sieve);
2361+
\end{alltt}
2362+
The function \texttt{mp\_sieve\_init} is equivalent to
2363+
\begin{alltt}
2364+
mp_sieve sieve = {NULL, NULL, 0};
2365+
\end{alltt}
2366+
2367+
Free the memory used by the sieve with
2368+
\index{mp\_sieve\_clear}
2369+
\begin{alltt}
2370+
void mp_sieve_clear(mp_sieve *sieve);
2371+
\end{alltt}
2372+
2373+
\subsection{Primality Test of Small Numbers}
2374+
Individual small numbers can be tested for primality with
2375+
\index{mp\_is\_small\_prime}
2376+
\begin{alltt}
2377+
int mp_is_small_prime(mp_sieve_prime n, mp_sieve_prime *result,
2378+
mp_sieve *sieve);
2379+
\end{alltt}
2380+
The implementation of this function does all the heavy lifting--the building of the base sieve and the segment
2381+
if one is necessary. The base sieve stays, so this function can be used to ``warm up'' the sieve and, if
2382+
\texttt{n} is slightly larger than the upper limit of the base sieve, ``warm up'' the first segment, too.
2383+
2384+
It will return \texttt{MP\_SIEVE\_MAX\_REACHED} to flag the content of \texttt{result} as the last valid one.
2385+
2386+
\subsection{Find Adjacent Primes}
2387+
To find the prime bigger than a number \texttt{n} use
2388+
\index{mp\_next\_small\_prime}
2389+
\begin{alltt}
2390+
int mp_next_small_prime(mp_sieve_prime n, mp_sieve_prime *result,
2391+
mp_sieve *sieve);
2392+
\end{alltt}
2393+
and to find the one smaller than \texttt{n}
2394+
\index{mp\_prec\_small\_prime}
2395+
\begin{alltt}
2396+
int mp_prec_small_prime(mp_sieve_prime n, mp_sieve_prime *result,
2397+
mp_sieve *sieve);
2398+
\end{alltt}
2399+
Both functions return \texttt{n} if \texttt{n} is prime. Use \texttt{n + 1} to get
2400+
the prime after \texttt{n} if \texttt{n} is prime and \texttt{n - 1} to get the
2401+
the prime preceding \texttt{n} if \texttt{n} is prime.
2402+
2403+
\subsection{Useful Constants}
2404+
\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.
2409+
2410+
\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.
2419+
2420+
\item[\texttt{MP\_SIEVE\_PR\_UINT}] Choses the correct macro from \texttt{inttypes.h} to print a\\
2421+
\texttt{mp\_sieve\_prime}. The header \texttt{inttypes.h} must be included before\\
2422+
\texttt{tommath.h} for it to work.
2423+
\end{description}
2424+
\subsection{Examples}\label{sec:spnexamples}
2425+
\subsubsection{Initialization and Clearing}
2426+
Using a sieve follows the same procedure as using a bigint:
2427+
\begin{alltt}
2428+
/* Declaration */
2429+
mp_sieve sieve;
2430+
2431+
/*
2432+
Initialization.
2433+
Cannot fail, only sets a handful of default values.
2434+
Memory allocation is done in the library itself
2435+
according to needs.
2436+
*/
2437+
mp_sieve_init(&sieve);
2438+
2439+
/* use the sieve */
2440+
2441+
/* Clean up */
2442+
mp_sieve_clear(&sieve);
2443+
\end{alltt}
2444+
\subsubsection{Primality Test}
2445+
A small program to test the input of a small number for primality.
2446+
\begin{alltt}
2447+
#include <stdlib.h>
2448+
#include <stdio.h>
2449+
#include <errno.h>
2450+
/*inttypes.h is needed for printing and must be included before tommath.h*/
2451+
#include <inttypes.h>
2452+
#include "tommath.h"
2453+
int main(int argc, char **argv)
2454+
{
2455+
mp_sieve_prime number;
2456+
mp_sieve *base = NULL;
2457+
mp_sieve *segment = NULL;
2458+
mp_sieve_prime single_segment_a = 0;
2459+
int e;
2460+
2461+
/* variable holding the result of mp_is_small_prime */
2462+
mp_sieve_prime result;
2463+
2464+
if (argc != 2) {
2465+
fprintf(stderr,"Usage %s number\textbackslash{}n", argv[0]);
2466+
exit(EXIT_FAILURE);
2467+
}
2468+
2469+
number = (mp_sieve_prime) strtoul(argv[1],NULL, 10);
2470+
if (errno == ERANGE) {
2471+
fprintf(stderr,"strtoul(l) failed: input out of range\textbackslash{}n");
2472+
goto LTM_ERR;
2473+
}
2474+
2475+
mp_sieve_init(&sieve);
2476+
2477+
if ((e = mp_is_small_prime(number, &result, &sieve)) != MP_OKAY) {
2478+
fprintf(stderr,"mp_is_small_prime failed: \textbackslash{}"%s\textbackslash{}"\textbackslash{}n",
2479+
mp_error_to_string(e));
2480+
goto LTM_ERR;
2481+
}
2482+
2483+
printf("The number %" LTM_SIEVE_PR_UINT " is %s prime\textbackslash{}n",
2484+
number,(result)?"":"not");
2485+
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+
mp_sieve_clear(&sieve);
2590+
exit(EXIT_SUCCESS);
2591+
LTM_ERR:
2592+
mp_clear_multi(&total, &t, NULL);
2593+
mp_sieve_clear(&sieve);
2594+
exit(EXIT_FAILURE);
2595+
}
2596+
\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+
23502599
\chapter{Random Number Generation}
23512600
\section{PRNG}
23522601
\index{mp\_rand}
@@ -2417,6 +2666,9 @@ \subsection{To ASCII}
24172666
mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream);
24182667
\end{alltt}
24192668

2669+
2670+
2671+
24202672
\subsection{From ASCII}
24212673
\index{mp\_read\_radix}
24222674
\begin{alltt}

tommath_private.h

+2
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ extern void MP_FREE(void *mem, size_t size);
107107
/* Size of the base sieve of mp_sieve*/
108108
#if ( (defined MP_64BIT) && (defined MP_SIEVE_USE_LARGE_SIEVE) )
109109
# define MP_SIEVE_PRIME_MAX 0xFFFFFFFFFFFFFFFFllu
110+
#ifndef MP_SIEVE_PRIME_MAX_SQRT
110111
# define MP_SIEVE_PRIME_MAX_SQRT 0xFFFFFFFFllu
112+
#endif
111113
#else
112114
# define MP_SIEVE_PRIME_MAX 0xFFFFFFFFlu
113115
# define MP_SIEVE_PRIME_MAX_SQRT 0xFFFFlu

0 commit comments

Comments
 (0)