851 lines
24 KiB
C
851 lines
24 KiB
C
|
/*=============================================================================
|
||
|
|
||
|
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 <assert.h>
|
||
|
|
||
|
#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;
|
||
|
}
|