diff options
Diffstat (limited to 'usr.bin/factor/factor.c')
-rw-r--r-- | usr.bin/factor/factor.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/usr.bin/factor/factor.c b/usr.bin/factor/factor.c new file mode 100644 index 000000000000..9d8fdbe1fb0f --- /dev/null +++ b/usr.bin/factor/factor.c @@ -0,0 +1,399 @@ +/* + * 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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * factor - factor a number into primes + * + * By: Landon Curt Noll chongo@toad.com, ...!{sun,tolsoft}!hoptoad!chongo + * + * chongo <for a good prime call: 391581 * 2^216193 - 1> /\oo/\ + * + * usage: + * factor [-h] [number] ... + * + * The form of the output is: + * + * number: factor1 factor1 factor2 factor3 factor3 factor3 ... + * + * where factor1 <= factor2 <= factor3 <= ... + * + * If no args are given, the list of numbers are read from stdin. + */ + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "primes.h" + +#ifdef HAVE_OPENSSL + +#include <openssl/bn.h> + +#if OPENSSL_VERSION_NUMBER < 0x30000000L +static inline int +BN_check_prime(BIGNUM *p, BN_CTX *ctx, BN_GENCB *cb) +{ + const int nchecks = 5; + + return BN_is_prime_ex(p, nchecks, ctx, cb); +} +#endif + +static void pollard_pminus1(BIGNUM *); /* print factors for big numbers */ + +#else + +typedef ubig BIGNUM; +typedef u_long BN_ULONG; + +#define BN_CTX int +#define BN_CTX_new() NULL +#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)) + +static int BN_dec2bn(BIGNUM **, const char *); +static int BN_hex2bn(BIGNUM **, const char *); +static BN_ULONG BN_div_word(BIGNUM *, BN_ULONG); +static void BN_print_fp(FILE *, const BIGNUM *); + +#endif + +static void BN_print_dec_fp(FILE *, const BIGNUM *); +static void convert_str2bn(BIGNUM **, char *); +static bool is_hex_str(char *); +static void pr_fact(BIGNUM *); /* print factors of a value */ +static void pr_print(BIGNUM *); /* print a prime */ +static void usage(void); + +static BN_CTX *ctx; /* just use a global context */ +static int hflag; + +int +main(int argc, char *argv[]) +{ + BIGNUM *val; + int ch; + char *p, buf[LINE_MAX]; /* > max number of digits. */ + + ctx = BN_CTX_new(); + val = BN_new(); + if (val == NULL) + errx(1, "can't initialise bignum"); + + while ((ch = getopt(argc, argv, "h")) != -1) + switch (ch) { + case 'h': + hflag++; + break; + case '?': + default: + usage(); + } + 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(*p); ++p); + if (*p == '\n' || *p == '\0') + continue; + convert_str2bn(&val, p); + pr_fact(val); + } + /* Factor the arguments. */ + else + for (p = *argv; p != NULL; p = *++argv) { + convert_str2bn(&val, p); + pr_fact(val); + } + exit(0); +} + +/* + * pr_fact - print the factors of a number + * + * Print the factors of the number, from the lowest to the highest. + * A factor will be printed multiple times if it divides the value + * multiple times. + * + * Factors are printed with leading tabs. + */ +static void +pr_fact(BIGNUM *val) +{ + const ubig *fact; /* The factor found. */ + + /* Firewall - catch 0 and 1. */ + if (BN_is_zero(val)) /* Historical practice; 0 just exits. */ + exit(0); + if (BN_is_one(val)) { + printf("1: 1\n"); + return; + } + + /* Factor value. */ + + if (hflag) { + fputs("0x", stdout); + BN_print_fp(stdout, val); + } else + BN_print_dec_fp(stdout, val); + putchar(':'); + for (fact = &prime[0]; !BN_is_one(val); ++fact) { + /* Look for the smallest factor. */ + do { + if (BN_mod_word(val, (BN_ULONG)*fact) == 0) + break; + } while (++fact <= pr_limit); + + /* Watch for primes larger than the table. */ + if (fact > pr_limit) { +#ifdef HAVE_OPENSSL + BIGNUM *bnfact; + + bnfact = BN_new(); + BN_set_word(bnfact, *(fact - 1)); + if (!BN_sqr(bnfact, bnfact, ctx)) + errx(1, "error in BN_sqr()"); + if (BN_cmp(bnfact, val) > 0 || + BN_check_prime(val, NULL, NULL) == 1) + pr_print(val); + else + pollard_pminus1(val); +#else + pr_print(val); +#endif + break; + } + + /* Divide factor out until none are left. */ + do { + printf(hflag ? " 0x%" PRIx64 "" : " %" PRIu64 "", *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); + } + putchar('\n'); +} + +static void +pr_print(BIGNUM *val) +{ + if (hflag) { + fputs(" 0x", stdout); + BN_print_fp(stdout, val); + } else { + putchar(' '); + BN_print_dec_fp(stdout, val); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: factor [-h] [value ...]\n"); + exit(1); +} + +#ifdef HAVE_OPENSSL + +/* pollard p-1, algorithm from Jim Gillogly, May 2000 */ +static void +pollard_pminus1(BIGNUM *val) +{ + BIGNUM *base, *rbase, *num, *i, *x; + + base = BN_new(); + rbase = BN_new(); + num = BN_new(); + i = BN_new(); + x = BN_new(); + + BN_set_word(rbase, 1); +newbase: + if (!BN_add_word(rbase, 1)) + errx(1, "error in BN_add_word()"); + BN_set_word(i, 2); + BN_copy(base, rbase); + + for (;;) { + BN_mod_exp(base, base, i, val, ctx); + if (BN_is_one(base)) + goto newbase; + + BN_copy(x, base); + BN_sub_word(x, 1); + if (!BN_gcd(x, x, val, ctx)) + errx(1, "error in BN_gcd()"); + + if (!BN_is_one(x)) { + if (BN_check_prime(x, NULL, NULL) == 1) + pr_print(x); + else + pollard_pminus1(x); + fflush(stdout); + + BN_div(num, NULL, val, x, ctx); + if (BN_is_one(num)) + return; + if (BN_check_prime(num, NULL, NULL) == 1) { + pr_print(num); + fflush(stdout); + return; + } + BN_copy(val, num); + } + if (!BN_add_word(i, 1)) + errx(1, "error in BN_add_word()"); + } +} + +/* + * 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, "%s", buf); + free(buf); +} + +#else + +static void +BN_print_fp(FILE *fp, const BIGNUM *num) +{ + fprintf(fp, "%lx", (unsigned long)*num); +} + +static void +BN_print_dec_fp(FILE *fp, const BIGNUM *num) +{ + fprintf(fp, "%lu", (unsigned long)*num); +} + +static int +BN_dec2bn(BIGNUM **a, const char *str) +{ + char *p; + + errno = 0; + **a = strtoul(str, &p, 10); + return (errno == 0 ? 1 : 0); /* OpenSSL returns 0 on error! */ +} + +static int +BN_hex2bn(BIGNUM **a, const char *str) +{ + char *p; + + errno = 0; + **a = strtoul(str, &p, 16); + return (errno == 0 ? 1 : 0); /* OpenSSL returns 0 on error! */ +} + +static BN_ULONG +BN_div_word(BIGNUM *a, BN_ULONG b) +{ + BN_ULONG mod; + + mod = *a % b; + *a /= b; + return mod; +} + +#endif + +/* + * Scan the string from left-to-right to see if the longest substring + * is a valid hexadecimal number. + */ +static bool +is_hex_str(char *str) +{ + char c, *p; + bool saw_hex = false; + + for (p = str; *p; p++) { + if (isdigit(*p)) + continue; + c = tolower(*p); + if (c >= 'a' && c <= 'f') { + saw_hex = true; + continue; + } + break; /* Not a hexadecimal digit. */ + } + return saw_hex; +} + +/* Convert string pointed to by *str to a bignum. */ +static void +convert_str2bn(BIGNUM **val, char *p) +{ + int n = 0; + + if (*p == '+') p++; + if (*p == '-') + errx(1, "negative numbers aren't permitted."); + if (*p == '0') { + p++; + if (*p == 'x' || *p == 'X') + n = BN_hex2bn(val, ++p); + } else { + n = is_hex_str(p) ? BN_hex2bn(val, p) : BN_dec2bn(val, p); + } + if (n == 0) + errx(1, "%s: illegal numeric format.", p); +} |