]> git.cameronkatri.com Git - bsdgames-darwin.git/blobdiff - factor/factor.c
Follow the Fundamental Theory of Algebra. Disallow factorising of
[bsdgames-darwin.git] / factor / factor.c
index 73f8f6504cc4af53a4536fb5112c3e90a492288d..10de005d0fa3efe21b46fc487578c3eb95613692 100644 (file)
@@ -1,6 +1,8 @@
+/*     $NetBSD: factor.c,v 1.24 2010/05/15 21:22:39 joerg Exp $        */
+
 /*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Landon Curt Noll.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
- All rights reserved.\n";
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
+ The Regents of the University of California.  All rights reserved.");
 #endif /* not lint */
 
 #ifndef lint
-/*static char sccsid[] = "from: @(#)factor.c   4.4 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: factor.c,v 1.3 1993/12/08 07:27:50 mycroft Exp $";
+#if 0
+static char sccsid[] = "@(#)factor.c   8.4 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: factor.c,v 1.24 2010/05/15 21:22:39 joerg Exp $");
+#endif
 #endif /* not lint */
 
 /*
@@ -59,218 +60,131 @@ static char rcsid[] = "$Id: factor.c,v 1.3 1993/12/08 07:27:50 mycroft Exp $";
  *
  *     number: factor1 factor1 factor2 factor3 factor3 factor3 ...
  *
- * where factor1 < factor2 < factor3 < ...
+ * where factor1 <= factor2 <= factor3 <= ...
  *
  * If no args are given, the list of numbers are read from stdin.
  */
 
-#include <stdio.h>
 #include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/bn.h>
+#else
+typedef long   BIGNUM;
+typedef u_long BN_ULONG;
+static int BN_dec2bn(BIGNUM **a, const char *str);
+#define BN_new()               ((BIGNUM *)calloc(sizeof(BIGNUM), 1))
+#define BN_is_zero(v)          (*(v) == 0)
+#define BN_is_one(v)           (*(v) == 1)
+#define BN_new()               ((BIGNUM *)calloc(sizeof(BIGNUM), 1))
+#define BN_is_zero(v)          (*(v) == 0)
+#define BN_is_one(v)           (*(v) == 1)
+#define BN_mod_word(a, b)      (*(a) % (b))
+#endif
+
 #include "primes.h"
 
 /*
  * prime[i] is the (i-1)th prime.
  *
- * We are able to sieve 2^32-1 because this byte table yields all primes 
+ * We are able to sieve 2^32-1 because this byte table yields all primes
  * up to 65537 and 65537^2 > 2^32-1.
  */
-extern ubig prime[];
-extern ubig *pr_limit; /* largest prime in the prime array */
-
-#define MAX_LINE 255   /* max line allowed on stdin */
-
-void pr_fact();                /* print factors of a value */
-long small_fact();     /* find smallest factor of a value */
-char *read_num_buf();  /* read a number buffer */
-char *program;         /* name of this program */
-
-main(argc, argv)
-       int argc;       /* arg count */
-       char *argv[];   /* the args */
+extern const ubig prime[];
+extern const ubig *pr_limit;           /* largest prime in the prime array */
+#if 0 /* debugging: limit table use to stress the "pollard" code */
+#define pr_limit &prime[0]
+#endif
+
+#define        PRIME_CHECKS    5
+
+#ifdef HAVE_OPENSSL 
+static BN_CTX *ctx;                    /* just use a global context */
+#endif
+
+static void pr_fact(BIGNUM *);         /* print factors of a value */
+static void BN_print_dec_fp(FILE *, const BIGNUM *);
+static void usage(void) __dead;
+#ifdef HAVE_OPENSSL
+static void pollard_rho(BIGNUM *);     /* print factors for big numbers */
+#else
+static char *BN_bn2dec(const BIGNUM *);
+static BN_ULONG BN_div_word(BIGNUM *, BN_ULONG);
+#endif
+
+
+#ifndef HAVE_OPENSSL
+static int
+BN_dec2bn(BIGNUM **a, const char *str)
 {
-       int arg;        /* which arg to factor */
-       long val;       /* the value to factor */
-       char buf[MAX_LINE+1];   /* input buffer */
-
-       /* parse args */
-       program = argv[0];
-       if (argc >= 2) {
-
-               /* factor each arg */
-               for (arg=1; arg < argc; ++arg) {
-
-                       /* process the buffer */
-                       if (read_num_buf(NULL, argv[arg]) == NULL) {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-                       }
-
-                       /* factor the argument */
-                       if (sscanf(argv[arg], "%ld", &val) == 1) {
-                               pr_fact(val);
-                       } else {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-                       }
-               }
+       char *p;
 
-       /* no args supplied, read numbers from stdin */
-       } else {
-               /*
-                * read asciii numbers from input
-                */
-               while (read_num_buf(stdin, buf) != NULL) {
-
-                       /* factor the argument */
-                       if (sscanf(buf, "%ld", &val) == 1) {
-                               pr_fact(val);
-                       }
-               }
-       }
-       exit(0);
+       errno = 0;
+       **a = strtoul(str, &p, 10);
+       if (errno)
+               err(1, "%s", str);
+       return (*p == '\n' || *p == '\0');
 }
