diff --git a/rng/rng/.vscode/c_cpp_properties.json b/rng/rng/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..6275923 --- /dev/null +++ b/rng/rng/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "macos-clang-arm64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/rng/rng/.vscode/settings.json b/rng/rng/.vscode/settings.json deleted file mode 100644 index 00613a2..0000000 --- a/rng/rng/.vscode/settings.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "files.associations": { - "*.ts": "typescriptreact", - "array": "cpp", - "iostream": "cpp", - "__bit_reference": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__node_handle": "cpp", - "__split_buffer": "cpp", - "__threading_support": "cpp", - "__verbose_abort": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "complex": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "execution": "cpp", - "memory": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "locale": "cpp", - "mutex": "cpp", - "new": "cpp", - "optional": "cpp", - "ostream": "cpp", - "print": "cpp", - "queue": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "string_view": "cpp", - "tuple": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "variant": "cpp", - "vector": "cpp", - "algorithm": "cpp", - "__tree": "cpp", - "csignal": "cpp", - "map": "cpp", - "set": "cpp", - "unordered_set": "cpp" - } -} \ No newline at end of file diff --git a/rng/rng/generators/LCG/lehmer.cpp b/rng/rng/generators/LCG/lehmer.cpp index 7dabe7c..c99a218 100644 --- a/rng/rng/generators/LCG/lehmer.cpp +++ b/rng/rng/generators/LCG/lehmer.cpp @@ -6,38 +6,26 @@ // #include "../../rng.h" +#include "../generator.h" // parameters recommended by Nakazawa & Nakazawa // https://en.wikipedia.org/wiki/Lehmer_random_number_generator -uint64_t lehmer_minstd(uint64_t prev){ - uint64_t a = 7759097958782935LL; - uint64_t m = 18055400005099021LL; - - uint64_t result = (a * prev) % m; - - return result; -} - -class lehmer_generator { - uint64_t seed; - public: - uint32_t generate(){ - seed = lehmer_minstd(seed); - return seed; - } - lehmer_generator(uint64_t genSeed){ - seed = genSeed; - } -}; - -// std::vector> lehmer_generate(int seed, int amount){ -// std::vector> bits(amount); - -// for(int i=0; i> 32; + } + std::string getName() override { + return "lehmer"; + } + private: + uint64_t a = 7759097958782935LL; + uint64_t m = 18055400005099021LL; + }; +} \ No newline at end of file diff --git a/rng/rng/generators/generator.h b/rng/rng/generators/generator.h new file mode 100644 index 0000000..11d598d --- /dev/null +++ b/rng/rng/generators/generator.h @@ -0,0 +1,18 @@ +#include "../rng.h" + +#pragma once + +namespace splat { + class PRNG { + public: + virtual uint32_t generate() {return -1;}; + virtual ~PRNG() = default; + virtual std::string getName() {return "N/A";}; + PRNG(uint32_t genseed){ + seed = genseed; + } + protected: + uint32_t seed; + }; +} + diff --git a/rng/rng/generators/mt19937.cpp b/rng/rng/generators/mt19937.cpp index 1af9f00..4d5a653 100644 --- a/rng/rng/generators/mt19937.cpp +++ b/rng/rng/generators/mt19937.cpp @@ -1,4 +1,5 @@ #include "../rng.h" +#include "./generator.h" // more specifically this will be mt19937 - 32 bit @@ -63,33 +64,33 @@ std::array mt19937_temper(std::array state){ return tempered; } -class mt19937_generator { - public: - uint32_t seed; - uint32_t generate() { - uint32_t generated = random_values[position]; - position++; - if(position>=624){ +namespace splat { + class mt19937_generator : public PRNG { + public: + mt19937_generator(uint32_t genSeed) : PRNG(genSeed) { + state = mt19937_init(seed); nextblock(); } - return generated; - } - double generate_01(){ - return ((double)generate())/(4294967295); - } - mt19937_generator(uint32_t genSeed){ - seed = genSeed; - state = mt19937_init(seed); - nextblock(); - } - private: - std::array state; - std::array random_values; - int position; - // goes to next block of 624 values - void nextblock() { - state = mt19937_twist(state); - random_values = mt19937_temper(state); - position = 0; - } -}; + uint32_t generate() override { + uint32_t generated = random_values[position]; + position++; + if(position>=624){ + nextblock(); + } + return generated; + } + std::string getName() override { + return "mt19937-32"; + } + private: + std::array state; + std::array random_values; + int position; + // goes to next block of 624 values + void nextblock() { + state = mt19937_twist(state); + random_values = mt19937_temper(state); + position = 0; + } + }; +} \ No newline at end of file diff --git a/rng/rng/generators/xorshift.cpp b/rng/rng/generators/xorshift.cpp new file mode 100644 index 0000000..904e03c --- /dev/null +++ b/rng/rng/generators/xorshift.cpp @@ -0,0 +1,25 @@ +#include "../rng.h" +#include "./generator.h" + +// parameters recommended by Nakazawa & Nakazawa +// https://en.wikipedia.org/wiki/Lehmer_random_number_generator + +namespace splat { + class xorshift32 : public PRNG { + public: + xorshift32(uint32_t genSeed) : PRNG(genSeed) { + state = genSeed; + } + uint32_t generate() override { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; + } + std::string getName() override { + return "xorshift32"; + } + private: + uint32_t state; + }; +} \ No newline at end of file diff --git a/rng/rng/main.cpp b/rng/rng/main.cpp index fceac27..c943570 100644 --- a/rng/rng/main.cpp +++ b/rng/rng/main.cpp @@ -8,45 +8,78 @@ #include "rng.h" #include "generators/LCG/lehmer.cpp" #include "generators/mt19937.cpp" +#include "generators/xorshift.cpp" #include "randomness_tests/frequency_monobit.cpp" #include "randomness_tests/frequency_block.cpp" #include "randomness_tests/runs.cpp" +#include "randomness_tests/runs_ones.cpp" + + + +#include "generators/generator.h" +#include "randomness_tests/rngtest.h" + +namespace splat { + template + void addGen(std::vector> &generators, uint32_t &seed){ + generators.push_back(std::make_unique(seed)); + } + + std::vector> getAllGenerators(uint32_t seed){ + std::vector> generators; + + // add generators here + addGen(generators, seed); + addGen(generators, seed); + addGen(generators,seed); + + return generators; + } + + template + void addTest(std::vector> &tests, std::vector> &data){ + tests.push_back(std::make_unique(data)); + } + + std::vector> getAllTests(std::vector> &data){ + std::vector> tests; + + // add generators here + addTest(tests, data); + addTest(tests, data); + addTest(tests, data); + addTest(tests, data); + + return tests; + } + +} int main(int argc, const char * argv[]) { - int seed = 8898; + int seed = 235211213; int blocksgenerated = 1000000; - mt19937_generator mt2 = mt19937_generator(seed); - lehmer_generator lg = lehmer_generator(seed); + std::vector> generators = splat::getAllGenerators(1238124); + + for(const std::unique_ptr &generator : generators){ + std::cout << generator->getName() << ": \n"; + std::vector> bits; + for(int i=0; igenerate()); + } - std::vector> mtbits; - std::vector> lgbits; + std::vector> tests = splat::getAllTests(bits); - for(int i=0; i &test : tests){ + std::string namestr = test->getName(); + while(namestr.length() < 30){ + namestr.append(" "); + } + std::cout << " - " << namestr << " = " << (test->passed()?"PASS":"FAIL") << " ("<< std::fixed << std::setprecision(8) << test->value() <<")\n"; + } } - // for(auto bs : lgbits){ - // for(int i=0; i<32; i++){ - // std::cout << bs[i]; - // } - // } - - int freqblocksize = (blocksgenerated * 32) / 120; - - std::cout << "Linear Congruential Generator:\n"; - std::cout << " - FreqMonobit = "<< std::fixed << std::setprecision(10) << test_frequency_monobit(lgbits) << "\n"; - std::cout << " - FreqBlock (n/120) = "<< std::fixed << std::setprecision(10) << test_frequency_block(lgbits, freqblocksize) << "\n"; - std::cout << " - Runs = "<< std::fixed << std::setprecision(10) << test_runs(lgbits) << "\n"; - - std::cout << "Mersenne Twister mt19937-32:\n"; - std::cout << " - FreqMonobit = "<< std::fixed << std::setprecision(10) << test_frequency_monobit(mtbits) << "\n"; - std::cout << " - FreqBlock (n/120) = "<< std::fixed << std::setprecision(10) << test_frequency_block(mtbits, freqblocksize) << "\n"; - std::cout << " - Runs = "<< std::fixed << std::setprecision(10) << test_runs(mtbits) << "\n"; - - return 0; -} +}; diff --git a/rng/rng/main.o b/rng/rng/main.o new file mode 100755 index 0000000..3691112 Binary files /dev/null and b/rng/rng/main.o differ diff --git a/rng/rng/plot_data.py b/rng/rng/plot_data.py deleted file mode 100644 index 7abfd63..0000000 --- a/rng/rng/plot_data.py +++ /dev/null @@ -1,20 +0,0 @@ -import matplotlib.pyplot as plt - -x_coords = [] -y_coords = [] - -with open("data.txt", "r") as file: - for line in file: - x_coords.append(float(line.split(",")[0])) - y_coords.append(float(line.split(",")[1])) - -plt.figure(figsize=(10, 8)) -plt.scatter(x_coords, y_coords, alpha=0.6, s=1) -plt.grid(True, alpha=0.3) - -plt.axis('equal') -plt.xlim(-0.05, 1.05) -plt.ylim(-0.05, 1.05) - -plt.tight_layout() -plt.show() \ No newline at end of file diff --git a/rng/rng/randomness_tests/frequency_block.cpp b/rng/rng/randomness_tests/frequency_block.cpp index 1d85114..ffab45e 100644 --- a/rng/rng/randomness_tests/frequency_block.cpp +++ b/rng/rng/randomness_tests/frequency_block.cpp @@ -8,44 +8,50 @@ #include "../rng.h" #include "../math/incomplete_gamma.cpp" -/** - * Returns the p value from the block frequency test - */ -double test_frequency_block(std::vector> data, u_long block_size){ - - // std::cout << "BLOCK SIZE: "<< block_size<<"\n"; +#include "./rngtest.h" - long long bitcount = data.size() * 32; - long long chunks = bitcount / block_size; +namespace splat { + class frequency_block_test : public RNGTEST { + public: + frequency_block_test(std::vector> &testData) : RNGTEST(testData) { + block_size = data.size() * 32 / 150; + testPValue = runTest(data); + testPassed = testPValue > 0.01; + } + std::string getName() override { + return std::format("Frequency Block [{}]", block_size); + } + double runTest(std::vector> &data) override { - double x2stat = 0; - - for(int chunkIndex = 0; chunkIndex < chunks; chunkIndex++){ - // std::cout << "NEWCHUNK: "; - double onecount = 0; - for(int i=0; i .01n and N < 100. - */ -bool pass_frequency_monobit(std::vector> data, int chunkSize){ - double pv = test_frequency_block(data, chunkSize); - return pv >= 0.01; + // std::cout << "\n"; + } + + // std::cout << "[debug] calling igamc with "<<(chunks/2) <<','<<(x2stat/2) << "\n"; + double p = igam(chunks/2, x2stat/2); + return p; + } + private: + long long block_size; + + }; } \ No newline at end of file diff --git a/rng/rng/randomness_tests/frequency_monobit.cpp b/rng/rng/randomness_tests/frequency_monobit.cpp index f51470a..7b6d59e 100644 --- a/rng/rng/randomness_tests/frequency_monobit.cpp +++ b/rng/rng/randomness_tests/frequency_monobit.cpp @@ -6,30 +6,28 @@ // #include "../rng.h" +#include "./rngtest.h" - -double test_frequency_monobit(std::vector> data){ - - long long s = 0; - - for(auto bitset : data){ - for(int i=0; i<32; i++){ - s += (2*bitset[i])-1; +namespace splat { + class frequency_monobit_test : public RNGTEST { + public: + frequency_monobit_test(std::vector> &testData) : RNGTEST(testData) { + testPValue = runTest(data); + testPassed = testPValue > 0.01; } - } - - // std::cout << " [sval: "<> data){ - double pv = test_frequency_monobit(data); - return pv >= 0.01; + std::string getName() override { + return "Frequency Monobit"; + } + double runTest(std::vector> &data) override { + long long s = 0; + for(auto bitset : data){ + for(int i=0; i<32; i++){ + s += (2*bitset[i])-1; + } + } + double sobs = ((double)std::abs(s))/std::sqrt(data.size()*32); + double pvalue = std::erfc(sobs/std::sqrt(2)); + return pvalue; + } + }; } \ No newline at end of file diff --git a/rng/rng/randomness_tests/rngtest.h b/rng/rng/randomness_tests/rngtest.h new file mode 100644 index 0000000..ad2ffa7 --- /dev/null +++ b/rng/rng/randomness_tests/rngtest.h @@ -0,0 +1,25 @@ +#include "../rng.h" + +#pragma once + +namespace splat { + class RNGTEST { + public: + bool passed(){ + return testPassed; + } + double value(){ + return testPValue; + } + virtual ~RNGTEST() = default; + virtual std::string getName() {return "N/A";}; + RNGTEST(std::vector> &testData) : data(testData){ + }; + protected: + std::vector> &data; + bool testPassed; + double testPValue; + virtual double runTest(std::vector> &data) {return -1.0;}; + }; +} + diff --git a/rng/rng/randomness_tests/runs.cpp b/rng/rng/randomness_tests/runs.cpp index a06b501..20186d4 100644 --- a/rng/rng/randomness_tests/runs.cpp +++ b/rng/rng/randomness_tests/runs.cpp @@ -1,26 +1,38 @@ #include "../rng.h" +#include "./rngtest.h" -double test_runs(std::vector> data){ - - long long totalSize = data.size() * 32; - - double vnobs = 1; - - double onesproportion = 0; - - for(int i=0; i> &testData) : RNGTEST(testData) { + testPValue = runTest(data); + testPassed = testPValue > 0.01; } - onesproportion+=data[i/32][i%32]; - } + std::string getName() override { + return "Runs"; + } + double runTest(std::vector> &data) override { + long long totalSize = data.size() * 32; - onesproportion/=totalSize; + double vnobs = 1; - return std::erfc( - std::abs(vnobs-(2*totalSize*onesproportion*(1-onesproportion))) - / - (2*std::sqrt(2*totalSize)*onesproportion*(1-onesproportion)) - ); + double onesproportion = 0; + + for(int i=0; i> &testData) : RNGTEST(testData) { + testPValue = runTest(data); + testPassed = testPValue > 0.01; + } + std::string getName() override { + return "Runs of 1s"; + } + double runTest(std::vector> &data) override { + long long totalSize = data.size() * 32; + + if(totalSize < 750000){ + std::cout << "Error, runs of ones test requires more than 750K bits of input\n"; + return 0; + } + // we do it with M=10^4 here + + std::vector counts(7,0); + + double chunks = totalSize / 10000; + + for(int chunkIndex=0; chunkIndexlongestRun) longestRun = ones; + ones = 0; + } + } + + if(longestRun <=10){ + counts[0]++; + }else if(longestRun>=16){ + counts[6]++; + }else{ + counts[longestRun-10]++; + } + } + + double x2 = 0; + + std::vector probabilities = { + 0.0882, + 0.2092, + 0.2483, + 0.1933, + 0.1208, + 0.0675, + 0.0727 + }; + + for(int i=0; i<7; i++){ + x2 += std::pow((counts[i] - (chunks * probabilities[i])),2.0) /(chunks * probabilities[i]); + + } + return igamc(3, x2/2.0); + } + }; +} \ No newline at end of file diff --git a/rng/rng/readme.md b/rng/rng/readme.md new file mode 100644 index 0000000..e5b5843 --- /dev/null +++ b/rng/rng/readme.md @@ -0,0 +1,2 @@ +🫟 Splatter +c++ rng library with generators and tests \ No newline at end of file diff --git a/rng/rng/rng.h b/rng/rng/rng.h index 4476f4f..ca4b9d5 100644 --- a/rng/rng/rng.h +++ b/rng/rng/rng.h @@ -14,3 +14,4 @@ #include #include #include +#include \ No newline at end of file diff --git a/rng/rng/run.sh b/rng/rng/run.sh index acd9d33..f1d1aaa 100755 --- a/rng/rng/run.sh +++ b/rng/rng/run.sh @@ -1,3 +1,2 @@ -g++ -std=c++11 main.cpp -o main.o -./main.o -rm main.o \ No newline at end of file +g++ -std=c++20 main.cpp -o main.o +./main.o \ No newline at end of file