#include "oracle.h"

#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif

oracle::oracle(char *seed,int max_r_n_plus_1)
//      An eight (or fewer) character seed and the upper bound on the
// numbers to be returned specify the random number generator. 
  {
    modulus=max_r_n_plus_1;
    prime=equal_or_larger_prime(modulus);
    // A prime modulus is needed to insure that the number returned from
    // the random number generator are uniformly distributed.
    for (int r_n_index_1=0; ((r_n_index_1 < 8) && (seed[r_n_index_1]));
     r_n_index_1++)        // insert seed
      {
        if (seed[r_n_index_1] < '\0')
          seed[r_n_index_1]=-seed[r_n_index_1];
        r_n[r_n_index_1]=long(seed[r_n_index_1])%prime;
      }
    int r_n_index_2=7;
    while (r_n_index_1 > 0) // right justify
      {
         r_n_index_1--;
         r_n[r_n_index_2]=r_n[r_n_index_1];
         r_n_index_2--;
      }
    while (r_n_index_2 >= 0) // fill with leading 1's
      {
        r_n[r_n_index_2]=1;
        r_n_index_2--;
      }
  }

long oracle::equal_or_larger_prime(int starting_value)
//      Try consecutive values until a prime number is found.
  {
    long result=long(starting_value);
    while (! is_prime(result)) result++;
    return result;
  }

int oracle::is_prime(long possible_prime)
//      If no number up to the square root of a number evenly divides the
// number, then the number is prime.
  {
    long new_max_divisor;
    int  result=TRUE;
    long max_divisor=possible_prime;
    int  square_root=FALSE;

    while (! square_root)
      {
        if ((new_max_divisor=(max_divisor+possible_prime/max_divisor)/2l)
         < max_divisor)
          max_divisor=new_max_divisor;
        else
          square_root=TRUE;
      }  // max_divisor=sqrt(possible_prime) via Newton's method.
    for (long divisor=2l; ((result) && (divisor <= max_divisor)); divisor++)
      result=((divisor*(possible_prime/divisor)) != possible_prime);
    return result;
  }

int oracle::random_number()
//     Ignoring initialization, r_n[i] is the sum of the 8 previous values of
// r_n[i] mod prime. r_n[7] is computed until it's within the desired range and
// then it's returned.
//     It is assumed that a "long" can contain the sum of any two "int"s.
  {
    int  r_n_index_1;
    int  r_n_index_2;
    long result;
    long tem_long;

    do
      {
        result=r_n[0];
        r_n_index_1=0;
        r_n_index_2=1;
        while (r_n_index_2 < 8)
          {
            tem_long=r_n[r_n_index_2];
            r_n[r_n_index_1]=tem_long;
            result+=tem_long;
            if (result >= prime)
              result-=prime;
            r_n_index_1=r_n_index_2;
            r_n_index_2++;
          }
        r_n[7]=result;
      }
    while (result >= long(modulus));
    return int(result);
  }
