/* Copyright (C) 2019 Daniel Schultz This file is part of FLINT. FLINT 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 . */ #include "fmpz_mod.h" #include /* Assumption on fmpz_mod_discrete_log_pohlig_hellman_t: p is prime. The prime factors of p - 1 all fit a ulong. If the prime factors of p - 1 do not fit a ulong you do not want to calculate dlog mod p. so we have p - 1 = p1^e1 * ... * pn^en for ulong pi and ei The assumption p is prime could be removed, but then phi(p) needs to be calculated by someone somewhere. */ static int fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct_cmp( const fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct * lhs, const fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct * rhs) { return fmpz_cmp(lhs->gammapow, rhs->gammapow); } void fmpz_mod_discrete_log_pohlig_hellman_init(fmpz_mod_discrete_log_pohlig_hellman_t L) { fmpz_t two; fmpz_init_set_ui(two, 2); L->num_factors = 0; L->entries = NULL; fmpz_init(L->alpha); fmpz_init(L->alphainv); fmpz_init(L->pm1); fmpz_mod_ctx_init(L->fpctx, two); fmpz_clear(two); return; } void fmpz_mod_discrete_log_pohlig_hellman_clear(fmpz_mod_discrete_log_pohlig_hellman_t L) { slong i; ulong c; fmpz_mod_discrete_log_pohlig_hellman_entry_struct * Li; for (i = 0; i < L->num_factors; i++) { Li = L->entries + i; fmpz_clear(Li->idem); fmpz_clear(Li->co); fmpz_clear(Li->startinge); fmpz_clear(Li->startingbeta); fmpz_clear(Li->gamma); fmpz_clear(Li->gammainv); for (c = 0; c < Li->cbound; c++) { fmpz_clear(Li->table[c].gammapow); } flint_free(Li->table); } if (L->entries) { flint_free(L->entries); } fmpz_clear(L->alpha); fmpz_clear(L->alphainv); fmpz_clear(L->pm1); fmpz_mod_ctx_clear(L->fpctx); return; } static slong _pow_fmpz_cost(const fmpz_t pow) { slong cost = fmpz_bits(pow) + fmpz_popcnt(pow) - 2; return FLINT_MAX(cost, 0); } /* Assume that p is prime, don't check. Return an estimate on the number of multiplications need for one run. */ double fmpz_mod_discrete_log_pohlig_hellman_precompute_prime( fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t p) { slong i; ulong c; fmpz_mod_discrete_log_pohlig_hellman_entry_struct * Li; fmpz_factor_t factors; fmpz_t temp; double total_cost; /* just clear it and allocate everything again */ fmpz_mod_discrete_log_pohlig_hellman_clear(L); fmpz_init(L->alpha); fmpz_init(L->alphainv); fmpz_init(L->pm1); fmpz_mod_ctx_init(L->fpctx, p); fmpz_init(temp); fmpz_factor_init(factors); fmpz_sub_ui(L->pm1, p, 1); fmpz_factor(factors, L->pm1); L->num_factors = factors->num; L->entries = NULL; if (L->num_factors > 0) { L->entries = (fmpz_mod_discrete_log_pohlig_hellman_entry_struct*) flint_malloc( L->num_factors*sizeof(fmpz_mod_discrete_log_pohlig_hellman_entry_struct)); } for (i = 0; i < L->num_factors; i++) { fmpz_t pipow, recp; Li = L->entries + i; fmpz_init(Li->idem); fmpz_init(Li->co); fmpz_init(Li->startinge); fmpz_init(Li->startingbeta); fmpz_init(Li->gamma); fmpz_init(Li->gammainv); if (!fmpz_abs_fits_ui(factors->p + i)) { /* L is corrupted */ fmpz_clear(temp); fmpz_factor_clear(factors); flint_throw(FLINT_ERROR, "Exception in fmpz_mod_discrete_log_" "pohlig_hellman_precompute_prime: Prime factor is large.\n"); } Li->exp = factors->exp[i]; Li->prime = fmpz_get_ui(factors->p + i); fmpz_init(recp); fmpz_init_set_ui(pipow, Li->prime); fmpz_pow_ui(pipow, pipow, Li->exp); fmpz_divexact(recp, L->pm1, pipow); fmpz_invmod(temp, recp, pipow); fmpz_mul(temp, temp, recp); fmpz_mod(Li->idem, temp, L->pm1); fmpz_set(Li->co, recp); fmpz_divexact_ui(Li->startinge, pipow, Li->prime); fmpz_clear(pipow); fmpz_clear(recp); } fmpz_factor_clear(factors); /* alpha will be a primitive root */ fmpz_zero(L->alpha); try_alpha: fmpz_add_ui(L->alpha, L->alpha, 1); if (fmpz_cmp(L->alpha, p) >= 0) { /* L is corrupted */ fmpz_clear(temp); /* factors was already cleared */ flint_throw(FLINT_ERROR, "Exception in fmpz_mod_discrete_log_pohlig_" "hellman_precompute_prime: Could not find primitive root."); } for (i = 0; i < L->num_factors; i++) { Li = L->entries + i; fmpz_divexact_ui(temp, L->pm1, Li->prime); fmpz_mod_pow_fmpz(Li->gamma, L->alpha, temp, L->fpctx); if (fmpz_is_one(Li->gamma)) { goto try_alpha; } } fmpz_mod_inv(L->alphainv, L->alpha, L->fpctx); for (i = 0; i < L->num_factors; i++) { Li = L->entries + i; fmpz_mod_inv(Li->gammainv, Li->gamma, L->fpctx); fmpz_mod_pow_fmpz(Li->startingbeta, L->alphainv, Li->co, L->fpctx); Li->dbound = ceil(sqrt((double) Li->prime)); Li->cbound = (Li->prime + Li->dbound - 1)/Li->dbound; while (Li->cbound > 100) { Li->dbound *= 2; Li->cbound = (Li->prime + Li->dbound - 1)/Li->dbound; } FLINT_ASSERT(Li->dbound > 0); FLINT_ASSERT(Li->cbound > 0); Li->table = (fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct *) flint_malloc(Li->cbound*sizeof(fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct)); for (c = 0; c < Li->cbound; c++) { Li->table[c].cm = c*Li->dbound; fmpz_init(Li->table[c].gammapow); fmpz_mod_pow_ui(Li->table[c].gammapow, Li->gamma, Li->table[c].cm, L->fpctx); } qsort(Li->table, Li->cbound, sizeof(fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct), (int(*)(const void*, const void*)) fmpz_mod_discrete_log_pohlig_hellman_table_entry_struct_cmp); for (c = 1; c < Li->cbound; c++) { FLINT_ASSERT(fmpz_cmp(Li->table[c - 1].gammapow, Li->table[c].gammapow) < 0); } } fmpz_clear(temp); total_cost = 0; for (i = 0; i < L->num_factors; i++) { double this_cost = 0; fmpz_t e; slong j; Li = L->entries + i; this_cost += _pow_fmpz_cost(Li->co); fmpz_init_set(e, Li->startinge); j = 0; do { this_cost += _pow_fmpz_cost(e); this_cost += Li->dbound*(1 + log(Li->cbound)); /* bsgs search */ this_cost += 2*log(Li->prime); /* some power < Li->prime */ fmpz_divexact_ui(e, e, Li->prime); } while (++j < Li->exp); total_cost += this_cost; fmpz_clear(e); } return total_cost; } /* return with xx such that xx = alpha^y mod p, alpha is the p.r. L->alpha*/ void fmpz_mod_discrete_log_pohlig_hellman_run( fmpz_t xx, const fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t y) { slong i, j; ulong g; fmpz_t x; fmpz_t pipow, e, acc; ulong lo, mid, hi, d; fmpz_t beta, z, w, temp; fmpz_mod_discrete_log_pohlig_hellman_entry_struct * Li; fmpz_init(x); fmpz_init(acc); fmpz_init(pipow); fmpz_init(e); fmpz_init(beta); fmpz_init(z); fmpz_init(w); fmpz_init(temp); FLINT_ASSERT(!fmpz_is_zero(y)); FLINT_ASSERT(fmpz_mod_is_canonical(y, L->fpctx)); i = 0; if (i < L->num_factors && L->entries[i].prime == 2) { Li = L->entries + i; FLINT_ASSERT(Li->prime == 2); fmpz_mod_pow_fmpz(z, y, Li->co, L->fpctx); fmpz_set(beta, Li->startingbeta); fmpz_set(e, Li->startinge); j = 0; fmpz_one(pipow); /* Li->prime^j */ fmpz_zero(acc); do { fmpz_mod_pow_fmpz(w, z, e, L->fpctx); /* solve Li->gamma ^ g == w mod p */ if (fmpz_is_one(w)) { g = 0; } else { if (!fmpz_equal(w, Li->gamma)) { goto cleanup_and_throw; } g = 1; fmpz_mod_mul(z, z, beta, L->fpctx); } fmpz_mod_mul(beta, beta, beta, L->fpctx); fmpz_addmul_ui(acc, pipow, g); fmpz_mul_2exp(pipow, pipow, 1); fmpz_tdiv_q_2exp(e, e, 1); } while (++j < Li->exp); fmpz_addmul(x, acc, Li->idem); i = 1; } for (; i < L->num_factors; i++) { Li = L->entries + i; fmpz_mod_pow_fmpz(z, y, Li->co, L->fpctx); fmpz_set(beta, Li->startingbeta); fmpz_set(e, Li->startinge); j = 0; fmpz_one(pipow); /* Li->prime^j */ fmpz_zero(acc); do { fmpz_mod_pow_fmpz(w, z, e, L->fpctx); /* solve Li->gamma ^ g == w mod p */ d = 0; while (1) { lo = 0; hi = Li->cbound; while (hi - lo > 4) { int cmp; mid = lo + (hi - lo)/2; cmp = fmpz_cmp(Li->table[mid].gammapow, w); if (cmp == 0) { g = Li->table[mid].cm + d; goto found_g; } else if (cmp > 0) { hi = mid; } else { lo = mid; } } while (lo < hi) { if (fmpz_equal(Li->table[lo].gammapow, w)) { g = Li->table[lo].cm + d; goto found_g; } lo++; } fmpz_mod_mul(w, w, Li->gammainv, L->fpctx); d++; if (d >= Li->dbound) { goto cleanup_and_throw; } } found_g: FLINT_ASSERT(g < Li->prime); fmpz_mod_pow_ui(temp, beta, g, L->fpctx); fmpz_mod_mul(z, z, temp, L->fpctx); fmpz_mod_pow_ui(beta, beta, Li->prime, L->fpctx); fmpz_addmul_ui(acc, pipow, g); fmpz_mul_ui(pipow, pipow, Li->prime); fmpz_divexact_ui(e, e, Li->prime); } while (++j < Li->exp); fmpz_addmul(x, acc, Li->idem); } fmpz_mod(xx, x, L->pm1); fmpz_clear(acc); fmpz_clear(pipow); fmpz_clear(e); fmpz_clear(beta); fmpz_clear(z); fmpz_clear(w); fmpz_clear(temp); fmpz_clear(x); return; cleanup_and_throw: fmpz_clear(acc); fmpz_clear(pipow); fmpz_clear(e); fmpz_clear(beta); fmpz_clear(z); fmpz_clear(w); fmpz_clear(temp); fmpz_clear(x); flint_throw(FLINT_ERROR, "Exception in fmpz_mod_discrete_log_pohlig_" "hellman_run: Could not find log."); return; }