1 1.1 mrg /* mpz_oddfac_1(RESULT, N) -- Set RESULT to the odd factor of N!. 2 1.1 mrg 3 1.1 mrg Contributed to the GNU project by Marco Bodrato. 4 1.1 mrg 5 1.1 mrg THE FUNCTION IN THIS FILE IS INTERNAL WITH A MUTABLE INTERFACE. 6 1.1 mrg IT IS ONLY SAFE TO REACH IT THROUGH DOCUMENTED INTERFACES. 7 1.1 mrg IN FACT, IT IS ALMOST GUARANTEED THAT IT WILL CHANGE OR 8 1.1 mrg DISAPPEAR IN A FUTURE GNU MP RELEASE. 9 1.1 mrg 10 1.1.1.3 mrg Copyright 2010-2012, 2015-2017 Free Software Foundation, Inc. 11 1.1 mrg 12 1.1 mrg This file is part of the GNU MP Library. 13 1.1 mrg 14 1.1 mrg The GNU MP Library is free software; you can redistribute it and/or modify 15 1.1.1.2 mrg it under the terms of either: 16 1.1.1.2 mrg 17 1.1.1.2 mrg * the GNU Lesser General Public License as published by the Free 18 1.1.1.2 mrg Software Foundation; either version 3 of the License, or (at your 19 1.1.1.2 mrg option) any later version. 20 1.1.1.2 mrg 21 1.1.1.2 mrg or 22 1.1.1.2 mrg 23 1.1.1.2 mrg * the GNU General Public License as published by the Free Software 24 1.1.1.2 mrg Foundation; either version 2 of the License, or (at your option) any 25 1.1.1.2 mrg later version. 26 1.1.1.2 mrg 27 1.1.1.2 mrg or both in parallel, as here. 28 1.1 mrg 29 1.1 mrg The GNU MP Library is distributed in the hope that it will be useful, but 30 1.1 mrg WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 31 1.1.1.2 mrg or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 32 1.1.1.2 mrg for more details. 33 1.1 mrg 34 1.1.1.2 mrg You should have received copies of the GNU General Public License and the 35 1.1.1.2 mrg GNU Lesser General Public License along with the GNU MP Library. If not, 36 1.1.1.2 mrg see https://www.gnu.org/licenses/. */ 37 1.1 mrg 38 1.1 mrg #include "gmp-impl.h" 39 1.1 mrg #include "longlong.h" 40 1.1 mrg 41 1.1 mrg /* TODO: 42 1.1 mrg - split this file in smaller parts with functions that can be recycled for different computations. 43 1.1 mrg */ 44 1.1 mrg 45 1.1 mrg /**************************************************************/ 46 1.1 mrg /* Section macros: common macros, for mswing/fac/bin (&sieve) */ 47 1.1 mrg /**************************************************************/ 48 1.1 mrg 49 1.1 mrg #define FACTOR_LIST_APPEND(PR, MAX_PR, VEC, I) \ 50 1.1 mrg if ((PR) > (MAX_PR)) { \ 51 1.1 mrg (VEC)[(I)++] = (PR); \ 52 1.1 mrg (PR) = 1; \ 53 1.1 mrg } 54 1.1 mrg 55 1.1 mrg #define FACTOR_LIST_STORE(P, PR, MAX_PR, VEC, I) \ 56 1.1 mrg do { \ 57 1.1 mrg if ((PR) > (MAX_PR)) { \ 58 1.1 mrg (VEC)[(I)++] = (PR); \ 59 1.1 mrg (PR) = (P); \ 60 1.1 mrg } else \ 61 1.1 mrg (PR) *= (P); \ 62 1.1 mrg } while (0) 63 1.1 mrg 64 1.1 mrg #define LOOP_ON_SIEVE_CONTINUE(prime,end,sieve) \ 65 1.1 mrg __max_i = (end); \ 66 1.1 mrg \ 67 1.1 mrg do { \ 68 1.1 mrg ++__i; \ 69 1.1 mrg if (((sieve)[__index] & __mask) == 0) \ 70 1.1 mrg { \ 71 1.1.1.3 mrg mp_limb_t prime; \ 72 1.1.1.3 mrg prime = id_to_n(__i) 73 1.1 mrg 74 1.1 mrg #define LOOP_ON_SIEVE_BEGIN(prime,start,end,off,sieve) \ 75 1.1 mrg do { \ 76 1.1 mrg mp_limb_t __mask, __index, __max_i, __i; \ 77 1.1 mrg \ 78 1.1 mrg __i = (start)-(off); \ 79 1.1 mrg __index = __i / GMP_LIMB_BITS; \ 80 1.1 mrg __mask = CNST_LIMB(1) << (__i % GMP_LIMB_BITS); \ 81 1.1 mrg __i += (off); \ 82 1.1 mrg \ 83 1.1 mrg LOOP_ON_SIEVE_CONTINUE(prime,end,sieve) 84 1.1 mrg 85 1.1 mrg #define LOOP_ON_SIEVE_STOP \ 86 1.1 mrg } \ 87 1.1 mrg __mask = __mask << 1 | __mask >> (GMP_LIMB_BITS-1); \ 88 1.1 mrg __index += __mask & 1; \ 89 1.1.1.3 mrg } while (__i <= __max_i) 90 1.1 mrg 91 1.1 mrg #define LOOP_ON_SIEVE_END \ 92 1.1 mrg LOOP_ON_SIEVE_STOP; \ 93 1.1 mrg } while (0) 94 1.1 mrg 95 1.1 mrg /*********************************************************/ 96 1.1 mrg /* Section sieve: sieving functions and tools for primes */ 97 1.1 mrg /*********************************************************/ 98 1.1 mrg 99 1.1 mrg #if WANT_ASSERT 100 1.1 mrg static mp_limb_t 101 1.1 mrg bit_to_n (mp_limb_t bit) { return (bit*3+4)|1; } 102 1.1 mrg #endif 103 1.1 mrg 104 1.1 mrg /* id_to_n (x) = bit_to_n (x-1) = (id*3+1)|1*/ 105 1.1 mrg static mp_limb_t 106 1.1 mrg id_to_n (mp_limb_t id) { return id*3+1+(id&1); } 107 1.1 mrg 108 1.1 mrg /* n_to_bit (n) = ((n-1)&(-CNST_LIMB(2)))/3U-1 */ 109 1.1 mrg static mp_limb_t 110 1.1 mrg n_to_bit (mp_limb_t n) { return ((n-5)|1)/3U; } 111 1.1 mrg 112 1.1 mrg #if WANT_ASSERT 113 1.1 mrg static mp_size_t 114 1.1 mrg primesieve_size (mp_limb_t n) { return n_to_bit(n) / GMP_LIMB_BITS + 1; } 115 1.1 mrg #endif 116 1.1 mrg 117 1.1 mrg /*********************************************************/ 118 1.1.1.3 mrg /* Section mswing: 2-multiswing factorial */ 119 1.1 mrg /*********************************************************/ 120 1.1 mrg 121 1.1.1.3 mrg /* Returns an approximation of the sqare root of x. 122 1.1.1.3 mrg * It gives: 123 1.1.1.3 mrg * limb_apprsqrt (x) ^ 2 <= x < (limb_apprsqrt (x)+1) ^ 2 124 1.1.1.3 mrg * or 125 1.1.1.3 mrg * x <= limb_apprsqrt (x) ^ 2 <= x * 9/8 126 1.1.1.3 mrg */ 127 1.1 mrg static mp_limb_t 128 1.1 mrg limb_apprsqrt (mp_limb_t x) 129 1.1 mrg { 130 1.1 mrg int s; 131 1.1 mrg 132 1.1 mrg ASSERT (x > 2); 133 1.1.1.3 mrg count_leading_zeros (s, x); 134 1.1.1.3 mrg s = (GMP_LIMB_BITS - s) >> 1; 135 1.1.1.3 mrg return ((CNST_LIMB(1) << s) + (x >> s)) >> 1; 136 1.1 mrg } 137 1.1 mrg 138 1.1 mrg #if 0 139 1.1 mrg /* A count-then-exponentiate variant for SWING_A_PRIME */ 140 1.1 mrg #define SWING_A_PRIME(P, N, PR, MAX_PR, VEC, I) \ 141 1.1 mrg do { \ 142 1.1 mrg mp_limb_t __q, __prime; \ 143 1.1 mrg int __exp; \ 144 1.1 mrg __prime = (P); \ 145 1.1 mrg __exp = 0; \ 146 1.1 mrg __q = (N); \ 147 1.1 mrg do { \ 148 1.1 mrg __q /= __prime; \ 149 1.1 mrg __exp += __q & 1; \ 150 1.1 mrg } while (__q >= __prime); \ 151 1.1 mrg if (__exp) { /* Store $prime^{exp}$ */ \ 152 1.1 mrg for (__q = __prime; --__exp; __q *= __prime); \ 153 1.1 mrg FACTOR_LIST_STORE(__q, PR, MAX_PR, VEC, I); \ 154 1.1 mrg }; \ 155 1.1 mrg } while (0) 156 1.1 mrg #else 157 1.1 mrg #define SWING_A_PRIME(P, N, PR, MAX_PR, VEC, I) \ 158 1.1 mrg do { \ 159 1.1 mrg mp_limb_t __q, __prime; \ 160 1.1 mrg __prime = (P); \ 161 1.1 mrg FACTOR_LIST_APPEND(PR, MAX_PR, VEC, I); \ 162 1.1 mrg __q = (N); \ 163 1.1 mrg do { \ 164 1.1 mrg __q /= __prime; \ 165 1.1 mrg if ((__q & 1) != 0) (PR) *= __prime; \ 166 1.1 mrg } while (__q >= __prime); \ 167 1.1 mrg } while (0) 168 1.1 mrg #endif 169 1.1 mrg 170 1.1 mrg #define SH_SWING_A_PRIME(P, N, PR, MAX_PR, VEC, I) \ 171 1.1 mrg do { \ 172 1.1 mrg mp_limb_t __prime; \ 173 1.1 mrg __prime = (P); \ 174 1.1 mrg if ((((N) / __prime) & 1) != 0) \ 175 1.1 mrg FACTOR_LIST_STORE(__prime, PR, MAX_PR, VEC, I); \ 176 1.1 mrg } while (0) 177 1.1 mrg 178 1.1 mrg /* mpz_2multiswing_1 computes the odd part of the 2-multiswing 179 1.1 mrg factorial of the parameter n. The result x is an odd positive 180 1.1 mrg integer so that multiswing(n,2) = x 2^a. 181 1.1 mrg 182 1.1 mrg Uses the algorithm described by Peter Luschny in "Divide, Swing and 183 1.1 mrg Conquer the Factorial!". 184 1.1 mrg 185 1.1 mrg The pointer sieve points to primesieve_size(n) limbs containing a 186 1.1 mrg bit-array where primes are marked as 0. 187 1.1 mrg Enough (FIXME: explain :-) limbs must be pointed by factors. 188 1.1 mrg */ 189 1.1 mrg 190 1.1 mrg static void 191 1.1 mrg mpz_2multiswing_1 (mpz_ptr x, mp_limb_t n, mp_ptr sieve, mp_ptr factors) 192 1.1 mrg { 193 1.1 mrg mp_limb_t prod, max_prod; 194 1.1 mrg mp_size_t j; 195 1.1 mrg 196 1.1.1.3 mrg ASSERT (n > 25); 197 1.1 mrg 198 1.1 mrg j = 0; 199 1.1 mrg prod = -(n & 1); 200 1.1 mrg n &= ~ CNST_LIMB(1); /* n-1, if n is odd */ 201 1.1 mrg 202 1.1 mrg prod = (prod & n) + 1; /* the original n, if it was odd, 1 otherwise */ 203 1.1 mrg max_prod = GMP_NUMB_MAX / (n-1); 204 1.1 mrg 205 1.1 mrg /* Handle prime = 3 separately. */ 206 1.1 mrg SWING_A_PRIME (3, n, prod, max_prod, factors, j); 207 1.1 mrg 208 1.1 mrg /* Swing primes from 5 to n/3 */ 209 1.1 mrg { 210 1.1.1.3 mrg mp_limb_t s, l_max_prod; 211 1.1 mrg 212 1.1.1.3 mrg s = limb_apprsqrt(n); 213 1.1.1.3 mrg ASSERT (s >= 5); 214 1.1.1.3 mrg s = n_to_bit (s); 215 1.1.1.3 mrg ASSERT (bit_to_n (s+1) * bit_to_n (s+1) > n); 216 1.1.1.3 mrg ASSERT (s < n_to_bit (n / 3)); 217 1.1.1.3 mrg LOOP_ON_SIEVE_BEGIN (prime, n_to_bit (5), s, 0,sieve); 218 1.1.1.3 mrg SWING_A_PRIME (prime, n, prod, max_prod, factors, j); 219 1.1.1.3 mrg LOOP_ON_SIEVE_STOP; 220 1.1 mrg 221 1.1 mrg ASSERT (max_prod <= GMP_NUMB_MAX / 3); 222 1.1 mrg 223 1.1.1.3 mrg l_max_prod = max_prod * 3; 224 1.1 mrg 225 1.1.1.3 mrg LOOP_ON_SIEVE_CONTINUE (prime, n_to_bit (n/3), sieve); 226 1.1.1.3 mrg SH_SWING_A_PRIME (prime, n, prod, l_max_prod, factors, j); 227 1.1 mrg LOOP_ON_SIEVE_END; 228 1.1 mrg } 229 1.1 mrg 230 1.1.1.3 mrg /* Store primes from (n+1)/2 to n */ 231 1.1.1.3 mrg LOOP_ON_SIEVE_BEGIN (prime, n_to_bit (n >> 1) + 1, n_to_bit (n), 0,sieve); 232 1.1.1.3 mrg FACTOR_LIST_STORE (prime, prod, max_prod, factors, j); 233 1.1.1.3 mrg LOOP_ON_SIEVE_END; 234 1.1.1.3 mrg 235 1.1 mrg if (LIKELY (j != 0)) 236 1.1 mrg { 237 1.1 mrg factors[j++] = prod; 238 1.1 mrg mpz_prodlimbs (x, factors, j); 239 1.1 mrg } 240 1.1 mrg else 241 1.1 mrg { 242 1.1.1.3 mrg ASSERT (ALLOC (x) > 0); 243 1.1 mrg PTR (x)[0] = prod; 244 1.1 mrg SIZ (x) = 1; 245 1.1 mrg } 246 1.1 mrg } 247 1.1 mrg 248 1.1 mrg #undef SWING_A_PRIME 249 1.1 mrg #undef SH_SWING_A_PRIME 250 1.1 mrg #undef LOOP_ON_SIEVE_END 251 1.1 mrg #undef LOOP_ON_SIEVE_STOP 252 1.1 mrg #undef LOOP_ON_SIEVE_BEGIN 253 1.1 mrg #undef LOOP_ON_SIEVE_CONTINUE 254 1.1 mrg #undef FACTOR_LIST_APPEND 255 1.1 mrg 256 1.1 mrg /*********************************************************/ 257 1.1 mrg /* Section oddfac: odd factorial, needed also by binomial*/ 258 1.1 mrg /*********************************************************/ 259 1.1 mrg 260 1.1 mrg #if TUNE_PROGRAM_BUILD 261 1.1 mrg #define FACTORS_PER_LIMB (GMP_NUMB_BITS / (LOG2C(FAC_DSC_THRESHOLD_LIMIT-1)+1)) 262 1.1 mrg #else 263 1.1 mrg #define FACTORS_PER_LIMB (GMP_NUMB_BITS / (LOG2C(FAC_DSC_THRESHOLD-1)+1)) 264 1.1 mrg #endif 265 1.1 mrg 266 1.1 mrg /* mpz_oddfac_1 computes the odd part of the factorial of the 267 1.1 mrg parameter n. I.e. n! = x 2^a, where x is the returned value: an 268 1.1 mrg odd positive integer. 269 1.1 mrg 270 1.1 mrg If flag != 0 a square is skipped in the DSC part, e.g. 271 1.1 mrg if n is odd, n > FAC_DSC_THRESHOLD and flag = 1, x is set to n!!. 272 1.1 mrg 273 1.1 mrg If n is too small, flag is ignored, and an ASSERT can be triggered. 274 1.1 mrg 275 1.1 mrg TODO: FAC_DSC_THRESHOLD is used here with two different roles: 276 1.1 mrg - to decide when prime factorisation is needed, 277 1.1 mrg - to stop the recursion, once sieving is done. 278 1.1 mrg Maybe two thresholds can do a better job. 279 1.1 mrg */ 280 1.1 mrg void 281 1.1 mrg mpz_oddfac_1 (mpz_ptr x, mp_limb_t n, unsigned flag) 282 1.1 mrg { 283 1.1 mrg ASSERT (n <= GMP_NUMB_MAX); 284 1.1.1.3 mrg ASSERT (flag == 0 || (flag == 1 && n > ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 1 && ABOVE_THRESHOLD (n, FAC_DSC_THRESHOLD))); 285 1.1 mrg 286 1.1 mrg if (n <= ODD_FACTORIAL_TABLE_LIMIT) 287 1.1 mrg { 288 1.1.1.3 mrg MPZ_NEWALLOC (x, 1)[0] = __gmp_oddfac_table[n]; 289 1.1 mrg SIZ (x) = 1; 290 1.1 mrg } 291 1.1 mrg else if (n <= ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 1) 292 1.1 mrg { 293 1.1 mrg mp_ptr px; 294 1.1 mrg 295 1.1 mrg px = MPZ_NEWALLOC (x, 2); 296 1.1 mrg umul_ppmm (px[1], px[0], __gmp_odd2fac_table[(n - 1) >> 1], __gmp_oddfac_table[n >> 1]); 297 1.1 mrg SIZ (x) = 2; 298 1.1 mrg } 299 1.1 mrg else 300 1.1 mrg { 301 1.1 mrg unsigned s; 302 1.1 mrg mp_ptr factors; 303 1.1 mrg 304 1.1 mrg s = 0; 305 1.1 mrg { 306 1.1 mrg mp_limb_t tn; 307 1.1 mrg mp_limb_t prod, max_prod, i; 308 1.1 mrg mp_size_t j; 309 1.1 mrg TMP_SDECL; 310 1.1 mrg 311 1.1 mrg #if TUNE_PROGRAM_BUILD 312 1.1 mrg ASSERT (FAC_DSC_THRESHOLD_LIMIT >= FAC_DSC_THRESHOLD); 313 1.1 mrg ASSERT (FAC_DSC_THRESHOLD >= 2 * (ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 2)); 314 1.1 mrg #endif 315 1.1 mrg 316 1.1 mrg /* Compute the number of recursive steps for the DSC algorithm. */ 317 1.1 mrg for (tn = n; ABOVE_THRESHOLD (tn, FAC_DSC_THRESHOLD); s++) 318 1.1 mrg tn >>= 1; 319 1.1 mrg 320 1.1 mrg j = 0; 321 1.1 mrg 322 1.1 mrg TMP_SMARK; 323 1.1 mrg factors = TMP_SALLOC_LIMBS (1 + tn / FACTORS_PER_LIMB); 324 1.1 mrg ASSERT (tn >= FACTORS_PER_LIMB); 325 1.1 mrg 326 1.1 mrg prod = 1; 327 1.1 mrg #if TUNE_PROGRAM_BUILD 328 1.1 mrg max_prod = GMP_NUMB_MAX / FAC_DSC_THRESHOLD_LIMIT; 329 1.1 mrg #else 330 1.1 mrg max_prod = GMP_NUMB_MAX / FAC_DSC_THRESHOLD; 331 1.1 mrg #endif 332 1.1 mrg 333 1.1 mrg ASSERT (tn > ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 1); 334 1.1 mrg do { 335 1.1 mrg i = ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 2; 336 1.1 mrg factors[j++] = ODD_DOUBLEFACTORIAL_TABLE_MAX; 337 1.1 mrg do { 338 1.1 mrg FACTOR_LIST_STORE (i, prod, max_prod, factors, j); 339 1.1 mrg i += 2; 340 1.1 mrg } while (i <= tn); 341 1.1 mrg max_prod <<= 1; 342 1.1 mrg tn >>= 1; 343 1.1 mrg } while (tn > ODD_DOUBLEFACTORIAL_TABLE_LIMIT + 1); 344 1.1 mrg 345 1.1 mrg factors[j++] = prod; 346 1.1 mrg factors[j++] = __gmp_odd2fac_table[(tn - 1) >> 1]; 347 1.1 mrg factors[j++] = __gmp_oddfac_table[tn >> 1]; 348 1.1 mrg mpz_prodlimbs (x, factors, j); 349 1.1 mrg 350 1.1 mrg TMP_SFREE; 351 1.1 mrg } 352 1.1 mrg 353 1.1 mrg if (s != 0) 354 1.1 mrg /* Use the algorithm described by Peter Luschny in "Divide, 355 1.1 mrg Swing and Conquer the Factorial!". 356 1.1 mrg 357 1.1 mrg Improvement: there are two temporary buffers, factors and 358 1.1 mrg square, that are never used together; with a good estimate 359 1.1 mrg of the maximal needed size, they could share a single 360 1.1 mrg allocation. 361 1.1 mrg */ 362 1.1 mrg { 363 1.1 mrg mpz_t mswing; 364 1.1 mrg mp_ptr sieve; 365 1.1 mrg mp_size_t size; 366 1.1 mrg TMP_DECL; 367 1.1 mrg 368 1.1 mrg TMP_MARK; 369 1.1 mrg 370 1.1 mrg flag--; 371 1.1 mrg size = n / GMP_NUMB_BITS + 4; 372 1.1 mrg ASSERT (primesieve_size (n - 1) <= size - (size / 2 + 1)); 373 1.1 mrg /* 2-multiswing(n) < 2^(n-1)*sqrt(n/pi) < 2^(n+GMP_NUMB_BITS); 374 1.1 mrg one more can be overwritten by mul, another for the sieve */ 375 1.1 mrg MPZ_TMP_INIT (mswing, size); 376 1.1 mrg /* Initialize size, so that ASSERT can check it correctly. */ 377 1.1 mrg ASSERT_CODE (SIZ (mswing) = 0); 378 1.1 mrg 379 1.1 mrg /* Put the sieve on the second half, it will be overwritten by the last mswing. */ 380 1.1 mrg sieve = PTR (mswing) + size / 2 + 1; 381 1.1 mrg 382 1.1 mrg size = (gmp_primesieve (sieve, n - 1) + 1) / log_n_max (n) + 1; 383 1.1 mrg 384 1.1 mrg factors = TMP_ALLOC_LIMBS (size); 385 1.1 mrg do { 386 1.1 mrg mp_ptr square, px; 387 1.1 mrg mp_size_t nx, ns; 388 1.1 mrg mp_limb_t cy; 389 1.1 mrg TMP_DECL; 390 1.1 mrg 391 1.1 mrg s--; 392 1.1 mrg ASSERT (ABSIZ (mswing) < ALLOC (mswing) / 2); /* Check: sieve has not been overwritten */ 393 1.1 mrg mpz_2multiswing_1 (mswing, n >> s, sieve, factors); 394 1.1 mrg 395 1.1 mrg TMP_MARK; 396 1.1 mrg nx = SIZ (x); 397 1.1 mrg if (s == flag) { 398 1.1 mrg size = nx; 399 1.1 mrg square = TMP_ALLOC_LIMBS (size); 400 1.1 mrg MPN_COPY (square, PTR (x), nx); 401 1.1 mrg } else { 402 1.1 mrg size = nx << 1; 403 1.1 mrg square = TMP_ALLOC_LIMBS (size); 404 1.1 mrg mpn_sqr (square, PTR (x), nx); 405 1.1 mrg size -= (square[size - 1] == 0); 406 1.1 mrg } 407 1.1 mrg ns = SIZ (mswing); 408 1.1 mrg nx = size + ns; 409 1.1 mrg px = MPZ_NEWALLOC (x, nx); 410 1.1 mrg ASSERT (ns <= size); 411 1.1 mrg cy = mpn_mul (px, square, size, PTR(mswing), ns); /* n!= n$ * floor(n/2)!^2 */ 412 1.1 mrg 413 1.1 mrg SIZ(x) = nx - (cy == 0); 414 1.1.1.3 mrg TMP_FREE; 415 1.1 mrg } while (s != 0); 416 1.1 mrg TMP_FREE; 417 1.1 mrg } 418 1.1 mrg } 419 1.1 mrg } 420 1.1 mrg 421 1.1 mrg #undef FACTORS_PER_LIMB 422 1.1 mrg #undef FACTOR_LIST_STORE 423