+#endif
 
-/*
- * read_num_buf - read a number buffer from a stream
- *
- * Read a number on a line of the form:
- *
- *     ^[ \t]*\([+-]?[0-9][0-9]\)*.*$
- *
- * where ? is a 1-or-0 operator and the number is within \( \).
- *
- * If does not match the above pattern, it is ignored and a new
- * line is read.  If the number is too large or small, we will
- * print ouch and read a new line.
- *
- * We have to be very careful on how we check the magnitude of the
- * input.  We can not use numeric checks because of the need to
- * check values against maximum numeric values.
- *
- * This routine will return a line containing a ascii number between
- * NEG_SEMIBIG and SEMIBIG, or it will return NULL.
- *
- * If the stream is NULL then buf will be processed as if were
- * a single line stream.
- *
- * returns:
- *     char *  pointer to leading digit, + or -
- *     NULL    EOF or error
- */
-char *
-read_num_buf(input, buf)
-       FILE *input;            /* input stream or NULL */
-       char *buf;              /* input buffer */
+int
+main(int argc, char *argv[])
 {
-       static char limit[MAX_LINE+1];  /* ascii value of SEMIBIG */
-       static int limit_len;           /* digit count of limit */
-       static char neg_limit[MAX_LINE+1];      /* value of NEG_SEMIBIG */
-       static int neg_limit_len;               /* digit count of neg_limit */
-       int len;                        /* digits in input (excluding +/-) */
-       char *s;        /* line start marker */
-       char *d;        /* first digit, skip +/- */
-       char *p;        /* scan pointer */
-       char *z;        /* zero scan pointer */
-
-       /* form the ascii value of SEMIBIG if needed */
-       if (!isascii(limit[0]) || !isdigit(limit[0])) {
-               sprintf(limit, "%ld", SEMIBIG);
-               limit_len = strlen(limit);
-               sprintf(neg_limit, "%ld", NEG_SEMIBIG);
-               neg_limit_len = strlen(neg_limit)-1;    /* exclude - */
-       }
-       
-       /*
-        * the search for a good line
-        */
-       if (input != NULL && fgets(buf, MAX_LINE, input) == NULL) {
-               /* error or EOF */
-               return NULL;
-       }
-       do {
-
-               /* ignore leading whitespace */
-               for (s=buf; *s && s < buf+MAX_LINE; ++s) {
-                       if (!isascii(*s) || !isspace(*s)) {
-                               break;
-                       }
-               }
-
-               /* skip over any leading + or - */
-               if (*s == '+' || *s == '-') {
-                       d = s+1;
-               } else {
-                       d = s;
+       BIGNUM *val;
+       int ch;
+       char *p, buf[LINE_MAX];         /* > max number of digits. */
+
+#ifdef HAVE_OPENSSL 
+       ctx = BN_CTX_new();
+#endif
+       val = BN_new();
+       if (val == NULL)
+               errx(1, "can't initialise bignum");
+
+       while ((ch = getopt(argc, argv, "")) != -1)
+               switch (ch) {
+               case '?':
+               default:
+                       usage();
                }
-
-               /* note leading zeros */
-               for (z=d; *z && z < buf+MAX_LINE; ++z) {
-                       if (*z != '0') {
-                               break;
+       argc -= optind;
+       argv += optind;
+
+       /* No args supplied, read numbers from stdin. */
+       if (argc == 0)
+               for (;;) {
+                       if (fgets(buf, sizeof(buf), stdin) == NULL) {
+                               if (ferror(stdin))
+                                       err(1, "stdin");
+                               exit (0);
                        }
+                       for (p = buf; isblank((unsigned char)*p); ++p);
+                       if (*p == '\n' || *p == '\0')
+                               continue;
+                       if (*p == '-')
+                               errx(1, "negative numbers aren't permitted.");
+                       if (BN_dec2bn(&val, buf) == 0)
+                               errx(1, "%s: illegal numeric format.", argv[0]);
+                       pr_fact(val);
                }
-
-               /* scan for the first non-digit */
-               for (p=d; *p && p < buf+MAX_LINE; ++p) {
-                       if (!isascii(*p) || !isdigit(*p)) {
-                               break;
-                       }
-               }
-
-               /* ignore empty lines */
-               if (p == d) {
-                       continue;
-               }
-               *p = '\0';
-
-               /* object if too many digits */
-               len = strlen(z);
-               len = (len<=0) ? 1 : len;
-               if (*s == '-') {
-                       /* accept if digit count is below limit */
-                       if (len < neg_limit_len) {
-                               /* we have good input */
-                               return s;
-
-                       /* reject very large numbers */
-                       } else if (len > neg_limit_len) {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-
-                       /* carefully check against near limit numbers */
-                       } else if (strcmp(z, neg_limit+1) > 0) {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-                       }
-                       /* number is near limit, but is under it */
-                       return s;
-               
-               } else {
-                       /* accept if digit count is below limit */
-                       if (len < limit_len) {
-                               /* we have good input */
-                               return s;
-
-                       /* reject very large numbers */
-                       } else if (len > limit_len) {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-
-                       /* carefully check against near limit numbers */
-                       } else if (strcmp(z, limit) > 0) {
-                               fprintf(stderr, "%s: ouch\n", program);
-                               exit(1);
-                       }
-                       /* number is near limit, but is under it */
-                       return s;
+       /* Factor the arguments. */
+       else
+               for (; *argv != NULL; ++argv) {
+                       if (argv[0][0] == '-')
+                               errx(1, "numbers <= 1 aren't permitted.");
+                       if (BN_dec2bn(&val, argv[0]) == 0)
+                               errx(1, "%s: illegal numeric format.", argv[0]);
+                       pr_fact(val);
                }
-       } while (input != NULL && fgets(buf, MAX_LINE, input) != NULL);
-
-       /* error or EOF */
-       return NULL;
+       exit(0);
 }
 
-
 /*
  * pr_fact - print the factors of a number
  *
@@ -284,64 +198,184 @@ read_num_buf(input, buf)
  *
  * Factors are printed with leading tabs.
  */
-void
-pr_fact(val)
-       long val;       /* factor this value */
+static void
+pr_fact(BIGNUM *val)
 {
-       ubig *fact;     /* the factor found */
-
-       /* firewall - catch 0 and 1 */
-       switch (val) {
-       case -(2147483648U):
-               /* avoid negation problems */
-               puts("-2147483648: -1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2\n");
-               return;
-       case -1:
-               puts("-1: -1\n");
-               return;
-       case 0:
-               exit(0);
-       case 1:
-               puts("1: 1\n");
-               return;
-       default:
-               if (val < 0) {
-                       val = -val;
-                       printf("%ld: -1", val);
-               } else {
-                       printf("%ld:", val);
-               }
-               fflush(stdout);
-               break;
-       }
+       const ubig *fact;               /* The factor found. */
 
-       /*
-        * factor value
-        */
-       fact = &prime[0];
-       while (val > 1) {
+       /* Firewall - catch 0 and 1. */
+       if (BN_is_zero(val) || BN_is_one(val))
+               errx(1, "numbers <= 1 aren't permitted.");
 
-               /* look for the smallest factor */
-               do {
-                       if (val%(long)*fact == 0) {
+       /* Factor value. */
+
+       BN_print_dec_fp(stdout, val);
+       putchar(':');
+       for (fact = &prime[0]; !BN_is_one(val); ++fact) {
+               /* Look for the smallest factor. */
+               while (fact <= pr_limit) {
+                       if (BN_mod_word(val, (BN_ULONG)*fact) == 0)
                                break;
-                       }
-               } while (++fact <= pr_limit);
+                       fact++;
+               }
 
-               /* watch for primes larger than the table */
+               /* Watch for primes larger than the table. */
                if (fact > pr_limit) {
-                       printf(" %ld\n", val);
-                       return;
+#ifdef HAVE_OPENSSL
+                       BIGNUM *bnfact;
+
+                       bnfact = BN_new();
+                       BN_set_word(bnfact, (BN_ULONG)*(fact - 1));
+                       BN_sqr(bnfact, bnfact, ctx);
+                       if (BN_cmp(bnfact, val) > 0
+                           || BN_is_prime(val, PRIME_CHECKS, NULL, NULL,
+                                          NULL) == 1) {
+                               putchar(' ');
+                               BN_print_dec_fp(stdout, val);
+                       } else
+                               pollard_rho(val);
+#else
+                       printf(" %s", BN_bn2dec(val));
+#endif
+                       break;
                }
 
-               /* divide factor out until none are left */
+               /* Divide factor out until none are left. */
                do {
-                       printf(" %ld", *fact);
-                       val /= (long)*fact;
-               } while ((val % (long)*fact) == 0);
+                       printf(" %lu", *fact);
+                       BN_div_word(val, (BN_ULONG)*fact);
+               } while (BN_mod_word(val, (BN_ULONG)*fact) == 0);
+
+               /* Let the user know we're doing something. */
                fflush(stdout);
-               ++fact;
        }
        putchar('\n');
-       return;
 }
+
+/*
+ * Sigh..  No _decimal_ output to file functions in BN.
+ */
+static void
+BN_print_dec_fp(FILE *fp, const BIGNUM *num)
+{
+       char *buf;
+       
+       buf = BN_bn2dec(num);
+       if (buf == NULL)
+               return; /* XXX do anything here? */
+       fprintf(fp, buf);
+       free(buf);
+}
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: factor [value ...]\n");
+       exit (0);
+}
+
+
+
+
+#ifdef HAVE_OPENSSL
+static void
+pollard_rho(BIGNUM *val)
+{
+       BIGNUM *x, *y, *tmp, *num;
+       BN_ULONG a;
+       unsigned int steps_taken, steps_limit;
+
+       x = BN_new();
+       y = BN_new();
+       tmp = BN_new();
+       num = BN_new();
+       a = 1;
+restart:
+       steps_taken = 0;
+       steps_limit = 2;
+       BN_set_word(x, 1);
+       BN_copy(y, x);
+
+       for (;;) {
+               BN_sqr(tmp, x, ctx);
+               BN_add_word(tmp, a);
+               BN_mod(x, tmp, val, ctx);
+               BN_sub(tmp, x, y);
+               if (BN_is_zero(tmp)) {
+#ifdef DEBUG
+                       printf(" (loop)");
+#endif
+                       a++;
+                       goto restart;
+               }
+               BN_gcd(tmp, tmp, val, ctx);
+
+               if (!BN_is_one(tmp)) {
+                       if (BN_is_prime(tmp, PRIME_CHECKS, NULL, NULL,
+                           NULL) == 1) {
+                               putchar(' ');
+                               BN_print_dec_fp(stdout, tmp);
+                       } else {
+#ifdef DEBUG
+                               printf(" (recurse for ");
+                               BN_print_dec_fp(stdout, tmp);
+                               putchar(')');
+#endif
+                               pollard_rho(BN_dup(tmp));
+#ifdef DEBUG
+                               printf(" (back)");
+#endif
+                       }
+                       fflush(stdout);
+
+                       BN_div(num, NULL, val, tmp, ctx);
+                       if (BN_is_one(num))
+                               return;
+                       if (BN_is_prime(num, PRIME_CHECKS, NULL, NULL,
+                           NULL) == 1) {
+                               putchar(' ');
+                               BN_print_dec_fp(stdout, num);
+                               fflush(stdout);
+                               return;
+                       }
+                       BN_copy(val, num);
+                       goto restart;
+               }
+               steps_taken++;
+               if (steps_taken == steps_limit) {
+                       BN_copy(y, x); /* teleport the turtle */
+                       steps_taken = 0;
+                       steps_limit *= 2;
+                       if (steps_limit == 0) {
+#ifdef DEBUG
+                               printf(" (overflow)");
+#endif
+                               a++;
+                               goto restart;
+                       }
+               }
+       }
+}
+#else
+char *
+BN_bn2dec(const BIGNUM *val)
+{
+       char *buf;
+
+       buf = malloc(100);
+       if (!buf)
+               return buf;
+       snprintf(buf, 100, "%ld", (long)*val);
+       return buf;
+}
+
+static BN_ULONG
+BN_div_word(BIGNUM *a, BN_ULONG b)
+{
+       BN_ULONG mod;
+
+       mod = *a % b;
+       *a /= b;
+       return mod;
+}
+#endif