#include "ulong_extras.h"
#include "fmpz.h"
#include "fmpz_mod.h"
#include "aprcl.h"
slong
_aprcl_is_prime_jacobi_check_pk(const unity_zp j, const fmpz_t u, ulong v)
{
slong h;
ulong i, r;
unity_zp j0, jv, temp, aut;
r = n_pow(j->p, j->exp);
unity_zp_init(j0, j->p, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(jv, j->p, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(temp, j->p, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(aut, j->p, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_coeff_set_ui(j0, 0, 1);
unity_zp_coeff_set_ui(jv, 0, 1);
for (i = 1; i <= r; i++)
{
if (i % j->p == 0) continue;
unity_zp_pow_ui(temp, j, i);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, j0, aut);
unity_zp_swap(temp, j0);
unity_zp_pow_ui(temp, j, (v * i) / r);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, jv, aut);
unity_zp_swap(temp, jv);
}
unity_zp_pow_sliding_fmpz(temp, j0, u);
unity_zp_mul(j0, jv, temp);
h = unity_zp_is_unity(j0);
unity_zp_clear(aut);
unity_zp_clear(j0);
unity_zp_clear(jv);
unity_zp_clear(temp);
return h;
}
slong
_aprcl_is_prime_jacobi_check_21(ulong q, const fmpz_t n)
{
slong h;
fmpz_t qpow, ndec, temp;
fmpz_init(temp);
fmpz_init_set_ui(qpow, q);
fmpz_init_set(ndec, n);
fmpz_sub(qpow, n, qpow);
fmpz_sub_ui(ndec, ndec, 1);
fmpz_fdiv_q_2exp(temp, ndec, 1);
fmpz_powm(qpow, qpow, temp, n);
h = -1;
if (fmpz_equal_ui(qpow, 1))
h = 0;
if (fmpz_equal(qpow, ndec))
h = 1;
fmpz_clear(temp);
fmpz_clear(qpow);
fmpz_clear(ndec);
return h;
}
slong
_aprcl_is_prime_jacobi_check_22(const unity_zp j, const fmpz_t u, ulong v, ulong q)
{
slong h;
unity_zp j0, jv, j_pow;
unity_zp_init(j_pow, 2, 2, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(j0, 2, 2, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(jv, 2, 2, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_mul(j_pow, j, j);
unity_zp_mul_scalar_ui(j0, j_pow, q);
if (v == 1)
unity_zp_coeff_set_ui(jv, 0, 1);
else if (v == 3)
unity_zp_swap(jv, j_pow);
unity_zp_pow_sliding_fmpz(j_pow, j0, u);
unity_zp_mul(j0, jv, j_pow);
h = unity_zp_is_unity(j0);
unity_zp_clear(j_pow);
unity_zp_clear(j0);
unity_zp_clear(jv);
return h;
}
slong
_aprcl_is_prime_jacobi_check_2k(const unity_zp j, const unity_zp j2_1,
const unity_zp j2_2, const fmpz_t u, ulong v)
{
slong h;
ulong i, r;
unity_zp j_j0, j0, jv, temp, aut;
r = n_pow(j->p, j->exp);
unity_zp_init(temp, 2, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(j_j0, 2, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(aut, 2, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(j0, 2, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_init(jv, 2, j->exp, fmpz_mod_ctx_modulus(j->ctx));
unity_zp_coeff_set_ui(j0, 0, 1);
unity_zp_coeff_set_ui(jv, 0, 1);
unity_zp_mul(j_j0, j, j2_1);
for (i = 1; i < r;)
{
unity_zp_pow_ui(temp, j_j0, i);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, j0, aut);
unity_zp_swap(temp, j0);
unity_zp_pow_ui(temp, j_j0, (v * i) / r);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, jv, aut);
unity_zp_swap(temp, jv);
i += 2;
unity_zp_pow_ui(temp, j_j0, i);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, j0, aut);
unity_zp_swap(temp, j0);
unity_zp_pow_ui(temp, j_j0, (v * i) / r);
_unity_zp_reduce_cyclotomic(temp);
unity_zp_aut_inv(aut, temp, i);
unity_zp_mul(temp, jv, aut);
unity_zp_swap(temp, jv);
i += 6;
}
if (v % 8 != 1 && v % 8 != 3)
{
unity_zp_mul(temp, j2_2, j2_2);
unity_zp_mul(j_j0, jv, temp);
unity_zp_swap(j_j0, jv);
}
unity_zp_pow_sliding_fmpz(temp, j0, u);
unity_zp_mul(j0, jv, temp);
h = unity_zp_is_unity(j0);
unity_zp_clear(aut);
unity_zp_clear(j0);
unity_zp_clear(jv);
unity_zp_clear(j_j0);
unity_zp_clear(temp);
return h;
}
int
_aprcl_is_prime_jacobi_additional_test(const fmpz_t n, ulong p)
{
int result, p_counter, m;
ulong q;
fmpz_t npow, qmod;
result = 0;
p_counter = 50;
m = 9;
fmpz_init(npow);
fmpz_init(qmod);
while (p_counter > 0)
{
q = 2 * m * p + 1;
if (n_is_prime(q) && fmpz_fdiv_ui(n, q) != 0)
{
fmpz_set_ui(qmod, q);
fmpz_powm_ui(npow, n, (q - 1) / p, qmod);
if (!fmpz_equal_ui(npow, 1))
break;
p_counter--;
}
m += 2;
}
if (p_counter != 0)
{
if (fmpz_fdiv_ui(n, q) == 0 && !fmpz_equal_ui(n, q))
result = 2;
else
{
ulong v, k;
slong h;
fmpz_t u;
unity_zp jacobi_sum;
fmpz_init(u);
k = aprcl_p_power_in_q(q - 1, p);
unity_zp_init(jacobi_sum, p, k, n);
unity_zp_jacobi_sum_pq(jacobi_sum, q, p);
fmpz_tdiv_q_ui(u, n, n_pow(p, k));
v = fmpz_tdiv_ui(n, n_pow(p, k));
if (p == 2)
{
h = _aprcl_is_prime_jacobi_check_22(jacobi_sum, u, v, q);
if (h < 0 || h % 2 == 0)
result = 2;
else
{
fmpz_t ndec, ndecdiv, qpow;
fmpz_init_set(ndec, n);
fmpz_init(ndecdiv);
fmpz_init_set_ui(qpow, q);
fmpz_sub_ui(ndec, ndec, 1);
fmpz_fdiv_q_2exp(ndecdiv, ndec, 1);
fmpz_powm(qpow, qpow, ndecdiv, n);
if (fmpz_equal(qpow, ndec))
result = 1;
else
result = 2;
fmpz_clear(ndec);
fmpz_clear(ndecdiv);
fmpz_clear(qpow);
}
}
else
{
h = _aprcl_is_prime_jacobi_check_pk(jacobi_sum, u, v);
if (h < 0 || h % p == 0)
result = 2;
else
result = 1;
}
fmpz_clear(u);
unity_zp_clear(jacobi_sum);
}
}
if (p_counter == 0)
{
fmpz_t root;
if (fmpz_tdiv_ui(n, p) == 0)
result = 2;
fmpz_init(root);
if (fmpz_is_perfect_power(root, n))
result = 2;
fmpz_clear(root);
}
fmpz_clear(npow);
fmpz_clear(qmod);
return result;
}
primality_test_status
_aprcl_is_prime_jacobi(const fmpz_t n, const aprcl_config config)
{
int *lambdas;
ulong i, j, nmod4;
primality_test_status result;
fmpz_t temp, p2, ndec, ndecdiv, u, q_pow;
if (fmpz_cmp_ui(n, 2) == 0)
return PRIME;
if (fmpz_cmp_ui(n, 3) == 0)
return PRIME;
fmpz_init(q_pow);
fmpz_init(u);
fmpz_init(temp);
fmpz_init(p2);
fmpz_init(ndecdiv);
fmpz_init_set(ndec, n);
fmpz_sub_ui(ndec, ndec, 1);
fmpz_fdiv_q_2exp(ndecdiv, ndec, 1);
result = PROBABPRIME;
lambdas = (int*) flint_malloc(sizeof(int) * config->rs.num);
nmod4 = fmpz_tdiv_ui(n, 4);
for (i = 0; i < config->rs.num; i++)
{
ulong p = config->rs.p[i];
if (p > 2)
{
fmpz_set_ui(p2, p * p);
fmpz_powm_ui(temp, n, p - 1, p2);
if (fmpz_equal_ui(temp, 1) == 0)
lambdas[i] = 1;
else
lambdas[i] = 0;
}
else
lambdas[i] = 0;
}
if (aprcl_is_mul_coprime_ui_fmpz(config->R, config->s, n) == 0)
result = COMPOSITE;
for (i = 0; i < config->qs->num; i++)
{
n_factor_t q_factors;
ulong q;
if (config->qs_used[i] == 0)
continue;
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 pind;
slong h;
ulong v, p, r, k;
unity_zp jacobi_sum, jacobi_sum2_1, jacobi_sum2_2;
if (result == COMPOSITE)
break;
p = q_factors.p[j];
k = q_factors.exp[j];
r = n_pow(p, k);
pind = _aprcl_p_ind(config, p);
fmpz_set_ui(q_pow, q);
if (lambdas[pind] == 0 && p == 2)
fmpz_powm(q_pow, q_pow, ndecdiv, n);
fmpz_tdiv_q_ui(u, n, r);
v = fmpz_tdiv_ui(n, r);
unity_zp_init(jacobi_sum, p, k, n);
unity_zp_init(jacobi_sum2_1, p, k, n);
unity_zp_init(jacobi_sum2_2, p, k, n);
unity_zp_jacobi_sum_pq(jacobi_sum, q, p);
if (p == 2 && k >= 3)
{
unity_zp_jacobi_sum_2q_one(jacobi_sum2_1, q);
unity_zp_jacobi_sum_2q_two(jacobi_sum2_2, q);
}
if (p == 2 && k == 1)
{
h = _aprcl_is_prime_jacobi_check_21(q, n);
if (h < 0)
result = COMPOSITE;
if (lambdas[pind] == 0 && h == 1 && nmod4 == 1)
lambdas[pind] = 1;
}
if (p == 2 && k == 2)
{
h = _aprcl_is_prime_jacobi_check_22(jacobi_sum, u, v, q);
if (h < 0)
result = COMPOSITE;
if (h % 2 != 0 && lambdas[pind] == 0 && fmpz_equal(q_pow, ndec))
lambdas[pind] = 1;
}
if (p == 2 && k >= 3)
{
h = _aprcl_is_prime_jacobi_check_2k(jacobi_sum,
jacobi_sum2_1, jacobi_sum2_2, u, v);
if (h < 0)
result = COMPOSITE;
if (h % 2 != 0 && lambdas[pind] == 0 && fmpz_equal(q_pow, ndec))
lambdas[pind] = 1;
}
if (p != 2)
{
h = _aprcl_is_prime_jacobi_check_pk(jacobi_sum, u, v);
if (h < 0)
result = COMPOSITE;
if (h % p != 0 && lambdas[pind] == 0)
lambdas[pind] = 1;
}
unity_zp_clear(jacobi_sum);
unity_zp_clear(jacobi_sum2_1);
unity_zp_clear(jacobi_sum2_2);
}
}
if (result == PROBABPRIME)
{
for (i = 0; i < config->rs.num; i++)
{
if (lambdas[i] == 0)
{
int r = _aprcl_is_prime_jacobi_additional_test(n, config->rs.p[i]);
if (r == 2)
{
result = COMPOSITE;
}
else if (r == 1)
{
lambdas[i] = 1;
}
else
{
result = UNKNOWN;
}
}
}
}
if (result == PROBABPRIME)
{
if (aprcl_is_prime_final_division(n, config->s, config->R) == 1)
result = PRIME;
else
result = COMPOSITE;
}
flint_free(lambdas);
fmpz_clear(u);
fmpz_clear(q_pow);
fmpz_clear(p2);
fmpz_clear(ndec);
fmpz_clear(ndecdiv);
fmpz_clear(temp);
return result;
}
int
aprcl_is_prime_jacobi(const fmpz_t n)
{
primality_test_status result;
aprcl_config config;
aprcl_config_jacobi_init(config, n);
result = _aprcl_is_prime_jacobi(n, config);
aprcl_config_jacobi_clear(config);
if (result == PROBABPRIME || result == UNKNOWN)
{
flint_throw(FLINT_ERROR, "aprcl_is_prime_jacobi: failed to prove n prime for n = %s\n", fmpz_get_str(NULL, 10, n));
}
if (result == PRIME)
return 1;
return 0;
}