/*****************************************************************************/
/*                                                                           */
/*  random.c        ;pseudo-random number generator functions for IC         */
/*                  ;works with both the 6.270 board and the Handy Board     */
/*                                                                           */
/*  by                                                                       */
/*                                                                           */
/*  Dr. Richard F. Drushel                                                   */
/*  Department of Biology                                                    */
/*  Case Western Reserve University                                          */
/*  Biology 300                                                              */
/*  2080 Adelbert Road                                                       */
/*  Cleveland, Ohio  44106-7080  U.S.A.                                      */
/*  rfd@po.cwru.edu                                                          */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  VERSION HISTORY:                                                         */
/*                                                                           */
/*  Whenever the program is updated, record it here.  Add new version info   */
/*  to the top of this list, so the newest version is always first.          */
/*                                                                           */
/*  1.2     18 May 1997         ;Richard F. Drushel                          */
/*                              ;comment style standardization               */
/*                              ;also incorporates random() function from    */
/*                              ;Fred Martin's lib_hb.c for the Handy Board  */
/*                                                                           */
/*  1.1     24 April 1997       ;David F. Meeker                             */
/*                              ;ECMP 375, Spring 1996, CWRU                 */
/*                              ;rnd() and randomize() algorithms made       */
/*                              ;thread-safe                                 */
/*                                                                           */
/*  1.0     24 August 1996      ;Richard F. Drushel                          */
/*                              ;initial code and rnd() algorithm            */
/*                              ;uses resource locking because state machine */
/*                              ;is non-reentrant                            */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PUBLIC FUNCTIONS:                                                        */
/*                                                                           */
/*  These functions may be used freely by user programs.                     */
/*                                                                           */
/*  int rnd(int arg)        ;returns pseudo-random number                    */
/*                          ;(16-bit signed int) based on value of arg       */
/*                          ;arg==0  returns last random number (for debug)  */
/*                          ;arg>0   returns random number                   */
/*                          ;arg<0   reseeds random number generator         */
/*                                   with default seeds and returns          */
/*                                   the first number in that series         */
/*                          ;RFD invented the algorithm, and it's not        */
/*                          ;perfect, but in testing it looks reasonably     */
/*                          ;random.                                         */
/*                          ;Note:  rnd() is *NOT* reentrant!!               */
/*                          ;it sets a busy flag _RND_BUSY if another        */
/*                          ;process is currently calling rnd().             */
/*                          ;made thread-safe by DFM 4/24/1997               */
/*                                                                           */
/*  void randomize(int seed)    ;reseeds random number generator to user-    */
/*                              ;defined seed                                */
/*                              ;non-reentrant, so waits until busy flag is  */
/*                              ;cleared.                                    */
/*                              ;made thread-safe by DFM 4/27/1997           */
/*                                                                           */
/*  int random(int mod)     ;random integers from peeking at 2 MHZ system    */
/*                          ;clock.  mod is a modulo value which can range   */
/*                          ;2 to 32767.                                     */
/*                          ;code by Fred G. Martin from lib_hb.c            */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PUBLIC GLOBAL VARIABLES:                                                 */
/*                                                                           */
/*  These global variables may be accessed freely by user programs.  Any     */
/*  restrictions on their use (e.g., flags which are read-only) should be    */
/*  noted here.  It is suggested that global variables be named in all       */
/*  capital letters, to avoid confusion with local varibles which are        */
/*  defined within functions.                                                */
/*                                                                           */
/*      None.                                                                */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PRIVATE FUNCTIONS:                                                       */
/*                                                                           */
/*  These are internal functions which have no public entry points, and      */
/*  which user programs should not access directly.  It is suggested that    */
/*  private functions be named with a leading underscore (_), to avoid       */
/*  confusion with user-defined functions which might have the same name.    */
/*                                                                           */
/*  int _rotate(int value, int direction, int rotcount)     ;rotate bits     */
/*                                                                           */
/*  void _rnd()         ;actually do the pseudo-random number algorithm      */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PRIVATE GLOBAL VARIABLES:                                                */
/*                                                                           */
/*  These are internal global variables which user programs should not       */
/*  access directly.  It is suggested that private global variables be       */
/*  named in all capital letters with a leading underscore (_), to avoid     */
/*  confusion with user-defined global variables which might have the same   */
/*  name.                                                                    */
/*                                                                           */
/*  int _RND_BUSY           ;busy flag since rnd() is *NOT* reentrant!!      */
/*  int _SEED1              ;lobyte of seed (only uses lower 8 bits)         */
/*  int _SEED2              ;hibyte of seed (only uses lower 8 bits)         */
/*  int _ROTATE1COUNT       ;working bit rotate count, seed 1                */
/*  int _ROTATE1DIR         ;working rotate direction, seed 1 (0=left,       */
/*                          ;1=right)                                        */
/*  int _ROTATE2COUNT       ;working bit rotate count, seed 2                */
/*  int _ROTATE2DIR         ;working rotate direction, seed 2 (0=left,       */
/*                          ;1=right)                                        */
/*  int _RNDMASK            ;bit mask for both seeds                         */
/*  int _NHITS              ;how many times we've done a certain task        */
/*  int _CRITICAL           ;threshold value for resetting _NHITS            */
/*  int _RNDFUDGE           ;fudge value                                     */
/*  int _ODD_OR_EVEN        ;whether to return the next rnd or to throw it   */
/*                          ;away                                            */
/*  int _AM_ERSTEN          ;flag for whether a random number has been       */
/*                          ;obtained since the last rnd(-1) call.  This     */
/*                          ;lets rnd(-1) return the same value as the very  */
/*                          ;first call to rnd(1).                           */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  EXTERNAL LIBRARY FILE DEPENDENCIES:                                      */
/*                                                                           */
/*  Any external functions or variables which are used but not defined in    */
/*  this file should be noted here.  List the external library filename,     */
/*  the library function, and the local calling function.                    */
/*                                                                           */
/*  math.c          ;uf2int() used by rnd()                                  */
/*                                                                           */
/*****************************************************************************/


