using System;
using System.Collections.Generic;
namespace Amphora.RandomComparisons
{
public static class Bits
{
///
/// Returns how many bits of the word are set.
///
public static int PopCount(ulong value)
{
value -= (value >> 1) & 0x5555555555555555UL;
value = (value & 0x3333333333333333UL) + ((value >> 2) & 0x3333333333333333UL);
value = (value + (value >> 4)) & 0x0F0F0F0F0F0F0F0FUL;
return (int)((value * 0x0101010101010101UL) >> 56);
}
}
///
/// Checks how the first draws look when generators are started from consecutive seeds, the way a program starts
/// many of them from a counter.
///
public static class AdjacentSeeds
{
///
/// Runs the serial test over the first outputs of consecutive seeds, once across the several outputs of each
/// seed, and once across the first output of consecutive seeds.
///
public static List Run(Subject subject, Window window, ulong firstSeed, long seeds)
{
var returnMe = new List();
string where = " " + window.ToString().ToLowerInvariant() + " bits";
var insideEachSeed = new WordStream(new AdjacentSeedWords(subject.Make, firstSeed, wordsPerSeed: 8));
returnMe.Add(new NamedOutcome("first 8 draws of each seed," + where,
SerialTest.Run(insideEachSeed, window, dimensions: 8, bitsPerCoordinate: 2, tuples: seeds)));
var acrossSeeds = new WordStream(new AdjacentSeedWords(subject.Make, firstSeed, wordsPerSeed: 1));
returnMe.Add(new NamedOutcome("first draw of consecutive seeds," + where,
SerialTest.Run(acrossSeeds, window, dimensions: 3, bitsPerCoordinate: 6, tuples: seeds)));
return returnMe;
}
}
///
/// Starts a generator from a state with a single bit set and counts the bits set in each of its first draws. A
/// generator that mixes its state well has about half the bits set from the first draw. One that doesn't takes a
/// number of draws to get there.
///
public static class SparseStateRecovery
{
public const int Draws = 64;
public const double StandardDeviations = 5;
public static Outcome Run(Subject subject)
{
var totals = new double[Draws];
var buffer = new ulong[Draws];
for (int bit = 0; bit < subject.StateBits; bit++)
{
var state = new ulong[(subject.StateBits + 63) / 64];
state[bit / 64] = 1UL << (bit % 64);
subject.FromState(state).Fill(buffer, 0, Draws);
for (int draw = 0; draw < Draws; draw++)
{
totals[draw] += Bits.PopCount(buffer[draw]);
}
}
// A fair 64-bit word has 32 bits set give or take 4, so the average over the states is much tighter.
double tolerance = StandardDeviations * 4 / Math.Sqrt(subject.StateBits);
int settled = Draws;
while (settled > 0 && Math.Abs(totals[settled - 1] / subject.StateBits - 32) <= tolerance)
{
settled--;
}
return Outcome.Information("draws until the bits set look fair: " + settled + " (of " + Draws + ", from " +
subject.StateBits + " single-bit states)");
}
}
}