/*=============================================================================
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 "qfb.h"
/*
Iterate through all factors of a number given factorisation
into n prime powers whose maximum values are stored in exp,
storing the values at the current iteration in pows.
*/
int pow_incr(int * pows, int * exp, int n)
{
int i;
for (i = 0; i < n; i++)
{
pows[i]++;
if (pows[i] > exp[i])
pows[i] = 0;
else
return 1;
}
return 0;
}
slong qfb_reduced_forms_large(qfb ** forms, slong d)
{
slong a, j, k, p, alim, alloc, num, roots, sqrt, i, prod, prime_i;
mp_srcptr primes;
const double * prime_inverses;
mp_limb_t a2;
n_factor_t * fac;
if (d >= 0)
{
printf("Exception: qfb_reduced_forms not implemented for positive discriminant.\n");
abort();
}
alim = n_sqrt(-d/3); /* maximum a value to check */
(*forms) = NULL;
alloc = 0;
if ((-d & 3) == 2 || (-d & 3) == 1) /* ensure d is 0, 1 mod 4 */
return 0;
fac = flint_calloc(alim + 1, sizeof(n_factor_t));
for (a = 2; a <= alim; a += 2) /* find powers of 2 dividing 4a values */
{
a2 = a;
fac[a].exp[0] = n_remove(&a2, 2) + 2;
fac[a].p[0] = 2;
fac[a].num = 1;
}
for (a = 1; a <= alim; a += 2)
{
fac[a].exp[0] = 2;
fac[a].p[0] = 2;
fac[a].num = 1;
}
sqrt = n_sqrt(alim);
primes = n_primes_arr_readonly(FLINT_MAX(sqrt, 10000));
prime_inverses = n_prime_inverses_arr_readonly(FLINT_MAX(sqrt, 10000));
prime_i = 1;
while ((p = primes[prime_i]) <= sqrt) /* sieve for factors of 4a values */
{
for (a = p; a <= alim; a+= p)
{
a2 = a;
num = fac[a].num;
fac[a].exp[num] = n_remove2_precomp(&a2, p, prime_inverses[prime_i]);
fac[a].p[num] = p;
fac[a].num++;
}
prime_i++;
}
for (a = 1; a <= alim; a++) /* write any remaining prime factor of each 4a value */
{
prod = 1;
for (i = 0; i < fac[a].num; i++)
prod *= n_pow(fac[a].p[i], fac[a].exp[i]);
p = (4*a)/prod;
if (p != 1)
{
num = fac[a].num;
fac[a].exp[num] = 1;
fac[a].p[num] = p;
fac[a].num++;
}
}
num = 0;
for (a = 1; a <= alim; a++) /* loop through possible a's */
{
mp_limb_t * s;
roots = n_sqrtmodn(&s, n_negmod((-d)%(4*a), 4*a), fac + a);
for (j = 0; j < roots; j++) /* loop through all square roots of d mod 4a */
{
mp_limb_signed_t b = s[j];
if (b > 2*a) b -= 4*a;
if (-a < b && b <= a) /* we may have a form */
{
/*
let B = FLINT_BITS
-sqrt(2^(B-1)/3) < b < sqrt(2^(B-1)/3)
0 < -d < 2^(B-1)
*/
mp_limb_t c = ((mp_limb_t) (b*b) + (mp_limb_t) (-d))/(4*(mp_limb_t) a);
if (c >= (mp_limb_t) a && (b >= 0 || a != c)) /* we have a form */
{
mp_limb_t g;
if (b)
{
g = n_gcd(c, FLINT_ABS(b));
g = n_gcd(a, g);
} else
g = n_gcd(c, a);
if (g == 1) /* we have a primitive form */
{
if (num == alloc) /* realloc if necessary */
{
(*forms) = flint_realloc(*forms, (alloc + FLINT_MIN(alim, 100))*sizeof(qfb));
alloc += FLINT_MIN(alim, 100);
for (k = num; k < alloc; k++)
qfb_init((*forms) + k);
}
fmpz_set_si((*forms)[num].a, a); /* record form */
fmpz_set_si((*forms)[num].b, b);
fmpz_set_si((*forms)[num++].c, c);
}
}
}
}
flint_free(s);
}
flint_free(fac);
return num;
}
slong qfb_reduced_forms(qfb ** forms, slong d)
{
slong a, b, k, c, p, blim, alloc, num, sqrt, i, prod, prime_i;
mp_srcptr primes;
const double * prime_inverses;
mp_limb_t b2, exp, primes_cutoff = 0;
n_factor_t * fac;
mp_limb_t * s;
if (d >= 0)
{
printf("Exception: qfb_reduced_forms not implemented for positive discriminant.\n");
abort();
}
blim = n_sqrt(-d/3); /* maximum a value to check */
(*forms) = NULL;
alloc = 0;
if ((-d & 3) == 2 || (-d & 3) == 1) /* ensure d is 0, 1 mod 4 */
return 0;
sqrt = n_sqrt(blim*blim - d);
n_nth_prime_bounds(&primes_cutoff, &primes_cutoff, sqrt);
if (primes_cutoff > FLINT_PRIMES_SMALL_CUTOFF*FLINT_PRIMES_SMALL_CUTOFF)
return qfb_reduced_forms_large(forms, d);
primes = n_primes_arr_readonly(FLINT_MAX(sqrt, 10000));
prime_inverses = n_prime_inverses_arr_readonly(FLINT_MAX(sqrt, 10000));
fac = flint_calloc(blim + 1, sizeof(n_factor_t));
prime_i = 1;
while ((p = primes[prime_i]) <= sqrt) /* sieve for factors of p^exp */
{
num = n_sqrtmod_primepow(&s, n_negmod((-d) % p, p), p, 1);
for (i = 0; i < num; i++) /* sieve with each sqrt mod p */
{
mp_limb_t off = s[i];
while (off <= blim)
{
b2 = (off*off - (mp_limb_t) d)/4;
fac[off].p[fac[off].num] = p;
fac[off].exp[fac[off].num] = n_remove2_precomp(&b2, p, prime_inverses[prime_i]);
fac[off].num++;
off += p;
}
}
prime_i++;
flint_free(s);
}
for (b = (d & 1); b <= blim; b += 2) /* write any remaining factors, including 2^exp */
{
b2 = ((mp_limb_t)(b*b - d))/4;
count_trailing_zeros(exp, b2); /* powers of 2 */
if (exp)
{
fac[b].p[fac[b].num] = 2;
fac[b].exp[fac[b].num] = exp;
fac[b].num++;
}
prod = 1;
for (i = 0; i < fac[b].num; i++)
prod *= n_pow(fac[b].p[i], fac[b].exp[i]);
b2 /= prod;
if (b2 != 1)
{
fac[b].p[fac[b].num] = b2;
fac[b].exp[fac[b].num] = 1;
fac[b].num++;
}
}
num = 0;
for (b = (d & 1); b <= blim; b += 2) /* compute forms for each b */
{
int pows[FLINT_MAX_FACTORS_IN_LIMB];
int n = fac[b].num;
b2 = ((mp_limb_t)(b*b - d))/4;
for (i = 0; i < n; i++)
pows[i] = 0;
do
{
a = 1;
for (i = 0; i < n; i++)
a *= n_pow(fac[b].p[i], pows[i]);
c = b2 / a;
if (a <= c && b <= a) /* we have a form */
{
mp_limb_t g;
if (b)
{
g = n_gcd(c, b);
g = n_gcd(a, g);
} else
g = n_gcd(c, a);
if (g == 1) /* primitive form */
{
if (num == alloc) /* realloc if necessary */
{
(*forms) = flint_realloc(*forms, (alloc + FLINT_MIN(blim, 100))*sizeof(qfb));
alloc += FLINT_MIN(blim, 100);
for (k = num; k < alloc; k++)
qfb_init((*forms) + k);
}
fmpz_set_si((*forms)[num].a, a); /* record form */
fmpz_set_si((*forms)[num].b, b);
fmpz_set_si((*forms)[num++].c, c);
if (b && a != c && b != a)
{
if (num == alloc) /* realloc if necessary */
{
(*forms) = flint_realloc(*forms, (alloc + FLINT_MIN(blim, 100))*sizeof(qfb));
alloc += FLINT_MIN(blim, 100);
for (k = num; k < alloc; k++)
qfb_init((*forms) + k);
}
fmpz_set_si((*forms)[num].a, a); /* record opposite form */
fmpz_set_si((*forms)[num].b, -b);
fmpz_set_si((*forms)[num++].c, c);
}
}
}
} while (pow_incr(pows, fac[b].exp, n));
}
flint_free(fac);
return num;
}