/* sr5sieve.h -- (C) Geoffrey Reynolds, April 2006.

   Srsieve specialised for the Sierpinski/Riesel Base 5 projects.

   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.
*/

#ifndef _SR5SIEVE_H
#define _SR5SIEVE_H

#include <limits.h>
#include <stdint.h>
#include "config.h"

/* Also check config.h for compiler settings.
 */

#define XSTR(ARG) STR(ARG)
#define STR(ARG) #ARG

/* Fixed base b for k*b^n+c.
 */
#define BASE 5

/* Allow sieving in base b^Q for Q chosen from the divisors of LIMIT_BASE.
 */
#define LIMIT_BASE 720

/* Allow sequences k*b^n+c that have both odd and even n remaining (Such as
   151026*5^n-1 in the base 5 case).
*/
#define ALLOW_MIXED_PARITY_SEQUENCES 1

/* Set USE_MOD_PRODUCTS=1 to employ code that avoids computing some 64/32
   remainders by computing additional 32/32 remainders. Sounds like a good
   idea, but the gain is barely noticable.
*/
#if (USE_ASM && __i386__)
# define USE_MOD_PRODUCTS 1
#else
# define USE_MOD_PRODUCTS 0
#endif

/* For a hash table expected to hold M elements, use a main table of at
   least M/HASH_MAX_DENSITY elements.
*/
#define HASH_MAX_DENSITY 0.65

/* Set NEWPGEN_OPT=1 to allow the --newpgen K N0 N1 command line switch. 
 */
#define NEWPGEN_OPT 1

/* Checkpoint file written periodiclly and when sieving is interrupted.
 */
#define CHECKPOINT_FILE_NAME "checkpoint.txt"

/* File to write log reports to.
 */
#define LOG_FILE_NAME "sr" XSTR(BASE) "sieve.log"

/* Date format used for log file entries (see date --help)
 */
#define LOG_STRFTIME_FORMAT "%c "

/* Report period in seconds.
 */
#define REPORT_PERIOD 120

/* Save period in seconds.
 */
#define SAVE_PERIOD 300

/* Set CHECK_FACTORS=1 to double check found factors.
 */
#define CHECK_FACTORS 1

/* Set REPORT_DUPLICATES=1 to report (to the screen only) factors which
   don't result in a term being eliminated.
*/
#define REPORT_DUPLICATES 0


/* Nothing below here should normally need adjustment. */

#ifdef __GNUC__
/* macros that evaluate their arguments only once. From the GCC manual.
 */
#define MIN(a,b) \
   ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
      _a < _b ? _a : _b; })
#define MAX(a,b) \
   ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
      _a > _b ? _a : _b; })
#define ABS(a) \
   ({ typeof (a) _a = (a); \
      _a < 0 ? -_a : _a; })
#else
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define ABS(a) ((a) < 0 ? -(a) : (a))
#endif


/* bsgs.c */

void sieve(void);


/* choose.c */

uint32_t find_best_Q(uint32_t Q, uint32_t *subseqs);


/* clock.c */

uint32_t millisec_clock(void);
uint32_t sec_clock(void);


/* events.c */

typedef enum
{
  initialise_events,
  received_sigterm,
  received_sigint,
  report_due,
  save_due,

  sieve_parameters_changed,
  factor_found,

  /* add more events here */

  last_event
} event_t;

static inline void check_events(uint64_t current_prime)
{
  extern volatile int event_happened;
  extern void process_events(uint64_t);

  if (event_happened)
    process_events(current_prime);
}
void notify_event(event_t event);
void check_progress(void);


/* factors.c */

extern const char *factors_file_name;

void save_factor(uint32_t k, int32_t c, uint32_t n, uint64_t p);
#if CHECK_FACTORS
int is_factor(uint32_t k, int32_t c, uint32_t n, uint64_t p);
#endif


/* files.c */

#ifdef EOF
FILE *xfopen(const char *fn, const char *mode, void (*fun)(const char *,...));
void xfclose(FILE *f, const char *fn);
#endif
int read_abcd_file(const char *file_name);
void overwrite_abcd(uint64_t p, const char *file_name, uint32_t del_k);
#if NEWPGEN_OPT
void write_newpgen_k(uint32_t seq, uint32_t n0, uint32_t n1);
#endif
uint64_t read_checkpoint(uint64_t pmin, uint64_t pmax);
void write_checkpoint(uint64_t p);
void remove_checkpoint(void);

#if (BASE == 2)
int read_dat_file(const char *file_name, int32_t c);
#endif


/* legendre.c */

void generate_legendre_lookup_table(uint32_t seq, unsigned parity);

#if USE_MOD_PRODUCTS
extern uint32_t *mod_product, mod_product_count;
void generate_mod_products(void);
#endif


/* primes.c */

void init_prime_sieve(uint64_t pmax);
void fini_prime_sieve(void);
void prime_sieve(uint64_t pmin, uint64_t pmax, void (*fun)(uint64_t));


/* sequences.c */

typedef struct
{
  uint32_t k;       /* k in k*b^n+c */
  int32_t c;        /* c in k*b^n+c */
#if USE_MOD_PRODUCTS
  uint32_t prod;    /* mod divides mod_products[prod] */
#endif
  union
  {
    uint32_t ncount;  /* number of remaining n */
    uint32_t mod;     /* (p/2)%mod gives index into map[] for (-ckb^n/p) */
  };
  union
  {
    uint32_t *N;        /* list of remaining n */
    uint_fast32_t *map; /* bitmap of positive results for (-ckb^n/p) */
  };
  union
  {
    uint32_t nsize;   /* N has room for nsize n */
    uint32_t first;   /* first subsequence */
  };
  uint32_t last;    /* last subsequence */
} seq_t;

extern seq_t      *SEQ;
extern uint32_t    seq_count;
extern uint32_t    factor_count;

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

const char *kbnc_str(uint32_t k, uint32_t n, int32_t c);
const char *seq_str(uint32_t seq);
uint32_t new_seq(uint32_t k, int32_t c);
void add_seq_n(uint32_t seq, uint32_t n);
void finish_candidate_seqs(void);


/* sr5sieve.c */

void print_status(uint64_t p, uint32_t p_per_sec);
void start_srsieve(void);
void finish_srsieve(const char *reason, uint64_t p);


/* subseq.c */

typedef struct
{
  uint32_t seq;
  uint32_t d;
  uint_fast32_t *M;
  uint32_t mcount;
} subseq_t;

extern subseq_t *SUBSEQ;
extern uint32_t subseq_count;
extern uint32_t subseq_Q;

void make_subseqs(void);
void eliminate_term(uint32_t subseq, uint32_t m, uint64_t p);


/* util.c */

void error(const char *fmt, ...) attribute ((noreturn,format(printf,1,2)));
void warning(const char *fmt, ...) attribute ((format(printf,1,2)));
void logger(const char *fmt, ...) attribute ((format(printf,1,2)));
void *xmalloc(uint32_t sz) attribute ((malloc));
void *xrealloc(void *d, uint32_t sz);
void report(int scroll, const char *fmt, ...) attribute ((format(printf,2,3)));
static inline const char *plural(int n)
{
  return (n == 1) ? "" : "s";
}

#endif /* _SR5SIEVE_H */
