using System;
using System.Collections.Generic;
namespace Amphora.RandomComparisons
{
///
/// Counts how often each of the 64 bits is set.
///
public static class BitBalance
{
public static Outcome Run(WordStream stream, long words)
{
var ones = new long[64];
for (long i = 0; i < words; i++)
{
ulong word = stream.Next();
for (int bit = 0; bit < 64; bit++)
{
ones[bit] += (long)((word >> bit) & 1);
}
}
double standardDeviation = Math.Sqrt(words) / 2;
double worstP = 1;
int worstBit = 0;
for (int bit = 0; bit < 64; bit++)
{
double p = Stats.NormalTwoSided((ones[bit] - words / 2.0) / standardDeviation);
if (p < worstP)
{
worstP = p;
worstBit = bit;
}
}
// The worst of 64 bits is the one to report, so it is judged against 64 chances to look bad.
return new Outcome(Stats.WorstOf(worstP, 64), "worst is bit " + worstBit + ", set in " +
(100.0 * ones[worstBit] / words).ToString("0.000") + "% of " + words + " words");
}
}
///
/// Counts how often each combination of consecutive draws shows up. Planes and lattices in the output leave most
/// combinations empty, which shows as a chi-square that is far too big.
///
public static class SerialTest
{
///
/// The bits kept of each draw for tuples of 1 to 8 draws, chosen so the cells number from 2^14 to 2^18.
///
public static readonly int[] BitsPerCoordinate = { 0, 16, 8, 6, 4, 3, 3, 2, 2 };
public const int MaximumDimensions = 8;
public static Outcome Run(WordStream stream, Window window, int dimensions, int bitsPerCoordinate,
long tuples)
{
int cells = 1 << (dimensions * bitsPerCoordinate);
var counts = new int[cells];
for (long tuple = 0; tuple < tuples; tuple++)
{
int index = 0;
for (int dimension = 0; dimension < dimensions; dimension++)
{
index = (index << bitsPerCoordinate) | (int)Windows.Take(stream.Next(), window, bitsPerCoordinate);
}
counts[index]++;
}
double expected = (double)tuples / cells;
double statistic = 0;
for (int cell = 0; cell < cells; cell++)
{
double difference = counts[cell] - expected;
statistic += difference * difference;
}
statistic /= expected;
return new Outcome(Stats.ChiSquareTwoSided(statistic, cells - 1), dimensions + " draws of " +
bitsPerCoordinate + " bits, chi-square " + statistic.ToString("0.0") + " on " + (cells - 1) + " df");
}
///
/// Runs the test for every tuple size and returns the worst.
///
public static Outcome Series(WordStream stream, Window window, long tuples)
{
var outcomes = new List();
for (int dimensions = 1; dimensions <= MaximumDimensions; dimensions++)
{
outcomes.Add(Run(stream, window, dimensions, BitsPerCoordinate[dimensions], tuples));
}
Outcome worst = Outcome.Worst(outcomes);
return new Outcome(Stats.WorstOf(worst.P, MaximumDimensions), "worst of 1 to 8 draws: " + worst.Detail);
}
}
}