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)"); } } }