/* Copyright (C) 2014 William Hart 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 . */ #define ulong ulongxx /* interferes with system includes */ #include #include #undef ulong #define ulong mp_limb_t #include #include "flint.h" #include "ulong_extras.h" #include "fmpz.h" #include "fmpz_vec.h" int fmpz_divisor_in_residue_class_lenstra(fmpz_t fac, const fmpz_t n, const fmpz_t r, const fmpz_t s) { fmpz_t r1, r2, a0, a1, b0, b1, c0, c1, q, d, d1, s1, s2, ns2; slong i; int res = 0; fmpz_init(r1); fmpz_init(r2); fmpz_init(a0); fmpz_init(a1); fmpz_init(b0); fmpz_init(b1); fmpz_init(c0); fmpz_init(c1); fmpz_init(q); fmpz_init(d); fmpz_init(d1); fmpz_init(s1); fmpz_init(s2); fmpz_init(ns2); /* ns2 = n/s^2 */ fmpz_mul(ns2, s, s); fmpz_tdiv_q(ns2, n, ns2); /* initialise */ fmpz_invmod(r1, r, s); /* r1 = r^-1 mod s */ fmpz_mul(r2, r1, n); fmpz_mod(r2, r2, s); /* r2 = r1*n mod s */ fmpz_set(a0, s); /* a0 = s */ fmpz_mul(a1, r1, r2); /* a1 = r1*r2 mod s */ fmpz_mod(a1, a1, s); if (fmpz_is_zero(a1)) fmpz_add(a1, a1, s); /* 0 < a1 <= s */ fmpz_zero(b0); fmpz_one(b1); fmpz_zero(c0); fmpz_mul(c1, r, r2); /* c1 = (n - r*r2)/s * r1 mod s */ fmpz_sub(c1, n, c1); fmpz_divexact(c1, c1, s); fmpz_mul(c1, c1, r1); fmpz_mod(c1, c1, s); /* deal with a0, b0, c0 */ if (!fmpz_is_one(r) && !fmpz_equal(n, r) && fmpz_divisible(n, r)) { fmpz_set(fac, r); res = 1; goto cleanup; } for (i = 1; ; i++) { if ((i & 1) == 0) { fmpz_mod(s1, c1, s); fmpz_neg(s2, s); } else { fmpz_mul(s2, a1, b1); /* s2 = a1*b1 */ fmpz_add(s1, s2, ns2); /* s1 = a1*b1 + n/s^2 */ fmpz_mod(q, s1, s); /* s1 largest integer < a1*b1 + n/s^2 congruent to c1 mod s */ if (fmpz_cmp(q, c1) < 0) fmpz_sub(s1, s1, s); fmpz_sub(s1, s1, q); fmpz_add(s1, s1, c1); fmpz_add(s2, s2, s2); /* s2 = 2*a1*b1 - 1 */ fmpz_sub_ui(s2, s2, 1); } while (fmpz_cmp(s1, s2) > 0) /* for each value s1 in range */ { fmpz_mul(d, s, s1); /* d = (s1*s + a1*r + b1*r2)^2 - 4*a1*b1*n */ fmpz_addmul(d, a1, r); fmpz_addmul(d, b1, r2); fmpz_set(d1, d); /* d1 = s1*s + a1*r + b1*r2 */ fmpz_mul(d, d, d); fmpz_mul(q, a1, b1); fmpz_mul(q, q, n); fmpz_submul_ui(d, q, 4); if (fmpz_is_square(d)) /* divisor exists, roots are (d1 +/- sqrt(d))/2 */ { fmpz_sqrt(d, d); fmpz_add(d, d, d1); fmpz_tdiv_q_2exp(d, d, 1); if (fmpz_is_zero(a1)) { fmpz_tdiv_q(fac, s1, b1); /* y*b1 = s1 */ fmpz_mul(fac, fac, s); /* check if ys + r2 is factor */ fmpz_add(fac, fac, r2); fmpz_abs(fac, fac); if (!fmpz_is_zero(fac) && !fmpz_is_one(fac) && !fmpz_equal(fac, n) && fmpz_divisible(n, fac)) { res = 1; break; } } else if (fmpz_is_zero(b1)) { fmpz_tdiv_q(fac, s1, a1); /* x*a1 = s1 */ fmpz_mul(fac, fac, s); /* check if xs + r is factor */ fmpz_add(fac, fac, r); fmpz_abs(fac, fac); if (!fmpz_is_zero(fac) && !fmpz_is_one(fac) && !fmpz_equal(fac, n) && fmpz_divisible(n, fac)) { res = 1; break; } } else { /* either d/a1 or d/b1 is a divisor of n */ fmpz_tdiv_q(fac, d, a1); fmpz_abs(fac, fac); if (!fmpz_is_zero(fac) && !fmpz_is_one(fac) && !fmpz_equal(fac, n) && fmpz_divisible(n, fac)) { res = 1; break; } fmpz_tdiv_q(fac, d, b1); fmpz_abs(fac, fac); if (!fmpz_is_zero(fac) && !fmpz_is_one(fac) && !fmpz_equal(fac, n) && fmpz_divisible(n, fac)) { res = 1; break; } } } fmpz_sub(s1, s1, s); } if (fmpz_is_zero(a1) || res == 1) /* Euclidean chain has terminated */ break; /* Euclidean chain: a1, a0 = a0 - q*a1, a1, where 0 <= a1 < a0 for i even and 0 < a1 <= a0 for i odd */ fmpz_tdiv_qr(q, a0, a0, a1); if ((i & 1) == 0 && fmpz_is_zero(a0)) { fmpz_sub_ui(q, q, 1); fmpz_add(a0, a0, a1); } fmpz_swap(a0, a1); /* b1, b0 = b0 - q*b1, b1 */ fmpz_submul(b0, q, b1); fmpz_swap(b0, b1); /* c1, c0 = c0 - q*c1, c1 mod s */ fmpz_submul(c0, q, c1); fmpz_mod(c0, c0, s); fmpz_swap(c0, c1); } cleanup: fmpz_clear(r1); fmpz_clear(r2); fmpz_clear(a0); fmpz_clear(a1); fmpz_clear(b0); fmpz_clear(b1); fmpz_clear(c0); fmpz_clear(c1); fmpz_clear(q); fmpz_clear(d); fmpz_clear(d1); fmpz_clear(s1); fmpz_clear(s2); fmpz_clear(ns2); return res; }