File: matho-primes.c

package info (click to toggle)
mathomatic 16.0.5-5.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,192 kB
  • sloc: ansic: 22,029; makefile: 340; sh: 319; python: 96; awk: 39
file content (596 lines) | stat: -rw-r--r-- 16,580 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
#include <float.h>
#include <assert.h>
#if	!NO_GETOPT_H
#include <getopt.h>
#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);
}