/* bsgs.c -- (C) Geoffrey Reynolds, April 2006.

   Implementation of a baby step giant step algorithm for finding all
   n in the range nmin <= n <= nmax satisfying b^n=d_i (mod p) where b
   and each d_i are relatively prime to p.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
*/

#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include "sr5sieve.h"
#include "arithmetic.h"
#include "bitmap.h"
#include "hashtable.h"


/*
  Giant step baby step algorithm, from Wikipaedea:

  input: A cyclic group G of order n, having a generator a and an element b.

  Output: A value x satisfying a^x = b (mod n).

  1. m <-- ceiling(sqrt(n))
  2. For all j where 0 <= j < m:
     1. Compute a^j mod n and store the pair (j, a^j) in a table.
  3. Compute a^(-m).
  4. g <-- b.
  5. For i = 0 to (m - 1):
     1. Check if g is the second component (a^j) of any pair in the table.
     2. If so, return im + j.
     3. If not, g <-- ga^(-m) mod n.
*/

static uint32_t m;     /* Number of baby steps */
static uint32_t M;     /* Number of giant steps */

static uint32_t baby_steps64(uint64_t b, uint64_t bj0, uint64_t p)
{
  uint64_t bj;
  uint32_t j;

  clear_hashtable();
  pre2_mulmod64_init(b);
  for (j = 0, bj = bj0; j < m; j++)
  {
    insert64(j, bj);
    bj = pre2_mulmod64(bj,b,p);
    if (bj == bj0)
    {
      pre2_mulmod64_fini();
      return j+1;
    }
  }

  pre2_mulmod64_fini();
  return 0;
}

static uint64_t bQ;           /* 1/b^Q (mod p) */
static uint32_t *C32;         /* List of candidate subsequences for BSGS */
static uint64_t *D64;         /* D64[j] holds -c/(k*b^(im+d)) (mod p) for
                                 subsequence C[j] at giant step i. */

#if (USE_MOD_PRODUCTS && __GNUC__ == 3 && __GNUC_MINOR__ == 4)
/* Use a dynamic array to avoid a problem with GCC 3.4's handling of
   variable-length automatic arrays. */
static uint32_t *rem32;
#endif

/* This function builds the list C32[] of subsequences for which p may be a
   factor (each term of these subsequences has Legendre symbol 1) and
   initialises the table D64[] with the values -c/(k*b^d) (mod p). As a
   side effect, bQ is set to the value 1/b^Q (mod p) for use later in
   bsgs64(). Returns the number of subsequences listed in C32[].
*/
static uint32_t setup64(uint64_t p)
{
  uint64_t inv, bm;
#if (__GNUC__ == 3 && __GNUC_MINOR__ == 4)
  /* GCC 3.4 has problems with variable sized automatic arrays. */
  uint64_t bd64[LIMIT_BASE];
#else
  /* GCC 3.3, 4.0, 4.1 are OK */
  uint64_t bd64[subseq_Q];
#if USE_MOD_PRODUCTS
  uint32_t rem32[mod_product_count];
#endif
#endif
  uint32_t h, i, j;

  /* Precompute 1/b^d (mod p) for 0 <= d < Q. */
#if (BASE == 2)
  inv = (p+1)/2;
#else
  inv = invmod32_64(BASE,-1,p);
#endif
  pre2_mulmod64_init(inv);
  bd64[0] = 1;
  for (i = 1, bm = inv; i < subseq_Q; i++)
  {
    bd64[i] = bm;
    bm = pre2_mulmod64(bm,inv,p);
  }
  pre2_mulmod64_fini();
  bQ = bm;  /* bQ <-- 1/b^Q. */

  bm = p/2;
#if USE_MOD_PRODUCTS
  for (i = 0; i < mod_product_count; i++)
    rem32[i] = rem64_32(bm,mod_product[i]);
#endif

  /* For each subsequence (k*b^d)*(b^Q)^(n/Q)+c with valid Legendre symbol,
     compute -c/(k*b^d) (mod p) and add the subsequence to the bsgs list. */
  for (i = j = 0; i < seq_count; i++)
#if USE_MOD_PRODUCTS
    if (test_bit(SEQ[i].map,rem32[SEQ[i].prod]%SEQ[i].mod))
#else
    if (test_bit(SEQ[i].map,bm%SEQ[i].mod)) /* (-ckb^n/p) == +1 */
#endif
    {
      inv = invmod32_64(SEQ[i].k,SEQ[i].c,p);
      pre2_mulmod64_init(inv);
      for (h = SEQ[i].first; h <= SEQ[i].last; h++)
      {
        C32[j] = h;
        D64[j] = pre2_mulmod64(bd64[SUBSEQ[h].d],inv,p);
        j++;
      }
      pre2_mulmod64_fini();
    }

  return j;
}

