(root)/
gmp-6.3.0/
demos/
primes.c
       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
      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
      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
     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
     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
     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  }