/*****************************************************************************/
/*                                                                           */
/*                        global variable declarations                       */
/*                                                                           */
/*****************************************************************************/

int _SEED1=0x0AA;
int _SEED2=0x055;
int _ROTATE1COUNT=1;
int _ROTATE1DIR=0;
int _ROTATE2COUNT=3;
int _ROTATE2DIR=1;
int _RNDMASK=0x0F0;
int _NHITS=0;
int _CRITICAL=512;          /* an empirical value that seems to work well */
int _RNDFUDGE=0;
int _ODD_OR_EVEN=0;
int _AM_ERSTEN=1;           /* set our initial use flag */
int _RND_BUSY=0;            /* initial state is free */

/*****************************************************************************/
/*                                                                           */
/*                            function declarations                          */
/*                                                                           */
/*****************************************************************************/

void randomize(int seed)    /* reseed random number generator to user-     */
                            /* defined value                               */
                            /* not reentrant; looks at _RND_BUSY flag and  */
                            /* waits until the flag is clear               */
                            /* DFM 4/24/97:   Fixed problem where, if a    */
                            /* process was swapped out while holding the   */
                            /* lock, then killed, all following calls to   */
                            /* rnd() or randomize() would hang the process */
                            /* forever.                                    */
{
    float f;
    while (_RND_BUSY==1)
    {
        /* wait until busy flag clears */
        defer();    /* DFM 4/24/97: The lock won't be returned */
                    /* by this process, so swap it out. */
    }
    hog_processor();        /* DFM 4/24/97: Get a timeslice long enough to */
                            /* guarantee that no multitasking will occur   */
                            /* while we have the lock.                     */
    _RND_BUSY=1;            /* lock the generator */
    _SEED1=seed&0x0FF;      /* get lobyte of seed */
    f=(float)(seed);
    if (f<0.)
        f=f+65536.;
    _SEED2=(int)(f/256.);   /* kludge to get hibyte of seed */
                            /* necessary because dear old IC won't do */
                            /* -1/256 and get 255 the way it should :-( */
    _RND_BUSY=0;            /* free the generator for other processes */
    defer();                /* DFM 4/24/97: Defer remainder of timeslice. */
}

/*****************************************************************************/

int _rotate(int value, int direction, int rotcount)
                                    /* internal routine which rotates bits */
                                    /* on entry, a=value to be rotated,    */
                                    /* b=direction (0=left, 1=right), and  */
                                    /* c=number of times to rotate it      */
                                    /* Note:  has fudge so that rotcount=0 */
                                    /* always results in 1 rotation.  This */
                                    /* is needed to break the symmetry of  */
                                    /* the algorithm; otherwise you get    */
                                    /* fractal patterns in the output.     */
{
    int n,mybit0;
    if (rotcount==0)
        rotcount++;             /* fudge */
    if (direction==0)
    {
        for (n=0;n<rotcount;n++)
        {
            value=value*2;          /* do a left shift */
            if ((value&256)>0)      /* we had a carry into bit 8 */
            {
                value=value|1;      /* so copy into bit 0 */
                value=value&255;    /* and wipe bits 8-15 */
            }
        }
    }
    else
    {
        for (n=0;n<rotcount;n++)
        {
            mybit0=value&1;         /* get state of bit 0 */
            value=value/2;          /* do a right shift   */
            if (mybit0>0)           /* if we had a carry, */
                value=value|128;    /* put it into bit 7  */
        }
    }
    return(value);      /* at wast, at wast, the scwewwy wabbits awe done! */
}

/*****************************************************************************/

