/* * Generate batches of consecutive prime numbers using a modified Sieve of Eratosthenes * algorithm that doesn't use much memory by using a windowing sieve buffer. * * Copyright (C) 1987-2012 George Gesslein II. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The chief copyright holder can be contacted at gesslein@mathomatic.org, or George Gesslein II, P.O. Box 224, Lansing, NY 14882-0224 USA. */ /* * Changes: * * 11/22/05 - converted everything to long doubles. Now uses C99 long double functions. * 3/25/06 - made primes buffer variable size. * 3/30/08 - Allow long double to be aliased to double when long double isn't supported. * 2/11/09 - Cleanup calculation of number of decimal digits and max_integer. * 9/12/10 - General cleanup and added error message for when the requested number of primes to display is not reached. * 10/25/10 - Added -c and -h options. * 10/26/10 - Using usage2() instead of usage() most of the time, now. * 10/28/10 - Fixed to work with 16-bit integers. * 1/16/11 - Allow to use doubles instead of long doubles with USE_DOUBLES define, for systems that don't fully support long doubles. * 5/10/11 - Added -u (unbuffered output) option, for real-time output. * 11/13/11 - Use EXIT_SUCCESS and EXIT_FAILURE macros. * 11/30/11 - Added -m option. * 12/31/11 - Added a compile-time warning when the number of digits of precision of long doubles is less than 18. * 07/11/12 - Added -v option to display program name with version number and then exit successfully. * 08/09/12 - Allow "matho-primes all" for endless output of consecutive primes. */ #include #include #include #include #include #include #include #include #include #include #if !NO_GETOPT_H #include #endif #define VERSION "1.4" /* The current version number of this primes program. */ #define true 1 #define false 0 #ifndef max #define max(a, b) (((a) > (b)) ? (a) : (b)) /* return the maximum of two values */ #endif #ifndef min #define min(a, b) (((a) < (b)) ? (a) : (b)) /* return the minimum of two values */ #endif #define ARR_CNT(a) ((int) (sizeof(a)/sizeof(a[0]))) /* returns the number of elements in an array */ #if !USE_DOUBLES #ifndef LDBL_DIG #define USE_DOUBLES 1 #warning "long doubles not supported by this C compiler." #elif LDBL_DIG < 18 #warning "long double number of digits precision is less than 18, probably meaning matho-primes is double precision only." #warning "Due to the above floating point warning, more testing or compiling with -DUSE_DOUBLES is recommended." #endif #endif #if USE_DOUBLES typedef double double_type; #warning "Using only double floats instead of long double floats, as requested." #else typedef long double double_type; #endif /* Maximum memory usage in bytes; can be set to any size. */ #ifndef BUFFER_SIZE #define BUFFER_SIZE 2000000 #endif #if BUFFER_SIZE >= (INT_MAX / 2) || BUFFER_SIZE < 100 #warning BUFFER_SIZE out of range, using default. #undef BUFFER_SIZE #define BUFFER_SIZE min((INT_MAX / 2), 2000000) #endif void generate_primes(void); int test_pal(double_type d, double_type base); void usage(int ev); void usage2(int ev); int get_double_type_int(char *cp, double_type *dp); #if !USE_DOUBLES long double powl(), ceill(), sqrtl(), fmodl(), strtold(); #endif double_type max_integer; /* largest value of a double_type integral value */ double_type start_value; /* where to start finding primes */ double_type number; /* number of prime lines to display */ int count_requested; /* true if the number of primes to display is set by "number" above */ double_type default_number = 20; /* default number of primes to display */ double_type end_value; /* where to stop finding primes */ double_type skip_multiples[] = { /* Additive array that skips over multiples of 2, 3, 5, and 7. */ 10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2,10, 2 }; /* sum of all numbers = 210 = (2*3*5*7) */ int pal_flag, twin_flag; double_type pal_base = 10; /* The palindrome base, if displaying palindromic primes. */ char *prime; /* The boolean sieve array (buffer) for the current window of numbers being tested for primality; */ /* each char (byte) contains true or false, true if prime. */ int buffer_size; /* Number of bytes for variable size prime[] buffer above. */ char *prog_name = "matho-primes"; int main(int argc, char *argv[]) { #if NO_GETOPT_H /* if no getopt.h is available */ extern char *optarg; /* set by getopt(3) */ extern int optind; #endif int i; char buf[1000]; char *cp = NULL; double new_size = 0; buffer_size = BUFFER_SIZE; /* set the highest number this program will work with: */ #if USE_DOUBLES max_integer = pow(10.0, (double) (DBL_DIG)); #else max_integer = powl(10.0L, (long double) (LDBL_DIG)); #endif while (max_integer == max_integer + 1.0) { #if USE_DOUBLES fprintf(stderr, "Warning: max_integer (%g) is too large; size of double = %u bytes.\n", max_integer, (unsigned) sizeof(double)); max_integer /= 10.0; #else fprintf(stderr, "Warning: max_integer (%Lg) is too large; size of long double = %u bytes.\n", max_integer, (unsigned) sizeof(long double)); max_integer /= 10.0L; #endif } #if MINGW || __APPLE__ do_again: #endif start_value = -1.0; end_value = max_integer; number = 0; count_requested = false; /* process command line options: */ while ((i = getopt(argc, argv, "c:thuvp:m:")) >= 0) { switch (i) { case 'c': count_requested = true; if (optarg && !get_double_type_int(optarg, &number)) { usage2(EXIT_FAILURE); } break; case 'h': usage2(EXIT_SUCCESS); break; case 't': twin_flag = true; break; case 'p': pal_flag = true; if (optarg && !get_double_type_int(optarg, &pal_base)) { usage2(EXIT_FAILURE); } break; case 'm': if (optarg) new_size = strtod(optarg, &cp) * BUFFER_SIZE; if (optarg == NULL || cp == NULL || *cp || new_size < 100 || new_size >= (INT_MAX / 2)) { fprintf(stderr, "%s: Invalid memory size multiplier specified.\n", prog_name); exit(EXIT_FAILURE); } buffer_size = (int) new_size; fprintf(stderr, "%s: Window size = %d bytes.\n", prog_name, buffer_size); break; case 'u': setbuf(stdout, NULL); /* make output unbuffered */ setbuf(stderr, NULL); break; case 'v': printf("%s version %s\n", prog_name, VERSION); exit(0); default: usage2(EXIT_FAILURE); } } for (; argc > optind; optind++) { if (strcasecmp(argv[optind], "all") == 0) { if (start_value < 0) start_value = 0; count_requested = true; number = max_integer; continue; } if (strcasecmp(argv[optind], "twin") == 0) { twin_flag = true; continue; } break; } if (argc > optind && isdigit(argv[optind][0])) { if (get_double_type_int(argv[optind], &start_value)) { optind++; } else { usage2(EXIT_FAILURE); } if (argc > optind && isdigit(argv[optind][0])) { if (get_double_type_int(argv[optind], &end_value)) { if (end_value < start_value) { fprintf(stderr, "End value is less than start value.\n"); usage2(EXIT_FAILURE); } optind++; if (number == 0) number = max_integer; } else { usage2(EXIT_FAILURE); } } } for (; argc > optind; optind++) { if (strcasecmp(argv[optind], "all") == 0) { if (start_value < 0) start_value = 0; count_requested = true; number = max_integer; continue; } if (strcasecmp(argv[optind], "twin") == 0) { twin_flag = true; continue; } break; } if (argc > optind) { if (strncasecmp(argv[optind], "pal", 3) == 0) { pal_flag = true; optind++; } else { fprintf(stderr, "Unrecognized argument: \"%s\".\n", argv[optind]); usage(EXIT_FAILURE); } if (argc > optind && isdigit(argv[optind][0])) { if (!get_double_type_int(argv[optind], &pal_base)) { usage(EXIT_FAILURE); } optind++; } } for (; argc > optind; optind++) { if (strcasecmp(argv[optind], "all") == 0) { if (start_value < 0) start_value = 0; count_requested = true; number = max_integer; continue; } if (strcasecmp(argv[optind], "twin") == 0) { twin_flag = true; continue; } break; } if (argc > optind) { fprintf(stderr, "Unrecognized argument: \"%s\".\n", argv[optind]); usage(EXIT_FAILURE); } if (pal_base < 2 || pal_base >= INT_MAX) { fprintf(stderr, "Palindrome number base must be >= 2.\n"); usage(EXIT_FAILURE); } if (start_value < 0.0) { get1: fprintf(stderr, "Enter number to start finding consecutive primes at (0): "); fflush(NULL); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(EXIT_SUCCESS); switch (buf[0]) { case '\0': case '\n': case '\r': start_value = 0; break; default: if (!get_double_type_int(buf, &start_value)) { goto get1; } } } if (number == 0) { get2: fprintf(stderr, "Enter number of%s%s primes to output (0 to end)", pal_flag ? " palindromic" : " consecutive", twin_flag ? " twin" : ""); #if USE_DOUBLES fprintf(stderr, " (%g): ", default_number); #else fprintf(stderr, " (%Lg): ", default_number); #endif fflush(NULL); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(EXIT_SUCCESS); switch (buf[0]) { case '\0': case '\n': case '\r': number = default_number; break; default: if (!get_double_type_int(buf, &number)) { goto get2; } } count_requested = true; } if (prime == NULL) { /* allocate the prime[] buffer: */ prime = (char *) malloc(buffer_size); } if (prime == NULL) { fprintf(stderr, "%s: Not enough memory for buffer_size = %d.\n", prog_name, buffer_size); exit(EXIT_FAILURE); } generate_primes(); #if MINGW || __APPLE__ fflush(NULL); if (argc <= 1 && number > 0) { goto do_again; } #endif exit(EXIT_SUCCESS); } /* * Eliminate all multiples of "arg" from the sieve array by setting their entries to false. * The sieve array "prime[]" is the prime truth values of a consecutive * batch of numbers starting at "start_value". * * When all multiplies of all primes up to the square root * of the highest value represented in sieve array are registered, * the sieve array will tell whether the numbers in it are prime or not. * This is how the Sieve of Eratosthenes works. */ void elim_factor(double_type arg) { double_type d; int i, j; #if USE_DOUBLES d = ceil(start_value / arg); #else d = ceill(start_value / arg); #endif if (d < 2.0) d = 2.0; d *= arg; d -= start_value; if (d >= buffer_size) return; i = (int) d; if (i >= buffer_size) return; assert(i >= 0); if (arg >= buffer_size) { prime[i] = false; } else { j = (int) arg; assert(j > 0); for (; i < buffer_size; i += j) { prime[i] = false; } } } /* * Generate and display at most "number" consecutive prime numbers, * between "start_value" and "end_value". */ void generate_primes(void) { int n, j; double_type count, d, ii, sqrt_value, last_prime = -3.0; for (count = 0; count < number; start_value += buffer_size) { if (start_value > end_value) { goto check_return; } /* generate a batch of consecutive primes with the prime number sieve */ memset(prime, true, buffer_size); /* set the prime array to all true */ #if USE_DOUBLES sqrt_value = 1.0 + sqrt(min(start_value + (double) buffer_size, end_value)); #else sqrt_value = 1.0 + sqrtl(min(start_value + (long double) buffer_size, end_value)); #endif elim_factor((double_type) 2.0); elim_factor((double_type) 3.0); elim_factor((double_type) 5.0); elim_factor((double_type) 7.0); d = 1.0; while (d <= sqrt_value) { for (j = 0; j < ARR_CNT(skip_multiples); j++) { d += skip_multiples[j]; elim_factor(d); } } /* display the requested part of the batch of generated prime numbers */ for (n = 0; n < buffer_size && count < number; n++) { if (prime[n]) { /* if prime number */ ii = start_value + n; if (ii > end_value) { goto check_return; } if (ii > 1.0) { if (pal_flag && !test_pal(ii, pal_base)) continue; if (twin_flag) { if ((last_prime + 2.0) == ii) { #if USE_DOUBLES printf("%.0f %.0f\n", last_prime, ii); #else printf("%.0Lf %.0Lf\n", last_prime, ii); #endif count++; } } else { #if USE_DOUBLES printf("%.0f\n", ii); #else printf("%.0Lf\n", ii); #endif count++; } last_prime = ii; } } } } check_return: if (count_requested && count < number) { fprintf(stderr, "%s: Number of primes requested not reached.\n", prog_name); exit(EXIT_FAILURE); } } /* * Parse a space or null terminated ASCII number in the string pointed to by cp. * * Return true with a floating point double_type value in *dp * if a valid positive integer or zero, * otherwise display an error message and return false. */ int get_double_type_int(char *cp, double_type *dp) { char *cp1; if (cp == NULL || dp == NULL) { return false; } errno = 0; #if USE_DOUBLES *dp = strtod(cp, &cp1); #else *dp = strtold(cp, &cp1); #endif if (errno) { perror(NULL); return false; } if (cp == cp1) { fprintf(stderr, "Invalid number.\n"); return false; } switch (*cp1) { case '\0': case '\r': case '\n': break; default: if (isspace(*cp1)) { break; } fprintf(stderr, "Invalid number.\n"); return false; } if (*dp > max_integer) { #if USE_DOUBLES fprintf(stderr, "Number is too large, maximum is %g.\n", max_integer); #else fprintf(stderr, "Number is too large, maximum is %Lg.\n", max_integer); #endif return false; } #if USE_DOUBLES if (*dp < 0.0 || fmod(*dp, 1.0) != 0.0) { #else if (*dp < 0.0 || fmodl(*dp, 1.0L) != 0.0) { #endif fprintf(stderr, "Number must be a positive integer or zero.\n"); return false; } return true; } /* * Return true if "d" is a palindromic number, base "base". */ int test_pal(double_type d, double_type base) { #define MAX_DIGITS 1000 int i, j; int digits[MAX_DIGITS]; /* build the array of digits[] */ for (i = 0; d >= 1.0; i++) { assert(i < MAX_DIGITS); #if USE_DOUBLES digits[i] = (int) (fmod(d, base)); #else digits[i] = (int) (fmodl(d, base)); #endif d /= base; } /* compare the array of digits[] end to end */ for (j = 0, i--; j < i; j++, i--) { if (digits[i] != digits[j]) return false; } return true; } void usage(int ev) { printf("Prime number generator version %s\n", VERSION); printf("Usage: %s [start [stop] or \"all\"] [\"twin\"] [\"pal\" [base]]\n\n", prog_name); #if USE_DOUBLES printf("Generate consecutive prime numbers from start to stop, up to %g.\n", max_integer); #else printf("Generate consecutive prime numbers from start to stop, up to %Lg.\n", max_integer); #endif printf("If \"twin\" is specified, output only twin primes.\n"); printf("If \"pal\" is specified, output only palindromic primes.\n"); printf("The palindrome number base may be specified, the default is base 10.\n"); exit(ev); } void usage2(int ev) { printf("Prime number generator version %s\n", VERSION); printf("Usage: %s [options] [start [stop]]\n\n", prog_name); #if USE_DOUBLES printf("Generate consecutive prime numbers from start to stop, up to %g.\n", max_integer); #else printf("Generate consecutive prime numbers from start to stop, up to %Lg.\n", max_integer); #endif printf("Options:\n"); printf(" -c count Count lines of primes, stop when count reached.\n"); printf(" -h Display this help and exit.\n"); printf(" -m number Specify a memory size multiplier.\n"); printf(" -p base Output only palindromic primes.\n"); printf(" -t Output only twin primes.\n"); printf(" -u Set all output to be unbuffered.\n"); printf(" -v Display version number, then exit successfully.\n"); exit(ev); }