// Exponential.java
//
// Ethan Bolker, October 1999, 
// based on code written at/for BMC Software.
//
// Small improvements April 2003

import java.util.Random;

/**
 * Exponential objects generate random integers from 
 * an exponential distribution with mean
 * <pre>
 *         (requested mean) * scale
 * </pre>
 * where scale defaults to 1000.
 */
public class Exponential 
{
    private int scale = 1000; 
    private Random random;
    private double mean;

    /**
     * Construct a new Exponential object.
     *
     * @param mean scale*mean is the mean of the desired distribution.
     */
    public Exponential(double mean) 
    {
	this.mean = mean;
	random = new Random();
    }

    /**
     * Construct a new Exponential object with a seeded random
     * generator. Exponential objects with the same seed, scale
     * and mean will generate the same sequence of values.
     * <p>
     *
     * @param mean scale*mean is the mean of the desired distribution
     * @param seed the seed for the generator
     */
    public Exponential(double mean, long seed) 
    {
	this.mean = mean;
	if (seed == 0) 	random = new Random();
	else
	   random = new Random(seed);
    }
    
    /** 
     * How much have we scaled by?
     *
     * @return the scale.
     */
    public int getScale()
    {
	return scale;
    }

    /** 
     * How much should we scale by?
     *
     * @param the desired scale (default 1000).
     */
    public void setScale(int scale)
    {
	this.scale = scale;
    }

    /**
     * Generate the next random selection.
     *
     * @return the next random selection.
     */
    public int next() 
    {
	return (int)(-scale*Math.log(random.nextDouble())*mean);
    }

    /**
     * For unit testing.
     */
     public static void main (String[] args) 
    {
	int n = 3000;
	int m = 15;
	double meanGoal = 2.0;
	Exponential p0 = new Exponential(meanGoal);

	System.out.println("First " + m + " of " + n + 
			   " samples from an Exponential distribution\n" +
			   "with mean " + meanGoal +
			   " (scaled up by " + p0.getScale() + "):");
	double total = 0;
	double totalSquares = 0;
	for (int i=0; i < n; i++) {
	    int next = p0.next();
	    if ( i < m ) {
		System.out.println(next);
	    }
	    total        += next;
	    totalSquares += next*next;
	}
	double mean      = total/n;
	double variance  = totalSquares/n - mean*mean;
	System.out.println("mean goal:          " + meanGoal*p0.getScale() + 
		         "\nachieved:           " + mean +
			 "\nstandard deviation: " + Math.sqrt(variance));


	System.out.println("\ntest seeding and setting scale"); 
	Exponential p1 = new Exponential(1.0, 1234);
	Exponential p2 = new Exponential(1.0, 1234);
	p2.setScale(10000);
	for (int i=0; i < 10; i++) {
	    System.out.println(p1.next() + " " + p2.next());
	}
    }
}







