Blame demos/primes.c

Packit 5c3484
/* List and count primes.
Packit 5c3484
   Written by tege while on holiday in Rodupp, August 2001.
Packit 5c3484
   Between 10 and 500 times faster than previous program.
Packit 5c3484
Packit 5c3484
Copyright 2001, 2002, 2006, 2012 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This program is free software; you can redistribute it and/or modify it under
Packit 5c3484
the terms of the GNU General Public License as published by the Free Software
Packit 5c3484
Foundation; either version 3 of the License, or (at your option) any later
Packit 5c3484
version.
Packit 5c3484
Packit 5c3484
This program is distributed in the hope that it will be useful, but WITHOUT ANY
Packit 5c3484
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
Packit 5c3484
PARTICULAR PURPOSE.  See the GNU General Public License for more details.
Packit 5c3484
Packit 5c3484
You should have received a copy of the GNU General Public License along with
Packit 5c3484
this program.  If not, see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include <stdlib.h>
Packit 5c3484
#include <stdio.h>
Packit 5c3484
#include <string.h>
Packit 5c3484
#include <math.h>
Packit 5c3484
#include <assert.h>
Packit 5c3484
Packit 5c3484
/* IDEAS:
Packit 5c3484
 * Do not fill primes[] with real primes when the range [fr,to] is small,
Packit 5c3484
   when fr,to are relatively large.  Fill primes[] with odd numbers instead.
Packit 5c3484
   [Probably a bad idea, since the primes[] array would become very large.]
Packit 5c3484
 * Separate small primes and large primes when sieving.  Either the Montgomery
Packit 5c3484
   way (i.e., having a large array a multiple of L1 cache size), or just
Packit 5c3484
   separate loops for primes <= S and primes > S.  The latter primes do not
Packit 5c3484
   require an inner loop, since they will touch the sieving array at most once.
Packit 5c3484
 * Pre-fill sieving array with an appropriately aligned ...00100100... pattern,
Packit 5c3484
   then omit 3 from primes array.  (May require similar special handling of 3
Packit 5c3484
   as we now have for 2.)
Packit 5c3484
 * A large SIEVE_LIMIT currently implies very large memory usage, mainly due
Packit 5c3484
   to the sieving array in make_primelist, but also because of the primes[]
Packit 5c3484
   array.  We might want to stage the program, using sieve_region/find_primes
Packit 5c3484
   to build primes[].  Make report() a function pointer, as part of achieving
Packit 5c3484
   this.
Packit 5c3484
 * Store primes[] as two arrays, one array with primes represented as delta
Packit 5c3484
   values using just 8 bits (if gaps are too big, store bogus primes!)
Packit 5c3484
   and one array with "rem" values.  The latter needs 32-bit values.
Packit 5c3484
 * A new entry point, mpz_probab_prime_likely_p, would be useful.
Packit 5c3484
 * Improve command line syntax and versatility.  "primes -f FROM -t TO",
Packit 5c3484
   allow either to be omitted for open interval.  (But disallow
Packit 5c3484
   "primes -c -f FROM" since that would be infinity.)  Allow printing a
Packit 5c3484
   limited *number* of primes using syntax like "primes -f FROM -n NUMBER".
Packit 5c3484
 * When looking for maxgaps, we should not perform any primality testing until
Packit 5c3484
   we find possible record gaps.  Should speed up the searches tremendously.
Packit 5c3484
 */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
