// example code for: http://stackoverflow.com/a/21976027/616460

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


// the important bits are the implementations of next() below. everything
// else is (somewhat sloppy) code to support testing and output.
class Dist {

    // modify MAX_VALUE and RUN_COUNT as you see fit
    static final int MAX_VALUE = 60;
    static final int RUN_COUNT = 10000;
    
    static final Random random = new Random();

    // base class for a test; generates a set of RUN_COUNT random values
    // in the range [0, MAX_VALUE) and stores a histogram of the results.
    // subclasses should implement next() to return a value [0, MAXVALUE).
    static abstract class RandomTest {
        final int[] count = new int[MAX_VALUE];
        final String name;
        RandomTest (String name) { this.name = name; }
        RandomTest run () {
            System.out.println("Running " + name + "...");
            for (int n = 0; n < RUN_COUNT; ++ n) count[next()] ++;
            return this;
        }
        abstract int next ();
    }
    
    // Uniform distribution
    static class Uniform extends RandomTest {
        Uniform () { super("Uniform"); } 
        @Override int next () {
            return random.nextInt(MAX_VALUE);
        }
    }
    
    // pow(value, bias)
    static class Pow extends RandomTest {
        final double bias;
        Pow (double bias) { super("Pow(" + bias + ")"); this.bias = bias; }
        @Override int next () {
            double v = Math.pow(random.nextDouble(), bias);
            return (int)(v * MAX_VALUE);
        }
    }

    // [0,1) section of gaussian distribution, scaled by given value
    static class Gaussian extends RandomTest {
    	final double scale;
        Gaussian (double scale) { super("G(" + scale + ")"); this.scale = scale; }
        @Override int next () {
            double v;
            do {
                v = Math.abs(random.nextGaussian() / scale);
            } while (v >= 1.0);
            return (int)(v * MAX_VALUE);
        }
    }
    
    // cubic S
    static class Cubic extends RandomTest {
        Cubic () { super("Cubic"); }
        @Override int next () {
            double v = random.nextDouble();
            v = 3*v*v - 2*v*v*v;
            return (int)(v * MAX_VALUE);
        }
    }

    // print one row of results; prints header if index == -1
    static void printResultRow (List<RandomTest> tests, int index) {
        System.out.printf("%-5s:", (index < 0) ? "Name" : Integer.toString(index));
        for (RandomTest test:tests) {
            String s = (index < 0) ? test.name : Integer.toString(test.count[index]);
            System.out.printf("%9s ", s);
        }
        System.out.println();
    }

    // run all tests and print results    
    public static void main (String[] args) {
        
        List<RandomTest> tests = new ArrayList<RandomTest>();
        tests.add(new Uniform().run());
        tests.add(new Pow(0.5).run());
        tests.add(new Pow(2.0).run());
        tests.add(new Pow(10.0).run());
        tests.add(new Gaussian(1.0).run());
        tests.add(new Gaussian(3.5).run());
        tests.add(new Cubic().run());

        for (int n = -1; n < MAX_VALUE; ++ n)
            printResultRow(tests, n);
        
    }
    
}
