/* Copyright (C) 2015 Kushagra Singh This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ /* This is an implementation of the pollard rho algorithm, with a more efficient cycle finding algorithm, as proposed by Richard Brent. Details can be found in the paper https://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf, pseudocode is available on page 182 of the same paper */ #include #include "flint.h" #include "fmpz.h" #include "ulong_extras.h" #include "mpn_extras.h" /* Sets y to (y^2 + a) % n */ void flint_mpn_sqr_and_add_a(mp_ptr y, mp_ptr a, mp_ptr n, mp_limb_t n_size, mp_ptr ninv, mp_limb_t normbits) { mp_limb_t cy; flint_mpn_mulmod_preinvn(y, y, y, n_size, n, ninv, normbits); /* y^2 mod n */ cy = mpn_add_n(y, y, a, n_size); /* Since carry cannot be greater than 1, if carry then simply subtract for modulo (a < n, y < n, a + y < 2n). If no carry, then check if y > n before subtracting. */ if (cy) mpn_sub_n(y, y, n, n_size); else if (mpn_cmp(y, n, n_size) > 0) mpn_sub_n(y, y, n, n_size); } int flint_mpn_factor_pollard_brent_single(mp_ptr factor, mp_ptr n, mp_ptr ninv, mp_ptr a, mp_ptr y, mp_limb_t n_size, mp_limb_t normbits, mp_limb_t max_iters) { /* n_size >= 2, one limb fmpz_t's are passed on to n_factor_pollard_brent in outer funtion */ mp_ptr x, q, ys, subval; mp_limb_t iter, i, k, minval, m, one_shift_norm, gcdlimbs; int ret, j; TMP_INIT; TMP_START; x = TMP_ALLOC(n_size * sizeof(mp_limb_t)); /* initial value to evaluate f(x) */ q = TMP_ALLOC(n_size * sizeof(mp_limb_t)); /* product of gcd's */ ys = TMP_ALLOC(n_size * sizeof(mp_limb_t)); subval = TMP_ALLOC(n_size * sizeof(mp_limb_t)); /* one shifted by normbits, used for comparisons */ one_shift_norm = UWORD(1) << normbits; /* set factor and q to one (shifted) */ mpn_zero(q, n_size); mpn_zero(factor, n_size); q[0] = one_shift_norm; factor[0] = one_shift_norm; m = 100; iter = 1; do { flint_mpn_copyi(x, y, n_size); k = 0; for (i = 0; i < iter; i++) flint_mpn_sqr_and_add_a(y, a, n, n_size, ninv, normbits); do { minval = iter - k; if (m < minval) minval = m; flint_mpn_copyi(ys, y, n_size); for (i = 0; i < minval; i++) { flint_mpn_sqr_and_add_a(y, a, n, n_size, ninv, normbits); if (mpn_cmp(x, y, n_size) > 0) mpn_sub_n(subval, x, y, n_size); else mpn_sub_n(subval, y, x, n_size); flint_mpn_mulmod_preinvn(q, q, subval, n_size, n, ninv, normbits); } /* if q is 0, then gcd is n (gcd(0, a) = a). Not passing through flint_mpn_gcd_full due to input paramete restrictions. */ if (flint_mpn_zero_p(q, n_size) == 0) gcdlimbs = flint_mpn_gcd_full(factor, q, n_size, n, n_size); else { flint_mpn_copyi(factor, n, n_size); gcdlimbs = n_size; } k += m; j = ((gcdlimbs == 1) && (factor[0] == one_shift_norm)); /* gcd == 1 */ } while ((k < iter) && j); if (iter > max_iters) /* max iterations crossed */ break; iter *= 2; } while (j); /* if gcd == n, could be possible q has all factors of n, start backtracing. if gcd != 1 after backtracing, then at least one factor has been found (can be n) */ if ((gcdlimbs == n_size) && (mpn_cmp(factor, n, n_size) == 0)) { do { flint_mpn_sqr_and_add_a(ys, a, n, n_size, ninv, normbits); if (mpn_cmp(x, ys, n_size) > 0) mpn_sub_n(subval, x, ys, n_size); else mpn_sub_n(subval, ys, x, n_size); if (flint_mpn_zero_p(q, n_size) == 0) gcdlimbs = flint_mpn_gcd_full(factor, q, n_size, n, n_size); else { flint_mpn_copyi(factor, n, n_size); gcdlimbs = n_size; } j = ((gcdlimbs == 1) && (factor[0] == one_shift_norm)); } while (j); /* gcd == 1 */ } ret = gcdlimbs; /* if gcd == 1 or gcd == n, trivial factor found. return 0 */ if ((gcdlimbs == 1) && (factor[0] == one_shift_norm)) /* gcd == 1 */ ret = 0; else if ((gcdlimbs == n_size && (mpn_cmp(factor, n, n_size) == 0))) /* gcd == n*/ ret = 0; if (ret) { /* If in case after shifting, "actual" factor has lesser limbs than gcdlimbs, then decrease ret by 1. */ j = n_sizeinbase(factor[gcdlimbs - 1], 2); if (normbits >= j) ret -= 1; if (normbits) mpn_rshift(factor, factor, gcdlimbs, normbits); } TMP_END; return ret; } int fmpz_factor_pollard_brent_single(fmpz_t p_factor, fmpz_t n_in, fmpz_t yi, fmpz_t ai, mp_limb_t max_iters) { mp_ptr a, y, n, ninv, temp; mp_limb_t n_size, normbits, ans, size, cy; mp_limb_t al, yl, val, valinv; __mpz_struct *fac, *mpz_ptr; int ret; TMP_INIT; if (fmpz_is_even(n_in)) { fmpz_set_ui(p_factor, 2); return 1; } n_size = fmpz_size(n_in); if (n_size == 1) { val = fmpz_get_ui(n_in); count_leading_zeros(normbits, val); val <<= normbits; valinv = n_preinvert_limb(val); al = fmpz_get_ui(ai); yl = fmpz_get_ui(yi); al <<= normbits; yl <<= normbits; ret = n_factor_pollard_brent_single(&ans, val, valinv, al, yl, normbits, max_iters); ans >>= normbits; fmpz_set_ui(p_factor, ans); return ret; } mpz_ptr = COEFF_TO_PTR(*yi); temp = COEFF_TO_PTR(*n_in)->_mp_d; count_leading_zeros(normbits, temp[n_size - 1]); TMP_START; a = TMP_ALLOC(n_size * sizeof(mp_limb_t)); y = TMP_ALLOC(n_size * sizeof(mp_limb_t)); n = TMP_ALLOC(n_size * sizeof(mp_limb_t)); ninv = TMP_ALLOC(n_size * sizeof(mp_limb_t)); /* copying n_in onto n, and normalizing */ temp = COEFF_TO_PTR(*n_in)->_mp_d; count_leading_zeros(normbits, temp[n_size - 1]); if (normbits) mpn_lshift(n, temp, n_size, normbits); else flint_mpn_copyi(n, temp, n_size); flint_mpn_preinvn(ninv, n, n_size); fac = _fmpz_promote(p_factor); mpz_realloc2(fac, n_size * FLINT_BITS); fac->_mp_size = n_size; mpn_zero(a, n_size); mpn_zero(y, n_size); if (normbits) { if ((!COEFF_IS_MPZ(*yi))) { y[0] = fmpz_get_ui(yi); cy = mpn_lshift(y, y, 1, normbits); if (cy) y[1] = cy; } else { mpz_ptr = COEFF_TO_PTR(*yi); temp = mpz_ptr->_mp_d; size = mpz_ptr->_mp_size; cy = mpn_lshift(y, temp, size, normbits); if (cy) y[size] = cy; } if ((!COEFF_IS_MPZ(*ai))) { a[0] = fmpz_get_ui(ai); cy = mpn_lshift(a, a, 1, normbits); if (cy) a[1] = cy; } else { mpz_ptr = COEFF_TO_PTR(*ai); temp = mpz_ptr->_mp_d; size = mpz_ptr->_mp_size; cy = mpn_lshift(a, temp, size, normbits); if (cy) a[size] = cy; } } else { temp = COEFF_TO_PTR(*yi)->_mp_d; flint_mpn_copyi(y, temp, n_size); temp = COEFF_TO_PTR(*ai)->_mp_d; flint_mpn_copyi(a, temp, n_size); } ret = flint_mpn_factor_pollard_brent_single(fac->_mp_d, n, ninv, a, y, n_size, normbits, max_iters); if (ret) { fac->_mp_size = ret; /* ret is number of limbs of factor found */ _fmpz_demote_val(p_factor); } TMP_END; return ret; }