source: trunk/third/gmp/demos/primes.c @ 18191

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