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

   Routines for creating and manipulating candidates sequences.

   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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "sr5sieve.h"

seq_t   *SEQ = NULL;          /* global array of candidate sequences */
uint32_t seq_count = 0;       /* current number of sequences */
uint32_t factor_count;        /* candidate n eliminated so far */

uint64_t p_min;
uint64_t p_max;
uint32_t n_min;
uint32_t n_max;

static uint32_t seq_alloc = 0;

#define SEQ_GROW_SIZE 64
uint32_t new_seq(uint32_t k, int32_t c)
{
  uint32_t new;

  if (seq_count == seq_alloc)
  {
    seq_alloc += SEQ_GROW_SIZE;
    SEQ = xrealloc(SEQ, seq_alloc*sizeof(seq_t));
  }

  new = seq_count++;
  SEQ[new].k = k;
  SEQ[new].c = c;
  SEQ[new].ncount = 0;
  SEQ[new].N = NULL;
  SEQ[new].nsize = 0;

  return new;
}

static char seq_buf[44];
const char *kbnc_str(uint32_t k, uint32_t n, int32_t c)
{
  sprintf(seq_buf,"%"PRIu32"*%"PRIu32"^%"PRIu32"%+"PRId32,k,BASE,n,c);

  return seq_buf;
}

const char *seq_str(uint32_t seq)
{
  assert (seq < seq_count);

  sprintf(seq_buf,"%"PRIu32"*%"PRIu32"^n%+"PRId32,SEQ[seq].k,BASE,SEQ[seq].c);

  return seq_buf;
}

#define SEQ_N_GROW_SIZE 2048
void add_seq_n(uint32_t seq, uint32_t n)
{
  uint32_t count;

  assert(seq < seq_count);

  count = SEQ[seq].ncount;
  if (count == SEQ[seq].nsize)
  {
    SEQ[seq].nsize += SEQ_N_GROW_SIZE;
    SEQ[seq].N = xrealloc(SEQ[seq].N, SEQ[seq].nsize*sizeof(uint32_t));
  }

  SEQ[seq].N[count] = n;
  SEQ[seq].ncount++;
}

/* Set n_min and n_max from the current candidate sequences.
 */
static void calculate_n_range(void)
{
  uint32_t i, nmin = UINT32_MAX, nmax = 0;

  for (i = 0; i < seq_count; i++)
  {
    nmin = MIN(nmin, SEQ[i].N[0]);
    nmax = MAX(nmax, SEQ[i].N[SEQ[i].ncount-1]);
  }

  n_min = nmin;
  n_max = nmax;
}

void finish_candidate_seqs(void)
{
  uint32_t i;

  for (i = 0; i < seq_count; i++)
  {
    if (SEQ[i].ncount == 0)
      error("%s: Empty sequence.",seq_str(i));
    if (SEQ[i].k < 1)
      error("%s: k in k*b^n+c must be positive.", seq_str(i));
    if (ABS(SEQ[i].c) != 1)
      error("%s: |c| in k*b^n+c must be 1.", seq_str(i));
    if (BASE % 2 == 1 && SEQ[i].k % 2 == 1)
      error("%s: Every term is divisible by 2.", seq_str(i));
  }

  calculate_n_range();
  make_subseqs();
#if USE_MOD_PRODUCTS
  generate_mod_products();
#endif
}
