/*
Science and technology promotion license applied 
(third-party licenses/rights auto-cascaded, free usage of this program for non-commercial purposes, 
the liability of Zhikai Wang/ www.heteroclinic.net is to remove/modify matters in dispute)
(c) Zhikai Wang/ www.heteroclinic.net 2013
*/
/* 
Introduction:
Race Condition Random Seeder Release is a tool aiming to create random seeds for random generators. It utilizes modern computer systems' high frequency CPU clock and multitasking in paralell features. By creating man-made race condition, we retrieve a volatile boolean value that threads race upon. The values retrieved thus form a bit-bundle representing a numerical value.
We can say, we can achieve to some degree true random, or software random, in an isolated system.
Such system is like modern computer system, having two or more independent computing units.
So I may say you may let a computer do something all by itself, in some degree (to a great degree) independent of the environment.

In this release R-2.2, we tried to repeat the same principle we did in C/C++ in Java. The effect is not quite satisfying by observation.
In my experiments, we have to force the threads sleep/context switch to add the entropy.
Mainly, this should relate particularly to JVM thread slicing settings. By observation, it causes a single thread dominate the shared flip.
In C++/C test, forcing threads to sleep/context switch thus yielding each other is not necessary.
We have to mention, the particular setting shown as below worked at a particular combo of OS, JVM and hardware.
You can tune the parameters so the results can be statistically satisfying.

Note: forcing the threads to sleep/context switch thus yielding each other significantly drags down the performance.
In contrast in C++/C test, forcing threads to sleep/context switch thus yielding each other is not necessary (but again, it is a given combo of conditions). 
Again, you can find some key parameters to tune with by studying the source code.
This program due to its nature will not be of high performance. It is suggested to be used as a seeder program for other random generators, e.g. linear congruential random generator etc.
*/
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;



public class RaceConditionRandomSeederR2d2JavaImpl {
	public static final int nooftasks =4;
	// In my experiments, we have to force the threads sleep/context switch to add the entropy.
	// Mainly, this should relate particularly to JVM thread slicing settings. By observation, it causes a single thread dominate the shared flip.
	// In C++/C test, forcing threads to sleep/context switch thus yielding each other is not necessary.
	// We have to mention, for the particular setting as below, worked at a particular combo of OS, JVM and hardware.
	// We can say, we can achieve to some degree true random, or software random, in an isolated system.
	// Such system is like modern computer system, having two or more independent computing units.
	// So I may say you may let a computer do something all by itself, in some degree (to a great degree) independent of the environment.
	// You can tune the parameters so the results can be statistically satisfying.
	// Note: forcing the threads to sleep/context switch thus yielding each other significantly drags down the performance.
	// In C++/C test, forcing threads to sleep/context switch thus yielding each other is not necessary. 
	public static final long racerSlice = 10l; //Micro seconds
	public static final long anchorSlice = 100l; //Micro seconds
	public static CountDownLatch latch = new CountDownLatch(nooftasks);
	public static volatile boolean shared = false;
	protected static List <String > results ;
	public static synchronized List <String > getResults(){
		if (null == results)
			results = new ArrayList<String>();
		return results;
	}
	public static String incarnateBitsetviaByte (BitSet bs, String separator) {
		 return incarnateBitsetviaByte  (bs, separator, -1);
	}
	public static String incarnateBitsetviaByte (BitSet bs, String separator,int totalBytesUseingForAlign) {
		String result ="";
		int i = 0;
		byte[] barr = bs.toByteArray();
		if (separator != null && separator.length() > 0) {
			for (i=barr.length-1; i>=0; i--) {
				String str = Integer.toBinaryString( 0xff & barr[i]);
				result += str.length()<Byte.SIZE ? String.format("%"+Byte.SIZE+"s", str).replace(' ', '0'): str;
				result += separator;
			}
			//remove trailing separator
			if (barr.length >= 2 && result.substring(result.length() - separator.length(), result.length()).equals(separator)) {
				result = result.substring(0,result.length() - separator.length());
			}
		} else {
			for (i=barr.length-1; i>=0; i--) {
				String str = Integer.toBinaryString( 0xff & barr[i]);
				result += str.length()<Byte.SIZE ? String.format("%"+Byte.SIZE+"s", str).replace(' ', '0'): str;
			}
		}
		if (totalBytesUseingForAlign <=barr.length )
			return result; 
		else {
			String larger = "";
			if (separator != null && separator.length() > 0) {
				for (i=0; i< totalBytesUseingForAlign; i++) {
					String str = Integer.toBinaryString( 0);
					larger += str.length()<Byte.SIZE ? String.format("%"+Byte.SIZE+"s", str).replace(' ', '0'): str;
					larger += separator;
				}
				if ( totalBytesUseingForAlign >= 2 && larger.substring(larger.length() - separator.length(), larger.length()).equals(separator)) {
					larger = larger.substring(0,larger.length() - separator.length());
				}
			} else {
				for (i=0; i< totalBytesUseingForAlign; i++) {
					String str = Integer.toBinaryString( 0);
					larger += str.length()<Byte.SIZE ? String.format("%"+Byte.SIZE+"s", str).replace(' ', '0'): str;
				}
			}
			String part1 = larger.substring(0, larger.length() - result.length());
			return part1 + result;
		}
	}
	static class Shared {
		
