/*============================================================================= This file is part of FLINT. FLINT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. FLINT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FLINT; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =============================================================================*/ /****************************************************************************** Copyright (C) 2012 Sebastian Pancratz ******************************************************************************/ #include #include "nmod_mat.h" #include "fmpz_mod_poly.h" #include "qadic.h" /* FILE DOCUMENTATION. This file contains routines for computing square roots in finite fields and Hensel lifting. - _find_nonresidue() - _artin_schreier_preimage() - _fmpz_mod_poly_sqrtmod_p() - _fmpz_mod_poly_sqrtmod_2() - _qadic_sqrt_p() - _qadic_sqrt() - qadic_sqrt() */ /* Sets \code{(rop,d)} to a non-residue in $\mathbf{F_q}$ for odd $p$ and $d \geq 2$. */ static void _find_nonresidue(fmpz *rop, const fmpz *a, const slong *j, slong lena, const fmpz_t p) { const slong d = j[lena - 1]; fmpz *w; fmpz_t pm1, z; slong i; w = _fmpz_vec_init(2 * d - 1); fmpz_init(pm1); fmpz_init(z); fmpz_sub_ui(pm1, p, 1); fmpz_pow_ui(z, p, d); fmpz_sub_ui(z, z, 1); fmpz_fdiv_q_2exp(z, z, 1); /* Find a non-residue g; uses stack-based depth first traversal starting from [0,...,0,1] */ _fmpz_vec_zero(rop, d); fmpz_one(rop + (d - 1)); for (i = d; i > 0; ) { if (i == d) { /* Consider this element, rop^{(q-1)/2} == -1 ? */ _qadic_pow(w, rop, d, z, a, j, lena, p); if (fmpz_equal(w + 0, pm1) && _fmpz_vec_is_zero(w + 1, d - 1)) break; /* Backtrace, find the next element */ for (i--; i >= 0 && fmpz_equal(rop + i, pm1); i--) ; if (i >= 0) { fmpz_add_ui(rop + i, rop + i, 1); i++; } } else { _fmpz_vec_zero(rop + i, d - i); i = d; } } _fmpz_vec_clear(w, 2 * d - 1); fmpz_clear(pm1); fmpz_clear(z); } /* Given an element $d$ as \code{(op,len)}, returns whether it has a preimage $u$ under the Artin Schreier map $Y \mapsto Y^2 + Y$, and if so sets \code{(rop,d)} to $u$. In this case, the other preimage is given by $u + 1$. This completes the set of preimages as the kernel of the Artin Schreier map is $\mathbf{F}_2$. The value of \code{(rop,d)}$ is undefined when the return value is zero. */ int _artin_schreier_preimage(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena) { const slong d = j[lena - 1]; const fmpz_t p = {WORD(2)}; int ans; fmpz *e, *f; nmod_mat_t A; slong i, k, rk, *P; e = _fmpz_vec_init(d); f = _fmpz_vec_init(2 * d - 1); nmod_mat_init(A, d, d, 2); P = flint_malloc(d * sizeof(slong)); /* Construct Artin Schreier matrix ------------------------------------- */ for (i = 0; i < d; i++) { fmpz_one(e + i); _fmpz_poly_sqr(f, e, i+1); _fmpz_poly_reduce(f, 2*(i+1)-1, a, j, lena); fmpz_add_ui(f + i, f + i, 1); _fmpz_vec_scalar_mod_fmpz(f, f, d, p); for (k = 0; k < d; k++) { nmod_mat_entry(A, k, i) = (mp_limb_t) f[k]; } fmpz_zero(e + i); } /* Perform LUP decomposition ------------------------------------------- */ /* Write LU = PA and r = rank(A) so that U is in row echelon form with only the top r rows non-zero, and L is lower unit triangular truncated to r columns. We know that Y^2 + Y = 0 if and only if Y is in the base field, i.e., dim ker(A) = 1 and rank(A) = d-1. Consider the linear system A x = b, which we can then write as LU x = Pb, hence L y = Pb and U x = y. */ rk = nmod_mat_lu(P, A, 0); assert(rk == d-1); /* Solve for a preimage of (op,len) ------------------------------------ */ _fmpz_vec_zero(rop, d); for (i = 0; i < d; i++) { rop[i] = P[i] < len ? op[P[i]] : 0; for (k = 0; k < i; k++) rop[i] ^= nmod_mat_entry(A, i, k) & rop[k]; } ans = (rop[d-1] == 0); if (ans) { slong c; for (c = 0; c < d; c++) if (nmod_mat_entry(A, c, c) == 0) break; for (k = d - 1; k > c; k--) { rop[k] = rop[k-1]; if ((rop[k])) for (i = k - 2; i >= 0; i--) rop[i] ^= nmod_mat_entry(A, i, k); } rop[k] = 0; for (k--; k >= 0; k--) { if ((rop[k])) for (i = k - 1; i >= 0; i--) rop[i] ^= nmod_mat_entry(A, i, k); } } /* Clean-up ------------------------------------------------------------ */ _fmpz_vec_clear(e, d); _fmpz_vec_clear(f, 2 * d - 1); nmod_mat_clear(A); flint_free(P); return ans; } /* Returns whether the non-zero element \code{(op, len)} is a square, and if so sets \code{(rop, 2 * d - 1)} to its square root. Assumes that $p$ is an odd prime. Assumes that $d \geq 1$. Does not support aliasing. */ static int _fmpz_mod_poly_sqrtmod_p(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena, const fmpz_t p) { const slong d = j[lena - 1]; int ans; /* When $q \equiv 3 \pmod{4}$... A non-zero element $x$ is a square if and only if $x^{(q-1)/2} = 1$, and in this case one of its square roots is given by $x^{(q+1)/4}$. To avoid recomputation of powers of $x$, we compute $x^{(q-3)/4}$, multiply this by $x$ to obtain the potential square root $x^{(q+1)/4}$, and then combine these two powers to find $x^{(q-1)/2}$. */ if (fmpz_fdiv_ui(p, 4) == 3 && (d & WORD(1))) { fmpz_t z; fmpz *v, *w; fmpz_init(z); v = _fmpz_vec_init(4 * d - 2); w = v + (2 * d - 1); fmpz_pow_ui(z, p, d); fmpz_sub_ui(z, z, 3); fmpz_fdiv_q_2exp(z, z, 2); _qadic_pow(v, op, len, z, a, j, lena, p); _fmpz_poly_mul(rop, v, d, op, len); _fmpz_vec_zero(rop + d + len - 1, d - len); _fmpz_mod_poly_reduce(rop, d + len - 1, a, j, lena, p); _fmpz_poly_mul(w, rop, d, v, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); ans = fmpz_is_one(w + 0); fmpz_clear(z); _fmpz_vec_clear(v, 4 * d - 2); } /* When $q \equiv 5 \pmod{8}$... A non-zero element $x$ is a square if and only if $y = x^{(q-1)/4}$ is $\pm 1$. If $y = +1$, a square root is given by $x^{(q+3)/8}$, otherwise it is $(2x) (4x)^{(q-5)/8}$. We begin by computing $v = x^{(q-5)/8}$, $w = x v = x^{(q+3)/8}$ and $y = v w = x^{(q-1)/4}$. If $y = +1$ we return $w$, and if $y = -1$ we return $2^{(q-1)/4} x^{(q+3)/8} = 2^{(q-1)/4} w$. */ else if (fmpz_fdiv_ui(p, 8) == 5 && (d & WORD(1))) { fmpz_t f, g, pm1; fmpz *v; /* v = x^{(q-5)/8} */ fmpz *w; /* w = x^{(q+3)/8} */ fmpz *y; /* y = x^{(q-1)/4} */ fmpz_init(f); fmpz_init(g); fmpz_init(pm1); fmpz_sub_ui(pm1, p, 1); v = _fmpz_vec_init(2 * d - 1); w = _fmpz_vec_init(2 * d - 1); y = _fmpz_vec_init(2 * d - 1); fmpz_pow_ui(g, p, d); fmpz_sub_ui(g, g, 5); fmpz_fdiv_q_2exp(g, g, 3); _qadic_pow(v, op, len, g, a, j, lena, p); _fmpz_poly_mul(w, v, d, op, len); _fmpz_mod_poly_reduce(w, d + len - 1, a, j, lena, p); _fmpz_poly_mul(y, v, d, w, d); _fmpz_mod_poly_reduce(y, 2 * d - 1, a, j, lena, p); if ((fmpz_is_one(y + 0) || fmpz_equal(y + 0, pm1)) /* q.r. */ && _fmpz_vec_is_zero(y + 1, d - 1)) { if (fmpz_is_one(y + 0)) { _fmpz_vec_set(rop, w, d); } else { fmpz_t two = {WORD(2)}; fmpz_zero(g); fmpz_pow_ui(g, p, d); fmpz_sub_ui(g, g, 1); fmpz_fdiv_q_2exp(g, g, 2); _fmpz_vec_set(rop, v, d); fmpz_powm(f, two, g, p); _fmpz_mod_poly_scalar_mul_fmpz(rop, w, d, f, p); } _fmpz_vec_zero(rop + d, d - 1); ans = 1; } else /* q.n.r. */ { ans = 0; } fmpz_clear(f); fmpz_clear(g); fmpz_clear(pm1); _fmpz_vec_clear(v, 2 * d - 1); _fmpz_vec_clear(w, 2 * d - 1); _fmpz_vec_clear(y, 2 * d - 1); } /* General case for odd $q$... TODO: Find a better way to integrate the check for square-ness into the computation of a potential square root. */ else { slong i, s; fmpz_t t, pm1, qm1, z; fmpz *b, *g, *bpow, *gpow, *w; fmpz_init(t); fmpz_init(pm1); fmpz_init(qm1); fmpz_init(z); fmpz_sub_ui(pm1, p, 1); fmpz_pow_ui(qm1, p, d); fmpz_sub_ui(qm1, qm1, 1); b = _fmpz_vec_init(2 * d - 1); g = _fmpz_vec_init(2 * d - 1); bpow = _fmpz_vec_init(2 * d - 1); gpow = _fmpz_vec_init(2 * d - 1); w = _fmpz_vec_init(2 * d - 1); /* Check whether op is a square, i.e. op^{(q-1}/2} == 1 */ fmpz_fdiv_q_2exp(z, qm1, 1); _qadic_pow(w, op, len, z, a, j, lena, p); ans = fmpz_is_one(w); if (ans) { /* Find a non-residue g */ _find_nonresidue(g, a, j, lena, p); /* Write q - 1 = 2^s t */ for (s = 0, fmpz_set(t, qm1); fmpz_is_even(t); s++) fmpz_fdiv_q_2exp(t, t, 1); /* Set g = g^t */ _qadic_pow(w, g, d, t, a, j, lena, p); _fmpz_vec_set(g, w, d); /* Set rop = op^{(t+1)/2} */ fmpz_add_ui(z, t, 1); fmpz_fdiv_q_2exp(z, z, 1); _qadic_pow(rop, op, len, z, a, j, lena, p); /* Set b = op^t */ _qadic_pow(b, op, len, t, a, j, lena, p); while (!_fmpz_poly_is_one(b, d)) { slong k; _fmpz_vec_set(bpow, b, d); for (k = 1; (k < s) && !_fmpz_poly_is_one(bpow, d); k++) { _fmpz_poly_sqr(w, bpow, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); _fmpz_vec_scalar_mod_fmpz(bpow, w, d, p); } _fmpz_vec_set(gpow, g, d); for (i = 1; i < s - k; i++) { _fmpz_poly_sqr(w, gpow, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); _fmpz_vec_scalar_mod_fmpz(gpow, w, d, p); } _fmpz_poly_mul(w, rop, d, gpow, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); _fmpz_vec_scalar_mod_fmpz(rop, w, d, p); _fmpz_poly_sqr(w, gpow, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); _fmpz_vec_scalar_mod_fmpz(gpow, w, d, p); _fmpz_poly_mul(w, b, d, gpow, d); _fmpz_mod_poly_reduce(w, 2 * d - 1, a, j, lena, p); _fmpz_vec_scalar_mod_fmpz(b, w, d, p); s = k; } } fmpz_clear(t); fmpz_clear(pm1); fmpz_clear(qm1); fmpz_clear(z); _fmpz_vec_clear(b, 2 * d - 1); _fmpz_vec_clear(g, 2 * d - 1); _fmpz_vec_clear(bpow, 2 * d - 1); _fmpz_vec_clear(gpow, 2 * d - 1); _fmpz_vec_clear(w, 2 * d - 1); } return ans; } /* Sets \code{(rop, 2 * d - 1)} to the square root of \code{(op, len)}. Note that the group of units of $\mathbf{F}_q$ is cyclic of order $q - 1$, which is odd if $p = 2$. We have $x^{q-1} = 1$ for every non-zero $x$, so $x^q = x$ for every $x$ and $u = x^{q/2}$ satisfies $u^2 = x$. Assumes that $d \geq 1$. */ static void _fmpz_mod_poly_sqrtmod_2(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena) { const fmpz_t p = {WORD(2)}; const slong d = j[lena - 1]; fmpz_t z; fmpz_init(z); fmpz_setbit(z, d - 1); _qadic_pow(rop, op, len, z, a, j, lena, p); fmpz_clear(z); } /* Returns whether \code{(op, len)} is a square, and if so sets \code{(rop, 2 * d - 1)} to a square root mod $p^N$. Assumes that \code{(op, len)} has valuation $0$. */ static int _qadic_sqrt_p(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena, const fmpz_t p, slong N) { const slong d = j[lena - 1]; int ans; if (N == 1) { ans = _fmpz_mod_poly_sqrtmod_p(rop, op, len, a, j, lena, p); return ans; } else { slong *e, i, k, n; fmpz *pow, *u; fmpz *r, *s, *t, *f; n = FLINT_CLOG2(N) + 1; /* Compute sequence of exponents */ e = flint_malloc(n * sizeof(slong)); for (e[i = 0] = N; e[i] > 1; i++) e[i + 1] = (e[i] + 1) / 2; pow = _fmpz_vec_init(n); u = _fmpz_vec_init(len * n); r = _fmpz_vec_init(2 * d - 1); s = _fmpz_vec_init(2 * d - 1); t = _fmpz_vec_init(2 * d - 1); f = _fmpz_vec_init(d + 1); /* Compute powers of p */ { fmpz_one(t); fmpz_set(pow + i, p); } for (i--; i >= 1; i--) { if (e[i] & WORD(1)) { fmpz_mul(pow + i, t, pow + (i + 1)); fmpz_mul(t, t, t); } else { fmpz_mul(t, t, pow + (i + 1)); fmpz_mul(pow + i, pow + (i + 1), pow + (i + 1)); } } { if (e[i] & WORD(1)) fmpz_mul(pow + i, t, pow + (i + 1)); else fmpz_mul(pow + i, pow + (i + 1), pow + (i + 1)); } /* Compute reduced units */ { _fmpz_vec_scalar_mod_fmpz(u + 0 * len, op, len, pow + 0); } for (i = 1; i < n; i++) { _fmpz_vec_scalar_mod_fmpz(u + i * len, u + (i - 1) * len, len, pow + i); } /* Run Newton iteration */ i = n - 1; { ans = _fmpz_mod_poly_sqrtmod_p(t, u + i * len, len, a, j, lena, p); if (!ans) goto exit; /* Dense copy of f, used for inversion */ for (k = 0; k < lena; k++) fmpz_set(f + j[k], a + k); _fmpz_mod_poly_invmod(rop, t, d, f, d + 1, p); } for (i--; i >= 1; i--) /* z := z - z (a z^2 - 1) / 2 */ { _fmpz_poly_sqr(s, rop, d); _fmpz_poly_reduce(s, 2 * d - 1, a, j, lena); _fmpz_poly_mul(t, s, d, u + i * len, len); _fmpz_poly_reduce(t, d + len - 1, a, j, lena); fmpz_sub_ui(t, t, 1); for (k = 0; k < d; k++) { if (fmpz_is_odd(t + k)) fmpz_add(t + k, t + k, pow + i); fmpz_fdiv_q_2exp(t + k, t + k, 1); } _fmpz_poly_mul(s, t, d, rop, d); _fmpz_poly_reduce(s, 2 * d - 1, a, j, lena); _fmpz_poly_sub(rop, rop, d, s, d); _fmpz_vec_scalar_mod_fmpz(rop, rop, d, pow + i); } { _fmpz_poly_mul(s, rop, d, u + 1 * len, len); _fmpz_poly_reduce(s, d + len - 1, a, j, lena); _fmpz_poly_sqr(t, s, d); _fmpz_poly_reduce(t, 2 * d - 1, a, j, lena); _fmpz_poly_sub(t, u + 0 * len, len, t, d); for (k = 0; k < d; k++) { if (fmpz_is_odd(t + k)) fmpz_add(t + k, t + k, pow + 0); fmpz_fdiv_q_2exp(t + k, t + k, 1); } _fmpz_poly_mul(r, rop, d, t, d); _fmpz_poly_reduce(r, 2 * d - 1, a, j, lena); _fmpz_poly_add(rop, r, d, s, d); _fmpz_vec_scalar_mod_fmpz(rop, rop, d, pow + 0); } exit: _fmpz_vec_clear(pow, n); _fmpz_vec_clear(u, len * n); _fmpz_vec_clear(r, 2 * d - 1); _fmpz_vec_clear(s, 2 * d - 1); _fmpz_vec_clear(t, 2 * d - 1); _fmpz_vec_clear(f, d + 1); flint_free(e); return ans; } } /* Returns whether \code{(op, len)} is a square, and if so sets \code{(rop, 2 * d - 1)} to a square root mod $2^N$. Assumes that \code{(op, len)} has valuation $0$. */ static int _qadic_sqrt_2(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena, slong N) { int ans; const slong d = j[lena - 1]; const fmpz_t p = {WORD(2)}; fmpz *g, *r, *s, *t; slong i; g = _fmpz_vec_init(2 * d - 1); r = _fmpz_vec_init(2 * d - 1); s = _fmpz_vec_init(2 * d - 1); t = _fmpz_vec_init(2 * d - 1); /* Compute r = 1/op (mod 8) */ _qadic_inv(r, op, len, a, j, lena, p, 3); /* Compute s = invsqrt(op) (mod 2) */ _fmpz_vec_scalar_fdiv_r_2exp(t, r, d, 1); _fmpz_mod_poly_sqrtmod_2(s, r, d, a, j, lena); /* Compute t = (1/op - s^2) / 4 (mod 2) if that makes sense */ _fmpz_poly_sqr(t, s, d); _fmpz_poly_reduce(t, 2*d - 1, a, j, lena); _fmpz_vec_sub(t, r, t, d); _fmpz_vec_zero(rop, 2 * d - 1); ans = 1; for (i = 0; i < d; i++) { if (fmpz_val2(t + i) == 1) ans = 0; } if (ans) { _fmpz_vec_scalar_fdiv_q_2exp(t, t, d, 2); _fmpz_vec_scalar_fdiv_r_2exp(t, t, d, 1); /* Check whether X^2 + s X = t (mod 2) has a solution */ /* u = (op,len) is a square in Zq iff it is a square modulo 8, which is the case iff X^2 + s X + t == 0 (mod 2) is soluble. Let g be t/s^2. Then the above quadratic is soluble iff Y^2 + Y + d = 0 is soluble. We can observe that 1/s^2 = op (mod 2). */ _fmpz_vec_scalar_fdiv_r_2exp(r, op, len, 1); _fmpz_poly_mul(g, t, d, r, len); _fmpz_mod_poly_reduce(g, 2 * d - 1, a, j, lena, p); ans = _artin_schreier_preimage(r, g, d, a, j, lena); if (ans) { if (N == 1) { _fmpz_mod_poly_sqrtmod_2(rop, op, len, a, j, lena); } else { /* Set t = r s, a root of X^2 + s X = t (mod 2) */ _fmpz_poly_mul(t, r, d, s, d); _fmpz_mod_poly_reduce(t, 2 * d - 1, a, j, lena, p); /* Now s + 2t is an invsqrt of (op, len) to precision 4. */ _fmpz_vec_scalar_addmul_si(s, t, d, 2); if (N == 2) { _qadic_inv(rop, s, d, a, j, lena, p, 2); } else /* N >= 3 */ { slong *e, n; fmpz *u; n = FLINT_CLOG2(N - 1) + 1; /* Compute sequence of exponents */ e = flint_malloc(n * sizeof(slong)); for (e[i = 0] = N; e[i] > 2; i++) e[i + 1] = e[i] / 2 + 1; u = _fmpz_vec_init(len * n); /* Compute reduced units */ { _fmpz_vec_scalar_fdiv_r_2exp(u + 0 * len, op, len, e[0]); } for (i = 1; i < n; i++) { _fmpz_vec_scalar_fdiv_r_2exp(u + i * len, u + (i - 1) * len, len, e[i] + 1); } /* Run Newton iteration */ { _fmpz_vec_set(rop, s, d); } for (i = n - 2; i >= 1; i--) /* z := z - z (a z^2 - 1) / 2 */ { _fmpz_poly_sqr(s, rop, d); _fmpz_poly_reduce(s, 2 * d - 1, a, j, lena); _fmpz_poly_mul(t, s, d, u + i * len, len); _fmpz_poly_reduce(t, d + len - 1, a, j, lena); fmpz_sub_ui(t, t, 1); _fmpz_vec_scalar_fdiv_q_2exp(t, t, d, 1); _fmpz_poly_mul(s, t, d, rop, d); _fmpz_poly_reduce(s, 2 * d - 1, a, j, lena); _fmpz_poly_sub(rop, rop, d, s, d); _fmpz_vec_scalar_fdiv_r_2exp(rop, rop, d, e[i]); } /* z := a z + z (a - (a z)^2) / 2 */ { _fmpz_poly_mul(s, rop, d, u + len, len); _fmpz_poly_reduce(s, d + len - 1, a, j, lena); _fmpz_poly_sqr(t, s, d); _fmpz_poly_reduce(t, 2 * d - 1, a, j, lena); _fmpz_poly_sub(t, u + 0 * len, len, t, d); _fmpz_vec_scalar_fdiv_q_2exp(t, t, d, 1); _fmpz_poly_mul(r, rop, d, t, d); _fmpz_poly_reduce(r, 2 * d - 1, a, j, lena); _fmpz_poly_add(rop, r, d, s, d); _fmpz_vec_scalar_fdiv_r_2exp(rop, rop, d, e[0]); } _fmpz_vec_clear(u, len * n); flint_free(e); } } } } _fmpz_vec_clear(g, 2 * d - 1); _fmpz_vec_clear(r, 2 * d - 1); _fmpz_vec_clear(s, 2 * d - 1); _fmpz_vec_clear(t, 2 * d - 1); return ans; } /* Returns whether \code{(op, len)} is a square, and if so sets \code{(rop, 2 * d - 1)} to a square root, reduced modulo $2^N$. Assumes that \code{(op, len)} has valuation $0$. */ int _qadic_sqrt(fmpz *rop, const fmpz *op, slong len, const fmpz *a, const slong *j, slong lena, const fmpz_t p, slong N) { if (*p == WORD(2)) { return _qadic_sqrt_2(rop, op, len, a, j, lena, N); } else { return _qadic_sqrt_p(rop, op, len, a, j, lena, p, N); } } int qadic_sqrt(qadic_t rop, const qadic_t op, const qadic_ctx_t ctx) { const fmpz *p = (&ctx->pctx)->p; const slong d = qadic_ctx_degree(ctx); const slong N = qadic_prec(rop); fmpz *t; int ans; if (qadic_is_zero(op)) { qadic_zero(rop); return 1; } if (op->val & WORD(1)) { return 0; } rop->val = op->val / 2; /* FIXME: In this case, we don't actually check whether the element is a square! */ if (rop->val >= N) { qadic_zero(rop); return 1; } if (rop == op) { t = _fmpz_vec_init(2 * d - 1); } else { padic_poly_fit_length(rop, 2 * d - 1); t = rop->coeffs; } ans = _qadic_sqrt(t, op->coeffs, op->length, ctx->a, ctx->j, ctx->len, p, N - rop->val); if (rop == op) { _fmpz_vec_clear(rop->coeffs, rop->alloc); rop->coeffs = t; rop->alloc = 2 * d - 1; rop->length = d; } _padic_poly_set_length(rop, d); _padic_poly_normalise(rop); if (padic_poly_length(rop) == 0) padic_poly_val(rop) = 0; return ans; }