#include "ulong_extras.h"
#include "fmpz.h"
#include "aprcl.h"
int
_aprcl_is_gausspower_2q_equal_first(ulong q, const fmpz_t n)
{
int result;
fmpz_t npow, nval, ncmp;
fmpz_init_set(npow, n);
fmpz_init_set_ui(nval, q);
fmpz_init_set(ncmp, n);
fmpz_sub_ui(ncmp, ncmp, 1);
if ((q - 1) % 2 == 1)
{
fmpz_neg(nval, nval);
fmpz_add(nval, nval, n);
}
fmpz_sub_ui(npow, npow, 1);
fmpz_fdiv_q_2exp(npow, npow, 1);
fmpz_powm(nval, nval, npow, n);
result = 0;
if (fmpz_equal(nval, ncmp))
result = 1;
fmpz_clear(npow);
fmpz_clear(nval);
fmpz_clear(ncmp);
return result;
}
int
_aprcl_is_gausspower_2q_equal_second(ulong q, const fmpz_t n)
{
int result;
fmpz_t npow, nval, ncmp;
fmpz_init_set(npow, n);
fmpz_init_set_ui(nval, q);
fmpz_init_set(ncmp, n);
fmpz_sub_ui(ncmp, ncmp, 1);
fmpz_sub_ui(npow, npow, 1);
fmpz_fdiv_q_2exp(npow, npow, 1);
fmpz_powm(nval, nval, npow, n);
result = 0;
if (fmpz_equal(nval, ncmp))
result = 1;
fmpz_clear(npow);
fmpz_clear(nval);
fmpz_clear(ncmp);
return result;
}
slong
_aprcl_is_gausspower_from_unity_p(ulong q, ulong r, const fmpz_t n)
{
slong result;
ulong i;
unity_zpq temp, gauss, gausspow, gausssigma;
unity_zpq_init(gauss, q, r, n);
unity_zpq_init(gausssigma, q, r, n);
unity_zpq_init(gausspow, q, r, n);
unity_zpq_init(temp, q, r, n);
unity_zpq_gauss_sum(gauss, q, r);
unity_zpq_gauss_sum_sigma_pow(gausssigma, q, r);
unity_zpq_pow(gausspow, gauss, n);
result = -1;
for (i = 0; i < r; i++)
{
unity_zpq_mul_unity_p_pow(temp, gausspow, i);
if (unity_zpq_equal(gausssigma, temp))
{
result = i;
break;
}
}
unity_zpq_clear(gauss);
unity_zpq_clear(gausssigma);
unity_zpq_clear(gausspow);
unity_zpq_clear(temp);
return result;
}
primality_test_status
_aprcl_is_prime_gauss(const fmpz_t n, const aprcl_config config)
{
int *lambdas;
ulong i, j, k, nmod4;
primality_test_status result;
lambdas = (int*) flint_malloc(sizeof(int) * config->rs.num);
for (i = 0; i < config->rs.num; i++)
lambdas[i] = 0;
result = PROBABPRIME;
nmod4 = fmpz_tdiv_ui(n, 4);
for (i = 0; i < config->qs->num; i++)
{
n_factor_t q_factors;
ulong q;
if (result == COMPOSITE) break;
q = fmpz_get_ui(config->qs->p + i);
if (fmpz_equal_ui(n, q))
{
result = PRIME;
break;
}
n_factor_init(&q_factors);
n_factor(&q_factors, q - 1, 1);
for (j = 0; j < q_factors.num; j++)
{
int state, pind;
ulong p;
if (result == COMPOSITE) break;
p = q_factors.p[j];
pind = _aprcl_p_ind(config, p);
state = lambdas[pind];
if (p == 2 && state == 0 && nmod4 == 1)
{
if (_aprcl_is_gausspower_2q_equal_first(q, n) == 1)
{
state = 3;
lambdas[pind] = state;
}
}
if (p == 2 && (state == 0 || state == 2) && nmod4 == 3)
{
if (_aprcl_is_gausspower_2q_equal_second(q, n) == 1)
{
if (state == 2)
state = 3;
else
state = 1;
lambdas[pind] = state;
}
}
for (k = 1; k <= q_factors.exp[j]; k++)
{
int unity_power;
ulong r;
r = n_pow(p, k);
if (aprcl_is_mul_coprime_ui_ui(q, r, n) == 0)
{
result = COMPOSITE;
break;
}
unity_power = _aprcl_is_gausspower_from_unity_p(q, r, n);
if (unity_power < 0)
{
result = COMPOSITE;
break;
}
if (p > 2 && state == 0 && unity_power > 0)
{
ulong upow = unity_power;
if (n_gcd(r, upow) == 1)
{
state = 3;
lambdas[pind] = state;
}
}
if (p == 2 && unity_power > 0
&& (state == 0 || state == 1) && nmod4 == 3)
{
ulong upow = unity_power;
if (n_gcd(r, upow) == 1)
{
if (state == 0)
{
state = 2;
lambdas[pind] = state;
}
if (state == 1)
{
state = 3;
lambdas[pind] = state;
}
}
}
}
}
}
if (result == PROBABPRIME)
for (i = 0; i < config->rs.num; i++)
if (lambdas[i] != 3)
result = UNKNOWN;
if (result == UNKNOWN || result == PROBABPRIME)
{
int f_division;
f_division = aprcl_is_prime_final_division(n, config->s, config->R);
if (result == PROBABPRIME && f_division == 1)
result = PRIME;
if (result == UNKNOWN && f_division == 1)
result = PROBABPRIME;
if (f_division == 0)
result = COMPOSITE;
}
flint_free(lambdas);
return result;
}
int
aprcl_is_prime_gauss_min_R(const fmpz_t n, ulong R)
{
primality_test_status result;
aprcl_config config;
aprcl_config_gauss_init_min_R(config, n, R);
result = _aprcl_is_prime_gauss(n, config);
aprcl_config_gauss_clear(config);
if (result == PRIME)
return 1;
return 0;
}
int
aprcl_is_prime_gauss(const fmpz_t n)
{
ulong R;
primality_test_status result;
aprcl_config config;
if (fmpz_cmp_ui(n, 2) < 0)
return 0;
aprcl_config_gauss_init_min_R(config, n, 180);
result = _aprcl_is_prime_gauss(n, config);
R = config->R;
aprcl_config_gauss_clear(config);
if (result == PROBABPRIME)
{
R = R * 2;
aprcl_config_gauss_init_min_R(config, n, R);
result = _aprcl_is_prime_gauss(n, config);
aprcl_config_gauss_clear(config);
}
if (result == PROBABPRIME)
{
R = R * 3;
aprcl_config_gauss_init_min_R(config, n, R);
result = _aprcl_is_prime_gauss(n, config);
aprcl_config_gauss_clear(config);
}
if (result == PROBABPRIME)
{
R = R * 5;
aprcl_config_gauss_init_min_R(config, n, R);
result = _aprcl_is_prime_gauss(n, config);
aprcl_config_gauss_clear(config);
}
if (result == PROBABPRIME || result == UNKNOWN)
{
flint_throw(FLINT_ERROR, "aprcl_is_prime_gauss: failed to prove n prime for n = %s\n", fmpz_get_str(NULL, 10, n));
}
if (result == PRIME)
return 1;
return 0;
}