/*
 * ------------------------------------------------------------------------------
 * isaac.c: By Bob Jenkins.  My random number generator, ISAAC.
 * MODIFIED:
 * 960327: Creation (addition of randinit, really)
 * 970719: use context, not global variables, for internal state
 * 980324: make a portable version
 * 991209: modified for inclusion with GNU Backgammon by Gary Wong  
 * ------------------------------------------------------------------------------
 * 
 * $Id: isaac.c,v 1.8 2013/06/16 02:16:24 mdpetch Exp $
 */

/*lint --e{661,662}      Ignore warnings of out of bounds array access (as ok) */

/* No configuration used in this file
 * #include "config.h" */
#include "isaac.h"

#define ind(mm,x)  ((mm)[((x)>>2)&(RANDSIZ-1)])
#define rngstep(mix,a,b,mm,m,m2,r,x) \
{ \
  x = *m;  \
  a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
  *(m++) = y = (ind(mm,x) + a + b) & 0xffffffff; \
  *(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \
}

void
isaac(randctx * ctx)
{
    register ub4 a, b, x, y, *m, *mm, *m2, *r, *mend;
    mm = ctx->randmem;
    r = ctx->randrsl;
    a = ctx->randa;
    b = (ctx->randb + (++ctx->randc)) & 0xffffffff;
    for (m = mm, mend = m2 = m + (RANDSIZ / 2); m < mend;) {
        rngstep(a << 13, a, b, mm, m, m2, r, x);
        rngstep(a >> 6, a, b, mm, m, m2, r, x);
        rngstep(a << 2, a, b, mm, m, m2, r, x);
        rngstep(a >> 16, a, b, mm, m, m2, r, x);
    }
    for (m2 = mm; m2 < mend;) {
        rngstep(a << 13, a, b, mm, m, m2, r, x);
        rngstep(a >> 6, a, b, mm, m, m2, r, x);
        rngstep(a << 2, a, b, mm, m, m2, r, x);
        rngstep(a >> 16, a, b, mm, m, m2, r, x);
    }
    ctx->randb = b;
    ctx->randa = a;
}


#define mix(a,b,c,d,e,f,g,h) \
{ \
   a^=b<<11; d+=a; b+=c; \
   b^=c>>2;  e+=b; c+=d; \
   c^=d<<8;  f+=c; d+=e; \
   d^=e>>16; g+=d; e+=f; \
   e^=f<<10; h+=e; f+=g; \
   f^=g>>4;  a+=f; g+=h; \
   g^=h<<8;  b+=g; h+=a; \
   h^=a>>9;  c+=h; a+=b; \
}

/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
void
irandinit(randctx * ctx, word flag)
{
    word i;
    ub4 a, b, c, d, e, f, g, h;
    ub4 *m, *r;
    ctx->randa = ctx->randb = ctx->randc = 0;
    m = ctx->randmem;
    r = ctx->randrsl;
    a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */

    for (i = 0; i < 4; ++i) {   /* scramble it */
        mix(a, b, c, d, e, f, g, h);
    }

    if (flag) {
        /* initialize using the contents of r[] as the seed */
        for (i = 0; i < RANDSIZ; i += 8) {
            a += r[i];
            b += r[i + 1];
            c += r[i + 2];
            d += r[i + 3];
            e += r[i + 4];
            f += r[i + 5];
            g += r[i + 6];
            h += r[i + 7];
            mix(a, b, c, d, e, f, g, h);
            m[i] = a;
            m[i + 1] = b;
            m[i + 2] = c;
            m[i + 3] = d;
            m[i + 4] = e;
            m[i + 5] = f;
            m[i + 6] = g;
            m[i + 7] = h;
        }
        /* do a second pass to make all of the seed affect all of m */
        for (i = 0; i < RANDSIZ; i += 8) {
            a += m[i];
            b += m[i + 1];
            c += m[i + 2];
            d += m[i + 3];
            e += m[i + 4];
            f += m[i + 5];
            g += m[i + 6];
            h += m[i + 7];
            mix(a, b, c, d, e, f, g, h);
            m[i] = a;
            m[i + 1] = b;
            m[i + 2] = c;
            m[i + 3] = d;
            m[i + 4] = e;
            m[i + 5] = f;
            m[i + 6] = g;
            m[i + 7] = h;
        }
    } else {
        for (i = 0; i < RANDSIZ; i += 8) {
            /* fill in mm[] with messy stuff */
            mix(a, b, c, d, e, f, g, h);
            m[i] = a;
            m[i + 1] = b;
            m[i + 2] = c;
            m[i + 3] = d;
            m[i + 4] = e;
            m[i + 5] = f;
            m[i + 6] = g;
            m[i + 7] = h;
        }
    }

    isaac(ctx);                 /* fill in the first set of results */
    ctx->randcnt = RANDSIZ;     /* prepare to use the first set of results */
}
