xref: /netbsd-src/external/lgpl3/gmp/dist/demos/primes.c (revision ce54336801cf28877c3414aa2fcb251dddd543a2)
1 /* List and count primes.
2    Written by tege while on holiday in Rodupp, August 2001.
3    Between 10 and 500 times faster than previous program.
4 
5 Copyright 2001, 2002, 2006, 2012 Free Software Foundation, Inc.
6 
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free Software
9 Foundation; either version 3 of the License, or (at your option) any later
10 version.
11 
12 This program is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along with
17 this program.  If not, see https://www.gnu.org/licenses/.  */
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <math.h>
23 #include <assert.h>
24 
25 /* IDEAS:
26  * Do not fill primes[] with real primes when the range [fr,to] is small,
27    when fr,to are relatively large.  Fill primes[] with odd numbers instead.
28    [Probably a bad idea, since the primes[] array would become very large.]
29  * Separate small primes and large primes when sieving.  Either the Montgomery
30    way (i.e., having a large array a multiple of L1 cache size), or just
31    separate loops for primes <= S and primes > S.  The latter primes do not
32    require an inner loop, since they will touch the sieving array at most once.
33  * Pre-fill sieving array with an appropriately aligned ...00100100... pattern,
34    then omit 3 from primes array.  (May require similar special handling of 3
35    as we now have for 2.)
36  * A large SIEVE_LIMIT currently implies very large memory usage, mainly due
37    to the sieving array in make_primelist, but also because of the primes[]
38    array.  We might want to stage the program, using sieve_region/find_primes
39    to build primes[].  Make report() a function pointer, as part of achieving
40    this.
41  * Store primes[] as two arrays, one array with primes represented as delta
42    values using just 8 bits (if gaps are too big, store bogus primes!)
43    and one array with "rem" values.  The latter needs 32-bit values.
44  * A new entry point, mpz_probab_prime_likely_p, would be useful.
45  * Improve command line syntax and versatility.  "primes -f FROM -t TO",
46    allow either to be omitted for open interval.  (But disallow
47    "primes -c -f FROM" since that would be infinity.)  Allow printing a
48    limited *number* of primes using syntax like "primes -f FROM -n NUMBER".
49  * When looking for maxgaps, we should not perform any primality testing until
50    we find possible record gaps.  Should speed up the searches tremendously.
51  */
52 
53 #include "gmp.h"
54 
55 struct primes
56 {
57   unsigned int prime;
58   int rem;
59 };
60 
61 struct primes *primes;
62 unsigned long n_primes;
63 
64 void find_primes (unsigned char *, mpz_t, unsigned long, mpz_t);
65 void sieve_region (unsigned char *, mpz_t, unsigned long);
66 void make_primelist (unsigned long);
67 
68 int flag_print = 1;
69 int flag_count = 0;
70 int flag_maxgap = 0;
71 unsigned long maxgap = 0;
72 unsigned long total_primes = 0;
73 
74 void
report(mpz_t prime)75 report (mpz_t prime)
76 {
77   total_primes += 1;
78   if (flag_print)
79     {
80       mpz_out_str (stdout, 10, prime);
81       printf ("\n");
82     }
83   if (flag_maxgap)
84     {
85       static unsigned long prev_prime_low = 0;
86       unsigned long gap;
87       if (prev_prime_low != 0)
88 	{
89 	  gap = mpz_get_ui (prime) - prev_prime_low;
90 	  if (maxgap < gap)
91 	    maxgap = gap;
92 	}
93       prev_prime_low = mpz_get_ui (prime);
94     }
95 }
96 
97 int
main(int argc,char * argv[])98 main (int argc, char *argv[])
99 {
100   char *progname = argv[0];
101   mpz_t fr, to;
102   mpz_t fr2, to2;
103   unsigned long sieve_lim;
104   unsigned long est_n_primes;
105   unsigned char *s;
106   mpz_t tmp;
107   mpz_t siev_sqr_lim;
108 
109   while (argc != 1)
110     {
111       if (strcmp (argv[1], "-c") == 0)
112 	{
113 	  flag_count = 1;
114 	  argv++;
115 	  argc--;
116 	}
117       else if (strcmp (argv[1], "-p") == 0)
118 	{
119 	  flag_print = 2;
120 	  argv++;
121 	  argc--;
122 	}
123       else if (strcmp (argv[1], "-g") == 0)
124 	{
125 	  flag_maxgap = 1;
126 	  argv++;
127 	  argc--;
128 	}
129       else
130 	break;
131     }
132 
133   if (flag_count || flag_maxgap)
134     flag_print--;		/* clear unless an explicit -p  */
135 
136   mpz_init (fr);
137   mpz_init (to);
138   mpz_init (fr2);
139   mpz_init (to2);
140 
141   if (argc == 3)
142     {
143       mpz_set_str (fr, argv[1], 0);
144       if (argv[2][0] == '+')
145 	{
146 	  mpz_set_str (to, argv[2] + 1, 0);
147 	  mpz_add (to, to, fr);
148 	}
149       else
150 	mpz_set_str (to, argv[2], 0);
151     }
152   else if (argc == 2)
153     {
154       mpz_set_ui (fr, 0);
155       mpz_set_str (to, argv[1], 0);
156     }
157   else
158     {
159       fprintf (stderr, "usage: %s [-c] [-p] [-g] [from [+]]to\n", progname);
160       exit (1);
161     }
162 
163   mpz_set (fr2, fr);
164   if (mpz_cmp_ui (fr2, 3) < 0)
165     {
166       mpz_set_ui (fr2, 2);
167       report (fr2);
168       mpz_set_ui (fr2, 3);
169     }
170   mpz_setbit (fr2, 0);				/* make odd */
171   mpz_sub_ui (to2, to, 1);
172   mpz_setbit (to2, 0);				/* make odd */
173 
174   mpz_init (tmp);
175   mpz_init (siev_sqr_lim);
176 
177   mpz_sqrt (tmp, to2);
178 #define SIEVE_LIMIT 10000000
179   if (mpz_cmp_ui (tmp, SIEVE_LIMIT) < 0)
180     {
181       sieve_lim = mpz_get_ui (tmp);
182     }
183   else
184     {
185       sieve_lim = SIEVE_LIMIT;
186       mpz_sub (tmp, to2, fr2);
187       if (mpz_cmp_ui (tmp, sieve_lim) < 0)
188 	sieve_lim = mpz_get_ui (tmp);	/* limit sieving for small ranges */
189     }
190   mpz_set_ui (siev_sqr_lim, sieve_lim + 1);
191   mpz_mul_ui (siev_sqr_lim, siev_sqr_lim, sieve_lim + 1);
192 
193   est_n_primes = (size_t) (sieve_lim / log((double) sieve_lim) * 1.13) + 10;
194   primes = malloc (est_n_primes * sizeof primes[0]);
195   make_primelist (sieve_lim);
196   assert (est_n_primes >= n_primes);
197 
198 #if DEBUG
199   printf ("sieve_lim = %lu\n", sieve_lim);
200   printf ("n_primes = %lu (3..%u)\n",
201 	  n_primes, primes[n_primes - 1].prime);
202 #endif
203 
204 #define S (1 << 15)		/* FIXME: Figure out L1 cache size */
205   s = malloc (S/2);
206   while (mpz_cmp (fr2, to2) <= 0)
207     {
208       unsigned long rsize;
209       rsize = S;
210       mpz_add_ui (tmp, fr2, rsize);
211       if (mpz_cmp (tmp, to2) > 0)
212 	{
213 	  mpz_sub (tmp, to2, fr2);
214 	  rsize = mpz_get_ui (tmp) + 2;
215 	}
216 #if DEBUG
217       printf ("Sieving region ["); mpz_out_str (stdout, 10, fr2);
218       printf (","); mpz_add_ui (tmp, fr2, rsize - 2);
219       mpz_out_str (stdout, 10, tmp); printf ("]\n");
220 #endif
221       sieve_region (s, fr2, rsize);
222       find_primes (s, fr2, rsize / 2, siev_sqr_lim);
223 
224       mpz_add_ui (fr2, fr2, S);
225     }
226   free (s);
227 
228   if (flag_count)
229     printf ("Pi(interval) = %lu\n", total_primes);
230 
231   if (flag_maxgap)
232     printf ("max gap: %lu\n", maxgap);
233 
234   return 0;
235 }
236 
237 /* Find primes in region [fr,fr+rsize).  Requires that fr is odd and that
238    rsize is even.  The sieving array s should be aligned for "long int" and
239    have rsize/2 entries, rounded up to the nearest multiple of "long int".  */
240 void
sieve_region(unsigned char * s,mpz_t fr,unsigned long rsize)241 sieve_region (unsigned char *s, mpz_t fr, unsigned long rsize)
242 {
243   unsigned long ssize = rsize / 2;
244   unsigned long start, start2, prime;
245   unsigned long i;
246   mpz_t tmp;
247 
248   mpz_init (tmp);
249 
250 #if 0
251   /* initialize sieving array */
252   for (ii = 0; ii < (ssize + sizeof (long) - 1) / sizeof (long); ii++)
253     ((long *) s) [ii] = ~0L;
254 #else
255   {
256     long k;
257     long *se = (long *) (s + ((ssize + sizeof (long) - 1) & -sizeof (long)));
258     for (k = -((ssize + sizeof (long) - 1) / sizeof (long)); k < 0; k++)
259       se[k] = ~0L;
260   }
261 #endif
262 
263   for (i = 0; i < n_primes; i++)
264     {
265       prime = primes[i].prime;
266 
267       if (primes[i].rem >= 0)
268 	{
269 	  start2 = primes[i].rem;
270 	}
271       else
272 	{
273 	  mpz_set_ui (tmp, prime);
274 	  mpz_mul_ui (tmp, tmp, prime);
275 	  if (mpz_cmp (fr, tmp) <= 0)
276 	    {
277 	      mpz_sub (tmp, tmp, fr);
278 	      if (mpz_cmp_ui (tmp, 2 * ssize) > 0)
279 		break;		/* avoid overflow at next line, also speedup */
280 	      start = mpz_get_ui (tmp);
281 	    }
282 	  else
283 	    {
284 	      start = (prime - mpz_tdiv_ui (fr, prime)) % prime;
285 	      if (start % 2 != 0)
286 		start += prime;		/* adjust if even divisible */
287 	    }
288 	  start2 = start / 2;
289 	}
290 
291 #if 0
292       for (ii = start2; ii < ssize; ii += prime)
293 	s[ii] = 0;
294       primes[i].rem = ii - ssize;
295 #else
296       {
297 	long k;
298 	unsigned char *se = s + ssize; /* point just beyond sieving range */
299 	for (k = start2 - ssize; k < 0; k += prime)
300 	  se[k] = 0;
301 	primes[i].rem = k;
302       }
303 #endif
304     }
305   mpz_clear (tmp);
306 }
307 
308 /* Find primes in region [fr,fr+rsize), using the previously sieved s[].  */
309 void
find_primes(unsigned char * s,mpz_t fr,unsigned long ssize,mpz_t siev_sqr_lim)310 find_primes (unsigned char *s, mpz_t  fr, unsigned long ssize,
311 	     mpz_t siev_sqr_lim)
312 {
313   unsigned long j, ij;
314   mpz_t tmp;
315 
316   mpz_init (tmp);
317   for (j = 0; j < (ssize + sizeof (long) - 1) / sizeof (long); j++)
318     {
319       if (((long *) s) [j] != 0)
320 	{
321 	  for (ij = 0; ij < sizeof (long); ij++)
322 	    {
323 	      if (s[j * sizeof (long) + ij] != 0)
324 		{
325 		  if (j * sizeof (long) + ij >= ssize)
326 		    goto out;
327 		  mpz_add_ui (tmp, fr, (j * sizeof (long) + ij) * 2);
328 		  if (mpz_cmp (tmp, siev_sqr_lim) < 0 ||
329 		      mpz_probab_prime_p (tmp, 10))
330 		    report (tmp);
331 		}
332 	    }
333 	}
334     }
335  out:
336   mpz_clear (tmp);
337 }
338 
339 /* Generate a list of primes and store in the global array primes[].  */
340 void
make_primelist(unsigned long maxprime)341 make_primelist (unsigned long maxprime)
342 {
343 #if 1
344   unsigned char *s;
345   unsigned long ssize = maxprime / 2;
346   unsigned long i, ii, j;
347 
348   s = malloc (ssize);
349   memset (s, ~0, ssize);
350   for (i = 3; ; i += 2)
351     {
352       unsigned long isqr = i * i;
353       if (isqr >= maxprime)
354 	break;
355       if (s[i * i / 2 - 1] == 0)
356 	continue;				/* only sieve with primes */
357       for (ii = i * i / 2 - 1; ii < ssize; ii += i)
358 	s[ii] = 0;
359     }
360   n_primes = 0;
361   for (j = 0; j < ssize; j++)
362     {
363       if (s[j] != 0)
364 	{
365 	  primes[n_primes].prime = j * 2 + 3;
366 	  primes[n_primes].rem = -1;
367 	  n_primes++;
368 	}
369     }
370   /* FIXME: This should not be needed if fencepost errors were fixed... */
371   if (primes[n_primes - 1].prime > maxprime)
372     n_primes--;
373   free (s);
374 #else
375   unsigned long i;
376   n_primes = 0;
377   for (i = 3; i <= maxprime; i += 2)
378     {
379       if (i < 7 || (i % 3 != 0 && i % 5 != 0 && i % 7 != 0))
380 	{
381 	  primes[n_primes].prime = i;
382 	  primes[n_primes].rem = -1;
383 	  n_primes++;
384 	}
385     }
386 #endif
387 }
388