/*
------------------------------------------------------------------------------
isaac64.h: Definitions for a fast cryptographic random number generator
Bob Jenkins, 1996, Public Domain

Modified for modularity by Tom Bartol and Rex Kerr

Reference:
http://burtleburtle.net/bob/rand/isaacafa.html
Jenkins, R.J. (1996) ISAAC, in Fast Software Encryption, vol. 1039,
    ed. Gollmann, D.  Spinger-Verlag, Cambridge

------------------------------------------------------------------------------
*/
#ifndef ISAAC64_H
#define ISAAC64_H

#include <nrnconf.h>
#if defined(HAVE_STDINT_H)
#include <stdint.h>
#endif

#define RANDSIZL   (4) /* I recommend 8 for crypto, 4 for simulations */
#define RANDSIZ    (1<<RANDSIZL)
#define RANDMAX    (2*RANDSIZ)

typedef unsigned long long ub8;
#if defined(uint32_t)
typedef uint32_t       ub4;
#else
typedef unsigned int       ub4;
#endif
typedef unsigned short int ub2;
typedef unsigned char      ub1;

#define DBL32 (2.3283064365386962890625e-10)
#define DBL53 (1.1102230246251565404236316680908203125e-16)
#define DBL64 (5.42101086242752217003726400434970855712890625e-20)
#define MSK53 0x001FFFFFFFFFFFFFLL

struct isaac64_state
{
  int randcnt;
  ub8 aa;
  ub8 bb;
  ub8 cc;
  ub8 randrsl[RANDSIZ];
  ub8 mm[RANDSIZ];
};



void isaac64_init(struct isaac64_state *rng, ub4 seed);

void isaac64_generate(struct isaac64_state *rng);

/*
------------------------------------------------------------------------------
Macros to get individual random numbers
------------------------------------------------------------------------------
*/

#define isaac64_uint32(rng) \
   (rng->randcnt>0 ? \
     ( *(((ub4 *)(rng->randrsl)) + (rng->randcnt-=1)) ) : \
     ( isaac64_generate(rng), \
       rng->randcnt=RANDMAX-1, \
       *(((ub4 *)(rng->randrsl)) + rng->randcnt) ))

#define isaac64_uint64(rng) \
   (rng->randcnt>1 ? \
     ( *((ub8 *)(((ub4 *)(rng->randrsl)) + (rng->randcnt-=2))) ) : \
     ( isaac64_generate(rng), \
       rng->randcnt=RANDMAX-2, \
       *((ub8 *)(((ub4 *)(rng->randrsl)) + rng->randcnt)) ))

#define isaac64_dbl32(rng) \
   (rng->randcnt>0 ? \
     ( DBL32 * (*(((ub4 *)(rng->randrsl)) + (rng->randcnt-=1)) ) ) : \
     ( isaac64_generate(rng), \
       rng->randcnt=RANDMAX-1, \
       DBL32 * (*(((ub4 *)(rng->randrsl)) + rng->randcnt)) ))

#define isaac64_dbl53(rng) \
   (rng->randcnt>1 ? \
     ( DBL53 * ((*((ub8 *)(((ub4 *)(rng->randrsl)) + (rng->randcnt-=2))))>>11) ) : \
     ( isaac64_generate(rng), \
       rng->randcnt=RANDMAX-2, \
       DBL64 * ((*((ub8 *)(((ub4 *)(rng->randrsl)) + rng->randcnt)))>>11) ))

#define isaac64_dbl64(rng) \
   (rng->randcnt>1 ? \
     ( DBL64 * (*((ub8 *)(((ub4 *)(rng->randrsl)) + (rng->randcnt-=2)))) ) : \
     ( isaac64_generate(rng), \
       rng->randcnt=RANDMAX-2, \
       DBL64 * (*((ub8 *)(((ub4 *)(rng->randrsl)) + rng->randcnt))) ))


#endif  /* ISAAC64_H */
