package edu.cornell.lassp.houle.RngPack;

import java.util.*;

/**
*   RandomElement is an abstract class that encapsulates random number
* generators.  To base a class on it,  you must define the method raw,
* described below,  as well as defining a constructor to initialize or
* seed the generator.
*
*   Other classes defined in RandomElement add value to the random
* numbers generated by raw().
*
* <P>
* <A HREF="../src/edu/cornell/lassp/houle/RngPack/RandomElement.java" TARGET="edu.cornell.lassp.houle.source">
* Source code </A> is available.
* 
* @author <A HREF="http://www.msc.cornell.edu/~houle" TARGET="edu.cornell.lassp.houle.author"> Paul Houle </A> (E-mail: <A HREF="mailto:ph18@cornell.edu">ph18@cornell.edu</A>)
* @version 0.9a
* 
* @see RandomJava
* @see RandomShuffle
*/

public abstract class RandomElement extends Object {

  Double BMoutput;                // constant needed by Box-Mueller algorithm

/**
The abstract method that must be defined to make a working RandomElement.
See the class RandomJava for an example of how to do this.

@see RandomJava

@return a random double in the range [0,1]
*/

  abstract public double raw();     

/**  
@param hi upper limit of range
@return a random integer in the range 1,2,... ,<STRONG>hi</STRONG>
*/
  public int choose(int hi) {
  
  	return (int) (1+hi*raw());
  }
  
/**
@param lo lower limit of range
@param hi upper limit of range
@return a random integer in the range <STRONG>lo</STRONG>, <STRONG>lo</STRONG>+1, ... ,<STRONG>hi</STRONG>
*/
  
  public int choose(int lo,int hi) {
  
    return (int) (lo+(hi-lo+1)*raw());
  };

/**
@param lo lower limit of range
@param hi upper limit of range
@return a uniform random real in the range [<STRONG>lo</STRONG>,<STRONG>hi</STRONG>]
*/
  public double uniform(double lo,double hi) {
    return (lo+(hi-lo)*raw());
  };

/** 
gaussian() uses the Box-Muller algorithm to transform raw()'s into
gaussian deviates.

@return a random real with a gaussian distribution,  standard deviation 

*/
 
  public double gaussian()

  {
     double out,x,y,r,z;

     if (BMoutput != null)    
     {
         out = BMoutput.doubleValue();
         BMoutput = null;
         return(out);
     };

     do {
         x=uniform(-1,1);
         y=uniform(-1,1);
         r=x*x+y*y;
     } while (r >= 1.0);

     z=Math.sqrt(-2.0*Math.log(r)/r);
     BMoutput=new Double(x*z);
     return(y*z); 
   }; 

/**

@param sd standard deviation
@return a gaussian distributed random real with standard deviation <STRONG>sd</STRONG>
*/

  public double gaussian(double sd)

  {
     return(gaussian()*sd);
  };
};




