From 2c37ba0ca63116ff5a8ce18b1781e4f6ba579ef8 Mon Sep 17 00:00:00 2001 From: Gekctek Date: Fri, 2 Aug 2024 11:24:34 -0700 Subject: [PATCH] Adding more pseudo algoritmns --- Makefile | 6 --- README.md | 14 +++---- mops.toml | 4 +- src/PseudoRandomX.mo | 72 ++++++++++++++++++++++------------ test/PseudoRandomX.test.mo | 79 ++++++++++++++++++++++++++++++-------- test/RandomX.test.mo | 19 --------- 6 files changed, 120 insertions(+), 74 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 8f404dd..0000000 --- a/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -.PHONY: test - -all: test - -test: - mops test diff --git a/README.md b/README.md index 5210522..6093bd6 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ getCurrentSeed : () -> Nat32 #### nextInt -Generates a random integer within the specified range (inclusive). +Generates a random integer within the specified range (exclusive). ```motoko nextInt : (min : Int, max : Int) -> Int @@ -58,7 +58,7 @@ nextInt : (min : Int, max : Int) -> Int #### nextNat -Generates a random natural number within the specified range (inclusive). +Generates a random natural number within the specified range (exclusive). ```motoko nextNat : (min : Nat, max : Nat) -> Nat @@ -137,14 +137,14 @@ Here are some examples of how to use the Pseudo Random Number Generator: let prng = PseudoRandomX.fromSeed(0); let randomInt = prng.nextInt(1, 10); -Debug.print("Random integer between 1 and 10 (inclusive): " # Int.toText(randomInt)); +Debug.print("Random integer between 1 and 10 (exclusive): " # Int.toText(randomInt)); let randomCoin = prng.nextCoin(); Debug.print("Random coin flip: " # Bool.toText(randomCoin)); let randomFloat = prng.nextFloat(0.0, 1.0); -Debug.print("Random float between 0.0 and 1.0 (inclusive): " # Float.toText(randomFloat)); +Debug.print("Random float between 0.0 and 1.0 (exclusive): " # Float.toText(randomFloat)); let buffer = Buffer.fromArray([1, 2, 3, 4, 5]); prng.shuffleBuffer(buffer); @@ -205,7 +205,7 @@ nextRatio : (trueCount : Nat, totalCount : Nat) -> ?Bool #### nextInt -Generates a random integer within the specified range (inclusive). +Generates a random integer within the specified range (exclusive). ```motoko nextInt : (min : Int, max : Int) -> ?Int @@ -213,7 +213,7 @@ nextInt : (min : Int, max : Int) -> ?Int #### nextNat -Generates a random natural number within the specified range (inclusive). +Generates a random natural number within the specified range (exclusive). ```motoko nextNat : (min : Nat, max : Nat) -> ?Nat @@ -235,7 +235,7 @@ Here are some examples of how to use the Finite Random Number Generator: let entropy : Blob = ...; // Initialize with a proper seed let randomGen = RandomX.fromEntropy(seed); let ?randomInt = randomGen.nextInt(1, 10) else return #err("Not enough entropy"); -Debug.print("Random integer between 1 and 10 (inclusive): " # Int.toText(randomInt)); +Debug.print("Random integer between 1 and 10 (exclusive): " # Int.toText(randomInt)); let ?randomCoin = randomGen.nextCoin() else return #err("Not enough entropy"); Debug.print("Random coin flip: " # Bool.toText(randomCoin)); diff --git a/mops.toml b/mops.toml index ae0c9be..d2d5842 100644 --- a/mops.toml +++ b/mops.toml @@ -1,9 +1,9 @@ [dependencies] -base = "0.11.1" +base = "0.12.0" [package] name = "xtended-random" -version = "0.1.0" +version = "1.0.0" description = "A library for random helper funtions" repository = "https://github.com/edjcase/motoko_random" keywords = [ "random", "pseudo" ] diff --git a/src/PseudoRandomX.mo b/src/PseudoRandomX.mo index 21818a0..6dd6b4c 100644 --- a/src/PseudoRandomX.mo +++ b/src/PseudoRandomX.mo @@ -35,15 +35,25 @@ module { shuffleBuffer : (buffer : Buffer.Buffer) -> (); }; + /// The kinds of pseudo-random number generation algorithms available. + /// - `#linearCongruential`: A simple and fast generator. Good for basic uses where speed is + /// prioritized over randomness quality + /// - `#xorshift32`: Offers a good balance of speed and randomness quality. Better statistical + /// properties than linear congruential + public type PseudoRandomKind = { + #linearCongruential; + #xorshift32; + }; + /// Creates a new PseudoRandomGenerator from a Blob. /// /// ```motoko include=import /// let blob : Blob = ...; /// let prng = PseudoRandom.fromBlob(blob); /// ``` - public func fromBlob(blob : Blob) : PseudoRandomGenerator { + public func fromBlob(blob : Blob, kind : PseudoRandomKind) : PseudoRandomGenerator { let seed = convertBlobToSeed(blob); - LinearCongruentialGenerator(seed); + DefaultPseudoRandomGenerator(seed, kind); }; /// Converts a Blob to a 32-bit seed for the random number generator. @@ -76,22 +86,36 @@ module { /// let seed : Nat32 = 12345; /// let prng = PseudoRandom.fromSeed(seed); /// ``` - public func fromSeed(seed : Nat32) : PseudoRandomGenerator { - LinearCongruentialGenerator(seed); + public func fromSeed(seed : Nat32, kind : PseudoRandomKind) : PseudoRandomGenerator { + DefaultPseudoRandomGenerator(seed, kind); }; - /// Implements a Linear Congruential Generator for pseudo-random number generation. - public class LinearCongruentialGenerator(seed : Nat32) : PseudoRandomGenerator { + /// Implements the specified algorithm kinds for pseudo-random number generation. + public class DefaultPseudoRandomGenerator(seed : Nat32, kind : PseudoRandomKind) : PseudoRandomGenerator { var currentSeed = seed; - let a : Nat32 = 1664525; - let c : Nat32 = 1013904223; + + let nextSeedInternal = switch (kind) { + case (#linearCongruential) func() : Nat32 { + let a : Nat32 = 1664525; + let c : Nat32 = 1013904223; + currentSeed + |> Nat32.mulWrap(a, _) + |> Nat32.addWrap(_, c); // Overflow is ok + }; + case (#xorshift32) func() : Nat32 { + var seed = currentSeed; + seed := seed ^ (seed << 13); + seed := seed ^ (seed >> 17); + seed := seed ^ (seed << 5); + seed; + }; + }; /// Generates the next seed in the sequence. - private func nextSeed() : Nat32 { - currentSeed := currentSeed - |> Nat32.mulWrap(a, _) - |> Nat32.addWrap(_, c); // Overflow is ok - return currentSeed; + let nextSeed = func() : Nat32 { + let newSeed = nextSeedInternal(); + currentSeed := newSeed; + newSeed; }; /// Returns the current seed of the generator. @@ -99,18 +123,18 @@ module { currentSeed; }; - /// Generates a random integer within the specified range (inclusive). + /// Generates a random integer within the specified range (exclusive). /// /// ```motoko include=import /// let prng : PseudoRandomGenerator = ...; - /// let randomInt = prng.nextInt(1, 10); + /// let randomInt = prng.nextInt(0, 10); // [0, 10) /// ``` public func nextInt(min : Int, max : Int) : Int { - if (min > max) { - Debug.trap("Min cannot be larger than max"); + if (min >= max) { + Debug.trap("Max must be larger than min"); }; let randNat32 = nextSeed(); - let rangeSize = max - min + 1; + let rangeSize = max - min; min + (Nat32.toNat(randNat32) % rangeSize); }; @@ -118,7 +142,7 @@ module { /// /// ```motoko include=import /// let prng : PseudoRandomGenerator = ...; - /// let randomNat = prng.nextNat(1, 10); + /// let randomNat = prng.nextNat(0, 10); // [0, 10) /// ``` public func nextNat(min : Nat, max : Nat) : Nat { let randInt = nextInt(min, max); @@ -139,7 +163,7 @@ module { /// /// ```motoko include=import /// let prng : PseudoRandomGenerator = ...; - /// let randomFloat = prng.nextFloat(0.0, 1.0); + /// let randomFloat = prng.nextFloat(0.0, 1.0); // [0.0, 1.0) /// ``` public func nextFloat(min : Float, max : Float) : Float { if (min > max) { @@ -165,7 +189,7 @@ module { if (trueCount > totalCount) { Debug.trap("True count cannot be larger than total count"); }; - let randomValue = nextNat(1, totalCount); + let randomValue = nextNat(1, totalCount + 1); return randomValue <= trueCount; }; @@ -181,7 +205,7 @@ module { if (bufferSize == 0) { Debug.trap("Cannot get random element from an empty buffer"); }; - let randomIndex = nextNat(0, bufferSize - 1); + let randomIndex = nextNat(0, bufferSize); buffer.get(randomIndex); }; @@ -197,7 +221,7 @@ module { if (arraySize == 0) { Debug.trap("Cannot get random element from an empty array"); }; - let randomIndex = nextNat(0, arraySize - 1); + let randomIndex = nextNat(0, arraySize); array[randomIndex]; }; @@ -263,7 +287,7 @@ module { var i : Nat = bufferSize; for (item in buffer.vals()) { i -= 1; - let randomIndex = nextNat(0, i); + let randomIndex = nextNat(0, i + 1); let temp = buffer.get(i); buffer.put(i, buffer.get(randomIndex)); buffer.put(randomIndex, temp); diff --git a/test/PseudoRandomX.test.mo b/test/PseudoRandomX.test.mo index 63e130d..2833ecc 100644 --- a/test/PseudoRandomX.test.mo +++ b/test/PseudoRandomX.test.mo @@ -25,15 +25,15 @@ let assertEqualBool = func(expected : Bool, actual : Bool) : () { }; test( - "nextInt", + "nextInt LCG", func() { let testCases : [{ seed : Nat32; expected : Int; min : Int; max : Int }] = [ - { seed = 1; expected = 7; min = 0; max = 10 }, - { seed = 2; expected = -1; min = -4; max = 5 }, + { seed = 1; expected = 8; min = 0; max = 10 }, + { seed = 2; expected = -2; min = -4; max = 5 }, { seed = 3; expected = 1018897797; min = -1; max = 1_000_000_000_000_000 }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randInt = rand.nextInt(testCase.min, testCase.max); assertEqualInt(testCase.expected, randInt); }; @@ -41,11 +41,23 @@ test( ); test( - "nextNat", + "nextInt LCG many", + func() { + let rand = PseudoRandomX.fromSeed(1, #linearCongruential); + let expected = [8, 7, 8, 5, 2, 7, 6, 1, 8, 5, 2, 1, 0, 5, 6, 9, 6, 5, 6, 9, 6, 7, 8, 9, 8, 3, 4, 3, 8, 5, 4, 3, 6, 1, 8, 9, 0, 5, 6, 1, 2, 3, 4, 7, 2, 5, 4, 3, 2, 9, 8, 1, 8, 5, 4, 5, 6, 9, 2, 5, 8, 1, 2, 9, 6, 3, 0, 1, 2, 9, 0, 5, 6, 5, 6, 5, 4, 9, 6, 7, 6, 9, 8, 5, 8, 7, 6, 7, 0, 5, 6, 7, 0, 1, 0, 3, 6, 9, 4, 5, 2, 1, 6, 3, 2, 1, 6, 7, 0, 7, 4, 9, 4, 9, 0, 5, 0, 3, 2, 7, 6, 1, 4, 5, 4, 7, 4, 9, 8, 1, 2, 3, 6, 9, 6, 9, 0, 3, 2, 5, 8, 5, 8, 7, 0, 1, 8, 1, 0, 3, 8, 3, 6, 3, 2, 1, 4, 5, 6, 1, 6, 3, 2, 5, 8, 3, 6, 5, 6, 7, 2, 7, 2, 5, 6, 5, 0, 3, 2, 9, 8, 5, 4, 1, 2, 5, 4, 5, 4, 3, 0, 1, 6, 9, 6, 1, 0, 7, 4, 1, 4, 1, 0, 1, 8, 5, 8, 5, 8, 5, 8, 5, 8, 3, 6, 3, 6, 5, 0, 1, 6, 1, 4, 7, 6, 1, 4, 7, 6, 3, 0, 9, 0, 3, 0, 9, 8, 1, 4, 5, 0, 1, 8, 9, 8, 9, 4, 1, 8, 5, 6, 9, 4, 3, 8, 9, 8, 7, 8, 1, 8, 3, 6, 5, 0, 1, 4, 9, 0, 1, 0, 1, 0, 1, 4, 3, 0, 9, 6, 5, 0, 1, 4, 9, 4, 1, 6, 7, 6, 1, 2, 9, 2, 1, 8, 3, 2, 7, 2, 9, 0, 7, 2, 5, 8, 1, 2, 1, 2, 7, 8, 1, 8, 5, 6, 9, 2, 3, 8, 5, 4, 9, 0, 1, 6, 9, 0, 3, 8, 7, 8, 1, 6, 7, 2, 3, 8, 5, 6, 5, 4, 7, 4, 9, 8, 9, 8, 7, 0, 1, 0, 5, 2, 3, 0, 7, 4, 1, 4, 7, 6, 5, 0, 9, 6, 5, 2, 1, 0, 1, 6, 5, 2, 3, 2, 3, 2, 7, 4, 1, 0, 5, 4, 1, 0, 3, 0, 1, 2, 5, 4, 5, 2, 7, 0, 5, 2, 5, 6, 3, 8, 5, 4, 9, 4, 7, 6, 3, 0, 1, 0, 5, 4, 3, 6, 1, 6, 3, 2, 9, 6, 1, 0, 3, 0, 9, 4, 1, 4, 3, 6, 1, 8, 9, 0, 9, 2, 3, 6, 3, 4, 3, 2, 1, 8, 3, 0, 1, 2, 5, 2, 5, 4, 7, 6, 5, 6, 9, 2, 1, 2, 5, 6, 3, 2, 3, 4, 9, 2, 7, 6, 1, 4, 1, 6, 3, 2, 3, 2, 9, 4, 3, 6, 5, 0, 3, 2, 7, 8, 1, 0, 3, 2, 9, 6, 9, 2, 7, 4, 9, 4, 3, 2, 1, 6, 7, 6, 5, 4, 5, 2, 1, 4, 1, 2, 9, 8, 3, 0, 7, 6, 7, 8, 3, 6, 5, 2, 1, 4, 7, 0, 9, 0, 5, 6, 9, 0, 1, 0, 3, 6, 1, 8, 7, 8, 1, 2, 7, 0, 3, 0, 9, 0, 9, 8, 1, 0, 7, 8, 1, 4, 1, 8, 1, 2, 5, 8, 9, 8, 3, 4, 3, 8, 7, 6, 5, 4, 5, 6, 3, 4, 1, 0, 1, 8, 7, 4, 1, 0, 5, 6, 3, 6, 5, 0, 7, 6, 9, 8, 7, 4, 1, 2, 7, 4, 7, 0, 9, 2, 7, 0, 5, 8, 5, 8, 3, 6, 5, 8, 7, 6, 9, 2, 7, 8, 5, 6, 5, 6, 5, 0, 5, 0, 3, 4, 3, 8, 7, 6, 9, 0, 9, 4, 5, 2, 3, 6, 5, 6, 1, 6, 5, 2, 5, 0, 9, 4, 3, 4, 9, 4, 3, 6, 9, 6, 1, 0, 1, 0, 7, 6, 3, 2, 3, 4, 5, 0, 7, 2, 5, 4, 9, 4, 3, 4, 7, 6, 7, 4, 3, 6, 3, 8, 1, 2, 5, 6, 9, 0, 9, 2, 7, 6, 3, 6, 1, 2, 5, 2, 7, 2, 1, 4, 3, 2, 7, 4, 7, 4, 1, 0, 7, 4, 9, 2, 1, 0, 3, 2, 3, 4, 9, 2, 9, 8, 5, 8, 5, 2, 9, 0, 5, 4, 9, 8, 9, 0, 7, 0, 5, 8, 9, 2, 1, 2, 3, 6, 1, 2, 9, 8, 5, 6, 1, 2, 3, 6, 5, 4, 5, 8, 9, 2, 3, 0, 3, 2, 7, 6, 7, 8, 3, 8, 9, 2, 3, 2, 7, 0, 3, 6, 5, 4, 9, 4, 9, 4, 1, 8, 3, 4, 1, 4, 5, 6, 7, 4, 7, 4, 5, 4, 1, 6, 1, 2, 3, 6, 5, 8, 5, 4, 7, 2, 3, 6, 9, 2, 9, 8, 9, 4, 9, 0, 1, 2, 1, 2, 3, 4, 9, 2, 9, 0, 1, 8, 3, 8, 1, 4, 7, 2, 5, 4, 9, 8, 1, 8, 9, 0, 7, 2, 9, 2, 9, 2, 7, 6, 9, 4, 5, 6, 9, 6, 1, 6, 5, 0, 9, 6, 7, 2, 3, 4, 3, 6, 9, 4, 7, 6, 5, 6, 1, 4, 9, 2, 5, 6, 1, 0, 9, 0, 3, 2, 7, 8, 1, 2, 9, 2, 7, 2, 9, 2, 3, 6, 9, 6, 9, 4, 9, 8, 9, 4, 9, 4, 9, 6, 3, 4, 3, 0, 1, 2, 7, 6, 7, 8, 3, 6, 3, 0, 3, 4, 7, 2, 1, 8, 1, 0, 1, 0, 5, 4, 9, 0, 5, 2, 3, 0, 9, 4, 7, 2, 9, 4, 9, 6, 3, 8, 5, 4, 9, 4, 7, 0, 9, 6, 7, 2, 5, 8, 1, 8, 7, 0, 5, 6, 9, 6, 9, 0, 5, 4, 7, 0, 3, 6, 7, 0, 7, 2]; + for (i in Iter.range(0, 1000)) { + let randInt = rand.nextInt(0, 10); + assertEqualInt(expected[i], randInt); + }; + }, +); + +test( + "nextNat LCG", func() { let testCases : [{ seed : Nat32; expected : Nat; min : Nat; max : Nat }] = [ - { seed = 1; expected = 7; min = 0; max = 10 }, - { seed = 2; expected = 4; min = 1; max = 5 }, + { seed = 1; expected = 8; min = 0; max = 10 }, + { seed = 2; expected = 2; min = 1; max = 5 }, { seed = 3; expected = 1018997797; @@ -54,13 +66,48 @@ test( }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randInt = rand.nextNat(testCase.min, testCase.max); assertEqualInt(testCase.expected, randInt); }; }, ); +test( + "nextNat LCG many 0-10", + func() { + let rand = PseudoRandomX.fromSeed(1, #linearCongruential); + let expected = [8, 7, 8, 5, 2, 7, 6, 1, 8, 5, 2, 1, 0, 5, 6, 9, 6, 5, 6, 9, 6, 7, 8, 9, 8, 3, 4, 3, 8, 5, 4, 3, 6, 1, 8, 9, 0, 5, 6, 1, 2, 3, 4, 7, 2, 5, 4, 3, 2, 9, 8, 1, 8, 5, 4, 5, 6, 9, 2, 5, 8, 1, 2, 9, 6, 3, 0, 1, 2, 9, 0, 5, 6, 5, 6, 5, 4, 9, 6, 7, 6, 9, 8, 5, 8, 7, 6, 7, 0, 5, 6, 7, 0, 1, 0, 3, 6, 9, 4, 5, 2, 1, 6, 3, 2, 1, 6, 7, 0, 7, 4, 9, 4, 9, 0, 5, 0, 3, 2, 7, 6, 1, 4, 5, 4, 7, 4, 9, 8, 1, 2, 3, 6, 9, 6, 9, 0, 3, 2, 5, 8, 5, 8, 7, 0, 1, 8, 1, 0, 3, 8, 3, 6, 3, 2, 1, 4, 5, 6, 1, 6, 3, 2, 5, 8, 3, 6, 5, 6, 7, 2, 7, 2, 5, 6, 5, 0, 3, 2, 9, 8, 5, 4, 1, 2, 5, 4, 5, 4, 3, 0, 1, 6, 9, 6, 1, 0, 7, 4, 1, 4, 1, 0, 1, 8, 5, 8, 5, 8, 5, 8, 5, 8, 3, 6, 3, 6, 5, 0, 1, 6, 1, 4, 7, 6, 1, 4, 7, 6, 3, 0, 9, 0, 3, 0, 9, 8, 1, 4, 5, 0, 1, 8, 9, 8, 9, 4, 1, 8, 5, 6, 9, 4, 3, 8, 9, 8, 7, 8, 1, 8, 3, 6, 5, 0, 1, 4, 9, 0, 1, 0, 1, 0, 1, 4, 3, 0, 9, 6, 5, 0, 1, 4, 9, 4, 1, 6, 7, 6, 1, 2, 9, 2, 1, 8, 3, 2, 7, 2, 9, 0, 7, 2, 5, 8, 1, 2, 1, 2, 7, 8, 1, 8, 5, 6, 9, 2, 3, 8, 5, 4, 9, 0, 1, 6, 9, 0, 3, 8, 7, 8, 1, 6, 7, 2, 3, 8, 5, 6, 5, 4, 7, 4, 9, 8, 9, 8, 7, 0, 1, 0, 5, 2, 3, 0, 7, 4, 1, 4, 7, 6, 5, 0, 9, 6, 5, 2, 1, 0, 1, 6, 5, 2, 3, 2, 3, 2, 7, 4, 1, 0, 5, 4, 1, 0, 3, 0, 1, 2, 5, 4, 5, 2, 7, 0, 5, 2, 5, 6, 3, 8, 5, 4, 9, 4, 7, 6, 3, 0, 1, 0, 5, 4, 3, 6, 1, 6, 3, 2, 9, 6, 1, 0, 3, 0, 9, 4, 1, 4, 3, 6, 1, 8, 9, 0, 9, 2, 3, 6, 3, 4, 3, 2, 1, 8, 3, 0, 1, 2, 5, 2, 5, 4, 7, 6, 5, 6, 9, 2, 1, 2, 5, 6, 3, 2, 3, 4, 9, 2, 7, 6, 1, 4, 1, 6, 3, 2, 3, 2, 9, 4, 3, 6, 5, 0, 3, 2, 7, 8, 1, 0, 3, 2, 9, 6, 9, 2, 7, 4, 9, 4, 3, 2, 1, 6, 7, 6, 5, 4, 5, 2, 1, 4, 1, 2, 9, 8, 3, 0, 7, 6, 7, 8, 3, 6, 5, 2, 1, 4, 7, 0, 9, 0, 5, 6, 9, 0, 1, 0, 3, 6, 1, 8, 7, 8, 1, 2, 7, 0, 3, 0, 9, 0, 9, 8, 1, 0, 7, 8, 1, 4, 1, 8, 1, 2, 5, 8, 9, 8, 3, 4, 3, 8, 7, 6, 5, 4, 5, 6, 3, 4, 1, 0, 1, 8, 7, 4, 1, 0, 5, 6, 3, 6, 5, 0, 7, 6, 9, 8, 7, 4, 1, 2, 7, 4, 7, 0, 9, 2, 7, 0, 5, 8, 5, 8, 3, 6, 5, 8, 7, 6, 9, 2, 7, 8, 5, 6, 5, 6, 5, 0, 5, 0, 3, 4, 3, 8, 7, 6, 9, 0, 9, 4, 5, 2, 3, 6, 5, 6, 1, 6, 5, 2, 5, 0, 9, 4, 3, 4, 9, 4, 3, 6, 9, 6, 1, 0, 1, 0, 7, 6, 3, 2, 3, 4, 5, 0, 7, 2, 5, 4, 9, 4, 3, 4, 7, 6, 7, 4, 3, 6, 3, 8, 1, 2, 5, 6, 9, 0, 9, 2, 7, 6, 3, 6, 1, 2, 5, 2, 7, 2, 1, 4, 3, 2, 7, 4, 7, 4, 1, 0, 7, 4, 9, 2, 1, 0, 3, 2, 3, 4, 9, 2, 9, 8, 5, 8, 5, 2, 9, 0, 5, 4, 9, 8, 9, 0, 7, 0, 5, 8, 9, 2, 1, 2, 3, 6, 1, 2, 9, 8, 5, 6, 1, 2, 3, 6, 5, 4, 5, 8, 9, 2, 3, 0, 3, 2, 7, 6, 7, 8, 3, 8, 9, 2, 3, 2, 7, 0, 3, 6, 5, 4, 9, 4, 9, 4, 1, 8, 3, 4, 1, 4, 5, 6, 7, 4, 7, 4, 5, 4, 1, 6, 1, 2, 3, 6, 5, 8, 5, 4, 7, 2, 3, 6, 9, 2, 9, 8, 9, 4, 9, 0, 1, 2, 1, 2, 3, 4, 9, 2, 9, 0, 1, 8, 3, 8, 1, 4, 7, 2, 5, 4, 9, 8, 1, 8, 9, 0, 7, 2, 9, 2, 9, 2, 7, 6, 9, 4, 5, 6, 9, 6, 1, 6, 5, 0, 9, 6, 7, 2, 3, 4, 3, 6, 9, 4, 7, 6, 5, 6, 1, 4, 9, 2, 5, 6, 1, 0, 9, 0, 3, 2, 7, 8, 1, 2, 9, 2, 7, 2, 9, 2, 3, 6, 9, 6, 9, 4, 9, 8, 9, 4, 9, 4, 9, 6, 3, 4, 3, 0, 1, 2, 7, 6, 7, 8, 3, 6, 3, 0, 3, 4, 7, 2, 1, 8, 1, 0, 1, 0, 5, 4, 9, 0, 5, 2, 3, 0, 9, 4, 7, 2, 9, 4, 9, 6, 3, 8, 5, 4, 9, 4, 7, 0, 9, 6, 7, 2, 5, 8, 1, 8, 7, 0, 5, 6, 9, 6, 9, 0, 5, 4, 7, 0, 3, 6, 7, 0, 7, 2]; + for (i in Iter.range(0, 1000)) { + let randInt = rand.nextNat(0, 10); + assertEqualInt(expected[i], randInt); + }; + }, +); +test( + "nextNat LCG many 0-3", + func() { + let rand = PseudoRandomX.fromSeed(1, #linearCongruential); + let expected = [0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0]; + for (i in Iter.range(0, 1000)) { + let randInt = rand.nextNat(0, 4); + assertEqualInt(expected[i], randInt); + }; + }, +); + +test( + "nextNat Xorshift32 many 0-3", + func() { + let rand = PseudoRandomX.fromSeed(2, #xorshift32); + let expected = [2, 2, 2, 2, 2, 3, 1, 2, 2, 3, 2, 1, 1, 0, 2, 2, 3, 3, 1, 3, 0, 0, 2, 1, 3, 0, 0, 1, 0, 1, 3, 3, 1, 3, 1, 2, 2, 3, 1, 1, 0, 1, 1, 2, 2, 3, 0, 0, 0, 3, 2, 0, 0, 2, 3, 3, 0, 1, 1, 1, 1, 3, 0, 2, 2, 0, 2, 2, 1, 1, 3, 3, 0, 2, 1, 3, 1, 2, 1, 2, 0, 2, 3, 2, 1, 3, 3, 1, 3, 2, 0, 3, 3, 2, 0, 2, 1, 0, 1, 3, 3, 3, 1, 0, 1, 3, 2, 0, 3, 1, 1, 0, 1, 3, 0, 2, 1, 1, 2, 3, 1, 1, 3, 3, 1, 0, 1, 1, 3, 3, 1, 0, 2, 3, 1, 2, 2, 1, 3, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 2, 3, 2, 0, 2, 1, 1, 1, 0, 2, 1, 1, 0, 3, 0, 2, 1, 3, 3, 2, 2, 3, 2, 2, 0, 2, 2, 3, 0, 3, 1, 3, 0, 2, 2, 3, 0, 0, 3, 1, 3, 2, 2, 1, 2, 2, 3, 0, 0, 2, 3, 1, 1, 3, 2, 2, 0, 1, 2, 2, 2, 2, 0, 0, 0, 3, 1, 0, 0, 1, 0, 1, 3, 3, 0, 1, 3, 3, 2, 3, 0, 2, 2, 3, 0, 0, 1, 3, 0, 1, 2, 3, 3, 1, 1, 0, 1, 1, 0, 2, 1, 0, 3, 0, 3, 3, 2, 2, 0, 0, 1, 0, 3, 2, 1, 1, 2, 0, 3, 0, 0, 3, 2, 0, 3, 3, 1, 1, 0, 1, 0, 3, 3, 2, 0, 2, 2, 1, 3, 3, 1, 0, 0, 3, 0, 3, 0, 1, 3, 3, 1, 3, 2, 3, 3, 2, 1, 2, 0, 0, 0, 3, 1, 0, 1, 3, 1, 1, 2, 1, 2, 0, 2, 1, 1, 3, 2, 1, 1, 1, 3, 1, 2, 0, 1, 1, 1, 0, 1, 1, 3, 1, 1, 1, 1, 2, 1, 3, 3, 1, 0, 0, 3, 1, 0, 0, 0, 3, 3, 2, 2, 1, 1, 0, 3, 2, 3, 2, 0, 3, 3, 1, 2, 1, 1, 2, 0, 1, 1, 2, 1, 0, 0, 3, 2, 0, 0, 2, 2, 0, 1, 3, 2, 0, 1, 1, 3, 0, 1, 1, 3, 0, 1, 3, 1, 2, 0, 0, 1, 1, 1, 1, 2, 3, 2, 0, 2, 1, 0, 2, 3, 3, 0, 0, 3, 2, 3, 1, 2, 1, 0, 1, 3, 1, 0, 2, 3, 2, 1, 3, 1, 3, 3, 3, 1, 1, 3, 2, 3, 2, 1, 1, 1, 0, 3, 2, 3, 2, 2, 1, 1, 1, 3, 0, 3, 2, 2, 0, 1, 0, 3, 1, 0, 1, 2, 0, 0, 3, 3, 1, 0, 1, 1, 1, 3, 2, 1, 0, 0, 3, 1, 0, 1, 0, 0, 2, 3, 0, 2, 1, 0, 2, 1, 2, 0, 1, 3, 0, 3, 1, 3, 3, 1, 1, 2, 0, 1, 2, 0, 1, 3, 3, 0, 1, 3, 1, 1, 1, 3, 2, 0, 1, 3, 1, 2, 2, 3, 0, 1, 2, 2, 2, 0, 1, 3, 3, 2, 3, 3, 2, 2, 2, 3, 0, 2, 3, 2, 1, 0, 3, 3, 1, 3, 0, 0, 2, 0, 3, 2, 0, 1, 3, 3, 0, 0, 0, 2, 2, 1, 0, 0, 1, 2, 1, 0, 0, 3, 2, 1, 0, 1, 2, 1, 3, 0, 2, 2, 1, 2, 2, 3, 1, 3, 1, 3, 0, 2, 2, 1, 0, 0, 2, 2, 3, 0, 0, 2, 0, 0, 3, 3, 3, 3, 3, 1, 2, 0, 3, 2, 0, 3, 2, 2, 1, 1, 2, 3, 1, 3, 1, 2, 0, 2, 2, 0, 1, 0, 2, 2, 1, 3, 1, 1, 0, 2, 1, 0, 3, 3, 2, 2, 1, 3, 0, 1, 3, 1, 2, 2, 0, 3, 0, 1, 1, 0, 3, 2, 2, 2, 3, 2, 0, 0, 2, 3, 2, 1, 3, 1, 2, 2, 1, 1, 2, 2, 2, 1, 0, 0, 3, 1, 3, 0, 2, 3, 2, 1, 2, 0, 0, 1, 3, 0, 3, 2, 2, 0, 1, 0, 1, 0, 3, 1, 1, 0, 0, 1, 0, 3, 1, 0, 0, 3, 1, 0, 0, 3, 3, 0, 1, 0, 0, 2, 2, 1, 2, 3, 1, 0, 2, 3, 2, 2, 3, 3, 1, 3, 1, 1, 1, 2, 1, 3, 3, 3, 0, 1, 0, 1, 3, 2, 3, 1, 0, 0, 2, 3, 2, 2, 0, 1, 3, 1, 2, 0, 0, 0, 2, 2, 0, 2, 2, 0, 3, 1, 0, 3, 1, 0, 1, 0, 3, 1, 2, 1, 3, 1, 2, 2, 1, 0, 3, 3, 3, 2, 2, 2, 2, 0, 3, 2, 3, 0, 2, 2, 2, 3, 1, 1, 2, 2, 0, 0, 3, 0, 3, 0, 2, 1, 3, 3, 0, 1, 3, 1, 1, 1, 3, 0, 3, 3, 0, 3, 3, 1, 3, 3, 1, 2, 1, 1, 0, 2, 2, 3, 0, 1, 3, 0, 1, 1, 0, 0, 0, 3, 2, 1, 2, 3, 0, 3, 1, 1, 1, 3, 1, 2, 0, 2, 3, 0, 1, 3, 1, 3, 3, 2, 0, 0, 0, 3, 1, 2, 1, 0, 3, 3, 3, 3, 1, 3, 0, 0, 0, 1, 0, 3, 3, 2, 0, 3, 0, 3, 3, 0, 1, 2, 2, 1, 1, 1, 0, 2, 0, 2, 2, 0, 2, 2, 3, 0, 1, 2, 0, 0, 1, 3, 2, 2, 0, 3, 1, 2, 3, 0, 3, 2, 0, 3, 3, 1, 1, 1, 2, 3, 2, 3, 0, 3, 0, 0, 0, 0, 0, 3, 1, 3, 3, 2, 2, 3, 1, 0, 2, 3, 1, 2, 0, 3, 1, 1, 2, 1, 1, 0, 0, 2, 2, 2, 1, 0, 3]; + for (i in Iter.range(0, 1000)) { + let randInt = rand.nextNat(0, 4); + assertEqualInt(expected[i], randInt); + }; + }, +); + test( "nextFloat", func() { @@ -80,7 +127,7 @@ test( }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randFloat = rand.nextFloat(testCase.min, testCase.max); assertEqualFloat(testCase.expected, randFloat, 0.000001); }; @@ -96,7 +143,7 @@ test( { seed = 3; expected = true }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randBool = rand.nextCoin(); assertEqualBool(testCase.expected, randBool); }; @@ -117,7 +164,7 @@ test( { seed = 3; expected = true; trueCount = 2; totalCount = 3 }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randBool = rand.nextRatio(testCase.trueCount, testCase.totalCount); assertEqualBool(testCase.expected, randBool); }; @@ -137,7 +184,7 @@ test( { seed = 3; expected = 8; buffer = Buffer.fromArray([7, 8, 9]) }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randElement = rand.nextBufferElement(testCase.buffer); assertEqualInt(testCase.expected, randElement); }; @@ -153,7 +200,7 @@ test( { seed = 3; expected = 8; array = [7, 8, 9] }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randElement = rand.nextArrayElement(testCase.array); assertEqualInt(testCase.expected, randElement); }; @@ -169,7 +216,7 @@ test( { seed = 3; expected = 9; array = [(7, 0.1), (8, 0.1), (9, 0.8)] }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randElement = rand.nextArrayElementWeighted(testCase.array); assertEqualInt(testCase.expected, randElement); }; @@ -207,7 +254,7 @@ test( }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let randElement = rand.nextArrayElementWeightedFunc(testCase.array, testCase.weightFunc); assertEqualInt(testCase.expected, randElement); }; @@ -223,7 +270,7 @@ test( { seed = 3; expected = [9, 7, 8]; input = [7, 8, 9] }, ]; for (testCase in Iter.fromArray(testCases)) { - let rand = PseudoRandomX.fromSeed(testCase.seed); + let rand = PseudoRandomX.fromSeed(testCase.seed, #linearCongruential); let buffer = Buffer.fromArray(testCase.input); rand.shuffleBuffer(buffer); for (i in Iter.range(0, buffer.size() - 1)) { diff --git a/test/RandomX.test.mo b/test/RandomX.test.mo index d166b31..7c5adb5 100644 --- a/test/RandomX.test.mo +++ b/test/RandomX.test.mo @@ -2,31 +2,12 @@ import Iter "mo:base/Iter"; import { test } "mo:test"; import Debug "mo:base/Debug"; import Int "mo:base/Int"; -import Float "mo:base/Float"; import Buffer "mo:base/Buffer"; import Blob "mo:base/Blob"; import Nat8 "mo:base/Nat8"; import Nat32 "mo:base/Nat32"; import RandomX "../src/RandomX"; -let assertEqualInt = func(expected : Int, actual : Int) : () { - if (actual != expected) { - Debug.trap("Expected " # Int.toText(expected) # ", got " # Int.toText(actual)); - }; -}; - -let assertEqualNat = func(expected : Nat, actual : Nat) : () { - if (actual != expected) { - Debug.trap("Expected " # Int.toText(expected) # ", got " # Int.toText(actual)); - }; -}; - -let assertEqualBool = func(expected : Bool, actual : Bool) : () { - if (actual != expected) { - Debug.trap("Expected " # debug_show (expected) # ", got " # debug_show (actual)); - }; -}; - let assertNotNull = func(value : ?T) : () { switch (value) { case (null) { Debug.trap("Expected non-null value, got null") };