void _rnd()         /* internal routine which does all the work */
                    /* requires that the global variables be already set up */
                    /* changes the value of the global variables! */
{
    if ((_SEED1&1)>0)                   /* set rotate2dir */
        _ROTATE2DIR=1;                  /* bit 0 was set */
    else
        _ROTATE2DIR=0;

    if ((_SEED2&8)>0)                   /* set rotate1dir */
        _ROTATE1DIR=1;                  /* bit 3 was set */
    else
        _ROTATE1DIR=0;

    _ROTATE2COUNT=(_ROTATE1COUNT^_SEED2)&7;     /* keep it 0-7 */
    _ROTATE1COUNT=(_ROTATE2COUNT&_SEED1)&7;     /* keep it 0-7 */

    _SEED1=_rotate(_SEED1,_ROTATE1DIR,_ROTATE1COUNT);   /* rotate the seed 1 bits */
    _SEED2=_rotate(_SEED2,_ROTATE2DIR,_ROTATE2COUNT);   /* rotate the seed 2 bits */

    _RNDMASK-=_SEED2;
    if (_RNDMASK<0)
        _RNDMASK+=256;          /* keep it a positive number */

    _RNDMASK-=_RNDFUDGE;
    if (_RNDMASK<0)
        _RNDMASK+=256;          /* keep it a positive number */

    _RNDMASK-=_SEED1;
    if (_RNDMASK<0)
        _RNDMASK+=256;          /* keep it a positive number */

    _SEED1=_SEED2^_RNDMASK;
    _SEED2=_SEED1&_RNDMASK;

    _RNDMASK=(255-_RNDMASK)&255;    /* 1's complement, keeping only lower 8 bits */

    _NHITS++;                       /* one more hit */
    if (_NHITS==_CRITICAL)          /* is it the magic threshold? */
    {
        _RNDFUDGE=(_RNDFUDGE+1)&255;        /* keep only lower 8 bits */
        _NHITS=0;                           /* and reset the hit parade */
    }
}

/*****************************************************************************/

int rnd(int arg)            /* pseudo-random number generator              */
                            /* this is the public entry point              */
                            /* not reentrant; looks at _RND_BUSY flag and  */
                            /* waits until the flag is clear               */
                            /* DFM 4/24/97:   Fixed problem where, if a    */
                            /* process was swapped out while holding the   */
                            /* lock, then killed, all following calls to   */
                            /* rnd() or randomize() would hang the process */
                            /* forever.                                    */

{
    float s1,s2;            /* needed to pass final arguments */
    while (_RND_BUSY==1)
    {
        /* wait until busy flag is clear */
        defer();    /* DFM 4/24/97: The lock won't be returned */
                    /* by this process, so swap it out. */
    }
    hog_processor();    /* DFM 4/24/97: Get a timeslice long enough to */
                        /* guarantee that no multitasking will occur while */
                        /* we have the lock. */

    _RND_BUSY=1;        /* lock the generator */
    if (arg>0)
    {
        if (_AM_ERSTEN==1)
        {
            /* do nothing, just return the existing random number */
        }
        else    /* do the whole shebang */
        {
            if (_ODD_OR_EVEN==1)    /* if it's an odd time */
            {
                _ODD_OR_EVEN=0;     /* flag that we're even */
                _rnd();             /* get a random number and throw it away */
            }
            else                    /* we're even */
                _ODD_OR_EVEN=1;     /* make us odd for next time */
            _rnd();                 /* now get a random number and keep it */
        }
    }
    else
    {   
        if (arg<0)
        {
            /* restore default values for our globals */
            _SEED1=0x0AA;
            _SEED2=0x055;
            _ROTATE1COUNT=1;
            _ROTATE1DIR=0;
            _ROTATE2COUNT=3;
            _ROTATE2DIR=1;
            _RNDMASK=0x0F0;
            _NHITS=0;
            _CRITICAL=512;      /* even though we don't change this, */
                                /* put it back for safety */
            _RNDFUDGE=0;
            _ODD_OR_EVEN=0;
        }
        else    /* (arg==0) */
        {
            /* there's nothing for us to do, */
            /* just return the existing random number */
        }
    }
    _AM_ERSTEN=0;                   /* mark that we've been here */
    s1=int2uf(_SEED1);              /* get our seeds as floats */
    s2=int2uf(_SEED2);
    _RND_BUSY=0;                    /* free the generator */

    defer();                        /* DFM 4/24/97: Defer remainder of timeslice. */
    return (uf2int(s1+(256.*s2)));  /* make the final signed integer */
}

/*****************************************************************************/

int random(int mod)             /* random integers from peeking at the */
                                /* 2 MHZ system clock.                 */
                                /* mod ranges from 2 to 32767.         */
                                /* original code by Fred G. Martin     */
{
    return (peekword(0x100e) & 0x7fff) % mod;
}

/*****************************************************************************/