static uint32_t sieve_low;
static uint32_t sieve_range;

static void bsgs64(uint64_t p)
{
  uint64_t b;
  uint32_t i, j, k, cc;

#if USE_ASM && defined(__ppc64__)
  getMagic(p);
#endif

  mod64_init(p);

  cc = setup64(p);

  /* b <- base^Q (mod p) */
  b = powmod64(BASE,subseq_Q,p);

  /* Baby steps. */
  if ((i = baby_steps64(b,powmod64(b,sieve_low,p),p)) > 0)
  {
    /* i is the order of b (mod p). This is all the information we need to
       determine every solution for this p, so no giant steps are needed. */

    for (k = 0; k < cc; k++)
      for (j = lookup64(D64[k]); j < sieve_range; j += i)
        eliminate_term(C32[k],sieve_low+j,p);
  }
  else
  {
    /* First giant step. */
    for (k = 0; k < cc; k++)
      if ((j = lookup64(D64[k])) != HASH_NOT_FOUND)
        eliminate_term(C32[k],sieve_low+j,p);

    /* Remaining giant steps. */
    if (M > 1)
    {
      b = powmod64(bQ,m,p); /* b <- 1/b^Qm (mod p) */
      pre2_mulmod64_init(b);
      for (i = 1; i < M; i++)
        for (k = 0; k < cc; k++)
        {
          D64[k] = pre2_mulmod64(D64[k],b,p);
          if ((j = lookup64(D64[k])) != HASH_NOT_FOUND)
            eliminate_term(C32[k],sieve_low+i*m+j,p);
        }
      pre2_mulmod64_fini();
    }
  }

  mod64_fini();
}

static void init_bsgs(void)
{
  uint32_t r, s;

  r = n_max/subseq_Q-n_min/subseq_Q+1;
  s = subseq_count;

  /*
    In the worst case we will do do one table insertion and one mulmod
    for m baby steps, then s table lookups and s mulmods for M giant
    steps. The average case depends on how many solutions are found
    and how early in the loop they are found, which I don't know how
    to analyse. However for the worst case we just want to minimise
    m + s*M subject to m*M >= r, which is when m = sqrt(s*r).
  */

  /* Quadratic residue analysis should result in only about half of the
     subsequences actually being tested in a typical iteration.
  */
  s = (s+1)/2;

  M = MAX(1,sqrt((double)r/s));
  m = MIN(r,ceil((double)r/M));

  if (m > HASH_MAX_ELTS)
  {
    M = (double)r/HASH_MAX_ELTS;
    m = ceil((double)r/M);
  }

  sieve_low = n_min / subseq_Q;
  sieve_range = m*M;

  assert(sieve_low <= n_min/subseq_Q);
  assert(n_max/subseq_Q < sieve_low+sieve_range);

  D64 = xmalloc(subseq_count*sizeof(uint64_t));
  C32 = xmalloc(subseq_count*sizeof(uint32_t));
#if (USE_MOD_PRODUCTS && __GNUC__ == 3 && __GNUC_MINOR__ == 4)
  rem32 = xmalloc(mod_product_count*sizeof(uint32_t));
#endif
  init_hashtable(m);
}

static void fini_bsgs(void)
{
  fini_hashtable();
#if (USE_MOD_PRODUCTS && __GNUC__ == 3 && __GNUC_MINOR__ == 4)
  free(rem32);
#endif
  free(C32);
  free(D64);
}

void sieve(void)
{
  init_bsgs();
  init_prime_sieve(p_max);
  start_srsieve();
  prime_sieve(p_min,p_max,bsgs64);
  finish_srsieve("range is complete",p_max);
  fini_prime_sieve();
  fini_bsgs();
}
