mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
318 lines
8.6 KiB
Plaintext
318 lines
8.6 KiB
Plaintext
/*
|
|
* Copyright (c) 1997 Landon Curt Noll
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and
|
|
* its documentation for any purpose and without fee is hereby granted,
|
|
* provided that the above copyright, this permission notice and text
|
|
* this comment, and the disclaimer below appear in all of the following:
|
|
*
|
|
* supporting documentation
|
|
* source copies
|
|
* source works derived from this source
|
|
* binaries derived from this source or from derived source
|
|
*
|
|
* LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
|
* EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
|
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Landon Curt Noll
|
|
* http://reality.sgi.com/chongo
|
|
*
|
|
* chongo <was here> /\../\
|
|
*/
|
|
|
|
|
|
/*
|
|
* hset method
|
|
*
|
|
* We will assume that mfactor is called with p_elim == 17.
|
|
*
|
|
* n = (the Mersenne exponent we are testing)
|
|
* Q = 4*2*3*5*7*11*13*17 (4 * pfact(of some reasonable integer))
|
|
*
|
|
* We first determine all values of h mod Q such that:
|
|
*
|
|
* gcd(h*n+1, Q) == 1 and h*n+1 == +/-1 mod 8
|
|
*
|
|
* There will be 2*1*2*4*6*10*12*16 such values of h.
|
|
*
|
|
* For efficiency, we keep the difference between consecutive h values
|
|
* in the hset[] difference array with hset[0] being the first h value.
|
|
* Last, we multiply the hset[] values by n so that we only need
|
|
* to add sequential values of hset[] to get factor candidates.
|
|
*
|
|
* We need only test factors of the form:
|
|
*
|
|
* (Q*g*n + hx) + 1
|
|
*
|
|
* where:
|
|
*
|
|
* g is an integer >= 0
|
|
* hx is computed from hset[] difference value described above
|
|
*
|
|
* Note that (Q*g*n + hx) is always even and that hx is a multiple
|
|
* of n. Thus the typical factor form:
|
|
*
|
|
* 2*k*n + 1
|
|
*
|
|
* implies that:
|
|
*
|
|
* k = (Q*g + hx/n)/2
|
|
*
|
|
* This allows us to quickly eliminate factor values that are divisible
|
|
* by 2, 3, 5, 7, 11, 13 or 17. (well <= p value found below)
|
|
*
|
|
* The following loop shows how test_factor is advanced to higher test
|
|
* values using hset[]. Here, hcount is the number of elements in hset[].
|
|
* It can be shown that hset[0] == 0. We add hset[hcount] to the hset[]
|
|
* array for looping control convenience.
|
|
*
|
|
* (* increase test_factor thru other possible test values *)
|
|
* test_factor = 0;
|
|
* hindx = 0;
|
|
* do {
|
|
* while (++hindx <= hcount) {
|
|
* test_factor += hset[hindx];
|
|
* }
|
|
* hindx = 0;
|
|
* } while (test_factor < some_limit);
|
|
*
|
|
* The test, mfactor(67, 1, 10000) took on an 200 Mhz r4k (user CPU seconds):
|
|
*
|
|
* 210.83 (prior to use of hset[])
|
|
* 78.35 (hset[] for p_elim = 7)
|
|
* 73.87 (hset[] for p_elim = 11)
|
|
* 73.92 (hset[] for p_elim = 13)
|
|
* 234.16 (hset[] for p_elim = 17)
|
|
* p_elim == 19 requires over 190 Megs of memory
|
|
*
|
|
* Over a long period of time, the call to load_hset() becomes insignificant.
|
|
* If we look at the user CPU seconds from the first 10000 cycle to the
|
|
* end of the test we find:
|
|
*
|
|
* 205.00 (prior to use of hset[])
|
|
* 75.89 (hset[] for p_elim = 7)
|
|
* 73.74 (hset[] for p_elim = 11)
|
|
* 70.61 (hset[] for p_elim = 13)
|
|
* 57.78 (hset[] for p_elim = 17)
|
|
* p_elim == 19 rejected because of memory size
|
|
*
|
|
* The p_elim == 17 overhead takes ~3 minutes on an 200 Mhz r4k CPU and
|
|
* requires about ~13 Megs of memory. The p_elim == 13 overhead
|
|
* takes about 3 seconds and requires ~1.5 Megs of memory.
|
|
*
|
|
* The value p_elim == 17 is best for long factorizations. It is the
|
|
* fastest even thought the initial startup overhead is larger than
|
|
* for p_elim == 13.
|
|
*
|
|
* NOTE: The values above are prior to optimizations where hset[] was
|
|
* multiplied by n plus other optimizations. Thus, the CPU
|
|
* times you may get will not likely match the above values.
|
|
*/
|
|
|
|
|
|
/*
|
|
* mfactor - find a factor of a Mersenne Number
|
|
*
|
|
* Mersenne numbers are numbers of the form:
|
|
*
|
|
* 2^n-1 for integer n > 0
|
|
*
|
|
* We know that factors of a Mersenne number are of the form:
|
|
*
|
|
* 2*k*n+1 and +/- 1 mod 8
|
|
*
|
|
* We make use of the hset[] difference array to eliminate factor
|
|
* candidates that would otherwise be divisible by 2, 3, 5, 7 ... p_elim.
|
|
*
|
|
* given:
|
|
* n attempt to factor M(n) = 2^n-1
|
|
* start_k the value k in 2*k*n+1 to start the search (def: 1)
|
|
* rept_loop loop cycle reporting (def: 10000)
|
|
* p_elim largest prime to eliminate from test factors (def: 17)
|
|
*
|
|
* returns:
|
|
* factor of (2^n)-1
|
|
*
|
|
* NOTE: The p_elim argument is optional and defaults to 17. A p_elim value
|
|
* of 17 is faster than 13 for even medium length runs. However 13
|
|
* uses less memory and has a shorter startup time.
|
|
*/
|
|
define mfactor(n, start_k, rept_loop, p_elim)
|
|
{
|
|
local Q; /* 4*pfact(p_elim), hset[] cycle size */
|
|
local hcount; /* elements in the hset[] difference array */
|
|
local loop; /* report loop count */
|
|
local q; /* test factor of 2^n-1 */
|
|
local g; /* g as in test candidate form: (Q*g*hset[i])*n + 1 */
|
|
local hindx; /* hset[] index */
|
|
local i;
|
|
local tmp;
|
|
local tmp2;
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (!isint(n) || n <= 0) {
|
|
quit "n must be an integer > 0";
|
|
}
|
|
if (!isint(start_k)) {
|
|
start_k = 1;
|
|
} else if (!isint(start_k) || start_k <= 0) {
|
|
quit "start_k must be an integer > 0";
|
|
}
|
|
if (!isint(rept_loop)) {
|
|
rept_loop = 10000;
|
|
}
|
|
if (rept_loop < 1) {
|
|
quit "rept_loop must be an integer > 0";
|
|
}
|
|
if (!isint(p_elim)) {
|
|
p_elim = 17;
|
|
}
|
|
if (p_elim < 3) {
|
|
quit "p_elim must be an integer > 2 (try 13 or 17)";
|
|
}
|
|
|
|
/*
|
|
* declare our global values
|
|
*/
|
|
Q = 4*pfact(p_elim);
|
|
hcount = 2;
|
|
/* allocate the h difference array */
|
|
for (i=2; i <= p_elim; i = nextcand(i)) {
|
|
hcount *= (i-1);
|
|
}
|
|
local mat hset[hcount+1];
|
|
|
|
/*
|
|
* load the hset[] difference array
|
|
*/
|
|
{
|
|
local x; /* h*n+1 mod 8 */
|
|
local h; /* potential h value */
|
|
local last_h; /* previous valid h value */
|
|
|
|
last_h = 0;
|
|
for (i=0,h=0; h < Q; ++h) {
|
|
if (gcd(h*n+1,Q) == 1) {
|
|
x = (h*n+1) % 8;
|
|
if (x == 1 || x == 7) {
|
|
hset[i++] = (h-last_h) * n;
|
|
last_h = h;
|
|
}
|
|
}
|
|
}
|
|
hset[hcount] = Q*n - last_h*n;
|
|
}
|
|
|
|
/*
|
|
* setup
|
|
*
|
|
* determine the next g and hset[] index (hindx) values such that:
|
|
*
|
|
* 2*start_k <= (Q*g + hset[hindx])
|
|
*
|
|
* and (Q*g + hset[hindx]) is a minimum and where:
|
|
*
|
|
* Q = (4 * pfact(of some reasonable integer))
|
|
* g = (some integer) (hset[] cycle number)
|
|
*
|
|
* We also compute 'q', the next test candidate.
|
|
*/
|
|
g = (2*start_k) // Q;
|
|
tmp = 2*start_k - Q*g;
|
|
for (tmp2=0, hindx=0;
|
|
hindx < hcount && (tmp2 += hset[hindx]/n) < tmp;
|
|
++hindx) {
|
|
}
|
|
if (hindx == hcount) {
|
|
/* we are beyond the end of a hset[] cycle, start at the next */
|
|
++g;
|
|
hindx = 0;
|
|
tmp2 = hset[0]/n;
|
|
}
|
|
q = (Q*g + tmp2)*n + 1;
|
|
|
|
/*
|
|
* look for a factor
|
|
*
|
|
* We ignore factors that themselves are divisible by a prime <=
|
|
* some small prime p.
|
|
*
|
|
* This process is guaranteed to find the smallest factor
|
|
* of 2^n-1. A smallest factor of 2^n-1 must be prime, otherwise
|
|
* the divisors of that factor would also be factors of 2^n-1.
|
|
* Thus we know that if a test factor itself is not prime, it
|
|
* cannot be the smallest factor of 2^n-1.
|
|
*
|
|
* Eliminating all non-prime test factors would take too long.
|
|
* However we can eliminate 80.81% of the test factors
|
|
* by not using test factors that are divisible by a prime <= 17.
|
|
*/
|
|
if (pmod(2,n,q) == 1) {
|
|
return q;
|
|
} else {
|
|
/* report this loop */
|
|
printf("at 2*%d*%d+1, cpu: %f\n",
|
|
(q-1)/(2*n), n, runtime());
|
|
fflush(files(1));
|
|
loop = 0;
|
|
}
|
|
do {
|
|
/*
|
|
* determine if we need to report
|
|
*
|
|
* NOTE: (q-1)/(2*n) is the k value from 2*k*n + 1.
|
|
*/
|
|
if (rept_loop <= ++loop) {
|
|
/* report this loop */
|
|
printf("at 2*%d*%d+1, cpu: %f\n",
|
|
(q-1)/(2*n), n, runtime());
|
|
fflush(files(1));
|
|
loop = 0;
|
|
}
|
|
|
|
/*
|
|
* skip if divisable by a prime <= 449
|
|
*
|
|
* The value 281 was determined by timing loops
|
|
* which found that 281 was at or near the
|
|
* minimum time to factor 2^(2^127-1)-1.
|
|
*
|
|
* The addition of the do { ... } while (factor(q, 449)>1);
|
|
* loop reduced the factoring loop time (36504 k values with
|
|
* the hset[] initialization time removed) from 25.69 sec to
|
|
* 15.62 sec of CPU time on a 200Mhz r4k.
|
|
*/
|
|
do {
|
|
/*
|
|
* determine the next factor candidate
|
|
*/
|
|
q += hset[++hindx];
|
|
if (hindx >= hcount) {
|
|
hindx = 0;
|
|
/*
|
|
* if we cared about g,
|
|
* then we wound ++g here too
|
|
*/
|
|
}
|
|
} while (factor(q, 449) > 1);
|
|
} while (pmod(2,n,q) != 1);
|
|
|
|
/*
|
|
* return the factor found
|
|
*
|
|
* q is a factor of (2^n)-1
|
|
*/
|
|
return q;
|
|
}
|
|
|
|
if (config("lib_debug") >= 0) {
|
|
print "mfactor(n [, start_k=1 [, rept_loop=10000 [, p_elim=17]]])"
|
|
}
|