Packit 5c3484
struct primes
Packit 5c3484
{
Packit 5c3484
  unsigned int prime;
Packit 5c3484
  int rem;
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
struct primes *primes;
Packit 5c3484
unsigned long n_primes;
Packit 5c3484
Packit 5c3484
void find_primes (unsigned char *, mpz_t, unsigned long, mpz_t);
Packit 5c3484
void sieve_region (unsigned char *, mpz_t, unsigned long);
Packit 5c3484
void make_primelist (unsigned long);
Packit 5c3484
Packit 5c3484
int flag_print = 1;
Packit 5c3484
int flag_count = 0;
Packit 5c3484
int flag_maxgap = 0;
Packit 5c3484
unsigned long maxgap = 0;
Packit 5c3484
unsigned long total_primes = 0;
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
report (mpz_t prime)
Packit 5c3484
{
Packit 5c3484
  total_primes += 1;
Packit 5c3484
  if (flag_print)
Packit 5c3484
    {
Packit 5c3484
      mpz_out_str (stdout, 10, prime);
Packit 5c3484
      printf ("\n");
Packit 5c3484
    }
Packit 5c3484
  if (flag_maxgap)
Packit 5c3484
    {
Packit 5c3484
      static unsigned long prev_prime_low = 0;
Packit 5c3484
      unsigned long gap;
Packit 5c3484
      if (prev_prime_low != 0)
Packit 5c3484
	{
Packit 5c3484
	  gap = mpz_get_ui (prime) - prev_prime_low;
Packit 5c3484
	  if (maxgap < gap)
Packit 5c3484
	    maxgap = gap;
Packit 5c3484
	}
Packit 5c3484
      prev_prime_low = mpz_get_ui (prime);
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
main (int argc, char *argv[])
Packit 5c3484
{
Packit 5c3484
  char *progname = argv[0];
Packit 5c3484
  mpz_t fr, to;
Packit 5c3484
  mpz_t fr2, to2;
Packit 5c3484
  unsigned long sieve_lim;
Packit 5c3484
  unsigned long est_n_primes;
Packit 5c3484
  unsigned char *s;
Packit 5c3484
  mpz_t tmp;
Packit 5c3484
  mpz_t siev_sqr_lim;
Packit 5c3484
Packit 5c3484
  while (argc != 1)
Packit 5c3484
    {
Packit 5c3484
      if (strcmp (argv[1], "-c") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_count = 1;
Packit 5c3484
	  argv++;
Packit 5c3484
	  argc--;
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (argv[1], "-p") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_print = 2;
Packit 5c3484
	  argv++;
Packit 5c3484
	  argc--;
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (argv[1], "-g") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_maxgap = 1;
Packit 5c3484
	  argv++;
Packit 5c3484
	  argc--;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	break;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (flag_count || flag_maxgap)
Packit 5c3484
    flag_print--;		/* clear unless an explicit -p  */
Packit 5c3484
Packit 5c3484
  mpz_init (fr);
Packit 5c3484
  mpz_init (to);
Packit 5c3484
  mpz_init (fr2);
Packit 5c3484
  mpz_init (to2);
Packit 5c3484
Packit 5c3484
  if (argc == 3)
Packit 5c3484
    {
Packit 5c3484
      mpz_set_str (fr, argv[1], 0);
Packit 5c3484
      if (argv[2][0] == '+')
Packit 5c3484
	{
Packit 5c3484
	  mpz_set_str (to, argv[2] + 1, 0);
Packit 5c3484
	  mpz_add (to, to, fr);
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	mpz_set_str (to, argv[2], 0);
Packit 5c3484
    }
Packit 5c3484
  else if (argc == 2)
Packit 5c3484
    {
Packit 5c3484
      mpz_set_ui (fr, 0);
Packit 5c3484
      mpz_set_str (to, argv[1], 0);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      fprintf (stderr, "usage: %s [-c] [-p] [-g] [from [+]]to\n", progname);
Packit 5c3484
      exit (1);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  mpz_set (fr2, fr);
Packit 5c3484
  if (mpz_cmp_ui (fr2, 3) < 0)
Packit 5c3484
    {
Packit 5c3484
      mpz_set_ui (fr2, 2);
Packit 5c3484
      report (fr2);
Packit 5c3484
      mpz_set_ui (fr2, 3);
Packit 5c3484
    }
Packit 5c3484
  mpz_setbit (fr2, 0);				/* make odd */
Packit 5c3484
  mpz_sub_ui (to2, to, 1);
Packit 5c3484
  mpz_setbit (to2, 0);				/* make odd */
Packit 5c3484
Packit 5c3484
  mpz_init (tmp);
Packit 5c3484
  mpz_init (siev_sqr_lim);
Packit 5c3484
Packit 5c3484
  mpz_sqrt (tmp, to2);
Packit 5c3484
#define SIEVE_LIMIT 10000000
Packit 5c3484
  if (mpz_cmp_ui (tmp, SIEVE_LIMIT) < 0)
Packit 5c3484
    {
Packit 5c3484
      sieve_lim = mpz_get_ui (tmp);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      sieve_lim = SIEVE_LIMIT;
Packit 5c3484
      mpz_sub (tmp, to2, fr2);
Packit 5c3484
      if (mpz_cmp_ui (tmp, sieve_lim) < 0)
Packit 5c3484
	sieve_lim = mpz_get_ui (tmp);	/* limit sieving for small ranges */
Packit 5c3484
    }
Packit 5c3484
  mpz_set_ui (siev_sqr_lim, sieve_lim + 1);
Packit 5c3484
  mpz_mul_ui (siev_sqr_lim, siev_sqr_lim, sieve_lim + 1);
Packit 5c3484
Packit 5c3484
  est_n_primes = (size_t) (sieve_lim / log((double) sieve_lim) * 1.13) + 10;
Packit 5c3484
  primes = malloc (est_n_primes * sizeof primes[0]);
Packit 5c3484
  make_primelist (sieve_lim);
Packit 5c3484
  assert (est_n_primes >= n_primes);
Packit 5c3484
Packit 5c3484
#if DEBUG
Packit 5c3484
  printf ("sieve_lim = %lu\n", sieve_lim);
Packit 5c3484
  printf ("n_primes = %lu (3..%u)\n",
Packit 5c3484
	  n_primes, primes[n_primes - 1].prime);
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#define S (1 << 15)		/* FIXME: Figure out L1 cache size */
Packit 5c3484
  s = malloc (S/2);
Packit 5c3484
  while (mpz_cmp (fr2, to2) <= 0)
Packit 5c3484
    {
Packit 5c3484
      unsigned long rsize;
Packit 5c3484
      rsize = S;
Packit 5c3484
      mpz_add_ui (tmp, fr2, rsize);
Packit 5c3484
      if (mpz_cmp (tmp, to2) > 0)
Packit 5c3484
	{
Packit 5c3484
	  mpz_sub (tmp, to2, fr2);
Packit 5c3484
	  rsize = mpz_get_ui (tmp) + 2;
Packit 5c3484
	}
Packit 5c3484
#if DEBUG
Packit 5c3484
      printf ("Sieving region ["); mpz_out_str (stdout, 10, fr2);
Packit 5c3484
      printf (","); mpz_add_ui (tmp, fr2, rsize - 2);
Packit 5c3484
      mpz_out_str (stdout, 10, tmp); printf ("]\n");
Packit 5c3484
#endif
Packit 5c3484
      sieve_region (s, fr2, rsize);
Packit 5c3484
      find_primes (s, fr2, rsize / 2, siev_sqr_lim);
Packit 5c3484
Packit 5c3484
      mpz_add_ui (fr2, fr2, S);
Packit 5c3484
    }
Packit 5c3484
  free (s);
Packit 5c3484
Packit 5c3484
  if (flag_count)
Packit 5c3484
    printf ("Pi(interval) = %lu\n", total_primes);
Packit 5c3484
Packit 5c3484
  if (flag_maxgap)
Packit 5c3484
    printf ("max gap: %lu\n", maxgap);
Packit 5c3484
Packit 5c3484
  return 0;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Find primes in region [fr,fr+rsize).  Requires that fr is odd and that
Packit 5c3484
   rsize is even.  The sieving array s should be aligned for "long int" and
Packit 5c3484
   have rsize/2 entries, rounded up to the nearest multiple of "long int".  */
Packit 5c3484
void
Packit 5c3484
sieve_region (unsigned char *s, mpz_t fr, unsigned long rsize)
Packit 5c3484
{
Packit 5c3484
  unsigned long ssize = rsize / 2;
Packit 5c3484
  unsigned long start, start2, prime;
Packit 5c3484
  unsigned long i;
Packit 5c3484
  mpz_t tmp;
Packit 5c3484
Packit 5c3484
  mpz_init (tmp);
Packit 5c3484
Packit 5c3484
#if 0
Packit 5c3484
  /* initialize sieving array */
Packit 5c3484
  for (ii = 0; ii < (ssize + sizeof (long) - 1) / sizeof (long); ii++)
Packit 5c3484
    ((long *) s) [ii] = ~0L;
Packit 5c3484
#else
Packit 5c3484
  {
Packit 5c3484
    long k;
Packit 5c3484
    long *se = (long *) (s + ((ssize + sizeof (long) - 1) & -sizeof (long)));
Packit 5c3484
    for (k = -((ssize + sizeof (long) - 1) / sizeof (long)); k < 0; k++)
Packit 5c3484
      se[k] = ~0L;
Packit 5c3484
  }
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
  for (i = 0; i < n_primes; i++)
Packit 5c3484
    {
Packit 5c3484
      prime = primes[i].prime;
Packit 5c3484
Packit 5c3484
      if (primes[i].rem >= 0)
Packit 5c3484
	{
Packit 5c3484
	  start2 = primes[i].rem;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  mpz_set_ui (tmp, prime);
Packit 5c3484
	  mpz_mul_ui (tmp, tmp, prime);
Packit 5c3484
	  if (mpz_cmp (fr, tmp) <= 0)
Packit 5c3484
	    {
Packit 5c3484
	      mpz_sub (tmp, tmp, fr);
Packit 5c3484
	      if (mpz_cmp_ui (tmp, 2 * ssize) > 0)
Packit 5c3484
		break;		/* avoid overflow at next line, also speedup */
Packit 5c3484
	      start = mpz_get_ui (tmp);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    {
Packit 5c3484
	      start = (prime - mpz_tdiv_ui (fr, prime)) % prime;
Packit 5c3484
	      if (start % 2 != 0)
Packit 5c3484
		start += prime;		/* adjust if even divisible */
Packit 5c3484
	    }
Packit 5c3484
	  start2 = start / 2;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
#if 0
Packit 5c3484
      for (ii = start2; ii < ssize; ii += prime)
Packit 5c3484
	s[ii] = 0;
Packit 5c3484
      primes[i].rem = ii - ssize;
Packit 5c3484
#else
Packit 5c3484
      {
Packit 5c3484
	long k;
Packit 5c3484
	unsigned char *se = s + ssize; /* point just beyond sieving range */
Packit 5c3484
	for (k = start2 - ssize; k < 0; k += prime)
Packit 5c3484
	  se[k] = 0;
Packit 5c3484
	primes[i].rem = k;
Packit 5c3484
      }
Packit 5c3484
#endif
Packit 5c3484
    }
Packit 5c3484
  mpz_clear (tmp);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Find primes in region [fr,fr+rsize), using the previously sieved s[].  */
Packit 5c3484
void
Packit 5c3484
find_primes (unsigned char *s, mpz_t  fr, unsigned long ssize,
Packit 5c3484
	     mpz_t siev_sqr_lim)
Packit 5c3484
{
Packit 5c3484
  unsigned long j, ij;
Packit 5c3484
  mpz_t tmp;
Packit 5c3484
Packit 5c3484
  mpz_init (tmp);
Packit 5c3484
  for (j = 0; j < (ssize + sizeof (long) - 1) / sizeof (long); j++)
Packit 5c3484
    {
Packit 5c3484
      if (((long *) s) [j] != 0)
Packit 5c3484
	{
Packit 5c3484
	  for (ij = 0; ij < sizeof (long); ij++)
Packit 5c3484
	    {
Packit 5c3484
	      if (s[j * sizeof (long) + ij] != 0)
Packit 5c3484
		{
Packit 5c3484
		  if (j * sizeof (long) + ij >= ssize)
Packit 5c3484
		    goto out;
Packit 5c3484
		  mpz_add_ui (tmp, fr, (j * sizeof (long) + ij) * 2);
Packit 5c3484
		  if (mpz_cmp (tmp, siev_sqr_lim) < 0 ||
Packit 5c3484
		      mpz_probab_prime_p (tmp, 10))
Packit 5c3484
		    report (tmp);
Packit 5c3484
		}
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
 out:
Packit 5c3484
  mpz_clear (tmp);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Generate a list of primes and store in the global array primes[].  */
Packit 5c3484
void
Packit 5c3484
make_primelist (unsigned long maxprime)
Packit 5c3484
{
Packit 5c3484
#if 1
Packit 5c3484
  unsigned char *s;
Packit 5c3484
  unsigned long ssize = maxprime / 2;
Packit 5c3484
  unsigned long i, ii, j;
Packit 5c3484
Packit 5c3484
  s = malloc (ssize);
Packit 5c3484
  memset (s, ~0, ssize);
Packit 5c3484
  for (i = 3; ; i += 2)
Packit 5c3484
    {
Packit 5c3484
      unsigned long isqr = i * i;
Packit 5c3484
      if (isqr >= maxprime)
Packit 5c3484
	break;
Packit 5c3484
      if (s[i * i / 2 - 1] == 0)
Packit 5c3484
	continue;				/* only sieve with primes */
Packit 5c3484
      for (ii = i * i / 2 - 1; ii < ssize; ii += i)
Packit 5c3484
	s[ii] = 0;
Packit 5c3484
    }
Packit 5c3484
  n_primes = 0;
Packit 5c3484
  for (j = 0; j < ssize; j++)
Packit 5c3484
    {
Packit 5c3484
      if (s[j] != 0)
Packit 5c3484
	{
Packit 5c3484
	  primes[n_primes].prime = j * 2 + 3;
Packit 5c3484
	  primes[n_primes].rem = -1;
Packit 5c3484
	  n_primes++;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  /* FIXME: This should not be needed if fencepost errors were fixed... */
Packit 5c3484
  if (primes[n_primes - 1].prime > maxprime)
Packit 5c3484
    n_primes--;
Packit 5c3484
  free (s);
Packit 5c3484
#else
Packit 5c3484
  unsigned long i;
Packit 5c3484
  n_primes = 0;
Packit 5c3484
  for (i = 3; i <= maxprime; i += 2)
Packit 5c3484
    {
Packit 5c3484
      if (i < 7 || (i % 3 != 0 && i % 5 != 0 && i % 7 != 0))
Packit 5c3484
	{
Packit 5c3484
	  primes[n_primes].prime = i;
Packit 5c3484
	  primes[n_primes].rem = -1;
Packit 5c3484
	  n_primes++;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
#endif
Packit 5c3484
}