package mikera.util;

import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

public final class Rand {
	/**
	 * State for random number generation
	 */
	private static volatile long state=xorShift64(System.nanoTime()|0xCAFEBABE);

	/**
	 * Gets a long random value
	 * @return Random long value based on static state
	 */
	public static final long nextLong() {
		long a=state;
		state = xorShift64(a);
		return a;
	}
	
	/**
	 * XORShift algorithm - very fast psuedo-random number generator 
	 * 
	 * Credit to George Marsaglia!
	 * 
	 * @param a Initial state
	 * @return new state
	 */
	public static final long xorShift64(long a) {
		a ^= (a << 21);
		a ^= (a >>> 35);
		a ^= (a << 4);
		return a;
	}
	
	/**
	 * XORShift algorithm - credit to George Marsaglia!
	 * @param a Initial state
	 * @return new state
	 */
	public static final int xorShift32(int a) {
		a ^= (a << 13);
		a ^= (a >>> 17);
		a ^= (a << 5);
		return a;
	}
	
	/**
	 * Returns true with a given probability
	 * @param d
	 * @return
	 */
	public static boolean chance(double d) {
		return Rand.nextDouble() n) || (s < 0))
			return 0;

		int[] rolls = new int[n];
		for (int i = 0; i < n; i++)
			rolls[i] = d(s);

		boolean found;
		do {
			found = false;
			for (int x = 0; x < n - 1; x++) {
				if (rolls[x] < rolls[x + 1]) {
					int t = rolls[x];
					rolls[x] = rolls[x + 1];
					rolls[x + 1] = t;
					found = true;
				}
			}
		} while (found);

		int sum = 0;
		for (int i = 0; i < r; i++)
			sum += rolls[i];
		return sum;
	}
	
	/**
	 * Sigmoid function
	 */
	public static double sigmoid (double a) {
		double ea=Math.exp(-a);
		double df=(1/(1.0f+ea));
		if (Double.isNaN(df)) return (a>0)?1:0;
		return df;
	}

	/**
	 *  logistic sigmoid  probability
	 * @param x
	 * @return
	 */
	public static int sig(float x) {
		return (Rand.chance(sigmoid(x))?1:0);
	}
	
	/**
	 *  Poisson distribution
	 * @param mean The mean of the poisson distrubution to sample from
	 * @return
	 */
	public static int po(double mean) {
		if (mean<=0) {
			if (mean<0) throw new IllegalArgumentException();
			return 0;
		}
		if (mean>400) {
			return poLarge(mean);
		}
		return poMedium(mean);
	}

	private static int poMedium(double mean) {
		int r = 0;
		double a = nextDouble();
		double p = Math.exp(-mean);

		while (a > p) {
			r++;
			a = a - p;
			p = p * mean / r;
		}
		return r;
	}
	
	private static int poLarge(double mean) {
		// normal approximation to poisson
		// strictly necessary for x>=746 (=> Math.exp(-x)==0)
		return (int)(0.5+n(mean,Math.sqrt(mean)));
	}

	/**
	 * Draw a sample from the possion distribution with parameter = numerator / denominator
	 * 
	 * @param numerator
	 * @param denominator
	 * @return
	 */
	public static int po(int numerator, int denominator) {
		return po(((double) numerator) / denominator);
	}
	
	/**
	 * Calculates the sum of (number) x (sides)-sided dice
	 * 
	 * @param number
	 * @param sides
	 * @return
	 */
	public static int d(int number, int sides) {
		int total = 0;
		for (int i = 0; i < number; i++) {
			total += d(sides);
		}
		return total;
	}
	
	
	public static double factorial(int n) {
		if (n<2) return 1;
		double result=1;
		for (int i=2; i<=n; i++) {
			result*=i;
		}
		return result;
	}
	
	/**
	 * Calculates n!/r!
	 * 
	 * Avoids the overhead of calculating the terms which cancel out
	 * 
	 * @param n
	 * @param r
	 * @return
	 */
	public static double factorialRatio(int n, int r) {
		if (n<2) return 1;
		if (n(n-r)) return combinations(n-r,n);
		return factorialRatio(n,n-r)/factorial(r);
	}
	
	public static double binomialChance(int r, int n, double p) {
		return combinations(r,n)*Math.pow(p,r)*Math.pow((1-p), n-r);
	}

	/**
	 * Returns a random sample from an exponential distribution
	 * 
	 * @param mean
	 * @return
	 */
	public static double exp(double mean) {
		return -Math.log(Rand.nextDouble())*mean;
	}
	
	/**
	 *  Returns a sample from a Geometric distribution
	 *  
	 *  Discrete distribution with decay rate p
	 *  Mean = (1-p) / p
	 *  Probability mass function for each integer output k = p.(1-p)^k
	 */	
	public static int geom(double p) {
		return (int)Math.floor(Math.log(Rand.nextDouble()) / Math.log(1-p));
	}
	
	
	/*
	 * Generators for standard Java types
	 */
	
	/**
	 * Sample a random signed 32-bit integer
	 * @return
	 */
	public static final int nextInt() {
		return (int)(nextLong()>>32);
	}
	
	/**
	 * Sample a random signed short value
	 * @return
	 */
	public static final short nextShort() {
		return (short)(nextLong()>>32);
	}
	
	/**
	 * Sample a random char
	 * @return
	 */
	public static final char nextChar() {
		return (char)(nextLong()>>32);
	}
	
	public static final String nextLetterString(int length) {
		char[] cs=new char[length];
		for (int i=0; i>32);
	}
	
	/**
	 * Sample a random boolean value with 50% chance
	 * 
	 * @return
	 */
	public static boolean nextBoolean() {
		return (nextLong()&65536)!=0;
	}
	
	/**
	 * Sample a random lowercase letter
	 * @return
	 */
	public static final char nextLetter() {
		return (char)Rand.range('a','z');
	}
	
	/**
	 * Random number from zero to s-1
	 * 
	 * @param s Upper bound (excluded)
	 * @return
	 */
	public static final int r(int s) {
		if (s<0) throw new IllegalArgumentException();
		long result=((nextLong()>>>32)*s)>>32;
		return (int) result;
	}
	
	/**
	 * Return a random index between 0 and max (exclusive) not equal to i
	 */
	public static final int otherIndex(int i, int max) {
		return (Rand.r(max-1)+i)%max;
	}

	
	private static final double DOUBLE_SCALE_FACTOR=1.0/Math.pow(2,63);
	private static final float FLOAT_SCALE_FACTOR=(float)(1.0/Math.pow(2,63));

	/**
	 * Returns standard double in range [0..1)
	 * 
	 * @return
	 */
	public static final double nextDouble() {
		return ( nextLong()>>>1 ) * DOUBLE_SCALE_FACTOR;
	}
	
	/**
	 * Sample a random float in range [0..1)
	 * @return
	 */
	public static final float nextFloat() {
		return ( nextLong()>>>1 ) * FLOAT_SCALE_FACTOR;
	}

	/**
	 * Sample a random uniform double in range [0..1)
	 * @return
	 */
	public static final double u() {
		return nextDouble();
	}
	
	/**
	 * Sample a random uniform double in range [0..max)
	 * @param max
	 * @return
	 */
	public static final double u(double max) {
		return u(0,max);
	}
	
	/**
	 * Sample a random uniform double in the range [min..max)
	 * @param min
	 * @param max
	 * @return
	 */
	public static final double u(double min,double max) {
		return min+nextDouble()*(max-min);
	}

	
	/**
	 * Randomly rounds to the nearest integer
	 */
	public static final int round(double d) {
		int i=(int)Math.floor(d);
		int rem=(nextDouble()<(d-i))?1:0;
		return i+rem;
	}
	
    /**
     *  Returns random number uniformly distributed in inclusive [n1, n2] range.
     *  It is allowed to have to n1 > n2, or n1 < n2, or n1 == n2.
     */
     public static final int range(int n1, int n2) {
        if (n1>n2) {
        	int t=n1; n1=n2; n2=t;
        }
        return n1+r(n2-n1+1);
     }
	
	/**
	 *  simulates a dice roll with the given number of sides
	 * @param sides
	 * @return
	 */
	public static final int d(int sides) {
		return r(sides) + 1;
	}
	
	public static final int d3() {
		return d(3);
	}

	public static final int d4() {
		return d(4);
	}

	public static final int d6() {
		return d(6);
	}

	public static final int d8() {
		return d(8);
	}

	public static final int d10() {
		return d(10);
	}

	public static final int d12() {
		return d(12);
	}

	public static final int d20() {
		return d(20);
	}

	public static final int d100() {
		return d(100);
	}
	
	/**
	 * Generates a normal distributed number with the given mean and standard deviation
	 * 
	 * @param u Mean
	 * @param sd Standard deviation
	 * @return
	 */
	public static double n(double u, double sd) {
		return nextGaussian()*sd+u;
	}
	
	public static double nextGaussian() {
		// create a guassian random variable based on
		// Box-Muller transform
		double x, y, d2;
		do { 
			// sample a point in the unit disc
			x = 2*nextDouble()-1;  
			y = 2*nextDouble()-1;  
			d2 = x*x + y*y;
		} while ((d2 > 1) || (d2==0));
		// create the radius factor
		double radiusFactor = Math.sqrt(-2 * Math.log(d2) / d2);
		return x * radiusFactor;
		// could save and use the other value?
		// double anotherGaussian = y * radiusFactor;
	}

	public static String nextString() {
		char[] cs=new char[Rand.po(4)];
		for (int i=0; i void shuffle(T[] ts) {
		for (int i=0; i<(ts.length-1); i++) {
			int j=Rand.r(ts.length-i);
			if (i!=j) {
				T t=ts[i];
				ts[i]=ts[j];
				ts[j]=t;
			}
		}
	}
	
	/**
	 * Chooses a set of distinct integers from a range 0 to maxValue-1
	 * 
	 * Resulting integers are sorted
	 */
	public static void chooseIntegers(int[] dest, int destOffset, int length, int maxValue) {
		if (length>maxValue) throw new Error("Cannot choose "+length+" items from a set of "+maxValue);
	
		if (maxValue>(4*length)) {
			chooseIntegersBySampling(dest,destOffset,length,maxValue);
			return;
		}
		
		chooseIntegersByExclusion(dest,destOffset,length,maxValue);

	}
	
	/**
	 * Creates a set of n random integers from a range of 0 to maxValue-1
	 */
	public static void randIntegers(int[] dest, int destOffset, int length, int maxValue) {
		for (int i=destOffset; i<(destOffset+length); i++) {
			dest[i]=Rand.r(maxValue);
		}
	}
	
	private static void chooseIntegersByExclusion(int[] dest, int destOffset, int n, int maxValue) {	
		while (n>0) {
			if ((n==maxValue)||Rand.r(maxValue) s=new TreeSet();
		
		while (s.size() T pick(T[] ts) {
		return ts[Rand.r(ts.length)];
	}
	
	/**
	 * Picks a random item from a given list
	 */
	public static  T pick(List ts) {
		return ts.get(Rand.r(ts.size()));
	}
	
	/**
	 * Picks a random item from a given collection
	 */
	public static  T pick(Collection ts) {
		int n=ts.size();
		if (n==0) throw new Error("Empty collection!");
		int p=Rand.r(n);
		for (T t: ts) {
			if (p--==0) return t;
		}
		throw new Error("Shouldn't get here!");
	}

	public static void fillUniform(float[] d, int start, int length) {
		for (int i=0; i 

Ads help maintain this website.