pqc/external/flint-2.4.3/qadic/log_balanced.c
2014-05-24 23:16:06 +02:00

309 lines
8.0 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 "fmpz_mod_poly.h"
#include "qadic.h"
extern slong _padic_log_bound(slong v, slong N, const fmpz_t p);
/*
Assumes that P, T are vectors of length 2 d - 1.
Assumes that 0 < len <= d.
Assumes that 1 <= lo < hi.
Does not support aliasing.
*/
static void
_qadic_log_bsplit_series(fmpz *P, fmpz_t B, fmpz *T,
const fmpz *y, slong len, slong lo, slong hi,
const fmpz *a, const slong *j, slong lena)
{
const slong d = j[lena - 1];
if (hi - lo == 1)
{
_fmpz_vec_set(P, y, len);
_fmpz_vec_zero(P + len, 2*d - 1 - len);
fmpz_set_si(B, lo);
_fmpz_vec_set(T, P, 2*d - 1);
}
else if (hi - lo == 2)
{
_fmpz_poly_sqr(P, y, len);
_fmpz_vec_zero(P + (2*len - 1), d - (2*len - 1));
_fmpz_poly_reduce(P, 2*len - 1, a, j, lena);
fmpz_set_si(B, lo);
fmpz_mul_si(B, B, lo + 1);
_fmpz_vec_scalar_mul_si(T, y, len, lo + 1);
_fmpz_vec_zero(T + len, d - len);
_fmpz_vec_scalar_addmul_si(T, P, d, lo);
}
else
{
const slong m = (lo + hi) / 2;
fmpz *RP, *RT, *W;
fmpz_t RB;
RP = _fmpz_vec_init(2*d - 1);
RT = _fmpz_vec_init(2*d - 1);
W = _fmpz_vec_init(2*d - 1);
fmpz_init(RB);
_qadic_log_bsplit_series(P, B, T, y, len, lo, m, a, j, lena);
_qadic_log_bsplit_series(RP, RB, RT, y, len, m, hi, a, j, lena);
_fmpz_poly_mul(W, RT, d, P, d);
_fmpz_poly_reduce(W, 2*d - 1, a, j, lena);
_fmpz_vec_swap(RT, W, d);
_fmpz_vec_scalar_mul_fmpz(T, T, d, RB);
_fmpz_vec_scalar_addmul_fmpz(T, RT, d, B);
_fmpz_poly_mul(W, P, d, RP, d);
_fmpz_poly_reduce(W, 2*d - 1, a, j, lena);
_fmpz_vec_swap(P, W, d);
fmpz_mul(B, B, RB);
_fmpz_vec_clear(RP, 2*d - 1);
_fmpz_vec_clear(RT, 2*d - 1);
_fmpz_vec_clear(W, 2*d - 1);
fmpz_clear(RB);
}
}
/*
Sets (z, d) to the sum
sum_{i=1}^{\infty} y^i / i mod p^N.
The result may not be reduced modulo p^N, but it is
reduced modulo f(X) given by the data (a, j, lena).
Supports aliasing between y and z.
*/
static void
_qadic_log_bsplit(fmpz *z, const fmpz *y, slong v, slong len,
const fmpz *a, const slong *j, slong lena,
const fmpz_t p, slong N)
{
const slong d = j[lena - 1];
fmpz *P, *T;
fmpz_t B, C;
slong n;
n = _padic_log_bound(v, N, p);
n = FLINT_MAX(n, 2);
P = _fmpz_vec_init(2*d - 1);
T = _fmpz_vec_init(2*d - 1);
fmpz_init(B);
fmpz_init(C);
_qadic_log_bsplit_series(P, B, T, y, len, 1, n, a, j, lena);
n = fmpz_remove(B, B, p);
fmpz_pow_ui(C, p, n);
_fmpz_vec_scalar_divexact_fmpz(T, T, d, C);
_padic_inv(B, B, p, N);
_fmpz_vec_scalar_mul_fmpz(z, T, d, B);
_fmpz_vec_clear(P, 2*d - 1);
_fmpz_vec_clear(T, 2*d - 1);
fmpz_clear(B);
fmpz_clear(C);
}
/*
Computes
\begin{equation*}
z = - \sum_{i = 1}^{\infty} \frac{y^i}{i} \pmod{p^N}.
\end{equation*}
Note that this can be used to compute the $p$-adic logarithm
via the equation
\begin{align*}
\log(x) & = \sum_{i=1}^{\infty} (-1)^{i-1} \frac{(x-1)^i}{i} \\
& = - \sum_{i=1}^{\infty} \frac{(1-x)^i}{i}.
\end{align*}
Assumes that $y = 1 - x$ is non-zero and that $v = \ord_p(y)$
is at least $1$ when $p$ is odd and at least $2$ when $p = 2$
so that the series converges.
Assumes that $v < N$.
Sets $(z, d)$.
Ensures that the result is reduced modulo $p^N$.
Does not support aliasing between $y$ and $z$.
*/
void _qadic_log_balanced(fmpz *z, const fmpz *y, slong len,
const fmpz *a, const slong *j, slong lena,
const fmpz_t p, slong N, const fmpz_t pN)
{
const slong d = j[lena - 1];
fmpz_t pv;
fmpz *r, *s, *t, *u;
slong i, w;
r = _fmpz_vec_init(d);
s = _fmpz_vec_init(2*d - 1);
t = _fmpz_vec_init(d);
u = _fmpz_vec_init(d);
fmpz_init(pv);
fmpz_set(pv, p);
_fmpz_vec_scalar_mod_fmpz(t, y, len, pN);
_fmpz_vec_zero(z, d);
w = 1;
while (!_fmpz_vec_is_zero(t, d))
{
fmpz_mul(pv, pv, pv);
for (i = 0; i < d; i++)
{
fmpz_fdiv_qr(t + i, r + i, t + i, pv);
}
if (!_fmpz_vec_is_zero(t, d))
{
_fmpz_vec_scalar_mul_fmpz(t, t, d, pv);
/* Temporarily set r = 1 - r to compute u = (1-r)^{-1} */
fmpz_sub_ui(r + 0, r + 0, 1);
_fmpz_vec_neg(r, r, d);
_qadic_inv(u, r, d, a, j, lena, p, N);
_fmpz_vec_neg(r, r, d);
fmpz_add_ui(r + 0, r + 0, 1);
_fmpz_poly_mul(s, t, d, u, d);
_fmpz_poly_reduce(s, 2 * d - 1, a, j, lena);
_fmpz_vec_scalar_mod_fmpz(t, s, d, pN);
}
if (!_fmpz_vec_is_zero(r, d))
{
_qadic_log_bsplit(r, r, w, d, a, j, lena, p, N);
_fmpz_vec_sub(z, z, r, d);
_fmpz_vec_scalar_mod_fmpz(z, z, d, pN);
}
w *= 2;
}
_fmpz_vec_clear(r, d);
_fmpz_vec_clear(s, 2*d - 1);
_fmpz_vec_clear(t, d);
_fmpz_vec_clear(u, d);
fmpz_clear(pv);
}
int qadic_log_balanced(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);
const slong len = op->length;
if (op->val < 0)
{
return 0;
}
else
{
fmpz *x;
fmpz_t pN;
int alloc, ans;
x = _fmpz_vec_init(len + 1);
alloc = _padic_ctx_pow_ui(pN, N, &ctx->pctx);
/* Set x := (1 - op) mod p^N */
fmpz_pow_ui(x + len, p, op->val);
_fmpz_vec_scalar_mul_fmpz(x, op->coeffs, len, x + len);
fmpz_sub_ui(x + 0, x + 0, 1);
_fmpz_vec_neg(x, x, len);
_fmpz_vec_scalar_mod_fmpz(x, x, len, pN);
if (_fmpz_vec_is_zero(x, len))
{
padic_poly_zero(rop);
ans = 1;
}
else
{
const slong v = _fmpz_vec_ord_p(x, len, p);
if (v >= 2 || (*p != WORD(2) && v >= 1))
{
if (v >= N)
{
padic_poly_zero(rop);
}
else
{
padic_poly_fit_length(rop, d);
_qadic_log_balanced(rop->coeffs, x, len,
ctx->a, ctx->j, ctx->len, p, N, pN);
rop->val = 0;
_padic_poly_set_length(rop, d);
_padic_poly_normalise(rop);
padic_poly_canonicalise(rop, p);
}
ans = 1;
}
else
{
ans = 0;
}
}
_fmpz_vec_clear(x, len + 1);
if (alloc)
fmpz_clear(pN);
return ans;
}
}