using System; using System.Collections.Generic; namespace Amphora.UnitTests { public class RandOfTests : UnitTestSharp.TestFixture { public static RandOf Seeded() { return new RandOf(new Sfc64(42)); } public class CtorTests : UnitTestSharp.TestFixture { public void TakesACopyOfTheGenerator() { var generator = new Sfc64(42); var rand = new RandOf(generator); rand.NextUInt64(); CheckEqual(new Sfc64(42).Counter, generator.Counter); } } public class GeneratorTests : UnitTestSharp.TestFixture { public void IsTheGeneratorAsItStands() { RandOf rand = Seeded(); var reference = new Sfc64(42); rand.NextUInt64(); reference.NextUInt64(); CheckEqual(reference.State0, rand.Generator.State0); CheckEqual(reference.State1, rand.Generator.State1); CheckEqual(reference.State2, rand.Generator.State2); CheckEqual(reference.Counter, rand.Generator.Counter); } public void IsACopy_SoDrawingFromItLeavesTheRandAlone() { RandOf rand = Seeded(); Sfc64 copy = rand.Generator; copy.NextUInt64(); CheckEqual(new Sfc64(42).Counter, rand.Generator.Counter); } } public class NextUInt32Tests : UnitTestSharp.TestFixture { public void Unbounded_IsTheGeneratorsNext32Bits() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextUInt32(), rand.NextUInt32()); CheckEqual(reference.NextUInt32(), rand.NextUInt32()); } public void Bounded_IsTheBoundedDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextUInt32(1000u), rand.NextUInt32(1000u)); CheckEqual(reference.NextUInt32(1000u), rand.NextUInt32(1000u)); } public void ZeroBound_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextUInt32(0u); } } public class NextUInt64Tests : UnitTestSharp.TestFixture { public void Unbounded_IsTheGeneratorsNext64Bits() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void Bounded_IsTheBoundedDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextUInt64(1000000000000UL), rand.NextUInt64(1000000000000UL)); CheckEqual(reference.NextUInt64(1000000000000UL), rand.NextUInt64(1000000000000UL)); } public void ZeroBound_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextUInt64(0UL); } } public class NextIntTests : UnitTestSharp.TestFixture { public void Range_IsTheRangeDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextInt(-5, 10), rand.NextInt(-5, 10)); CheckEqual(reference.NextInt(-5, 10), rand.NextInt(-5, 10)); } public void Maximum_IsTheDrawFromZeroOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextInt(1000), rand.NextInt(1000)); CheckEqual(reference.NextInt(1000), rand.NextInt(1000)); } public void EmptyRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextInt(3, 3); } public void EmptyMaximum_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextInt(0); } } public class NextLongTests : UnitTestSharp.TestFixture { public void Range_IsTheRangeDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextLong(-5L, 10000000000L), rand.NextLong(-5L, 10000000000L)); CheckEqual(reference.NextLong(-5L, 10000000000L), rand.NextLong(-5L, 10000000000L)); } public void Maximum_IsTheDrawFromZeroOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextLong(1000000000000L), rand.NextLong(1000000000000L)); CheckEqual(reference.NextLong(1000000000000L), rand.NextLong(1000000000000L)); } public void EmptyRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextLong(3L, 3L); } public void EmptyMaximum_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextLong(0L); } } public class NextDoubleTests : UnitTestSharp.TestFixture { public void Unit_IsTheGeneratorsUnitDraw() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextDouble(), rand.NextDouble()); CheckEqual(reference.NextDouble(), rand.NextDouble()); } public void Range_IsTheRangeDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextDouble(-3.5, 7.25), rand.NextDouble(-3.5, 7.25)); CheckEqual(reference.NextDouble(-3.5, 7.25), rand.NextDouble(-3.5, 7.25)); } public void EmptyRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextDouble(1.0, 1.0); } } public class NextFloatTests : UnitTestSharp.TestFixture { public void Unit_IsTheGeneratorsUnitDraw() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextFloat(), rand.NextFloat()); CheckEqual(reference.NextFloat(), rand.NextFloat()); } public void Range_IsTheRangeDrawOfTheGenerator() { var reference = new Sfc64(42); RandOf rand = Seeded(); CheckEqual(reference.NextFloat(-3.5f, 7.25f), rand.NextFloat(-3.5f, 7.25f)); CheckEqual(reference.NextFloat(-3.5f, 7.25f), rand.NextFloat(-3.5f, 7.25f)); } public void EmptyRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextFloat(1.0f, 1.0f); } } public class NextBoolTests : UnitTestSharp.TestFixture { public void IsTheTopBitOfTheNext32BitDraw() { var reference = new Sfc64(42); RandOf rand = Seeded(); for (int i = 0; i < 8; i++) { CheckEqual(reference.NextUInt32() >> 31 == 1, rand.NextBool()); } } public void ConsumesOne32BitDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextUInt32(); rand.NextBool(); CheckEqual(reference.NextUInt32(), rand.NextUInt32()); } } public class ShuffleTests : UnitTestSharp.TestFixture { public void SwapsEachItemWithOneNoLaterInTheList_FromTheLastDown() { var reference = new Sfc64(42); int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7 }; for (int last = expected.Length - 1; last > 0; last--) { int other = reference.NextInt(last + 1); (expected[last], expected[other]) = (expected[other], expected[last]); } int[] shuffled = { 0, 1, 2, 3, 4, 5, 6, 7 }; Seeded().Shuffle(shuffled); CheckEqual(expected, shuffled); } public void KeepsTheSameItems() { var items = new List { 5, 3, 9, 3, 1 }; Seeded().Shuffle(items); items.Sort(); CheckEqual(new[] { 1, 3, 3, 5, 9 }, items); } public void ListOfOneOrNone_DrawsNothing() { RandOf rand = Seeded(); var reference = new Sfc64(42); rand.Shuffle(new int[0]); rand.Shuffle(new[] { 7 }); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NullList_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().Shuffle(null); } public void AList_ShufflesInPlace() { var reference = new Sfc64(42); var items = new List { "a", "b", "c", "d" }; var expected = new[] { "a", "b", "c", "d" }; for (int last = expected.Length - 1; last > 0; last--) { int other = reference.NextInt(last + 1); (expected[last], expected[other]) = (expected[other], expected[last]); } Seeded().Shuffle(items); CheckEqual(expected, items); } } public class PickTests : UnitTestSharp.TestFixture { public void IsTheItemAtTheNextBoundedDraw() { var reference = new Sfc64(42); var items = new List { "a", "b", "c", "d", "e" }; RandOf rand = Seeded(); CheckEqual(items[reference.NextInt(5)], rand.Pick(items)); CheckEqual(items[reference.NextInt(5)], rand.Pick(items)); } public void Array_IsPickedFrom() { var reference = new Sfc64(42); int[] items = { 10, 20, 30 }; CheckEqual(items[reference.NextInt(3)], Seeded().Pick(items)); } public void NoItems_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().Pick(new int[0]); } public void NullList_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().Pick(null); } } public struct AllOnes : IRandomGenerator { public ulong NextUInt64() { return ulong.MaxValue; } public uint NextUInt32() { return uint.MaxValue; } } public class ChanceTests : UnitTestSharp.TestFixture { public void IsTrueWhenTheNextUnitDrawIsBelowTheChance() { var reference = new Sfc64(42); RandOf rand = Seeded(); for (int i = 0; i < 8; i++) { CheckEqual(reference.NextDouble() < 0.3, rand.Chance(0.3)); } } public void OneOrMore_IsAlwaysTrue() { RandOf rand = Seeded(); for (int i = 0; i < 50; i++) { CheckTrue(rand.Chance(1.0)); CheckTrue(rand.Chance(7.5)); CheckTrue(rand.Chance(double.PositiveInfinity)); } } public void ZeroOrLess_IsAlwaysFalse() { RandOf rand = Seeded(); for (int i = 0; i < 50; i++) { CheckFalse(rand.Chance(0.0)); CheckFalse(rand.Chance(-0.5)); CheckFalse(rand.Chance(double.NegativeInfinity)); } } public void ZeroChance_IsFalseEvenOnAZeroDraw() { var rand = new RandOf(new Zeros()); CheckFalse(rand.Chance(0.0)); } public void UsesOneDrawWhateverTheChance() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); reference.NextDouble(); reference.NextDouble(); rand.Chance(0.5); rand.Chance(3.0); rand.Chance(-3.0); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Chance(double.NaN); } } public struct Zeros : IRandomGenerator { public ulong NextUInt64() { return 0UL; } public uint NextUInt32() { return 0u; } } public class TryPickTests : UnitTestSharp.TestFixture { public void IsTheItemAtTheNextBoundedDraw() { var reference = new Sfc64(42); var items = new List { "a", "b", "c", "d", "e" }; string picked; bool found = Seeded().TryPick(items, out picked); CheckTrue(found); CheckEqual(items[reference.NextInt(5)], picked); } public void NoItems_IsFalseWithTheDefaultAndNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); string picked; bool found = rand.TryPick(new string[0], out picked); CheckFalse(found); CheckNull(picked); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NullList_Throws() { CheckThrow(typeof(ArgumentNullException)); int picked; Seeded().TryPick(null, out picked); } } public class PickWeightedIndexTests : UnitTestSharp.TestFixture { public void IsTheFirstPositionWhoseRunningWeightPassesTheScaledDraw() { var reference = new Sfc64(42); var weights = new List { 1.0, 2.0, 3.0, 4.0 }; RandOf rand = Seeded(); for (int i = 0; i < 12; i++) { double target = reference.NextDouble() * 10.0; int expected = target < 1.0 ? 0 : target < 3.0 ? 1 : target < 6.0 ? 2 : 3; CheckEqual(expected, rand.PickWeightedIndex(weights)); } } public void ZeroWeights_AreNeverPicked() { RandOf rand = Seeded(); var weights = new List { 0.0, 1.0, 0.0, 3.0, 0.0 }; for (int i = 0; i < 100; i++) { int index = rand.PickWeightedIndex(weights); CheckTrue(index == 1 || index == 3); } } public void ADrawThatRoundsUpToTheTotal_PicksTheLastPositiveWeight() { var rand = new RandOf(new AllOnes()); var weights = new List { 0.0, double.Epsilon, 0.0 }; CheckEqual(1, rand.PickWeightedIndex(weights)); } public void WeightsAddingToZero_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().PickWeightedIndex(new[] { 0.0, 0.0 }); } public void NoWeights_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().PickWeightedIndex(new double[0]); } public void NullWeights_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().PickWeightedIndex(null); } public void NegativeWeight_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().PickWeightedIndex(new[] { 1.0, -0.5 }); } public void NotANumberWeight_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().PickWeightedIndex(new[] { 1.0, double.NaN }); } public void InfiniteWeight_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().PickWeightedIndex(new[] { 1.0, double.PositiveInfinity }); } public void WeightsTooBigToAdd_Throw() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().PickWeightedIndex(new[] { double.MaxValue, double.MaxValue }); } } public class TryPickWeightedIndexTests : UnitTestSharp.TestFixture { public void PositiveWeights_GiveTheSameIndexAsPickWeightedIndex() { var weights = new List { 1.0, 2.0, 3.0 }; int index; bool found = Seeded().TryPickWeightedIndex(weights, out index); CheckTrue(found); CheckEqual(Seeded().PickWeightedIndex(weights), index); } public void WeightsAddingToZero_IsFalseWithNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); int index; bool found = rand.TryPickWeightedIndex(new[] { 0.0, 0.0 }, out index); CheckFalse(found); CheckEqual(-1, index); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NoWeights_IsFalse() { int index; CheckFalse(Seeded().TryPickWeightedIndex(new double[0], out index)); } } public class PickWeightedTests : UnitTestSharp.TestFixture { public void IsTheItemAtThePickedWeightedIndex() { var items = new List { "a", "b", "c" }; var weights = new List { 1.0, 5.0, 2.0 }; CheckEqual(items[Seeded().PickWeightedIndex(weights)], Seeded().PickWeighted(items, weights)); } public void WeightsAddingToZero_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().PickWeighted(new[] { "a", "b" }, new[] { 0.0, 0.0 }); } public void FewerWeightsThanItems_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().PickWeighted(new[] { "a", "b", "c" }, new[] { 1.0, 1.0 }); } public void MoreWeightsThanItems_Throws() { CheckThrow(typeof(ArgumentException)); Seeded().PickWeighted(new[] { "a" }, new[] { 1.0, 1.0 }); } public void NullItems_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().PickWeighted(null, new[] { 1.0 }); } public void NullWeights_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().PickWeighted(new[] { "a" }, null); } } public class TryPickWeightedTests : UnitTestSharp.TestFixture { public void IsTheItemAtThePickedWeightedIndex() { var items = new List { "a", "b", "c" }; var weights = new List { 1.0, 5.0, 2.0 }; string picked; bool found = Seeded().TryPickWeighted(items, weights, out picked); CheckTrue(found); CheckEqual(items[Seeded().PickWeightedIndex(weights)], picked); } public void WeightsAddingToZero_IsFalseWithTheDefault() { string picked; bool found = Seeded().TryPickWeighted(new[] { "a", "b" }, new[] { 0.0, 0.0 }, out picked); CheckFalse(found); CheckNull(picked); } public void NoItems_IsFalse() { string picked; CheckFalse(Seeded().TryPickWeighted(new string[0], new double[0], out picked)); } } public class SampleTests : UnitTestSharp.TestFixture { public void TakesEachItemWithTheChanceAStillNeededSlotFallsOnIt() { var reference = new Sfc64(42); var expected = new List(); int needed = 4; for (int i = 0; needed > 0; i++) { if (reference.NextInt(10 - i) < needed) { expected.Add(i); needed--; } } var sampled = new List(); Seeded().Sample(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 4, sampled); CheckEqual(expected, sampled); } public void GivesDistinctItemsInTheirOriginalOrder() { var rand = Seeded(); var items = new[] { 10, 20, 30, 40, 50, 60, 70, 80 }; for (int round = 0; round < 20; round++) { var sampled = new List(); rand.Sample(items, 3, sampled); CheckEqual(3, sampled.Count); CheckTrue(sampled[0] < sampled[1] && sampled[1] < sampled[2]); } } public void StopsDrawingOnceItHasEnough() { RandOf rand = Seeded(); var reference = new Sfc64(42); int drawn = 0; while (reference.NextInt(100 - drawn) >= 1) { drawn++; } rand.Sample(new int[100], 1, new List()); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void CountOfAll_TakesEveryItemInOrder() { var sampled = new List(); Seeded().Sample(new[] { 3, 1, 2 }, 3, sampled); CheckEqual(new[] { 3, 1, 2 }, sampled); } public void CountOfNone_AddsNothingAndUsesNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); var sampled = new List(); rand.Sample(new[] { 1, 2, 3 }, 0, sampled); CheckEqual(0, sampled.Count); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void AddsAfterWhatTheDestinationAlreadyHolds() { var sampled = new List { 99 }; Seeded().Sample(new[] { 1, 2, 3 }, 3, sampled); CheckEqual(new[] { 99, 1, 2, 3 }, sampled); } public void NegativeCount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Sample(new[] { 1, 2, 3 }, -1, new List()); } public void CountOverTheItems_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Sample(new[] { 1, 2, 3 }, 4, new List()); } public void NullItems_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().Sample(null, 0, new List()); } public void NullDestination_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().Sample(new[] { 1, 2, 3 }, 1, null); } public void DestinationThatIsTheItems_Throws() { CheckThrow(typeof(ArgumentException)); var items = new List { 1, 2, 3 }; Seeded().Sample(items, 1, items); } } public static int ReferenceRoll(int dice, int sides, int keep, bool highest) { var reference = new Sfc64(42); var rolled = new List(); for (int die = 0; die < dice; die++) { rolled.Add(reference.NextInt(sides) + 1); } rolled.Sort(); if (highest) { rolled.Reverse(); } int returnMe = 0; for (int i = 0; i < keep; i++) { returnMe += rolled[i]; } return returnMe; } public class RollTests : UnitTestSharp.TestFixture { public void IsTheTotalOfOneBoundedDrawPerDie() { var reference = new Sfc64(42); int expected = 0; for (int die = 0; die < 5; die++) { expected += reference.NextInt(6) + 1; } CheckEqual(expected, Seeded().Roll(5, 6)); } public void ZeroDice_IsZeroWithNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); CheckEqual(0, rand.Roll(0, 6)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void OneSidedDice_AreTheirCount() { CheckEqual(7, Seeded().Roll(7, 1)); } public void TheLargestDie_StaysInItsRange() { int rolled = Seeded().Roll(1, int.MaxValue); CheckTrue(rolled >= 1); } public void NegativeDice_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Roll(-1, 6); } public void NoSides_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Roll(2, 0); } public void TotalThatCouldPassAnInt_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Roll(2, int.MaxValue); } } public class RollKeepHighestTests : UnitTestSharp.TestFixture { public void FourD6DroppingTheLowest_IsTheSortedReference() { CheckEqual(ReferenceRoll(4, 6, 3, highest: true), Seeded().RollKeepHighest(4, 6, 3)); } public void ManyTies_IsTheSortedReference() { CheckEqual(ReferenceRoll(10, 3, 7, highest: true), Seeded().RollKeepHighest(10, 3, 7)); } public void KeepingOne_IsTheSortedReference() { CheckEqual(ReferenceRoll(2, 20, 1, highest: true), Seeded().RollKeepHighest(2, 20, 1)); } public void KeepingAll_IsTheTotal() { CheckEqual(Seeded().Roll(6, 8), Seeded().RollKeepHighest(6, 8, 6)); } public void KeepingNone_IsZero() { CheckEqual(0, Seeded().RollKeepHighest(6, 8, 0)); } public void FewDiceOfFewFaces_IsTheSortedReference() { CheckEqual(ReferenceRoll(100, 6, 50, highest: true), Seeded().RollKeepHighest(100, 6, 50)); } public void ManyFacesButFewerThanTheDice_IsTheSortedReference() { CheckEqual(ReferenceRoll(1000, 1000, 500, highest: true), Seeded().RollKeepHighest(1000, 1000, 500)); } public void ManyFacesAndAManyKept_IsTheSortedReference() { CheckEqual(ReferenceRoll(300, 100000, 150, highest: true), Seeded().RollKeepHighest(300, 100000, 150)); } public void LargeSides_IsTheSortedReference() { CheckEqual( ReferenceRoll(3, int.MaxValue / 3, 2, highest: true), Seeded().RollKeepHighest(3, int.MaxValue / 3, 2)); } public void UsesOneDrawPerDieRolled() { RandOf rand = Seeded(); var reference = new Sfc64(42); for (int die = 0; die < 4; die++) { reference.NextInt(6); } rand.RollKeepHighest(4, 6, 1); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void EveryWayOfKeeping_UsesOneDrawPerDieRolled() { int[][] hands = { new[] { 4, 6, 1 }, new[] { 100, 6, 50 }, new[] { 1000, 1000, 500 }, new[] { 300, 100000, 150 }, }; foreach (int[] hand in hands) { RandOf rand = Seeded(); var reference = new Sfc64(42); for (int die = 0; die < hand[0]; die++) { reference.NextInt(hand[1]); } rand.RollKeepHighest(hand[0], hand[1], hand[2]); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } } public void NegativeKeep_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().RollKeepHighest(4, 6, -1); } public void KeepOverTheDice_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().RollKeepHighest(4, 6, 5); } public void NoSides_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().RollKeepHighest(4, 0, 2); } } public class RollKeepLowestTests : UnitTestSharp.TestFixture { public void FourD6KeepingThree_IsTheSortedReference() { CheckEqual(ReferenceRoll(4, 6, 3, highest: false), Seeded().RollKeepLowest(4, 6, 3)); } public void ManyTies_IsTheSortedReference() { CheckEqual(ReferenceRoll(10, 3, 7, highest: false), Seeded().RollKeepLowest(10, 3, 7)); } public void KeepingOne_IsTheSortedReference() { CheckEqual(ReferenceRoll(2, 20, 1, highest: false), Seeded().RollKeepLowest(2, 20, 1)); } public void KeepingAll_IsTheTotal() { CheckEqual(Seeded().Roll(6, 8), Seeded().RollKeepLowest(6, 8, 6)); } public void KeepingNone_IsZero() { CheckEqual(0, Seeded().RollKeepLowest(6, 8, 0)); } public void FewDiceOfFewFaces_IsTheSortedReference() { CheckEqual(ReferenceRoll(100, 6, 50, highest: false), Seeded().RollKeepLowest(100, 6, 50)); } public void ManyFacesButFewerThanTheDice_IsTheSortedReference() { CheckEqual(ReferenceRoll(1000, 1000, 500, highest: false), Seeded().RollKeepLowest(1000, 1000, 500)); } public void ManyFacesAndAManyKept_IsTheSortedReference() { CheckEqual(ReferenceRoll(300, 100000, 150, highest: false), Seeded().RollKeepLowest(300, 100000, 150)); } public void KeepingAllButOne_IsTheSortedReference() { CheckEqual(ReferenceRoll(5, 12, 4, highest: false), Seeded().RollKeepLowest(5, 12, 4)); } public void NegativeKeep_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().RollKeepLowest(4, 6, -1); } public void KeepOverTheDice_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().RollKeepLowest(4, 6, 5); } } public struct Scripted : IRandomGenerator { private readonly ulong[] words; private int next; public Scripted(params ulong[] words) { this.words = words; next = 0; } public ulong NextUInt64() { return words[next++]; } public uint NextUInt32() { return (uint)(NextUInt64() >> 32); } // The word that makes NextDouble return this unit, which must be a multiple of 2^-53. public static ulong Word(double unit) { return (ulong)(unit * 9007199254740992.0) << 11; } public static Scripted OfUnits(params double[] units) { var words = new ulong[units.Length]; for (int i = 0; i < units.Length; i++) { words[i] = Word(units[i]); } return new Scripted(words); } } public class NextEventTests : UnitTestSharp.TestFixture { public void IsMinusTheMeanTimesTheLogOfOneMinusTheDraw() { double[] units = { 0.25, 0.5, 0.75, 0.25, 0.5, 0.75 }; double[] means = { 10.0, 10.0, 10.0, 0.3, 0.3, 0.3 }; double[] expected = { 2.8768207245178092, 6.931471805599453, 13.862943611198906, 0.08630462173553428, 0.2079441541679836, 0.4158883083359672, }; for (int i = 0; i < units.Length; i++) { var rand = new RandOf(Scripted.OfUnits(units[i])); CheckLess(PortableMathTests.UlpsApart(expected[i], rand.NextEvent(means[i])), 3L); } } public void LargestDraw_IsAboutThirtySevenMeans() { var rand = new RandOf(new Scripted(ulong.MaxValue)); CheckLess(PortableMathTests.UlpsApart(367.36800569677104, rand.NextEvent(10.0)), 3L); } public void DrawOfZero_IsPositiveZero() { var rand = new RandOf(Scripted.OfUnits(0.0)); CheckEqual(0L, BitConverter.DoubleToInt64Bits(rand.NextEvent(10.0))); } public void MeanOfZero_IsZero() { CheckEqual(0.0, Seeded().NextEvent(0.0)); } public void MeanOfInfinity_IsInfinity() { CheckEqual(double.PositiveInfinity, Seeded().NextEvent(double.PositiveInfinity)); } public void UsesOneDrawWhateverTheMean() { foreach (double mean in new[] { 0.0, 5.0, double.PositiveInfinity }) { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.NextEvent(mean); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEvent(-1.0); } public void NotANumberMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEvent(double.NaN); } } public class NextEventWithinTests : UnitTestSharp.TestFixture { public void IsTheWaitCutOffAtTheSpan() { double[] means = { 10.0, 10.0, 1.0, 1000000.0, 0.01, 10.0, 2.0 }; double[] spans = { 10.0, 10.0, 100.0, 1.0, 1.0, 10.0, 5.0 }; double[] units = { 0.5, 0.25, 0.5, 0.5, 0.5, 0.75, 0.9375 }; double[] expected = { 3.7988549304172246, 1.7201106075713024, 0.6931471805599453, 0.499999875, 0.006931471805599453, 6.426259804912115, 3.940031121051886, }; for (int i = 0; i < means.Length; i++) { var rand = new RandOf(Scripted.OfUnits(units[i])); CheckLess(PortableMathTests.UlpsApart(expected[i], rand.NextEventWithin(means[i], spans[i])), 4L); } } public void InfiniteSpan_IsTheWaitForTheNextEvent() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckLess( PortableMathTests.UlpsApart(6.931471805599453, rand.NextEventWithin(10.0, double.PositiveInfinity)), 3L); } public void InfiniteMean_IsEvenlySpreadOverTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.25)); CheckEqual(2.0, rand.NextEventWithin(double.PositiveInfinity, 8.0)); } public void MeanOfZero_IsZero() { CheckEqual(0.0, Seeded().NextEventWithin(0.0, 5.0)); } public void LargestDraw_StaysBelowTheSpan() { double[] means = { 0.01, 1.0, 10.0, 1000000.0, double.PositiveInfinity, 1e-300, 1e300 }; foreach (double mean in means) { var rand = new RandOf(new Scripted(ulong.MaxValue)); double time = rand.NextEventWithin(mean, 10.0); CheckTrue(time >= 0.0 && time < 10.0); } } public void UsesOneDrawWhateverTheMean() { foreach (double mean in new[] { 0.0, 5.0, double.PositiveInfinity }) { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.NextEventWithin(mean, 3.0); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } } public void InfiniteSpanAndMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(double.PositiveInfinity, double.PositiveInfinity); } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(-1.0, 5.0); } public void NotANumberMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(double.NaN, 5.0); } public void ZeroSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(5.0, 0.0); } public void NegativeSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(5.0, -1.0); } public void NotANumberSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(5.0, double.NaN); } } public class NextEventsWithinTests : UnitTestSharp.TestFixture { public void AreTheRunningTotalsOfTheGapsScaledToTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.75, 0.5)); var times = new List(); rand.NextEventsWithin(8.0, 2, times); CheckEqual(2, times.Count); CheckLess(Math.Abs(times[0] - 2.0), 1e-14); CheckLess(Math.Abs(times[1] - 6.0), 1e-14); } public void OneEvent_IsWhereTheFirstGapEndsInTwo() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5)); var times = new List(); rand.NextEventsWithin(8.0, 1, times); CheckLess(Math.Abs(times[0] - 4.0), 1e-14); } public void EqualGaps_AreEvenlySpaced() { var rand = new RandOf( new Scripted(ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue)); var times = new List(); rand.NextEventsWithin(4.0, 3, times); CheckLess(Math.Abs(times[0] - 1.0), 1e-12); CheckLess(Math.Abs(times[1] - 2.0), 1e-12); CheckLess(Math.Abs(times[2] - 3.0), 1e-12); } public void AllGapsZero_PutEveryEventAtTheStart() { var rand = new RandOf(new Zeros()); var times = new List(); rand.NextEventsWithin(4.0, 3, times); CheckEqual(new[] { 0.0, 0.0, 0.0 }, times); } public void ALastGapOfZero_StillLeavesTheLastEventBeforeTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5, 0.0)); var times = new List(); rand.NextEventsWithin(8.0, 2, times); CheckTrue(times[1] < 8.0 && times[1] > 7.999999999); } public void ManyEvents_AreInOrderAndInsideTheSpan() { var times = new List(); Seeded().NextEventsWithin(100.0, 200, times); CheckEqual(200, times.Count); for (int i = 0; i < times.Count; i++) { CheckTrue(times[i] >= 0.0 && times[i] < 100.0); if (i > 0) { CheckTrue(times[i] >= times[i - 1]); } } } public void AddsAfterWhatTheDestinationAlreadyHolds() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.75, 0.5)); var times = new List { 99.0 }; rand.NextEventsWithin(8.0, 2, times); CheckEqual(3, times.Count); CheckEqual(99.0, times[0]); CheckLess(Math.Abs(times[1] - 2.0), 1e-14); } public void UsesOneDrawMoreThanTheCount() { RandOf rand = Seeded(); var reference = new Sfc64(42); for (int i = 0; i < 6; i++) { reference.NextDouble(); } rand.NextEventsWithin(10.0, 5, new List()); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void CountOfNone_AddsNothingAndUsesNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); var times = new List(); rand.NextEventsWithin(10.0, 0, times); CheckEqual(0, times.Count); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NullDestination_Throws() { CheckThrow(typeof(ArgumentNullException)); Seeded().NextEventsWithin(10.0, 2, null); } public void NegativeCount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventsWithin(10.0, -1, new List()); } public void ZeroSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventsWithin(0.0, 2, new List()); } public void NegativeSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventsWithin(-1.0, 2, new List()); } public void InfiniteSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventsWithin(double.PositiveInfinity, 2, new List()); } public void NotANumberSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventsWithin(double.NaN, 2, new List()); } } public class NextEventDurationTests : UnitTestSharp.TestFixture { public void IsTheMeanTimesTheLogOfOneMinusTheDraw() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckEqual(Duration.FromTicks(6931472L), rand.NextEvent(Duration.FromSeconds(1.0))); } public void KeepsTicksThatAMillisecondRoundingWouldLose() { var rand = new RandOf(Scripted.OfUnits(0.0001)); CheckEqual(Duration.FromTicks(1L), rand.NextEvent(Duration.FromTicks(10000L))); } public void TimeSpanMean_IsAcceptedAsADuration() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckEqual(Duration.FromTicks(6931472L), rand.NextEvent(TimeSpan.FromSeconds(1.0))); } public void MeanOfZero_IsZero() { CheckEqual(Duration.Zero, Seeded().NextEvent(Duration.Zero)); } public void AWaitTooLongForADuration_IsMaxValueAndNotInfinite() { var rand = new RandOf(new Scripted(ulong.MaxValue)); Duration wait = rand.NextEvent(Duration.MaxValue); CheckEqual(Duration.MaxValue, wait); CheckTrue(wait.IsFinite); } public void ADrawUnderTheLongestDuration_IsNotCut() { var rand = new RandOf(Scripted.OfUnits(0.5)); double expected = 0.6931471805599453 * (double)Duration.MaxValue.Ticks; CheckLess(Math.Abs((double)rand.NextEvent(Duration.MaxValue).Ticks / expected - 1.0), 1e-9); } public void InfiniteMean_IsForeverAndUsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); CheckEqual(Duration.Forever, rand.NextEvent(Duration.Forever)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void UsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.NextEvent(Duration.FromDays(12.0)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEvent(Duration.FromSeconds(-1.0)); } public void NegativeForeverMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEvent(-Duration.Forever); } } public class NextEventWithinDurationTests : UnitTestSharp.TestFixture { public void IsTheWaitCutOffAtTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.5)); Duration time = rand.NextEventWithin(Duration.FromSeconds(10.0), Duration.FromSeconds(10.0)); CheckEqual(Duration.FromTicks(37988549L), time); } public void LargestDraw_StaysBelowTheSpan() { foreach (long spanTicks in new[] { 1L, 2L, 10000000L, 4611686018427387903L, long.MaxValue }) { var rand = new RandOf(new Scripted(ulong.MaxValue)); Duration span = Duration.FromTicks(spanTicks); Duration time = rand.NextEventWithin(span, span); CheckTrue(time >= Duration.Zero && time < span); } } public void ASpanOfOneTick_IsZero() { var rand = new RandOf(Scripted.OfUnits(0.9)); CheckEqual(Duration.Zero, rand.NextEventWithin(Duration.FromSeconds(1.0), Duration.FromTicks(1L))); } public void MeanOfZero_IsZero() { CheckEqual(Duration.Zero, Seeded().NextEventWithin(Duration.Zero, Duration.FromSeconds(5.0))); } public void InfiniteSpan_IsTheWaitForTheNextEvent() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckEqual(Duration.FromTicks(69314718L), rand.NextEventWithin(Duration.FromSeconds(10.0), Duration.Forever)); } public void InfiniteSpanAndAWaitTooLongForADuration_IsMaxValue() { var rand = new RandOf(new Scripted(ulong.MaxValue)); CheckEqual(Duration.MaxValue, rand.NextEventWithin(Duration.MaxValue, Duration.Forever)); } public void InfiniteMean_IsEvenlySpreadOverTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.25)); CheckEqual(Duration.FromSeconds(2.0), rand.NextEventWithin(Duration.Forever, Duration.FromSeconds(8.0))); } public void InfiniteMeanAndSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(Duration.Forever, Duration.Forever); } public void TimeSpansAreAcceptedAsDurations() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckEqual(Duration.FromTicks(37988549L), rand.NextEventWithin(TimeSpan.FromSeconds(10.0), TimeSpan.FromSeconds(10.0))); } public void UsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.NextEventWithin(Duration.FromDays(12.0), Duration.FromDays(3.0)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(Duration.FromSeconds(-1.0), Duration.FromSeconds(5.0)); } public void SpanOfZero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(Duration.FromSeconds(1.0), Duration.Zero); } public void NegativeSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().NextEventWithin(Duration.FromSeconds(1.0), -Duration.Forever); } } public class EventCountDurationTests : UnitTestSharp.TestFixture { public void CountsEventsInTheSpan() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5, 0.5)); CheckEqual(2, rand.EventCount(Duration.FromDays(1.0), Duration.FromDays(2.0))); } public void TimeSpansAreAcceptedAsDurations() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5, 0.5)); CheckEqual(2, rand.EventCount(TimeSpan.FromDays(1.0), TimeSpan.FromDays(2.0))); } public void ZeroSpan_IsZero() { CheckEqual(0, Seeded().EventCount(Duration.FromDays(1.0), Duration.Zero)); } public void InfiniteMean_IsZero() { CheckEqual(0, Seeded().EventCount(Duration.Forever, Duration.FromDays(30.0))); CheckEqual(0, Seeded().EventCount(Duration.Forever, Duration.Forever)); } public void InfiniteSpanWithAFiniteMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(Duration.FromDays(1.0), Duration.Forever); } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(Duration.FromSeconds(-1.0), Duration.FromDays(1.0)); } public void NegativeSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(Duration.FromDays(1.0), Duration.FromDays(-1.0)); } public void MoreThanAMillionEventsExpected_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(Duration.FromSeconds(1.0), Duration.FromDays(30.0)); } } public class KumaraswamyTests : UnitTestSharp.TestFixture { public void IsTheCurvesQuantileAtTheDraw() { var rand = new RandOf(Scripted.OfUnits(0.5)); CheckLess(PortableMathTests.UlpsApart(0.4542020189474065, rand.Kumaraswamy(2.0, 3.0)), 9L); } public void DrawOfZero_IsZero() { var rand = new RandOf(Scripted.OfUnits(0.0)); CheckEqual(0.0, rand.Kumaraswamy(2.0, 3.0)); } public void LargestDraw_StaysBelowOne() { foreach (double b in new[] { 0.001, 1.0, 3.0 }) { var rand = new RandOf(new Scripted(ulong.MaxValue)); double value = rand.Kumaraswamy(2.0, b); CheckTrue(value >= 0.0 && value < 1.0); } } public void UsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.Kumaraswamy(2.0, 3.0); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void BadShape_UsesNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); try { rand.Kumaraswamy(0.0, 3.0); } catch (ArgumentOutOfRangeException) { } CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void ShapeOfZeroOrLess_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Kumaraswamy(2.0, 0.0); } public void InfiniteShape_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Kumaraswamy(double.PositiveInfinity, 1.0); } public void NotANumberShape_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Kumaraswamy(1.0, double.NaN); } } public class BellTests : UnitTestSharp.TestFixture { public void ReferenceValues_AreCloseToTheirRelativeTrillionth() { double[] mins = { 2.0, -5.0, 0.0, 0.0, 0.0, 100.0, -1000000.0, 0.0 }; double[] maxes = { 10.0, 5.0, 1.0, 1.0, 1.0, 200.0, 1000000.0, 1.0 }; double[] modes = { 4.0, 0.0, 0.0, 1.0, 0.5, 130.0, 0.0, 0.3 }; double[] tightnesses = { 4.0, 4.0, 4.0, 4.0, 0.0, 10.0, 100.0, 1000.0 }; double[] units = { 0.5, 0.25, 0.5, 0.5, 0.5, 0.75, 0.5, 0.5 }; double[] expected = { 4.238719048872505, -1.328282542382784, 0.12944943670387585, 0.8705505632961241, 0.5, 134.89132527549285, -6775.185279962486, 0.2996382397575898, }; for (int i = 0; i < mins.Length; i++) { var rand = new RandOf(Scripted.OfUnits(units[i])); double actual = rand.Bell(mins[i], maxes[i], modes[i], tightnesses[i]); CheckLess(PortableMathTests.RelativeError(expected[i], actual), 1e-12); } } public void ModeAtTheStartOrEnd_IsAllowed() { var start = new RandOf(Scripted.OfUnits(0.5)); var end = new RandOf(Scripted.OfUnits(0.5)); CheckTrue(start.Bell(0.0, 1.0, 0.0, 4.0) < 0.5); CheckTrue(end.Bell(0.0, 1.0, 1.0, 4.0) > 0.5); } public void LargestDraw_StaysBelowMax_WhenRoundingReachesIt() { var positive = new RandOf(new Scripted(ulong.MaxValue)); var negative = new RandOf(new Scripted(ulong.MaxValue)); double high = positive.Bell(1e16, 1e16 + 2.0, 1e16 + 1.0, 0.0); double low = negative.Bell(-1e16 - 2.0, -1e16, -1e16 - 1.0, 0.0); CheckTrue(high < 1e16 + 2.0); CheckTrue(low < -1e16); } public void DrawOfZero_IsMin() { var rand = new RandOf(Scripted.OfUnits(0.0)); CheckEqual(2.0, rand.Bell(2.0, 10.0, 4.0, 4.0)); } public void UsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); rand.Bell(0.0, 10.0, 5.0, 4.0); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void BadArguments_UseNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); try { rand.Bell(0.0, 10.0, 5.0, -1.0); } catch (ArgumentOutOfRangeException) { } CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void EmptyRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(1.0, 1.0, 1.0, 4.0); } public void ReversedRange_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(2.0, 1.0, 1.5, 4.0); } public void InfiniteEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, double.PositiveInfinity, 1.0, 4.0); } public void NotANumberEnd_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(double.NaN, 1.0, 0.5, 4.0); } public void RangeTooWideForADouble_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(-1e308, 1e308, 0.0, 4.0); } public void ModeBelowMin_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, -1.0, 4.0); } public void ModeAboveMax_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, 11.0, 4.0); } public void NotANumberMode_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, double.NaN, 4.0); } public void NegativeTightness_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, 5.0, -1.0); } public void TightnessPastTheMost_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, 5.0, 1001.0); } public void NotANumberTightness_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().Bell(0.0, 10.0, 5.0, double.NaN); } } public class InCircleTests : UnitTestSharp.TestFixture { public void IsTheRadiusTimesTheSquareRootOfTheFirstDrawAtTheSecondDrawsAngle() { double[] radii = { 1.0, 2.0, 3.0, 1.0, 5.0, 10.0 }; double[] firstDraws = { 0.25, 0.5, 0.75, 0.0, 0.9375, 0.0625 }; double[] secondDraws = { 0.125, 0.0, 0.25, 0.3, 0.6, 0.9375 }; double[] expectedX = { 0.3535533905932738, 1.4142135623730951, 0.0, 0.0, -3.9166366825161885, 2.309698831278217, }; double[] expectedY = { 0.3535533905932738, 0.0, 2.598076211353316, 0.0, -2.845603116593842, -0.9567085809127245, }; for (int i = 0; i < radii.Length; i++) { var rand = new RandOf(Scripted.OfUnits(firstDraws[i], secondDraws[i])); double x; double y; rand.InCircle(radii[i], out x, out y); CheckLess(Math.Abs(x - expectedX[i]), 1e-14); CheckLess(Math.Abs(y - expectedY[i]), 1e-14); } } public void LargestDraws_StayInsideTheDisc() { var rand = new RandOf(new Scripted(ulong.MaxValue, 1UL << 62)); double x; double y; rand.InCircle(3.0, out x, out y); CheckTrue(x * x + y * y <= 9.0 * (1.0 + 1e-14)); } public void RadiusOfZero_IsTheOrigin() { double x; double y; Seeded().InCircle(0.0, out x, out y); CheckEqual(0.0, x); CheckEqual(0.0, y); } public void UsesTwoDrawsWhateverTheRadius() { foreach (double radius in new[] { 0.0, 3.0 }) { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); reference.NextDouble(); double x; double y; rand.InCircle(radius, out x, out y); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } } public void BadRadius_UsesNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); double x; double y; try { rand.InCircle(-1.0, out x, out y); } catch (ArgumentOutOfRangeException) { } CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NegativeRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InCircle(-1.0, out x, out y); } public void InfiniteRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InCircle(double.PositiveInfinity, out x, out y); } public void NotANumberRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InCircle(double.NaN, out x, out y); } } public class OnCircleTests : UnitTestSharp.TestFixture { public void IsTheRadiusAlongTheAngleOfTheDraw() { double[] radii = { 2.0, 1.0, 3.0, 1.0 }; double[] draws = { 0.125, 0.5, 0.25, 0.8 }; double[] expectedX = { 1.4142135623730951, -1.0, 0.0, 0.30901699437494745 }; double[] expectedY = { 1.4142135623730951, 0.0, 3.0, -0.9510565162951535 }; for (int i = 0; i < radii.Length; i++) { var rand = new RandOf(Scripted.OfUnits(draws[i])); double x; double y; rand.OnCircle(radii[i], out x, out y); CheckLess(Math.Abs(x - expectedX[i]), 1e-14); CheckLess(Math.Abs(y - expectedY[i]), 1e-14); } } public void IsAtTheRadius() { var rand = new RandOf(Scripted.OfUnits(0.3)); double x; double y; rand.OnCircle(7.0, out x, out y); CheckLess(Math.Abs(Math.Sqrt(x * x + y * y) - 7.0), 1e-14); } public void UsesOneDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); double x; double y; rand.OnCircle(3.0, out x, out y); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void NegativeRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().OnCircle(-1.0, out x, out y); } public void InfiniteRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().OnCircle(double.PositiveInfinity, out x, out y); } public void NotANumberRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().OnCircle(double.NaN, out x, out y); } } public class InRingTests : UnitTestSharp.TestFixture { public void IsTheOuterRadiusTimesTheSquareRootOfTheRatioSquaredPlusTheFirstDrawOfTheRest() { double[] inners = { 1.0, 1.0, 0.0, 3.0 }; double[] outers = { 2.0, 2.0, 2.0, 4.0 }; double[] firstDraws = { 0.5, 0.25, 0.5, 0.75 }; double[] secondDraws = { 0.0, 0.125, 0.25, 0.6 }; double[] expectedX = { 1.5811388300841898, 0.9354143466934853, 0.0, -3.0539721814256104 }; double[] expectedY = { 0.0, 0.9354143466934853, 1.4142135623730951, -2.2188406691510094 }; for (int i = 0; i < inners.Length; i++) { var rand = new RandOf(Scripted.OfUnits(firstDraws[i], secondDraws[i])); double x; double y; rand.InRing(inners[i], outers[i], out x, out y); CheckLess(Math.Abs(x - expectedX[i]), 1e-14); CheckLess(Math.Abs(y - expectedY[i]), 1e-14); } } public void DrawOfZero_IsOnTheInnerEdge() { var rand = new RandOf(Scripted.OfUnits(0.0, 0.0)); double x; double y; rand.InRing(1.0, 2.0, out x, out y); CheckEqual(1.0, x); CheckEqual(0.0, y); } public void LargestDraw_StaysWithinTheOuterEdge() { var rand = new RandOf(new Scripted(ulong.MaxValue, 1UL << 62)); double x; double y; rand.InRing(1.0, 2.0, out x, out y); CheckTrue(x * x + y * y <= 4.0 * (1.0 + 1e-14)); CheckTrue(x * x + y * y >= 1.0); } public void InnerEqualToOuter_IsTheCircle() { var rand = new RandOf(Scripted.OfUnits(0.7, 0.125)); double x; double y; rand.InRing(2.0, 2.0, out x, out y); CheckLess(Math.Abs(x - 1.4142135623730951), 1e-14); CheckLess(Math.Abs(y - 1.4142135623730951), 1e-14); } public void BothRadiiZero_IsTheOriginAndStillUsesTwoDraws() { RandOf rand = Seeded(); var reference = new Sfc64(42); reference.NextDouble(); reference.NextDouble(); double x; double y; rand.InRing(0.0, 0.0, out x, out y); CheckEqual(0.0, x); CheckEqual(0.0, y); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void HugeRadii_DoNotOverflow() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.0)); double x; double y; rand.InRing(1e300, 2e300, out x, out y); CheckTrue(x >= 1e300 && x <= 2e300); } public void InnerPastOuter_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InRing(3.0, 2.0, out x, out y); } public void NegativeInner_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InRing(-1.0, 2.0, out x, out y); } public void InfiniteOuter_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InRing(1.0, double.PositiveInfinity, out x, out y); } public void NotANumberRadius_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); double x; double y; Seeded().InRing(double.NaN, 2.0, out x, out y); } public void BadRadius_UsesNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); double x; double y; try { rand.InRing(3.0, 2.0, out x, out y); } catch (ArgumentOutOfRangeException) { } CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } } public class EventCountTests : UnitTestSharp.TestFixture { public void CountsDrawsMultipliedTogetherUntilTheProductFallsUnderTheLimit() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5, 0.5)); CheckEqual(2, rand.EventCount(1.0, 2.0)); } public void ADrawUnderTheLimitAtOnce_IsZero() { var rand = new RandOf(Scripted.OfUnits(0.1)); CheckEqual(0, rand.EventCount(1.0, 2.0)); } public void StopsDrawingOnceTheProductFalls() { var scripted = Scripted.OfUnits(0.5, 0.5, 0.5, 0.75); var rand = new RandOf(scripted); rand.EventCount(1.0, 2.0); CheckEqual(Scripted.Word(0.75), rand.NextUInt64()); } public void LongSpan_IsCountedInChunksOfTenAndARemainder() { var rand = new RandOf(Scripted.OfUnits( 0.5, 0.00001, 0.00001, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.75)); int count = rand.EventCount(1.0, 25.0); CheckEqual(8, count); CheckEqual(Scripted.Word(0.75), rand.NextUInt64()); } public void ExactMultipleOfTen_HasNoRemainderToCount() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.00001, 0.75)); int count = rand.EventCount(1.0, 10.0); CheckEqual(1, count); CheckEqual(Scripted.Word(0.75), rand.NextUInt64()); } public void FewerThanTenExpected_IsOneChunk() { var rand = new RandOf(Scripted.OfUnits(0.5, 0.5, 0.5, 0.5, 0.5, 0.75)); CheckEqual(4, rand.EventCount(2.0, 6.0)); CheckEqual(Scripted.Word(0.75), rand.NextUInt64()); } public void ZeroSpan_IsZeroWithNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); CheckEqual(0, rand.EventCount(3.0, 0.0)); CheckEqual(0, rand.EventCount(0.0, 0.0)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void MeanOfInfinity_IsZeroWithNoDraw() { RandOf rand = Seeded(); var reference = new Sfc64(42); CheckEqual(0, rand.EventCount(double.PositiveInfinity, 100.0)); CheckEqual(reference.NextUInt64(), rand.NextUInt64()); } public void AMillionExpected_IsCounted() { CheckTrue(Seeded().EventCount(1.0, 1000000.0) > 0); } public void MoreThanAMillionExpected_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(1.0, 1000001.0); } public void MeanOfZero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(0.0, 5.0); } public void InfiniteSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(3.0, double.PositiveInfinity); } public void NegativeMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(-1.0, 5.0); } public void NotANumberMean_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(double.NaN, 5.0); } public void NegativeSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(3.0, -5.0); } public void NotANumberSpan_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Seeded().EventCount(3.0, double.NaN); } } } }