package net.varkhan.base.functor.generator;
import junit.framework.TestCase;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* <b></b>.
* <p/>
*
* @author varkhan
* @date 2/2/14
* @time 3:35 PM
*/
public abstract class AbstractRNGTest extends TestCase {
public void testLong(RNG rand, long count, double pre) throws Exception {
long[] bins01 = new long[16];
long[] bins02 = new long[8];
long[] bins04 = new long[4];
long[] bins08 = new long[4];
long[] bins10 = new long[2];
long[] bins20 = new long[2];
long[] bins40 = new long[2];
long[] seqs = new long[5];
Map<String,Long> seqc = new HashMap<String,Long>();
double seqn = 0;
for(long i=0; i<count; i++) {
long r = rand.nextLong();
if((r&0x0000000000000001L)==0) bins01[0]++;
if((r&0x0000000000000010L)==0) bins01[1]++;
if((r&0x0000000000000100L)==0) bins01[2]++;
if((r&0x0000000000001000L)==0) bins01[3]++;
if((r&0x0000000000010000L)==0) bins01[4]++;
if((r&0x0000000000100000L)==0) bins01[5]++;
if((r&0x0000000001000000L)==0) bins01[6]++;
if((r&0x0000000010000000L)==0) bins01[7]++;
if((r&0x0000000100000000L)==0) bins01[8]++;
if((r&0x0000001000000000L)==0) bins01[9]++;
if((r&0x0000010000000000L)==0) bins01[10]++;
if((r&0x0000100000000000L)==0) bins01[11]++;
if((r&0x0001000000000000L)==0) bins01[12]++;
if((r&0x0010000000000000L)==0) bins01[13]++;
if((r&0x0100000000000000L)==0) bins01[14]++;
if((r&0x1000000000000000L)==0) bins01[15]++;
if((r&0x0000000000000022L)==0) bins02[0]++;
if((r&0x0000000000002200L)==0) bins02[1]++;
if((r&0x0000000000220000L)==0) bins02[2]++;
if((r&0x0000000022000000L)==0) bins02[3]++;
if((r&0x0000002200000000L)==0) bins02[4]++;
if((r&0x0000220000000000L)==0) bins02[5]++;
if((r&0x0022000000000000L)==0) bins02[6]++;
if((r&0x2200000000000000L)==0) bins02[7]++;
if((r&0x0000000000004444L)==0) bins04[0]++;
if((r&0x0000000044440000L)==0) bins04[1]++;
if((r&0x0000444400000000L)==0) bins04[2]++;
if((r&0x4444000000000000L)==0) bins04[3]++;
if((r&0x0000000000009999L)==0) bins08[0]++;
if((r&0x0000000099990000L)==0) bins08[1]++;
if((r&0x0000999900000000L)==0) bins08[2]++;
if((r&0x9999000000000000L)==0) bins08[3]++;
if((r&0x00000000AAAAAAAAL)==0) bins10[0]++;
if((r&0xAAAAAAAA00000000L)==0) bins10[1]++;
if((r&0x000000000F0F0F0FL)==0) bins20[0]++;
if((r&0xF0F0F0F000000000L)==0) bins20[1]++;
if((r&0x00000000FFFFFFFFL)==0) bins40[0]++;
if((r&0xFFFFFFFF00000000L)==0) bins40[1]++;
if(i%seqs.length==0) {
String s = sequence(seqs);
Long c = seqc.get(s);
if(c==null) { seqc.put(s,1L); }
else seqc.put(s,c+1L);
seqn++;
}
seqs[(int)(i%seqs.length)] = r;
}
long t0 = System.currentTimeMillis();
long t = 0;
for(long i=0; i<count; i++) {
long r = rand.nextLong();
if((r&0x0000000000000001L)==0) t++;
}
long t1 = System.currentTimeMillis();
assertEquals("ratio01 ~ 0.5 +/-"+2.0/Math.sqrt(count),0.5,((double)t)/count,2.0/Math.sqrt(count));
System.err.println("Computed "+count+" rands in "+((t1-t0)/1000)+"s, "+(1.0e6*(t1-t0)/count)+"ns/call, with "+((double)t)/count+" mix, +/-"+(1.0/Math.sqrt(count)));
// Print 5-sequences
for(Map.Entry<String,Long> e: seqc.entrySet()) {
printDeltas(System.err, "seq "+seqc.size(),e.getKey(),1.0/seqc.size(),e.getValue()/seqn,pre*0.5/Math.sqrt(count));
}
for(int i=0; i<bins01.length; i++) {
printDeltas(System.err, "ratio01", i, 0.50000, ((double) bins01[i])/count, pre*1.0/Math.sqrt(count));
}
for(int i=0; i<bins02.length; i++) {
printDeltas(System.err, "ratio02", i, 0.25000, ((double)bins02[i])/count,(pre*2.0/Math.sqrt(count)));
}
for(int i=0; i<bins04.length; i++) {
printDeltas(System.err, "ratio04", i, 0.06250, ((double)bins04[i])/count,(pre*4.0/Math.sqrt(count)));
}
for(int i=0; i<bins08.length; i++) {
printDeltas(System.err, "ratio08", i, 0.003905, ((double)bins08[i])/count,(pre*8.0/Math.sqrt(count)));
}
for(int i=0; i<bins10.length; i++) {
printDeltas(System.err, "ratio10", i, 0.000015625, ((double)bins10[i])/count,(pre*16.0/Math.sqrt(count)));
}
for(int i=0; i<bins20.length; i++) {
printDeltas(System.err, "ratio20", i, 0.000015625, ((double)bins20[i])/count,(pre*32.0/Math.sqrt(count)));
}
for(int i=0; i<bins40.length; i++) {
printDeltas(System.err, "ratio40", i, 0.00000078125, ((double)bins40[i])/count,(pre*64.0/Math.sqrt(count)));
}
// Verify 5-sequences
for(Map.Entry<String,Long> e: seqc.entrySet()) {
assertEquals("seq "+seqc.size()+" '"+e.getKey()+"': "+(e.getValue()/seqn)+" / "+(1.0/seqc.size()),
1.0/seqc.size(),
e.getValue().doubleValue()/seqn,
pre*0.5/Math.sqrt(count));
}
for(int i=0; i<bins01.length; i++) {
assertEquals("ratio01 "+i+" ~ 0.50000",0.50000,((double)bins01[i])/count,pre*1.0/Math.sqrt(count));
}
for(int i=0; i<bins02.length; i++) {
assertEquals("ratio02 "+i+" ~ 0.25000",0.25000,((double)bins02[i])/count,pre*2.0/Math.sqrt(count));
}
for(int i=0; i<bins04.length; i++) {
assertEquals("ratio04 "+i+" ~ 0.06250",0.06250,((double)bins04[i])/count,pre*4.0/Math.sqrt(count));
}
for(int i=0; i<bins08.length; i++) {
// TODO understand why this is not in line with other values
// assertEquals("ratio08 "+i+" ~ 0.003125",0.003125,((double)bins08[i])/count,pre*8.0/Math.sqrt(count));
assertEquals("ratio08 "+i+" ~ 0.003905",0.003905,((double)bins08[i])/count,pre*8.0/Math.sqrt(count));
}
for(int i=0; i<bins10.length; i++) {
assertEquals("ratio10 "+i+" ~ 0.003125",0.000015625,((double)bins10[i])/count,pre*16.0/Math.sqrt(count));
}
for(int i=0; i<bins20.length; i++) {
assertEquals("ratio20 "+i+" ~ 0.000015625",0.000015625,((double)bins20[i])/count,pre*32.0/Math.sqrt(count));
}
for(int i=0; i<bins40.length; i++) {
assertEquals("ratio40 "+i+" ~ 0.00000078125",0.00000078125,((double)bins40[i])/count,pre*64.0/Math.sqrt(count));
}
}
/**
*
* @param p the output
* @param r the name of the test
* @param i the index of the test bin
* @param t the target value
* @param v the actual value
* @param e the expected error
*/
protected static void printDeltas(PrintStream p, final String r, int i, double t, double v, double e) {
double d = t-v;
if(d<0) d=-d;
p.printf(r+" %02d ~ %f = %f +/- %f (%f)\n", i, t, v, d, e);
}
/**
*
* @param p the output
* @param r the name of the test
* @param i the name of the test bin
* @param t the target value
* @param v the actual value
* @param e the expected error
*/
protected static void printDeltas(PrintStream p, final String r, String i, double t, double v, double e) {
double d = t-v;
if(d<0) d=-d;
p.printf(r+" '%s' ~ %f = %f +/- %f (%f)\n", i, t, v, d, e);
}
/**
* Gets the order sequence of a series of values.
*
* @param vals the values
* @return an array containing the indices of each respective value in the sorted sequence of values
*/
protected static String sequence(long[] vals) {
if(vals==null) return null;
if(vals.length==0) return "";
long[] sort = Arrays.copyOf(vals,vals.length);
Arrays.sort(sort);
long min = sort[0]-1;
StringBuilder b = new StringBuilder();
for(long v: vals) {
int s=-1;
for(int p=0;p<sort.length;p++) if(sort[p]==v) { s=p; sort[p]=min; break; }
if(s<0) b.append(' ');
else if(s<10) b.append((char)('0'+s));
else b.append((char)('a'+s));
}
return b.toString();
}
}