123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- /*
- * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com>
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
- //config:config FACTOR
- //config: bool "factor"
- //config: default y
- //config: help
- //config: factor factorizes integers
- //applet:IF_FACTOR(APPLET(factor, BB_DIR_USR_BIN, BB_SUID_DROP))
- //kbuild:lib-$(CONFIG_FACTOR) += factor.o
- //usage:#define factor_trivial_usage
- //usage: "[NUMBER]..."
- //usage:#define factor_full_usage "\n\n"
- //usage: "Print prime factors"
- #include "libbb.h"
- #if 0
- # define dbg(...) bb_error_msg(__VA_ARGS__)
- #else
- # define dbg(...) ((void)0)
- #endif
- typedef unsigned long long wide_t;
- #if ULLONG_MAX == (UINT_MAX * UINT_MAX + 2 * UINT_MAX)
- /* "unsigned" is half as wide as ullong */
- typedef unsigned half_t;
- #define HALF_MAX UINT_MAX
- #define HALF_FMT ""
- #elif ULLONG_MAX == (ULONG_MAX * ULONG_MAX + 2 * ULONG_MAX)
- /* long is half as wide as ullong */
- typedef unsigned long half_t;
- #define HALF_MAX ULONG_MAX
- #define HALF_FMT "l"
- #else
- #error Cant find an integer type which is half as wide as ullong
- #endif
- static half_t isqrt_odd(wide_t N)
- {
- half_t s = isqrt(N);
- /* Subtract 1 from even s, odd s won't change: */
- /* (doesnt work for zero, but we know that s != 0 here) */
- s = (s - 1) | 1;
- return s;
- }
- static NOINLINE void factorize(wide_t N)
- {
- half_t factor;
- half_t max_factor;
- // unsigned count3;
- // unsigned count5;
- // unsigned count7;
- // ^^^^^^^^^^^^^^^ commented-out simple sieving code (easier to grasp).
- // Faster sieving, using one word for potentially up to 6 counters:
- // count upwards in each mask, counter "triggers" when it sets its mask to "100[0]..."
- // 10987654321098765432109876543210 - bits 31-0 in 32-bit word
- // 17777713333311111777775555333 - bit masks for counters for primes 3,5,7,11,13,17
- // 100000100001000010001001 - value for adding 1 to each mask
- // 10000010000010000100001000100 - value for checking that any mask reached msb
- enum {
- SHIFT_3 = 1 << 0,
- SHIFT_5 = 1 << 3,
- SHIFT_7 = 1 << 7,
- INCREMENT_EACH = SHIFT_3 | SHIFT_5 | SHIFT_7,
- MULTIPLE_OF_3 = 1 << 2,
- MULTIPLE_OF_5 = 1 << 6,
- MULTIPLE_OF_7 = 1 << 11,
- MULTIPLE_DETECTED = MULTIPLE_OF_3 | MULTIPLE_OF_5 | MULTIPLE_OF_7,
- };
- unsigned sieve_word;
- if (N < 4)
- goto end;
- while (!(N & 1)) {
- printf(" 2");
- N >>= 1;
- }
- /* The code needs to be optimized for the case where
- * there are large prime factors. For example,
- * this is not hard:
- * 8262075252869367027 = 3 7 17 23 47 101 113 127 131 137 823
- * (the largest factor to test is only ~sqrt(823) = 28)
- * but this is:
- * 18446744073709551601 = 53 348051774975651917
- * the last factor requires testing up to
- * 589959129 - about 100 million iterations.
- * The slowest case (largest prime) for N < 2^64 is
- * factor 18446744073709551557 (0xffffffffffffffc5).
- */
- max_factor = isqrt_odd(N);
- // count3 = 3;
- // count5 = 6;
- // count7 = 9;
- sieve_word = 0
- /* initial count for SHIFT_n is (n-1)/2*3: */
- + (MULTIPLE_OF_3 - 3 * SHIFT_3)
- + (MULTIPLE_OF_5 - 6 * SHIFT_5)
- + (MULTIPLE_OF_7 - 9 * SHIFT_7)
- //+ (MULTIPLE_OF_11 - 15 * SHIFT_11)
- //+ (MULTIPLE_OF_13 - 18 * SHIFT_13)
- //+ (MULTIPLE_OF_17 - 24 * SHIFT_17)
- ;
- factor = 3;
- for (;;) {
- /* The division is the most costly part of the loop.
- * On 64bit CPUs, takes at best 12 cycles, often ~20.
- */
- while ((N % factor) == 0) { /* not likely */
- N = N / factor;
- printf(" %"HALF_FMT"u", factor);
- max_factor = isqrt_odd(N);
- }
- next_factor:
- if (factor >= max_factor)
- break;
- factor += 2;
- /* Rudimentary wheel sieving: skip multiples of 3, 5 and 7:
- * Every third odd number is divisible by three and thus isn't a prime:
- * 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47...
- * ^ ^ ^ ^ ^ ^ ^ _ ^ ^ _ ^ ^ ^ ^
- * (^ = primes, _ = would-be-primes-if-not-divisible-by-5)
- * The numbers with space under them are excluded by sieve 3.
- */
- // count7--;
- // count5--;
- // count3--;
- // if (count3 && count5 && count7)
- // continue;
- sieve_word += INCREMENT_EACH;
- if (!(sieve_word & MULTIPLE_DETECTED))
- continue;
- /*
- * "factor" is multiple of 3 33% of the time (count3 reached 0),
- * else, multiple of 5 13% of the time,
- * else, multiple of 7 7.6% of the time.
- * Cumulatively, with 3,5,7 sieving we are here 54.3% of the time.
- */
- // if (count3 == 0)
- // count3 = 3;
- if (sieve_word & MULTIPLE_OF_3)
- sieve_word -= SHIFT_3 * 3;
- // if (count5 == 0)
- // count5 = 5;
- if (sieve_word & MULTIPLE_OF_5)
- sieve_word -= SHIFT_5 * 5;
- // if (count7 == 0)
- // count7 = 7;
- if (sieve_word & MULTIPLE_OF_7)
- sieve_word -= SHIFT_7 * 7;
- goto next_factor;
- }
- end:
- if (N > 1)
- printf(" %llu", N);
- bb_putchar('\n');
- }
- static void factorize_numstr(const char *numstr)
- {
- wide_t N;
- /* Leading + is ok (coreutils compat) */
- if (*numstr == '+')
- numstr++;
- N = bb_strtoull(numstr, NULL, 10);
- if (errno)
- bb_show_usage();
- printf("%llu:", N);
- factorize(N);
- }
- int factor_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int factor_main(int argc UNUSED_PARAM, char **argv)
- {
- //// coreutils has undocumented option ---debug (three dashes)
- //getopt32(argv, "");
- //argv += optind;
- argv++;
- if (!*argv) {
- /* Read from stdin, several numbers per line are accepted */
- for (;;) {
- char *numstr, *line;
- line = xmalloc_fgetline(stdin);
- if (!line)
- return EXIT_SUCCESS;
- numstr = line;
- for (;;) {
- char *end;
- numstr = skip_whitespace(numstr);
- if (!numstr[0])
- break;
- end = skip_non_whitespace(numstr);
- if (*end != '\0')
- *end++ = '\0';
- factorize_numstr(numstr);
- numstr = end;
- }
- free(line);
- }
- }
- do {
- /* Leading spaces are ok (coreutils compat) */
- factorize_numstr(skip_whitespace(*argv));
- } while (*++argv);
- return EXIT_SUCCESS;
- }
|