
/**
 * @author Marc Flesch
 */
public class MSSTest {

	public static void main(String[] args) {
		submit(new MSS(), "MSS");
		submit(new BusyLog(250), "Log");
	}

	private static void submit(Runnable r, String name) {
		new Thread(r, name).start();
	}

	private static String dt(long t1, long t2) {
		return ((t2 - t1) / 100) / 10.0 + " sec.";
	}

	private static final class BusyLog implements Runnable {
		private final long timeout;

		BusyLog(long timeout) {
			this.timeout = timeout;
		}

		@Override
		public void run() {
			int counter = -1;
			while (true) {
				long t1 = System.currentTimeMillis();
				while (System.currentTimeMillis() - t1 < timeout) {
					Math.log(Float.NaN);
				}
				long t2 = System.currentTimeMillis();
				counter++;
				String dt = dt(t1, t2);
				if (dt.equals(dt(0, timeout))) {
					System.out.print(".");
				} else {
					System.err.println("\nLog-" + counter + " took: " + dt + ", expected " + timeout + " ms.");
				}
			}
		}
	}

	private static class MSS implements Runnable {

		public void run() {
			System.out.println("Start");
			int binCount = 2000000;
			float mean = 0.76f;
			float sigma = 0.75f;
			float[] levels = new float[binCount];

			for (int binWindow = 2; binWindow < 64; binWindow += 4) {
				int n = levels.length;
				float[] gradient = new float[n];
				float inv2BinWidth = 1f / (binWindow * binWindow);
				float mean4 = mean / 4;
				float sigma2 = 4 / (sigma * sigma);
				float ms2 = mean / (sigma * sigma);
				int window = 3 * binWindow;
				float[] exponents = new float[window + 1];
				for (int i = 0; i <= window; i++) {
					exponents[i] = (float) (i * Math.exp(-0.5 * i * i * inv2BinWidth));
				}
				long t1 = System.currentTimeMillis();
				gradient(levels, n, gradient, mean4, sigma2, ms2, window, exponents);
				long t2 = System.currentTimeMillis();
				System.out.println("\n-> levels took: " + dt(t1, t2));
			}
			System.err.println("Done.");
			System.exit(0);
		}

		private void gradient(float[] levels, int n, float[] gradient, float mean4, float sigma2, float ms2, int window,
				float[] exponents) {
			for (int i = 0; i < n; i++) {
				float invB;
				int d = 0;
				if (levels[i] < mean4) {
					invB = sigma2;
				} else {
					invB = ms2 / levels[i];
				}
				for (int j = i + 1; j < n; j++) {
					d++;
					float inv_i;
					if (levels[j] < mean4) {
						inv_i = sigma2;
					} else {
						inv_i = ms2 / levels[j];
					}
					float r = levels[j] - levels[i];
					float val = -0.5f * r * r;
					gradient[i] += exponents[d] * Math.exp(val * invB);
					gradient[j] -= exponents[d] * Math.exp(val * inv_i);
					if (d == window) {
						break;
					}
				}
			}
		}
	}
}