		public static volatile boolean halted = false;
	}
	
	static class Task implements Runnable {
		static AtomicLong taskCount = new AtomicLong(0);
		protected volatile boolean stopRequested = false ;
		

		public boolean isStopRequested() {
			return stopRequested;
		}

		public void setStopRequested(boolean stopRequested) {
			this.stopRequested = stopRequested;
		}
		
		protected long ID;

		public Task() {
			this.ID = taskCount.incrementAndGet();
		}

		@Override
		public void run() {

			latch.countDown();
			try {
				latch.await();
				 //
				while (  !Shared.halted && !Thread.interrupted() && !this.stopRequested) {
					try {
						TimeUnit.MICROSECONDS.sleep(racerSlice);
					} catch (InterruptedException e) {
						// e.printStackTrace();
					}
					//Shared.shared = !Shared.shared;
					shared = !shared; // this effect is bad
					//shared = (this.ID %2 ==0);

				}
			}
			catch (InterruptedException e) {
				System.out.println(""+this.ID +" interrupted.");
			}
			finally {
				reachedBottom = true;
				if (this.stopRequested )
					System.out.println(""+this.ID+" end");
			}
		}
		protected volatile boolean reachedBottom;
	}

	/**
	 * @param args. If args.length >= 2, args[0] represents bit-length, 
	 * args[1] represents the number of bit-bundles you need. 
	 * You can call the main directly in Another function.
	 * Like  RaceConditionRandomSeederR2d2JavaImpl.main(64,1024), then access
	 * the public static List<String> results which buffered the bits bundles. 
	 */
	public static void main(String[] args) {
		int bundleLength = 128;
		int totalBundles = 64;
		try {
			if (args.length >= 2) {
				bundleLength = Integer.parseInt(args[0]);
				totalBundles = Integer.parseInt(args[1]);
			}
		} catch (Exception e) {
			bundleLength = 64;
			totalBundles = 32;
		}
		
		List<Task> tasks = new ArrayList<Task>();

		List<Future<?>> fl = new ArrayList<Future<?>>();
		ExecutorService exec = Executors.newCachedThreadPool();

		for (int i = 0; i < nooftasks; i++) {
			tasks.add(new Task());
		}
		

		for (Task t : tasks) {
			fl.add(exec.submit(t));
		}
		
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			TimeUnit.MICROSECONDS.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for (int i = 0; i < totalBundles; i++) {
			BitSet bs = new BitSet();
			for (int j = 0 ; j < bundleLength; j++) {
				try {
					TimeUnit.MICROSECONDS.sleep(anchorSlice);
				} catch (InterruptedException e) {
					// e.printStackTrace();
				}
				bs.set(j,shared);
			}
			 getResults().add(incarnateBitsetviaByte(bs,"-", bundleLength%Byte.SIZE==0 ? bundleLength/Byte.SIZE: bundleLength/Byte.SIZE + 1 ));
		}
		
		// There are various ways to stop the Task threads.
		// It is resembling brake and full stop a car or similar mechanical devices.
		for (Future<?> f : fl) {
			f.cancel(true);
			while (!f.isDone())
				;
		}
		Shared.halted = true;
		exec.shutdown();
		
		System.out.println("Show buffered results.");
		for (String s:  getResults()) {
			System.out.println(s);
		}
		
		System.out.println("End of main.");
	}

}
