/*
Copyright (C) 2008, 2009, 2018 William Hart
Copyright (C) 2010, 2011 Sebastian Pancratz
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 .
*/
#include
#include
#include "flint.h"
#include "fmpz.h"
#include "fmpz_vec.h"
#include "fmpz_poly.h"
#define FMPZ_POLY_SQRT_KS_HEURISTIC_BITS 3
int
_fmpz_poly_sqrt_KS(fmpz *rop, const fmpz *op, slong len)
{
slong i, len2, m, rlimbs;
int result = 1;
slong bits, bits2, limbs, limbs2, loglen;
mp_limb_t *arr, *arr2, *arr3;
/* the degree must be even */
if (len % 2 == 0)
return 0;
/* valuation must be even, and then can be reduced to 0 */
while (fmpz_is_zero(op))
{
if (!fmpz_is_zero(op + 1))
return 0;
fmpz_zero(rop);
op += 2;
len -= 2;
rop++;
}
/* check whether a square root exists modulo 2 */
m = (len + 1) / 2;
for (i = ((m - 1) | 1); i < len; i += 2)
if (!fmpz_is_even(op + i))
return 0;
for (i = 1; i < ((m - 1) | 1); i += 2)
if (!fmpz_is_even(op + i))
return 0;
/* check endpoints */
if (!fmpz_is_square(op))
return 0;
if (len > 1 && !fmpz_is_square(op + len - 1))
return 0;
bits = FLINT_ABS(_fmpz_vec_max_bits(op, len));
len2 = (len + 1) / 2;
bits += FMPZ_POLY_SQRT_KS_HEURISTIC_BITS + FLINT_BIT_COUNT(len);
limbs = (bits * len - 1) / FLINT_BITS + 1;
arr = (mp_limb_t *) flint_calloc(limbs, sizeof(mp_limb_t));
_fmpz_poly_bit_pack(arr, op, len, bits, 0);
limbs2 = (bits * len2 - 1) / FLINT_BITS + 1;
arr2 = (mp_limb_t *) flint_calloc(limbs2, sizeof(mp_limb_t));
arr3 = (mp_limb_t *) flint_calloc(limbs, sizeof(mp_limb_t));
while (limbs != 0 && arr[limbs - 1] == 0)
limbs--;
rlimbs = mpn_sqrtrem(arr2, arr3, arr, limbs);
loglen = FLINT_BIT_COUNT(len2);
if (rlimbs != 0)
result = 0;
else
{
_fmpz_poly_bit_unpack(rop, len2, arr2, bits, 0);
bits2 = _fmpz_vec_max_bits(rop, len2);
if (2*FLINT_ABS(bits2) + loglen + 1 > bits)
result = -1;
}
flint_free(arr);
flint_free(arr2);
flint_free(arr3);
return result;
}
int
fmpz_poly_sqrt_KS(fmpz_poly_t b, const fmpz_poly_t a)
{
slong blen, len = a->length;
int result;
if (len % 2 == 0)
{
fmpz_poly_zero(b);
return len == 0;
}
if (b == a)
{
fmpz_poly_t tmp;
fmpz_poly_init(tmp);
result = fmpz_poly_sqrt_KS(tmp, a);
fmpz_poly_swap(b, tmp);
fmpz_poly_clear(tmp);
return result;
}
blen = len / 2 + 1;
fmpz_poly_fit_length(b, blen);
_fmpz_poly_set_length(b, blen);
result = _fmpz_poly_sqrt_KS(b->coeffs, a->coeffs, len);
if (result <= 0)
_fmpz_poly_set_length(b, 0);
return result;
}