GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
/* List and count primes.1Written by tege while on holiday in Rodupp, August 2001.2Between 10 and 500 times faster than previous program.34Copyright 2001, 2002 Free Software Foundation, Inc.56This program is free software; you can redistribute it and/or modify it under7the terms of the GNU General Public License as published by the Free Software8Foundation; either version 2 of the License, or (at your option) any later9version.1011This program is distributed in the hope that it will be useful, but WITHOUT ANY12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A13PARTICULAR PURPOSE. See the GNU General Public License for more details.1415You should have received a copy of the GNU General Public License along with16this program; if not, write to the Free Software Foundation, Inc., 51 Franklin17Street, Fifth Floor, Boston, MA 02110-1301, USA. */1819#include <stdlib.h>20#include <stdio.h>21#include <string.h>22#include <math.h>23#include <assert.h>2425/* IDEAS:26* Do not fill primes[] with real primes when the range [fr,to] is small,27when 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 Montgomery30way (i.e., having a large array a multiple of L1 cache size), or just31separate loops for primes <= S and primes > S. The latter primes do not32require an inner loop, since they will touch the sieving array at most once.33* Pre-fill sieving array with an appropriately aligned ...00100100... pattern,34then omit 3 from primes array. (May require similar special handling of 335as we now have for 2.)36* A large SIEVE_LIMIT currently implies very large memory usage, mainly due37to the sieving array in make_primelist, but also because of the primes[]38array. We might want to stage the program, using sieve_region/find_primes39to build primes[]. Make report() a function pointer, as part of achieving40this.41* Store primes[] as two arrays, one array with primes represented as delta42values using just 8 bits (if gaps are too big, store bogus primes!)43and 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",46allow either to be omitted for open interval. (But disallow47"primes -c -f FROM" since that would be infinity.) Allow printing a48limited *number* of primes using syntax like "primes -f FROM -n NUMBER".49* When looking for maxgaps, we should not perform any primality testing until50we find possible record gaps. Should speed up the searches tremendously.51*/5253#include "gmp.h"5455struct primes56{57unsigned int prime;58int rem;59};6061struct primes *primes;62unsigned long n_primes;6364void find_primes __GMP_PROTO ((unsigned char *, mpz_t, unsigned long, mpz_t));65void sieve_region __GMP_PROTO ((unsigned char *, mpz_t, unsigned long));66void make_primelist __GMP_PROTO ((unsigned long));6768int flag_print = 1;69int flag_count = 0;70int flag_maxgap = 0;71unsigned long maxgap = 0;72unsigned long total_primes = 0;7374void75report (mpz_t prime)76{77total_primes += 1;78if (flag_print)79{80mpz_out_str (stdout, 10, prime);81printf ("\n");82}83if (flag_maxgap)84{85static unsigned long prev_prime_low = 0;86unsigned long gap;87if (prev_prime_low != 0)88{89gap = mpz_get_ui (prime) - prev_prime_low;90if (maxgap < gap)91maxgap = gap;92}93prev_prime_low = mpz_get_ui (prime);94}95}9697int98main (int argc, char *argv[])99{100char *progname = argv[0];101mpz_t fr, to;102mpz_t fr2, to2;103unsigned long sieve_lim;104unsigned long est_n_primes;105unsigned char *s;106mpz_t tmp;107mpz_t siev_sqr_lim;108109while (argc != 1)110{111if (strcmp (argv[1], "-c") == 0)112{113flag_count = 1;114argv++;115argc--;116}117else if (strcmp (argv[1], "-p") == 0)118{119flag_print = 2;120argv++;121argc--;122}123else if (strcmp (argv[1], "-g") == 0)124{125flag_maxgap = 1;126argv++;127argc--;128}129else130break;131}132133if (flag_count || flag_maxgap)134flag_print--; /* clear unless an explicit -p */135136mpz_init (fr);137mpz_init (to);138mpz_init (fr2);139mpz_init (to2);140141if (argc == 3)142{143mpz_set_str (fr, argv[1], 0);144if (argv[2][0] == '+')145{146mpz_set_str (to, argv[2] + 1, 0);147mpz_add (to, to, fr);148}149else150mpz_set_str (to, argv[2], 0);151}152else if (argc == 2)153{154mpz_set_ui (fr, 0);155mpz_set_str (to, argv[1], 0);156}157else158{159fprintf (stderr, "usage: %s [-c] [-p] [-g] [from [+]]to\n", progname);160exit (1);161}162163mpz_set (fr2, fr);164if (mpz_cmp_ui (fr2, 3) < 0)165{166mpz_set_ui (fr2, 2);167report (fr2);168mpz_set_ui (fr2, 3);169}170mpz_setbit (fr2, 0); /* make odd */171mpz_sub_ui (to2, to, 1);172mpz_setbit (to2, 0); /* make odd */173174mpz_init (tmp);175mpz_init (siev_sqr_lim);176177mpz_sqrt (tmp, to2);178#define SIEVE_LIMIT 10000000179if (mpz_cmp_ui (tmp, SIEVE_LIMIT) < 0)180{181sieve_lim = mpz_get_ui (tmp);182}183else184{185sieve_lim = SIEVE_LIMIT;186mpz_sub (tmp, to2, fr2);187if (mpz_cmp_ui (tmp, sieve_lim) < 0)188sieve_lim = mpz_get_ui (tmp); /* limit sieving for small ranges */189}190mpz_set_ui (siev_sqr_lim, sieve_lim + 1);191mpz_mul_ui (siev_sqr_lim, siev_sqr_lim, sieve_lim + 1);192193est_n_primes = (size_t) (sieve_lim / log((double) sieve_lim) * 1.13) + 10;194primes = malloc (est_n_primes * sizeof primes[0]);195make_primelist (sieve_lim);196assert (est_n_primes >= n_primes);197198#if DEBUG199printf ("sieve_lim = %lu\n", sieve_lim);200printf ("n_primes = %lu (3..%u)\n",201n_primes, primes[n_primes - 1].prime);202#endif203204#define S (1 << 15) /* FIXME: Figure out L1 cache size */205s = malloc (S/2);206while (mpz_cmp (fr2, to2) <= 0)207{208unsigned long rsize;209rsize = S;210mpz_add_ui (tmp, fr2, rsize);211if (mpz_cmp (tmp, to2) > 0)212{213mpz_sub (tmp, to2, fr2);214rsize = mpz_get_ui (tmp) + 2;215}216#if DEBUG217printf ("Sieving region ["); mpz_out_str (stdout, 10, fr2);218printf (","); mpz_add_ui (tmp, fr2, rsize - 2);219mpz_out_str (stdout, 10, tmp); printf ("]\n");220#endif221sieve_region (s, fr2, rsize);222find_primes (s, fr2, rsize / 2, siev_sqr_lim);223224mpz_add_ui (fr2, fr2, S);225}226free (s);227228if (flag_count)229printf ("Pi(interval) = %lu\n", total_primes);230231if (flag_maxgap)232printf ("max gap: %lu\n", maxgap);233234return 0;235}236237/* Find primes in region [fr,fr+rsize). Requires that fr is odd and that238rsize is even. The sieving array s should be aligned for "long int" and239have rsize/2 entries, rounded up to the nearest multiple of "long int". */240void241sieve_region (unsigned char *s, mpz_t fr, unsigned long rsize)242{243unsigned long ssize = rsize / 2;244unsigned long start, start2, prime;245unsigned long i;246mpz_t tmp;247248mpz_init (tmp);249250#if 0251/* initialize sieving array */252for (ii = 0; ii < (ssize + sizeof (long) - 1) / sizeof (long); ii++)253((long *) s) [ii] = ~0L;254#else255{256long k;257long *se = (long *) (s + ((ssize + sizeof (long) - 1) & -sizeof (long)));258for (k = -((ssize + sizeof (long) - 1) / sizeof (long)); k < 0; k++)259se[k] = ~0L;260}261#endif262263for (i = 0; i < n_primes; i++)264{265prime = primes[i].prime;266267if (primes[i].rem >= 0)268{269start2 = primes[i].rem;270}271else272{273mpz_set_ui (tmp, prime);274mpz_mul_ui (tmp, tmp, prime);275if (mpz_cmp (fr, tmp) <= 0)276{277mpz_sub (tmp, tmp, fr);278if (mpz_cmp_ui (tmp, 2 * ssize) > 0)279break; /* avoid overflow at next line, also speedup */280start = mpz_get_ui (tmp);281}282else283{284start = (prime - mpz_tdiv_ui (fr, prime)) % prime;285if (start % 2 != 0)286start += prime; /* adjust if even divisable */287}288start2 = start / 2;289}290291#if 0292for (ii = start2; ii < ssize; ii += prime)293s[ii] = 0;294primes[i].rem = ii - ssize;295#else296{297long k;298unsigned char *se = s + ssize; /* point just beyond sieving range */299for (k = start2 - ssize; k < 0; k += prime)300se[k] = 0;301primes[i].rem = k;302}303#endif304}305mpz_clear (tmp);306}307308/* Find primes in region [fr,fr+rsize), using the previously sieved s[]. */309void310find_primes (unsigned char *s, mpz_t fr, unsigned long ssize,311mpz_t siev_sqr_lim)312{313unsigned long j, ij;314mpz_t tmp;315316mpz_init (tmp);317for (j = 0; j < (ssize + sizeof (long) - 1) / sizeof (long); j++)318{319if (((long *) s) [j] != 0)320{321for (ij = 0; ij < sizeof (long); ij++)322{323if (s[j * sizeof (long) + ij] != 0)324{325if (j * sizeof (long) + ij >= ssize)326goto out;327mpz_add_ui (tmp, fr, (j * sizeof (long) + ij) * 2);328if (mpz_cmp (tmp, siev_sqr_lim) < 0 ||329mpz_probab_prime_p (tmp, 3))330report (tmp);331}332}333}334}335out:336mpz_clear (tmp);337}338339/* Generate a lits of primes and store in the global array primes[]. */340void341make_primelist (unsigned long maxprime)342{343#if 1344unsigned char *s;345unsigned long ssize = maxprime / 2;346unsigned long i, ii, j;347348s = malloc (ssize);349memset (s, ~0, ssize);350for (i = 3; ; i += 2)351{352unsigned long isqr = i * i;353if (isqr >= maxprime)354break;355if (s[i * i / 2 - 1] == 0)356continue; /* only sieve with primes */357for (ii = i * i / 2 - 1; ii < ssize; ii += i)358s[ii] = 0;359}360n_primes = 0;361for (j = 0; j < ssize; j++)362{363if (s[j] != 0)364{365primes[n_primes].prime = j * 2 + 3;366primes[n_primes].rem = -1;367n_primes++;368}369}370/* FIXME: This should not be needed if fencepost errors were fixed... */371if (primes[n_primes - 1].prime > maxprime)372n_primes--;373free (s);374#else375unsigned long i;376n_primes = 0;377for (i = 3; i <= maxprime; i += 2)378{379if (i < 7 || (i % 3 != 0 && i % 5 != 0 && i % 7 != 0))380{381primes[n_primes].prime = i;382primes[n_primes].rem = -1;383n_primes++;384}385}386#endif387}388389390