/*=============================================================================
This file is part of Antic.
Antic 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 .
=============================================================================*/
/******************************************************************************
Copyright (C) 2012 William Hart
******************************************************************************/
#include
#include
#include "flint/flint.h"
#include "flint/ulong_extras.h"
#include "flint/fmpz.h"
#include "qfb.h"
void qfb_prime_form(qfb_t r, fmpz_t D, fmpz_t p)
{
fmpz_t q, rem, s, t;
fmpz_init(s);
if (fmpz_cmp_ui(p, 2) == 0) /* special case, p = 2 */
{
ulong m8 = fmpz_fdiv_ui(D, 8);
if (m8 == 4)
fmpz_set_ui(r->b, 2);
else
fmpz_set_ui(r->b, m8);
fmpz_sub_ui(s, D, m8);
fmpz_neg(s, s);
fmpz_fdiv_q_2exp(r->c, s, 3);
fmpz_set(r->a, p);
fmpz_clear(s);
return;
}
fmpz_init(t);
fmpz_mod(t, D, p);
if (fmpz_is_zero(t)) /* special case, p | D */
{
fmpz_init(q);
fmpz_init(rem);
fmpz_fdiv_q(s, D, p); /* s = D/p */
if (fmpz_is_zero(s))
fmpz_set(t, s);
else
fmpz_sub(t, p, s); /* t = -s mod p */
while (fmpz_fdiv_ui(t, 4) != 0) /* ensure t is divisible by 4 */
fmpz_add(t, t, p);
fmpz_add(t, t, s); /* t = first possible value for d/p + 4c */
fmpz_fdiv_q(t, t, p);
fmpz_sqrtrem(q, rem, t);
if (!fmpz_is_zero(rem)) /* t/p is not a square */
{
if (fmpz_is_even(t)) /* b/p is even as t/p is */
fmpz_add_ui(q, q, 1 + fmpz_is_even(q));
else
fmpz_add_ui(q, q, 1 + fmpz_is_odd(q));
} /* q = b/p */
fmpz_mul(r->b, q, p);
fmpz_mul(q, q, q);
fmpz_mul(q, q, p);
fmpz_sub(q, q, s);
fmpz_fdiv_q_2exp(r->c, q, 2);
fmpz_set(r->a, p);
fmpz_clear(q);
fmpz_clear(rem);
} else
{
fmpz_sqrtmod(t, t, p);
fmpz_sub(s, D, t);
if (fmpz_is_odd(s))
fmpz_sub(t, p, t);
fmpz_set(r->a, p);
fmpz_set(r->b, t);
fmpz_mul(r->c, r->b, r->b);
fmpz_sub(r->c, r->c, D);
fmpz_divexact(r->c, r->c, r->a);
fmpz_fdiv_q_2exp(r->c, r->c, 2);
}
fmpz_clear(s);
fmpz_clear(t);
}