/*=============================================================================
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"
/*
find which power of the base is the exponent of f
*/
ulong find_power(qfb_t f, fmpz_t n, ulong base)
{
ulong s = 1;
do
{
qfb_pow_ui(f, f, n, base);
s *= base;
} while (!qfb_is_principal_form(f, n));
return s;
}
ulong qfb_exponent_element_stage2(qfb_t f, fmpz_t n, ulong B2_sqrt)
{
qfb_t pow, pow2, f2;
fmpz_t L, r;
slong i, i2, ret = 0;
slong depth = FLINT_BIT_COUNT(B2_sqrt) + 1;
qfb_hash_t * qhash = qfb_hash_init(depth);
fmpz_init(L);
fmpz_init(r);
fmpz_abs(L, n);
fmpz_root(L, L, 4);
qfb_init(f2);
qfb_init(pow);
qfb_init(pow2);
qfb_hash_insert(qhash, f, NULL, 1, depth);
qfb_nucomp(f2, f, f, n, L); /* large primes are odd */
qfb_reduce(f2, f2, n);
qfb_set(pow, f);
for (i = 1; i < B2_sqrt - 1; i += 2) /* baby steps */
{
qfb_nucomp(pow, pow, f2, n, L);
qfb_reduce(pow, pow, n);
qfb_hash_insert(qhash, pow, NULL, i + 2, depth);
}
qfb_nucomp(pow, pow, f, n, L); /* compute f^B2_sqrt */
qfb_reduce(pow, pow, n);
qfb_nucomp(pow, pow, pow, n, L); /* we hash for a form or its inverse,
so we can jump by f^(2x B2_sqrt) */
qfb_reduce(pow, pow, n);
qfb_set(pow2, pow);
for(i = 2; i <= B2_sqrt; i += 2) /* giant steps */
{
i2 = qfb_hash_find(qhash, pow2, depth);
if (i2 != -1) /* found collision */
{
fmpz_set_ui(r, B2_sqrt);
fmpz_mul_ui(r, r, i);
if (fmpz_sgn(qhash[i2].q->b) == fmpz_sgn(pow2->b))
fmpz_sub_ui(r, r, qhash[i2].iter);
else
fmpz_add_ui(r, r, qhash[i2].iter);
ret = (fmpz_size(r) > 1 ? 0 : fmpz_get_ui(r)); /* we probably should be more aggressive here */
break;
}
qfb_nucomp(pow2, pow2, pow, n, L);
qfb_reduce(pow2, pow2, n);
}
fmpz_clear(r);
fmpz_clear(L);
qfb_clear(f2);
qfb_clear(pow);
qfb_clear(pow2);
qfb_hash_clear(qhash, depth);
return ret;
}
typedef struct
{
ulong pr;
qfb_t pow;
slong i;
} qfb_restart_t;
#define go_restart \
do { \
need_restarts = 0; \
if (j == 0) \
{ \
qfb_pow(f2, f2, n, exponent); \
goto do_restart1; \
} \
j--; \
qfb_set(pow, restart[j].pow); \
qfb_pow(pow, pow, n, exponent); \
i = restart[j].i; \
n_primes_jump_after(iter, restart[j].pr); \
pr = restart[j].pr; \
goto do_restart; \
} while (0)
int qfb_exponent_element(fmpz_t exponent, qfb_t f, fmpz_t n, ulong B1, ulong B2_sqrt)
{
slong i, j, iters = 1024, restart_inc;
qfb_t pow, oldpow, f2;
ulong pr, oldpr, s2, sqrt, exp;
fmpz_t prod, L, pow2;
int ret = 1, num_restarts = 0, need_restarts = 1, clean2 = 0;
qfb_restart_t restart[128];
n_primes_t iter;
ulong hi, lo;
double quot;
mp_bitcnt_t bits0;
n_primes_init(iter);
sqrt = n_sqrt(B1);
bits0 = FLINT_BIT_COUNT(B1);
fmpz_set_ui(exponent, 1);
qfb_init(f2);
qfb_init(pow);
qfb_init(oldpow);
fmpz_init(pow2);
fmpz_init(prod);
fmpz_init(L);
fmpz_abs(L, n);
fmpz_root(L, L, 4);
qfb_set(f2, f);
do_restart1:
if (qfb_is_principal_form(f2, n))
goto cleanup2;
/* raise to appropriate power of 2 */
qfb_set(oldpow, f2);
fmpz_set_ui(pow2, 2);
fmpz_pow_ui(pow2, pow2, bits0);
qfb_pow(pow, oldpow, n, pow2);
if (qfb_is_principal_form(pow, n))
{
ulong s = find_power(oldpow, n, 2);
fmpz_mul_ui(exponent, exponent, s);
goto cleanup2;
}
for (i = 0; i < 128; i++)
qfb_init(restart[i].pow);
clean2 = 1;
n_prime_pi_bounds(&lo, &hi, B1);
restart_inc = (((hi/1024 + 127)/128))*1024;
quot = (double) B2_sqrt/(double) lo;
pr = 2;
n_primes_jump_after(iter, 2);
i = 0;
do_restart:
/* keep raising iters by a factor of 2 until pr exceeds B1 or exponent found */
do
{
/* raise to prime powers until the identity is found */
for ( ; i < iters; )
{
if ((i % restart_inc) == 0 && need_restarts)
{
qfb_set(restart[num_restarts].pow, pow);
restart[num_restarts].i = i;
restart[num_restarts].pr = pr;
num_restarts++;
}
j = FLINT_MIN(i + 1024, iters);
qfb_set(oldpow, pow);
oldpr = pr;
for ( ; i < j; i+=4)
{
pr = n_primes_next(iter);
fmpz_set_ui(prod, pr);
if (pr < sqrt)
{
exp = bits0/FLINT_BIT_COUNT(pr);
fmpz_pow_ui(prod, prod, exp);
}
pr = n_primes_next(iter);
if (pr < sqrt)
{
exp = bits0/FLINT_BIT_COUNT(pr);
fmpz_set_ui(pow2, pr);
fmpz_pow_ui(pow2, pow2, exp);
fmpz_mul(prod, prod, pow2);
} else
fmpz_mul_ui(prod, prod, pr);
pr = n_primes_next(iter);
if (pr < sqrt)
{
exp = bits0/FLINT_BIT_COUNT(pr);
fmpz_set_ui(pow2, pr);
fmpz_pow_ui(pow2, pow2, exp);
fmpz_mul(prod, prod, pow2);
} else
fmpz_mul_ui(prod, prod, pr);
pr = n_primes_next(iter);
if (pr < sqrt)
{
exp = bits0/FLINT_BIT_COUNT(pr);
fmpz_set_ui(pow2, pr);
fmpz_pow_ui(pow2, pow2, exp);
fmpz_mul(prod, prod, pow2);
} else
fmpz_mul_ui(prod, prod, pr);
qfb_pow_with_root(pow, pow, n, prod, L);
}
/* identity is found, compute exponent recursively */
if (qfb_is_principal_form(pow, n))
{
qfb_set(pow, oldpow);
n_primes_jump_after(iter, oldpr);
pr = oldpr;
while (1)
{
pr = n_primes_next(iter);
if (pr < sqrt)
{
ulong k;
exp = bits0/FLINT_BIT_COUNT(pr);
for (k = 1; k <= exp; k++)
{
qfb_pow_ui(pow, pow, n, pr);
if (qfb_is_principal_form(pow, n))
{
fmpz_set_ui(pow2, pr);
fmpz_pow_ui(pow2, pow2, k);
fmpz_mul(exponent, exponent, pow2);
for (j = 0; j < num_restarts; j++)
{
qfb_set(pow, restart[j].pow);
qfb_pow(pow, pow, n, exponent);
if (qfb_is_principal_form(pow, n))
break;
}
go_restart;
}
}
} else
{
qfb_pow_ui(pow, pow, n, pr);
if (qfb_is_principal_form(pow, n))
{
fmpz_mul_ui(exponent, exponent, pr);
for (j = 0; j < num_restarts; j++)
{
qfb_set(pow, restart[j].pow);
qfb_pow(pow, pow, n, exponent);
if (qfb_is_principal_form(pow, n))
break;
}
go_restart;
}
}
}
}
}
/* stage 2 */
s2 = qfb_exponent_element_stage2(pow, n, (ulong) ((double) iters * quot));
if (s2 && n_is_prime(s2)) /* we probably should be more aggressive here */
{
fmpz_mul_ui(exponent, exponent, s2);
for (j = 0; j < num_restarts; j++)
{
qfb_set(pow, restart[j].pow);
qfb_pow(pow, pow, n, exponent);
if (qfb_is_principal_form(pow, n))
break;
}
go_restart;
}
iters = FLINT_MIN(2*iters, hi);
} while (pr <= B1);
ret = 0;
cleanup2:
if (clean2)
for (i = 0; i < 128; i++)
qfb_clear(restart[i].pow);
qfb_clear(f2);
qfb_clear(pow);
qfb_clear(oldpow);
fmpz_clear(pow2);
fmpz_clear(prod);
fmpz_clear(L);
n_primes_clear(iter);
return ret;
}