From 90c9b0d515b826c3ff38e5c2eee581c28a5914c3 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 30 Dec 2024 11:07:51 -0500 Subject: [PATCH 01/95] Cpp and Cmake .gitignore --- .gitignore | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b638836 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json From 483a42b6bc0d048ef7a4b4a63a8656f59777dac9 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 30 Dec 2024 12:06:26 -0500 Subject: [PATCH 02/95] main.cpp created. --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main.cpp diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..864ab08 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,5 @@ +#include + +int main(int argc, char *argv[]){ + +} \ No newline at end of file From 03235e5ff7c98fc0b2c77b332374a85fcb96947f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Tue, 31 Dec 2024 11:33:14 -0500 Subject: [PATCH 03/95] Created initial code to get basic min and max from the commandline. --- src/main.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 864ab08..b049ba7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,46 @@ #include +#include + +// Extremely rudimentary help message for now. TODO: replace later + +struct Range { + int min; + int max; +}; + +Range get_range_from_args(int argc, char **argv){ + if (argc != 3){ + throw std::invalid_argument("Incorrect number of arguments. "); + } + + std::string min_str = std::string(argv[1]); + std::string max_str = std::string(argv[2]); + + // can throw an exception + Range range; + range.min = std::stoi(min_str); + range.max = std::stoi(max_str); + return range; +} + +void print_help_message(){ + std::string_view help = "Usage: randomizer \n"; + std::cout << help << std::endl; +} + int main(int argc, char *argv[]){ + Range range; + try { + range = get_range_from_args(argc, argv); + } catch (std::invalid_argument& e){ + print_help_message(); + exit(1); + } catch (std::out_of_range& e){ + print_help_message(); + exit(1); + } + return 0; + } \ No newline at end of file From 96408444538d95a9458a08b24c03c400fbbe5692 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Tue, 31 Dec 2024 11:50:50 -0500 Subject: [PATCH 04/95] Setting up directory structures for building --- .gitignore | 3 ++ .vscode/settings.json | 72 +++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 7 +++++ 3 files changed, 82 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 src/CMakeLists.txt diff --git a/.gitignore b/.gitignore index b638836..ae09150 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Project structure +build/* + # Prerequisites *.d diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a72700f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,72 @@ +{ + "files.associations": { + "random": "cpp", + "algorithm": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "semaphore": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "__nullptr": "cpp" + } +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..529e983 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(randomizer) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(${PROJECT_NAME} main.cpp) \ No newline at end of file From ba0d457037c421a39e8aa8f8bd9f67884429e863 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Wed, 1 Jan 2025 13:55:45 -0500 Subject: [PATCH 05/95] Basic prng class structure set up. --- src/prngs/prng.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/prngs/prng.cpp diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp new file mode 100644 index 0000000..8ecc22f --- /dev/null +++ b/src/prngs/prng.cpp @@ -0,0 +1,26 @@ +#include +#include + +class PseudoRandomNumberGenerator +{ + private: + std::uint64_t seed; + + public: + PseudoRandomNumberGenerator() { + + } + PseudoRandomNumberGenerator(std::uint64_t seed) { + seed = seed; + }; + + std::uint64_t generateCryptographicallyInsecureSeed(){ + auto current_time = std::chrono::system_clock::now(); + auto current_time_duration = current_time.time_since_epoch(); // convert a bare time to a duration + auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds + std::uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed + return seed_from_milliseconds; + } + + +}; \ No newline at end of file From f7a5814f80e3017ba8828890a935c7ae82ab6c03 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Wed, 1 Jan 2025 15:54:32 -0500 Subject: [PATCH 06/95] initial PseudoRandomNumberGenerator abstract base class. --- src/prngs/prng.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 8ecc22f..01e09e1 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -8,10 +8,11 @@ class PseudoRandomNumberGenerator public: PseudoRandomNumberGenerator() { - + this->seed = generateCryptographicallyInsecureSeed(); } - PseudoRandomNumberGenerator(std::uint64_t seed) { - seed = seed; + + PseudoRandomNumberGenerator(const std::uint64_t seed) { + this->seed = seed; }; std::uint64_t generateCryptographicallyInsecureSeed(){ @@ -22,5 +23,5 @@ class PseudoRandomNumberGenerator return seed_from_milliseconds; } - + virtual uint64_t generateRandomValue() = 0; }; \ No newline at end of file From 6b998b979c902bd0bca01c48c12b4c759284eeab Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Wed, 1 Jan 2025 17:10:28 -0500 Subject: [PATCH 07/95] Moved class interface to header file. --- src/prngs/prng.cpp | 36 ++++++++++++++---------------------- src/prngs/prng.hpp | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 src/prngs/prng.hpp diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 01e09e1..53af5b1 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -1,27 +1,19 @@ #include #include +#include "prng.hpp" -class PseudoRandomNumberGenerator -{ - private: - std::uint64_t seed; +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() { + this->seed = generateCryptographicallyInsecureSeed(); +}; - public: - PseudoRandomNumberGenerator() { - this->seed = generateCryptographicallyInsecureSeed(); - } +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) { + this->seed = seed; +}; - PseudoRandomNumberGenerator(const std::uint64_t seed) { - this->seed = seed; - }; - - std::uint64_t generateCryptographicallyInsecureSeed(){ - auto current_time = std::chrono::system_clock::now(); - auto current_time_duration = current_time.time_since_epoch(); // convert a bare time to a duration - auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds - std::uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed - return seed_from_milliseconds; - } - - virtual uint64_t generateRandomValue() = 0; -}; \ No newline at end of file +std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed() { + auto current_time = std::chrono::system_clock::now(); + auto current_time_duration = current_time.time_since_epoch(); // convert a bare time to a duration + auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds + std::uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed + return seed_from_milliseconds; +}; diff --git a/src/prngs/prng.hpp b/src/prngs/prng.hpp new file mode 100644 index 0000000..e95ce01 --- /dev/null +++ b/src/prngs/prng.hpp @@ -0,0 +1,21 @@ +#ifndef PRNG_H +#define PRNG_H + +#include + +class PseudoRandomNumberGenerator +{ + private: + std::uint64_t seed; + + public: + PseudoRandomNumberGenerator(); + + PseudoRandomNumberGenerator(const std::uint64_t seed); + + std::uint64_t generateCryptographicallyInsecureSeed(); + + virtual uint64_t generateRandomValue() = 0; +}; + +#endif \ No newline at end of file From f00ee7b7ab247afef7164bceed9d6cc83fa0a6af Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 14:18:01 -0400 Subject: [PATCH 08/95] Template for the MersenneTwister. --- src/prngs/mersenne_twister.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/prngs/mersenne_twister.cpp diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp new file mode 100644 index 0000000..8ee4302 --- /dev/null +++ b/src/prngs/mersenne_twister.cpp @@ -0,0 +1,23 @@ +#include "prng.hpp" + +class MersenneTwister : PseudoRandomNumberGenerator { + + /* MT19937-64 coefficients: + w = 64 + n = 312 + m = 156 + r = 31 + a = 0xb5026f5aa96619e9 + u = 29 + d = 0x5555555555555555 + s = 17 + b = 0x71d67fffeda60000 + t = 37 + c = 0xfff7eee000000000 + l = 43 + f = 6364136223846793005 + */ + MersenneTwister(){ + + } +}; \ No newline at end of file From 93334b32641114e9d3f388108e803b984117301e Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 14:54:55 -0400 Subject: [PATCH 09/95] Added linear congruential generator template. --- src/prngs/linear_congruential_generator.cpp | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/prngs/linear_congruential_generator.cpp diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp new file mode 100644 index 0000000..8ee4302 --- /dev/null +++ b/src/prngs/linear_congruential_generator.cpp @@ -0,0 +1,23 @@ +#include "prng.hpp" + +class MersenneTwister : PseudoRandomNumberGenerator { + + /* MT19937-64 coefficients: + w = 64 + n = 312 + m = 156 + r = 31 + a = 0xb5026f5aa96619e9 + u = 29 + d = 0x5555555555555555 + s = 17 + b = 0x71d67fffeda60000 + t = 37 + c = 0xfff7eee000000000 + l = 43 + f = 6364136223846793005 + */ + MersenneTwister(){ + + } +}; \ No newline at end of file From befb9a500cb02668e9864b9a369da314d26c115f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 16:18:48 -0400 Subject: [PATCH 10/95] Refactored the prng files. --- src/prngs/prng.cpp | 13 ++++++------- src/prngs/prng.hpp | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 53af5b1..f02667a 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -1,14 +1,13 @@ #include #include +#include #include "prng.hpp" -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() { - this->seed = generateCryptographicallyInsecureSeed(); -}; +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() + : m_seed(generateCryptographicallyInsecureSeed()) {}; -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) { - this->seed = seed; -}; +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) + : m_seed(seed) {}; std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed() { auto current_time = std::chrono::system_clock::now(); @@ -16,4 +15,4 @@ std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds std::uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed return seed_from_milliseconds; -}; +}; \ No newline at end of file diff --git a/src/prngs/prng.hpp b/src/prngs/prng.hpp index e95ce01..7043b41 100644 --- a/src/prngs/prng.hpp +++ b/src/prngs/prng.hpp @@ -6,7 +6,7 @@ class PseudoRandomNumberGenerator { private: - std::uint64_t seed; + const std::uint64_t m_seed; public: PseudoRandomNumberGenerator(); @@ -15,7 +15,7 @@ class PseudoRandomNumberGenerator std::uint64_t generateCryptographicallyInsecureSeed(); - virtual uint64_t generateRandomValue() = 0; + virtual std::uint64_t generateRandomValue() = 0; }; #endif \ No newline at end of file From 56355421a3a49f2fe1c98d10d8c980500d7dc605 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 16:19:25 -0400 Subject: [PATCH 11/95] Starting on the implementation of the Linear Congruential Generator. --- src/prngs/linear_congruential_generator.cpp | 97 +++++++++++++++++---- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 8ee4302..9c3e9a7 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -1,23 +1,84 @@ #include "prng.hpp" +#include -class MersenneTwister : PseudoRandomNumberGenerator { - - /* MT19937-64 coefficients: - w = 64 - n = 312 - m = 156 - r = 31 - a = 0xb5026f5aa96619e9 - u = 29 - d = 0x5555555555555555 - s = 17 - b = 0x71d67fffeda60000 - t = 37 - c = 0xfff7eee000000000 - l = 43 - f = 6364136223846793005 + +class LinearCongruentialGenerator : PseudoRandomNumberGenerator { + + // https://en.wikipedia.org/wiki/Linear_congruential_generator# + + /* + LCGs are defined as X_{n+1} = (aX_{n} + c) \mod{m} + Where X is the vector of pseudorandom values produced + The values a, c, and m are constants + m is the modulus + a is the multiplier + c is the increment + + + glibc uses m=2^{31} a=1103515245 c=12345 and masks the result with 0x3FFFFFFF + which is what is used for the default implementation here */ - MersenneTwister(){ + LinearCongruentialGenerator() + : PseudoRandomNumberGenerator(), + m_modulus(DefaultModulus), + m_multiplier(DefaultMultiplier), + m_increment(DefaultIncrement), + m_mask(DefaultMask) {}; + + LinearCongruentialGenerator(std::uint64_t seed) + : PseudoRandomNumberGenerator(seed), + m_modulus(DefaultModulus), + m_multiplier(DefaultMultiplier), + m_increment(DefaultIncrement), + m_mask(DefaultMask) {}; + + LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) + : PseudoRandomNumberGenerator(), + m_modulus(modulus), + m_multiplier(multiplier), + m_increment(increment), + m_mask(mask) {}; + + LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) + : PseudoRandomNumberGenerator(seed), + m_modulus(modulus), + m_multiplier(multiplier), + m_increment(increment), + m_mask(mask) {}; + + + + private: + + // Defaults used from the glibc implementation, see: + /* @misc{ enwiki:1280426923, + author = "{Wikipedia contributors}", + title = "Linear congruential generator --- {Wikipedia}{,} The Free Encyclopedia", + year = "2025", + url = "https://en.wikipedia.org/w/index.php?title=Linear_congruential_generator&oldid=1280426923", + note = "[Online; accessed 10-May-2025]" + } */ + static constexpr std::uint64_t DefaultModulus = 0x7FFFFFFF; // 2^31 - 1 + static constexpr std::uint64_t DefaultMultiplier = 1103515245; + static constexpr std::uint64_t DefaultIncrement = 12345; + static constexpr std::uint64_t DefaultMask = 0x3FFFFFFF; + + const std::uint64_t m_modulus; + const std::uint64_t m_multiplier; + const std::uint64_t m_increment; + const std::uint64_t m_mask; + std::uint64_t m_current_value = 0; // Current value of the generator + + public: + std::uint64_t generateRandomValue() override { + + // Compute the standard LCG formula for the next value + m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; + + // Apply the mask + m_current_value = m_current_value & m_mask; + + return m_current_value; + }; - } }; \ No newline at end of file From 62124f118e41756718d7379a27729d7b3bbb5fde Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 16:48:17 -0400 Subject: [PATCH 12/95] Moved the definition of LinearCongruentialGenerator into .hpp and implementation into .cpp --- src/prngs/linear_congruential_generator.cpp | 125 +++++++------------- src/prngs/linear_congruential_generator.hpp | 52 ++++++++ 2 files changed, 96 insertions(+), 81 deletions(-) create mode 100644 src/prngs/linear_congruential_generator.hpp diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 9c3e9a7..51bd882 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -1,84 +1,47 @@ -#include "prng.hpp" #include +#include "linear_congruential_generator.hpp" +#include "prng.hpp" -class LinearCongruentialGenerator : PseudoRandomNumberGenerator { - - // https://en.wikipedia.org/wiki/Linear_congruential_generator# - - /* - LCGs are defined as X_{n+1} = (aX_{n} + c) \mod{m} - Where X is the vector of pseudorandom values produced - The values a, c, and m are constants - m is the modulus - a is the multiplier - c is the increment - - - glibc uses m=2^{31} a=1103515245 c=12345 and masks the result with 0x3FFFFFFF - which is what is used for the default implementation here - */ - LinearCongruentialGenerator() - : PseudoRandomNumberGenerator(), - m_modulus(DefaultModulus), - m_multiplier(DefaultMultiplier), - m_increment(DefaultIncrement), - m_mask(DefaultMask) {}; - - LinearCongruentialGenerator(std::uint64_t seed) - : PseudoRandomNumberGenerator(seed), - m_modulus(DefaultModulus), - m_multiplier(DefaultMultiplier), - m_increment(DefaultIncrement), - m_mask(DefaultMask) {}; - - LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(), - m_modulus(modulus), - m_multiplier(multiplier), - m_increment(increment), - m_mask(mask) {}; - - LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(seed), - m_modulus(modulus), - m_multiplier(multiplier), - m_increment(increment), - m_mask(mask) {}; - - - - private: - - // Defaults used from the glibc implementation, see: - /* @misc{ enwiki:1280426923, - author = "{Wikipedia contributors}", - title = "Linear congruential generator --- {Wikipedia}{,} The Free Encyclopedia", - year = "2025", - url = "https://en.wikipedia.org/w/index.php?title=Linear_congruential_generator&oldid=1280426923", - note = "[Online; accessed 10-May-2025]" - } */ - static constexpr std::uint64_t DefaultModulus = 0x7FFFFFFF; // 2^31 - 1 - static constexpr std::uint64_t DefaultMultiplier = 1103515245; - static constexpr std::uint64_t DefaultIncrement = 12345; - static constexpr std::uint64_t DefaultMask = 0x3FFFFFFF; - - const std::uint64_t m_modulus; - const std::uint64_t m_multiplier; - const std::uint64_t m_increment; - const std::uint64_t m_mask; - std::uint64_t m_current_value = 0; // Current value of the generator - - public: - std::uint64_t generateRandomValue() override { - - // Compute the standard LCG formula for the next value - m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; - - // Apply the mask - m_current_value = m_current_value & m_mask; - - return m_current_value; - }; - -}; \ No newline at end of file +// Default constructor +LinearCongruentialGenerator::LinearCongruentialGenerator() + : PseudoRandomNumberGenerator(), + m_modulus(DefaultModulus), + m_multiplier(DefaultMultiplier), + m_increment(DefaultIncrement), + m_mask(DefaultMask) {} + +// Constructor with seed +LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t seed) + : PseudoRandomNumberGenerator(seed), + m_modulus(DefaultModulus), + m_multiplier(DefaultMultiplier), + m_increment(DefaultIncrement), + m_mask(DefaultMask) {} + +// Constructor with custom parameters +LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) + : PseudoRandomNumberGenerator(), + m_modulus(modulus), + m_multiplier(multiplier), + m_increment(increment), + m_mask(mask) {} + +// Constructor with seed and custom parameters +LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) + : PseudoRandomNumberGenerator(seed), + m_modulus(modulus), + m_multiplier(multiplier), + m_increment(increment), + m_mask(mask) {} + +// Generate a random value +std::uint64_t LinearCongruentialGenerator::generateRandomValue() { + // Compute the standard LCG formula for the next value + m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; + + // Apply the mask + m_current_value = m_current_value & m_mask; + + return m_current_value; +} \ No newline at end of file diff --git a/src/prngs/linear_congruential_generator.hpp b/src/prngs/linear_congruential_generator.hpp new file mode 100644 index 0000000..4abdcd3 --- /dev/null +++ b/src/prngs/linear_congruential_generator.hpp @@ -0,0 +1,52 @@ +#ifndef LINEAR_CONGRUENTIAL_GENERATOR_H +#define LINEAR_CONGRUENTIAL_GENERATOR_H + +#include +#include "prng.hpp" + +class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { + // https://en.wikipedia.org/wiki/Linear_congruential_generator# + + /* + LCGs are defined as X_{n+1} = (aX_{n} + c) \mod{m} + Where X is the vector of pseudorandom values produced + The values a, c, and m are constants + m is the modulus + a is the multiplier + c is the increment + + glibc uses m=2^{31} a=1103515245 c=12345 and masks the result with 0x3FFFFFFF + which is what is used for the default implementation here + */ +private: + + // Defaults used from the glibc implementation, see: + /* @misc{ enwiki:1280426923, + author = "{Wikipedia contributors}", + title = "Linear congruential generator --- {Wikipedia}{,} The Free Encyclopedia", + year = "2025", + url = "https://en.wikipedia.org/w/index.php?title=Linear_congruential_generator&oldid=1280426923", + note = "[Online; accessed 10-May-2025]" + } */ + + static constexpr std::uint64_t DefaultModulus = 0x7FFFFFFF; + static constexpr std::uint64_t DefaultMultiplier = 1103515245; + static constexpr std::uint64_t DefaultIncrement = 12345; + static constexpr std::uint64_t DefaultMask = 0x7FFFFFFF; // bits 0 through 30 + + const std::uint64_t m_modulus; + const std::uint64_t m_multiplier; + const std::uint64_t m_increment; + const std::uint64_t m_mask; + std::uint64_t m_current_value = 0; + +public: + LinearCongruentialGenerator(); + LinearCongruentialGenerator(const std::uint64_t seed); + LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask); + LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask); + + std::uint64_t generateRandomValue() override; +}; + +#endif \ No newline at end of file From 718cdae6362701a9119b404eae86e30da210035c Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 19:05:42 -0400 Subject: [PATCH 13/95] Re-arranged directory structure and fixed CMake file. --- CMakeLists.txt | 19 +++++++++++++++++++ .../linear_congruential_generator.hpp | 0 {src/prngs => include}/prng.hpp | 0 src/CMakeLists.txt | 7 ------- src/main.cpp | 2 ++ 5 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 CMakeLists.txt rename {src/prngs => include}/linear_congruential_generator.hpp (100%) rename {src/prngs => include}/prng.hpp (100%) delete mode 100644 src/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f7882d5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.10) +project(randomizer) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +# Set output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# Include directories +include_directories(include) + +# Automatically collect all source and header files +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE HEADERS "include/*.hpp") + +# Define main executable +add_executable(${PROJECT_NAME} ${SOURCES}) \ No newline at end of file diff --git a/src/prngs/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp similarity index 100% rename from src/prngs/linear_congruential_generator.hpp rename to include/linear_congruential_generator.hpp diff --git a/src/prngs/prng.hpp b/include/prng.hpp similarity index 100% rename from src/prngs/prng.hpp rename to include/prng.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 529e983..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(randomizer) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -add_executable(${PROJECT_NAME} main.cpp) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b049ba7..d7b14b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,8 @@ int main(int argc, char *argv[]){ exit(1); } + std::cout << "Random number between " << range.min << " and " << range.max << "\n"; + return 0; } \ No newline at end of file From e486055f031265df27174031481ce0d2f0d68788 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 19:19:55 -0400 Subject: [PATCH 14/95] Updated vscode settings for CMakeLists.txt location. --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a72700f..c63f557 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,5 +68,6 @@ "typeindex": "cpp", "typeinfo": "cpp", "__nullptr": "cpp" - } + }, + "cmake.sourceDirectory": "/home/eric/Projects/Randomizer/" } \ No newline at end of file From 07e97df791b6f68b3ce20acdaafc4dccecac9e50 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 21:22:39 -0400 Subject: [PATCH 15/95] Upped the minimum versino for cmake to version 3.21 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7882d5..6019290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.21) project(randomizer) set(CMAKE_CXX_STANDARD 17) From cbfd109fe7e3c9fc42459ad56d7346478b48722b Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 21:23:23 -0400 Subject: [PATCH 16/95] Fixed bug where LCG did not properly assign the initial value to seed and therefore produced the same stream every isntance. --- include/prng.hpp | 2 +- src/main.cpp | 11 ++++++++++- src/prngs/linear_congruential_generator.cpp | 12 ++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/prng.hpp b/include/prng.hpp index 7043b41..81c81da 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -5,7 +5,7 @@ class PseudoRandomNumberGenerator { - private: + protected: const std::uint64_t m_seed; public: diff --git a/src/main.cpp b/src/main.cpp index d7b14b1..60484d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,8 @@ #include #include +#include "prng.hpp" +#include "linear_congruential_generator.hpp" -// Extremely rudimentary help message for now. TODO: replace later struct Range { int min; @@ -41,8 +42,16 @@ int main(int argc, char *argv[]){ exit(1); } + auto lcg = LinearCongruentialGenerator(); + + std::cout << "Random number between " << range.min << " and " << range.max << "\n"; + std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + + return 0; } \ No newline at end of file diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 51bd882..9820511 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -9,7 +9,8 @@ LinearCongruentialGenerator::LinearCongruentialGenerator() m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), - m_mask(DefaultMask) {} + m_mask(DefaultMask), + m_current_value(m_seed) {} // Constructor with seed LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t seed) @@ -17,7 +18,8 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t see m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), - m_mask(DefaultMask) {} + m_mask(DefaultMask), + m_current_value(m_seed) {} // Constructor with custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) @@ -25,7 +27,8 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), - m_mask(mask) {} + m_mask(mask), + m_current_value(m_seed) {} // Constructor with seed and custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) @@ -33,7 +36,8 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), - m_mask(mask) {} + m_mask(mask), + m_current_value(m_seed) {} // Generate a random value std::uint64_t LinearCongruentialGenerator::generateRandomValue() { From 84abb7c6df8a4155e50da08f72ca392400eb77ee Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 10 May 2025 21:50:30 -0400 Subject: [PATCH 17/95] Got debugging working in vscode --- .vscode/launch.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8790b29 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [3, 4], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "CMake: build", + } + + ] +} \ No newline at end of file From 54610550bee57a4c019a716b45327ea1711d6810 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 11 May 2025 13:14:51 -0400 Subject: [PATCH 18/95] Updated PRNG and LCG to support unit normailzed floats as outputs. --- include/linear_congruential_generator.hpp | 12 +++++ include/prng.hpp | 8 ++- src/prngs/linear_congruential_generator.cpp | 59 ++++++++++++++++++--- src/prngs/prng.cpp | 22 ++++++-- 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/include/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp index 4abdcd3..0581ec8 100644 --- a/include/linear_congruential_generator.hpp +++ b/include/linear_congruential_generator.hpp @@ -40,6 +40,17 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { const std::uint64_t m_mask; std::uint64_t m_current_value = 0; + + // The minimum and maximum values can be determined by taking into + // account the bit indexes of the less significant and most significant + // bits of the mask. The minimum value would be 2^{i} - 1, where i is the + // index of the least significant bit of the mask. The maximum value + // would be 2^{j} - 1, where j is the index of the most significant bit + // of the mask. + static std::uint64_t getMinimumValueFromMask(const std::uint64_t mask); + + static std::uint64_t getMaximumValueFromMask(const std::uint64_t mask); + public: LinearCongruentialGenerator(); LinearCongruentialGenerator(const std::uint64_t seed); @@ -47,6 +58,7 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask); std::uint64_t generateRandomValue() override; + double generateUnitNormalRandomValue() override; }; #endif \ No newline at end of file diff --git a/include/prng.hpp b/include/prng.hpp index 81c81da..45d672b 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -7,15 +7,19 @@ class PseudoRandomNumberGenerator { protected: const std::uint64_t m_seed; + const std::uint64_t m_minimum_value; + const std::uint64_t m_maximum_value; + public: PseudoRandomNumberGenerator(); - - PseudoRandomNumberGenerator(const std::uint64_t seed); + PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value); + PseudoRandomNumberGenerator(const std::uint64_t seed, const std::uint64_t minimum_value, const std::uint64_t maximum_value); std::uint64_t generateCryptographicallyInsecureSeed(); virtual std::uint64_t generateRandomValue() = 0; + virtual double generateUnitNormalRandomValue() = 0; }; #endif \ No newline at end of file diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 9820511..60d0b6e 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -5,7 +5,7 @@ // Default constructor LinearCongruentialGenerator::LinearCongruentialGenerator() - : PseudoRandomNumberGenerator(), + : PseudoRandomNumberGenerator(getMinimumValueFromMask(DefaultMask), getMaximumValueFromMask(DefaultMask)), m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), @@ -14,7 +14,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator() // Constructor with seed LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t seed) - : PseudoRandomNumberGenerator(seed), + : PseudoRandomNumberGenerator(seed, getMinimumValueFromMask(DefaultMask), getMaximumValueFromMask(DefaultMask)), m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), @@ -23,7 +23,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t see // Constructor with custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(), + : PseudoRandomNumberGenerator(getMinimumValueFromMask(mask), getMaximumValueFromMask(mask)), m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), @@ -32,14 +32,45 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, // Constructor with seed and custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(seed), + : PseudoRandomNumberGenerator(seed, getMinimumValueFromMask(mask), getMaximumValueFromMask(mask)), m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), m_mask(mask), m_current_value(m_seed) {} -// Generate a random value + +// The minimum and maximum values can be determined by taking into +// account the bit indexes of the less significant and most significant +// bits of the mask. The minimum value would be 2^{i} - 1, where i is the +// index of the least significant bit of the mask. The maximum value +// would be 2^{j} - 1, where j is the index of the most significant bit +// of the mask. +static std::uint64_t getMinimumValueFromMask(const std::uint64_t mask) { + + // Find the index of the least significant bit + std::uint64_t least_significant_bit_index = 0; + std::uint64_t least_significant_compare_mask = mask; + while ((least_significant_compare_mask & 1) == 0) { + least_significant_compare_mask >>= 1; + least_significant_bit_index++; + } + return (1 << least_significant_bit_index) - 1; +} + +static std::uint64_t getMaximumValueFromMask(const std::uint64_t mask) { + // Find the index of the most significant bit + std::uint64_t most_significant_bit_index = 0; + std::uint64_t most_significant_compare_mask = mask; + while (most_significant_compare_mask > 1) { + most_significant_compare_mask >>= 1; + most_significant_bit_index++; + } + + return (1 << most_significant_bit_index) - 1; +} + +// Generate a random value anywhere in the range of the LCG std::uint64_t LinearCongruentialGenerator::generateRandomValue() { // Compute the standard LCG formula for the next value m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; @@ -48,4 +79,20 @@ std::uint64_t LinearCongruentialGenerator::generateRandomValue() { m_current_value = m_current_value & m_mask; return m_current_value; -} \ No newline at end of file +} + +// Generate a random value normalized to the range [0, 1) +double LinearCongruentialGenerator::generateUnitNormalRandomValue() { + + std::uint64_t random_value = generateRandomValue(); + + std::uint64_t range = m_maximum_value - m_minimum_value; + if (range == 0) { + return 0.0; // Avoid division by zero + } + + // Normalize the random value to [0, 1) + double normalized_value = static_cast(random_value - m_minimum_value) / range; + return normalized_value; +} + diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index f02667a..719fc77 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -1,13 +1,29 @@ #include #include #include +#include #include "prng.hpp" +/* The most default constructor will create a random seed from the system time +and assume that the range of the PRNG is the full range of the unsigned 64-bit +integer. While this is the only sensible default option, it is dangerous as it is +not generally true and will likely have the PRNG produce highly incorrect values when +generating uniform random floats. Use with caution! */ PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() - : m_seed(generateCryptographicallyInsecureSeed()) {}; + : m_seed(generateCryptographicallyInsecureSeed()), + m_minimum_value(std::numeric_limits::min()), + m_maximum_value(std::numeric_limits::max()) {} -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) - : m_seed(seed) {}; +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value) + : m_seed(generateCryptographicallyInsecureSeed()), + m_minimum_value(minimum_value), + m_maximum_value(maximum_value) {} + + +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed, const std::uint64_t minimum_value, const std::uint64_t maximum_value) + : m_seed(seed), + m_minimum_value(minimum_value), + m_maximum_value(maximum_value) {} std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed() { auto current_time = std::chrono::system_clock::now(); From c8e46a46f005b5bbcb03cf03976f0b1bcbec6c1c Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 11 May 2025 15:42:34 -0400 Subject: [PATCH 19/95] Fully bug fixed the LCF unit normal generation. --- include/linear_congruential_generator.hpp | 11 ++----- src/main.cpp | 6 ++-- src/prngs/linear_congruential_generator.cpp | 36 ++++++++------------- 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/include/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp index 0581ec8..afa6da4 100644 --- a/include/linear_congruential_generator.hpp +++ b/include/linear_congruential_generator.hpp @@ -41,15 +41,8 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { std::uint64_t m_current_value = 0; - // The minimum and maximum values can be determined by taking into - // account the bit indexes of the less significant and most significant - // bits of the mask. The minimum value would be 2^{i} - 1, where i is the - // index of the least significant bit of the mask. The maximum value - // would be 2^{j} - 1, where j is the index of the most significant bit - // of the mask. - static std::uint64_t getMinimumValueFromMask(const std::uint64_t mask); - - static std::uint64_t getMaximumValueFromMask(const std::uint64_t mask); + static std::uint64_t getMinimumValue(const std::uint64_t mask); + static std::uint64_t getMaximumValue(const std::uint64_t modulus, std::uint64_t mask); public: LinearCongruentialGenerator(); diff --git a/src/main.cpp b/src/main.cpp index 60484d6..41c9774 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,9 +47,9 @@ int main(int argc, char *argv[]){ std::cout << "Random number between " << range.min << " and " << range.max << "\n"; - std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; - std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; - std::cout << lcg.generateRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; return 0; diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 60d0b6e..f12c7d5 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -1,11 +1,12 @@ #include +#include #include "linear_congruential_generator.hpp" #include "prng.hpp" // Default constructor LinearCongruentialGenerator::LinearCongruentialGenerator() - : PseudoRandomNumberGenerator(getMinimumValueFromMask(DefaultMask), getMaximumValueFromMask(DefaultMask)), + : PseudoRandomNumberGenerator(getMinimumValue(DefaultMask), getMaximumValue(DefaultModulus, DefaultMask)), m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), @@ -14,7 +15,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator() // Constructor with seed LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t seed) - : PseudoRandomNumberGenerator(seed, getMinimumValueFromMask(DefaultMask), getMaximumValueFromMask(DefaultMask)), + : PseudoRandomNumberGenerator(seed, getMinimumValue(DefaultMask), getMaximumValue(DefaultModulus, DefaultMask)), m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), m_increment(DefaultIncrement), @@ -23,7 +24,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t see // Constructor with custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(getMinimumValueFromMask(mask), getMaximumValueFromMask(mask)), + : PseudoRandomNumberGenerator(getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), @@ -32,7 +33,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, // Constructor with seed and custom parameters LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) - : PseudoRandomNumberGenerator(seed, getMinimumValueFromMask(mask), getMaximumValueFromMask(mask)), + : PseudoRandomNumberGenerator(seed, getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), m_increment(increment), @@ -40,17 +41,13 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std m_current_value(m_seed) {} -// The minimum and maximum values can be determined by taking into -// account the bit indexes of the less significant and most significant -// bits of the mask. The minimum value would be 2^{i} - 1, where i is the -// index of the least significant bit of the mask. The maximum value -// would be 2^{j} - 1, where j is the index of the most significant bit -// of the mask. -static std::uint64_t getMinimumValueFromMask(const std::uint64_t mask) { +std::uint64_t LinearCongruentialGenerator::getMinimumValue(const std::uint64_t mask) { // Find the index of the least significant bit std::uint64_t least_significant_bit_index = 0; std::uint64_t least_significant_compare_mask = mask; + + // We shift bits until we reach a 1, incrementing the counter each time while ((least_significant_compare_mask & 1) == 0) { least_significant_compare_mask >>= 1; least_significant_bit_index++; @@ -58,16 +55,8 @@ static std::uint64_t getMinimumValueFromMask(const std::uint64_t mask) { return (1 << least_significant_bit_index) - 1; } -static std::uint64_t getMaximumValueFromMask(const std::uint64_t mask) { - // Find the index of the most significant bit - std::uint64_t most_significant_bit_index = 0; - std::uint64_t most_significant_compare_mask = mask; - while (most_significant_compare_mask > 1) { - most_significant_compare_mask >>= 1; - most_significant_bit_index++; - } - - return (1 << most_significant_bit_index) - 1; +std::uint64_t LinearCongruentialGenerator::getMaximumValue(const std::uint64_t modulus, std::uint64_t mask) { + return std::min(modulus, mask); } // Generate a random value anywhere in the range of the LCG @@ -76,7 +65,10 @@ std::uint64_t LinearCongruentialGenerator::generateRandomValue() { m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; // Apply the mask - m_current_value = m_current_value & m_mask; + m_current_value = (m_current_value & m_mask); + + // Ensure the value is within the specified range + m_current_value = m_current_value - m_minimum_value; return m_current_value; } From 1334534694a33c3f4d0e645c06cf29b9635e948d Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 11 May 2025 18:00:35 -0400 Subject: [PATCH 20/95] Implemented XORShift PRNG --- include/prng.hpp | 1 + include/xorshift.hpp | 40 +++++++++++++++++++++++++++++++ src/main.cpp | 9 +++---- src/prngs/prng.cpp | 6 +++++ src/prngs/xorshift.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 include/xorshift.hpp create mode 100644 src/prngs/xorshift.cpp diff --git a/include/prng.hpp b/include/prng.hpp index 45d672b..dee8313 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -13,6 +13,7 @@ class PseudoRandomNumberGenerator public: PseudoRandomNumberGenerator(); + PseudoRandomNumberGenerator(const std::uint64_t seed); PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value); PseudoRandomNumberGenerator(const std::uint64_t seed, const std::uint64_t minimum_value, const std::uint64_t maximum_value); diff --git a/include/xorshift.hpp b/include/xorshift.hpp new file mode 100644 index 0000000..8d2d20b --- /dev/null +++ b/include/xorshift.hpp @@ -0,0 +1,40 @@ +#ifndef XOR_SHIFT_H +#define XOR_SHIFT_H + +#include +#include "prng.hpp" + +class XORShift : public PseudoRandomNumberGenerator { + +// @misc{ enwiki:1287473197, +// author = "{Wikipedia contributors}", +// title = "Xorshift --- {Wikipedia}{,} The Free Encyclopedia", +// year = "2025", +// url = "https://en.wikipedia.org/w/index.php?title=Xorshift&oldid=1287473197", +// note = "[Online; accessed 11-May-2025]" +// } +private: + + + // Default values from https://en.wikipedia.org/wiki/Xorshift + static constexpr std::uint64_t DefaultA = 13; + static constexpr std::uint64_t DefaultB = 7; + static constexpr std::uint64_t DefaultC = 17; + + // XORShift has 3 constants that are used for shifting + const std::uint64_t m_a; + const std::uint64_t m_b; + const std::uint64_t m_c; + std::uint64_t m_current_value = 0; + +public: + XORShift(); + XORShift(const std::uint64_t seed); + XORShift(const std::uint64_t seed, const std::uint64_t a, const std::uint64_t b, const std::uint64_t c); + + + std::uint64_t generateRandomValue() override; + double generateUnitNormalRandomValue() override; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 41c9774..d700c5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include #include "prng.hpp" #include "linear_congruential_generator.hpp" +#include "xorshift.hpp" struct Range { @@ -42,14 +43,14 @@ int main(int argc, char *argv[]){ exit(1); } - auto lcg = LinearCongruentialGenerator(); + auto xorshift = XORShift(); std::cout << "Random number between " << range.min << " and " << range.max << "\n"; - std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; - std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; - std::cout << lcg.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the LCG.\n"; + std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; + std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; + std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; return 0; diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 719fc77..acd9e39 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -14,6 +14,12 @@ PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() m_minimum_value(std::numeric_limits::min()), m_maximum_value(std::numeric_limits::max()) {} + +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) + : m_seed(seed), + m_minimum_value(std::numeric_limits::min()), + m_maximum_value(std::numeric_limits::max()) {} + PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value) : m_seed(generateCryptographicallyInsecureSeed()), m_minimum_value(minimum_value), diff --git a/src/prngs/xorshift.cpp b/src/prngs/xorshift.cpp new file mode 100644 index 0000000..15eea9b --- /dev/null +++ b/src/prngs/xorshift.cpp @@ -0,0 +1,54 @@ +#include +#include "xorshift.hpp" +#include "prng.hpp" + + +// Default constructor +XORShift::XORShift() + : PseudoRandomNumberGenerator(), + m_a(DefaultA), + m_b(DefaultB), + m_c(DefaultC), + m_current_value(m_seed) {} + +XORShift::XORShift(const std::uint64_t seed) + : PseudoRandomNumberGenerator(seed), + m_a(DefaultA), + m_b(DefaultB), + m_c(DefaultC), + m_current_value(m_seed) {} + +XORShift::XORShift(const std::uint64_t seed, const std::uint64_t a, const std::uint64_t b, const std::uint64_t c) + : PseudoRandomNumberGenerator(seed), + m_a(a), + m_b(b), + m_c(c), + m_current_value(m_seed) {} + + + +// Generate a random value anywhere in the range of the XORShift algorithm +std::uint64_t XORShift::generateRandomValue() { + // Compute the standard LCG formula for the next value + m_current_value ^= (m_current_value << m_a); + m_current_value ^= (m_current_value >> m_b); + m_current_value ^= (m_current_value << m_c); + + return m_current_value; +} + +// Generate a random value normalized to the range [0, 1) +double XORShift::generateUnitNormalRandomValue() { + + std::uint64_t random_value = generateRandomValue(); + + std::uint64_t range = m_maximum_value - m_minimum_value; + if (range == 0) { + return 0.0; // Avoid division by zero + } + + // Normalize the random value to [0, 1) + double normalized_value = static_cast(random_value - m_minimum_value) / range; + return normalized_value; +} + From 6f8c421cfd5502d5d39d018d6575a03920655349 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 17 May 2025 15:48:53 -0400 Subject: [PATCH 21/95] Started code for getting command line arguments and parsing them with getopt --- src/arguments.cpp | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/arguments.cpp diff --git a/src/arguments.cpp b/src/arguments.cpp new file mode 100644 index 0000000..afc9290 --- /dev/null +++ b/src/arguments.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +void print_usage(const char* program_name) { + std::cout << "Usage: " << program_name << " [options]\n" + << "Options:\n" + << " -h, --help Show this help message\n" + << " -v, --version Show version information\n" + << " -a, --algorithm Specify the algorithm (default: xorshift)\n" + << " -m, --min Minimum value\n" + << " -M, --max Maximum value\n" + << " -c, --count Number of random numbers to generate\n" + << " -u, --unit Generate unit normal random numbers\n" + << " -f, --float Generate floating-point random numbers\n" + << " -i, --integer Generate integer random numbers\n"; +} + +enum class Algorithms { + XORShift, + LinearCongruentialGenerator, + // Add other algorithms here +}; + +std::optional get_algorithm(const std::string& algorithm_str) { + if (algorithm_str == "xorshift") { + return Algorithms::XORShift; + } else if (algorithm_str == "lcg" or algorithm_str == "linear-congruential-generator") { + return Algorithms::LinearCongruentialGenerator; + } + return std::nullopt; // Unknown algorithm +} + +enum class ProgramBehaviour { + Error, + Help, + Version, + GenerateUnitNormal, + GenerateFloating, + GenerateInteger +}; + +struct ProgramConfiguration { + ProgramBehaviour option; + Algorithms algorithm; + std::optional> min; + std::optional> max; + std::optional count; +}; + +struct RawArguments { + bool error; + bool show_help; + bool show_version; + bool unit; + bool floating; + bool integer; + std::string algorithm_str; + std::string min_str; + std::string max_str; + std::string count_str; +}; + + + +RawArguments parse_args(int argc, char* argv[]) { + const char* const short_opts = "hva:m:M:c:ufi"; + const option long_opts[] = { + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {"algorithm", required_argument, nullptr, 'a'}, + {"min", required_argument, nullptr, 'm'}, + {"max", required_argument, nullptr, 'M'}, + {"count", required_argument, nullptr, 'c'}, + {"unit", no_argument, nullptr, 'u'}, + {"float", no_argument, nullptr, 'f'}, + {"integer", no_argument, nullptr, 'i'}, + {nullptr, 0, nullptr, 0} + }; + + RawArguments raw_arguments; + + raw_arguments.error = false; + raw_arguments.show_help = false; + raw_arguments.show_version = false; + + raw_arguments.algorithm_str = "xorshift"; // Default algorithm + + // Standard defaults if a user does not provide a range they want to use, will roll a dice. + raw_arguments.min_str = "1"; + raw_arguments.max_str = "6"; + + raw_arguments.count_str = "1"; + + // By default we will be rolling a dice, so we will be generating integers. + raw_arguments.unit = false; + raw_arguments.floating = false; + raw_arguments.integer = true; + + while (true) { + const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr); + + if (opt == -1) + break; + + switch (opt) { + case 'h': + raw_arguments.show_help = true; + break; + case 'v': + raw_arguments.show_version = true; + break; + case 'a': + raw_arguments.algorithm_str = std::string(optarg); + break; + case 'm': + raw_arguments.min_str = std::string(optarg); + break; + case 'M': + raw_arguments.max_str = std::string(optarg); + break; + case 'c': + raw_arguments.count_str = std::string(optarg); + break; + case 'u': + raw_arguments.unit = true; + break; + case 'f': + raw_arguments.floating = true; + break; + case 'i': + raw_arguments.integer = true; + break; + case '?': // Unrecognized option + raw_arguments.error = true; + exit(1); + default: + raw_arguments.error = true; + exit(1); + } + } + + return raw_arguments; +} From 57ad7e0e87809a0087d4dde436273842d9f977c2 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 17 May 2025 17:30:04 -0400 Subject: [PATCH 22/95] Moving argument extraction into ProgramRunner class and included new header file --- .vscode/settings.json | 5 ++- include/program_runner.hpp | 65 +++++++++++++++++++++++++++++++ src/{arguments.cpp => runner.cpp} | 53 +++---------------------- 3 files changed, 75 insertions(+), 48 deletions(-) create mode 100644 include/program_runner.hpp rename src/{arguments.cpp => runner.cpp} (72%) diff --git a/.vscode/settings.json b/.vscode/settings.json index c63f557..065635c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,7 +67,10 @@ "cinttypes": "cpp", "typeindex": "cpp", "typeinfo": "cpp", - "__nullptr": "cpp" + "__nullptr": "cpp", + "any": "cpp", + "variant": "cpp", + "__locale": "cpp" }, "cmake.sourceDirectory": "/home/eric/Projects/Randomizer/" } \ No newline at end of file diff --git a/include/program_runner.hpp b/include/program_runner.hpp new file mode 100644 index 0000000..734a232 --- /dev/null +++ b/include/program_runner.hpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include + +class ProgramRunner { +public: + ProgramRunner(int argc, char **argv); + + void iterate(); + void iterate(std::uint64_t iterations); + void run(); + + const std::string version = "0.1"; + const std::string program_name = "randomizer"; + +private: + + + enum class Algorithm { + XORShift, + LinearCongruentialGenerator, + // Add other algorithms here + }; + + + + enum class ProgramBehaviour { + Error, + Help, + Version, + GenerateUnitNormal, + GenerateFloating, + GenerateInteger + }; + + struct RawArguments { + bool error; + bool show_help; + bool show_version; + bool unit; + bool floating; + bool integer; + std::string algorithm_str; + std::string min_str; + std::string max_str; + std::string count_str; + }; + + RawArguments parse_args(int argc, char **argv); + void print_help(); + void print_version(); + + ProgramBehaviour option; + std::optional algorithm; + std::optional> min; + std::optional> max; + std::optional count; + + + + +}; \ No newline at end of file diff --git a/src/arguments.cpp b/src/runner.cpp similarity index 72% rename from src/arguments.cpp rename to src/runner.cpp index afc9290..a8507c7 100644 --- a/src/arguments.cpp +++ b/src/runner.cpp @@ -3,8 +3,9 @@ #include #include #include +#include "program_runner.hpp" -void print_usage(const char* program_name) { +void ProgramRunner::print_help() { std::cout << "Usage: " << program_name << " [options]\n" << "Options:\n" << " -h, --help Show this help message\n" @@ -18,56 +19,14 @@ void print_usage(const char* program_name) { << " -i, --integer Generate integer random numbers\n"; } -enum class Algorithms { - XORShift, - LinearCongruentialGenerator, - // Add other algorithms here -}; - -std::optional get_algorithm(const std::string& algorithm_str) { - if (algorithm_str == "xorshift") { - return Algorithms::XORShift; - } else if (algorithm_str == "lcg" or algorithm_str == "linear-congruential-generator") { - return Algorithms::LinearCongruentialGenerator; - } - return std::nullopt; // Unknown algorithm +void ProgramRunner::print_version() { + std::cout << "Random Number Generator " << version << "\n"; } -enum class ProgramBehaviour { - Error, - Help, - Version, - GenerateUnitNormal, - GenerateFloating, - GenerateInteger -}; - -struct ProgramConfiguration { - ProgramBehaviour option; - Algorithms algorithm; - std::optional> min; - std::optional> max; - std::optional count; -}; - -struct RawArguments { - bool error; - bool show_help; - bool show_version; - bool unit; - bool floating; - bool integer; - std::string algorithm_str; - std::string min_str; - std::string max_str; - std::string count_str; -}; - - -RawArguments parse_args(int argc, char* argv[]) { +ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { const char* const short_opts = "hva:m:M:c:ufi"; - const option long_opts[] = { + const ::option long_opts[] = { {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, {"algorithm", required_argument, nullptr, 'a'}, From 77c31598b757c2cf43feb7e4df97c096a05545d7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 15:51:03 -0400 Subject: [PATCH 23/95] Implemented functions for identifying from command line input how the program should run --- .vscode/settings.json | 8 +- include/program_runner.hpp | 54 +++++++++--- src/runner.cpp | 174 +++++++++++++++++++++++++++++++------ 3 files changed, 194 insertions(+), 42 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 065635c..223abf0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,7 +70,13 @@ "__nullptr": "cpp", "any": "cpp", "variant": "cpp", - "__locale": "cpp" + "__locale": "cpp", + "charconv": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "queue": "cpp", + "stack": "cpp" }, "cmake.sourceDirectory": "/home/eric/Projects/Randomizer/" } \ No newline at end of file diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 734a232..a5dec42 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -18,6 +18,11 @@ class ProgramRunner { private: + // Defaults + + static constexpr uint32_t DefaultCount = 1; + static constexpr int64_t DefaultMin = 1; + static constexpr int64_t DefaultMax = 6; enum class Algorithm { XORShift, @@ -25,6 +30,13 @@ class ProgramRunner { // Add other algorithms here }; + const std::map algorithm_choices { + {"xorshift", Algorithm::XORShift}, + {"xor", Algorithm::XORShift}, + {"linear-congruential-generator", Algorithm::LinearCongruentialGenerator}, + {"lcg", Algorithm::LinearCongruentialGenerator} + }; + enum class ProgramBehaviour { @@ -36,30 +48,46 @@ class ProgramRunner { GenerateInteger }; + const std::map generation_types { + {"unit", ProgramBehaviour::GenerateUnitNormal}, + {"normal", ProgramBehaviour::GenerateUnitNormal}, + {"unit-normal", ProgramBehaviour::GenerateUnitNormal}, + {"normalized", ProgramBehaviour::GenerateUnitNormal}, + {"float", ProgramBehaviour::GenerateFloating}, + {"floating", ProgramBehaviour::GenerateFloating}, + {"decimal", ProgramBehaviour::GenerateFloating}, + {"floating-point", ProgramBehaviour::GenerateFloating}, + {"int", ProgramBehaviour::GenerateInteger}, + {"integer", ProgramBehaviour::GenerateInteger} + }; + struct RawArguments { + // what special strings should we show? + bool error; bool show_help; bool show_version; - bool unit; - bool floating; - bool integer; - std::string algorithm_str; - std::string min_str; - std::string max_str; - std::string count_str; + + // how do we want to generate the random numbers? + std::optional type; + + std::optional algorithm_str; + std::optional min_str; + std::optional max_str; + std::optional count_str; }; RawArguments parse_args(int argc, char **argv); + void determine_program_configuration(const RawArguments raw_Arguments); + bool erroneous_flag_args_provided(const RawArguments raw_Arguments); void print_help(); void print_version(); - ProgramBehaviour option; + ProgramBehaviour behaviour; std::optional algorithm; - std::optional> min; - std::optional> max; - std::optional count; - - + std::optional> min; + std::optional> max; + std::optional count; }; \ No newline at end of file diff --git a/src/runner.cpp b/src/runner.cpp index a8507c7..3f52f33 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -3,8 +3,13 @@ #include #include #include +#include +#include +#include #include "program_runner.hpp" + + void ProgramRunner::print_help() { std::cout << "Usage: " << program_name << " [options]\n" << "Options:\n" @@ -14,7 +19,7 @@ void ProgramRunner::print_help() { << " -m, --min Minimum value\n" << " -M, --max Maximum value\n" << " -c, --count Number of random numbers to generate\n" - << " -u, --unit Generate unit normal random numbers\n" + << " -t, --type Specify the type to output\n" << " -f, --float Generate floating-point random numbers\n" << " -i, --integer Generate integer random numbers\n"; } @@ -23,9 +28,138 @@ void ProgramRunner::print_version() { std::cout << "Random Number Generator " << version << "\n"; } +bool ProgramRunner::erroneous_flag_args_provided(const ProgramRunner::RawArguments raw_arguments){ + +} + + + +// Parses a string to uint32_t, returns std::nullopt on error or out-of-range +std::optional parse_count_value(const std::string &count_str) { + uint32_t value; + const auto [ptr, ec] = std::from_chars(count_str.data(), count_str.data() + count_str.size(), value); + if (ec != std::errc() || ptr != count_str.data() + count_str.size()) { + return std::nullopt; // Parsing failed or extra characters present + } + return value; +} + +std::optional> parse_min_and_max_integers(const std::string &min_str, const std::string &max_str) { + int64_t min_value; + const auto [ptr, ec] = std::from_chars(min_str.data(), min_str.data() + min_str.size(), min_value); + if (ec != std::errc() || ptr != min_str.data() + min_str.size()) { + return std::nullopt; // Parsing failed or extra characters present + } + + int64_t max_value; + const auto [ptr, ec] = std::from_chars(max_str.data(), max_str.data() + max_str.size(), max_value); + if (ec != std::errc() || ptr != max_str.data() + max_str.size()) { + return std::nullopt; // Parsing failed or extra characters present + } + + if (min_value <= max_value) { + std::pair range = {min_value, max_value}; + std::optional> range_opt = range; + return range_opt; + } + + + return std::nullopt; +} + +std::optional> parse_min_and_max_doubles(const std::string &min_str, const std::string &max_str) { + double min_value; + const auto [ptr, ec] = std::from_chars(min_str.data(), min_str.data() + min_str.size(), min_value); + if (ec != std::errc() || ptr != min_str.data() + min_str.size()) { + return std::nullopt; // Parsing failed or extra characters present + } + + double max_value; + const auto [ptr, ec] = std::from_chars(max_str.data(), max_str.data() + max_str.size(), max_value); + if (ec != std::errc() || ptr != max_str.data() + max_str.size()) { + return std::nullopt; // Parsing failed or extra characters present + } + + if (min_value <= max_value) { + std::pair range = {min_value, max_value}; + std::optional> range_opt = range; + return range_opt; + } + + + return std::nullopt; +} + +void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ + if (raw_args.error){ + this->behaviour = ProgramBehaviour::Error; + return; + } else if (raw_args.show_version){ + this->behaviour = ProgramBehaviour::Version; + return; + } + + if (!raw_args.algorithm_str.has_value()){ + this->algorithm = Algorithm::XORShift; // default algorithm to use + } else if (algorithm_choices.contains(raw_args.algorithm_str.value())) { // TODO: this check/get can be made more efficient using find + this->algorithm = algorithm_choices.at(raw_args.algorithm_str.value()); + } else { + this->behaviour = ProgramBehaviour::Error; + return; + } + + if (!raw_args.type.has_value()){ + this->behaviour = ProgramBehaviour::GenerateInteger; + } else if (generation_types.contains(raw_args.type.value())){ + this->behaviour = generation_types.at(raw_args.type.value()); + } else { + this->behaviour = ProgramBehaviour::Error; + } + + if (raw_args.count_str.has_value()){ + std::optional count_val = parse_count_value(raw_args.count_str.value()); + if (!count_val.has_value()){ + this->behaviour = ProgramBehaviour::Error; + return; + } + this->count = count_val; + } else { + this->count = DefaultCount; + } + + + + bool both_max_and_min_specified = raw_args.min_str.has_value() && raw_args.max_str.has_value(); + bool neither_max_or_min_specified = !raw_args.min_str.has_value() && !raw_args.max_str.has_value(); + if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ + auto min_and_max = parse_min_and_max_integers(raw_args.min_str.value(), raw_args.max_str.value()); + if (!min_and_max.has_value()){ + this->behaviour = ProgramBehaviour::Error; + return; + } + this->min = min_and_max.value().first; + this->max = min_and_max.value().second; + } else if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateFloating){ + auto min_and_max = parse_min_and_max_doubles(raw_args.min_str.value(), raw_args.max_str.value()); + if (!min_and_max.has_value()){ + this->behaviour = ProgramBehaviour::Error; + return; + } + this->min = min_and_max.value().first; + this->max = min_and_max.value().second; + } else if (neither_max_or_min_specified && this->behaviour == ProgramBehaviour::GenerateUnitNormal){ + this->min = DefaultMin; + this->max = DefaultMax; + } else { + this->behaviour = ProgramBehaviour::Error; + return; + } + + return; +} ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { - const char* const short_opts = "hva:m:M:c:ufi"; + const char* const short_opts = "hva:m:M:c:t:"; const ::option long_opts[] = { {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, @@ -33,9 +167,7 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { {"min", required_argument, nullptr, 'm'}, {"max", required_argument, nullptr, 'M'}, {"count", required_argument, nullptr, 'c'}, - {"unit", no_argument, nullptr, 'u'}, - {"float", no_argument, nullptr, 'f'}, - {"integer", no_argument, nullptr, 'i'}, + {"type", no_argument, nullptr, 't'}, {nullptr, 0, nullptr, 0} }; @@ -44,19 +176,11 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { raw_arguments.error = false; raw_arguments.show_help = false; raw_arguments.show_version = false; - - raw_arguments.algorithm_str = "xorshift"; // Default algorithm - - // Standard defaults if a user does not provide a range they want to use, will roll a dice. - raw_arguments.min_str = "1"; - raw_arguments.max_str = "6"; - - raw_arguments.count_str = "1"; - - // By default we will be rolling a dice, so we will be generating integers. - raw_arguments.unit = false; - raw_arguments.floating = false; - raw_arguments.integer = true; + raw_arguments.algorithm_str = std::nullopt; + raw_arguments.min_str = std::nullopt; + raw_arguments.max_str = std::nullopt; + raw_arguments.count_str = std::nullopt; + raw_arguments.type = std::nullopt; while (true) { const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr); @@ -83,21 +207,15 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { case 'c': raw_arguments.count_str = std::string(optarg); break; - case 'u': - raw_arguments.unit = true; - break; - case 'f': - raw_arguments.floating = true; - break; - case 'i': - raw_arguments.integer = true; + case 't': + raw_arguments.type = std::string(optarg); break; case '?': // Unrecognized option raw_arguments.error = true; - exit(1); + return raw_arguments; default: raw_arguments.error = true; - exit(1); + return raw_arguments; } } From fafe1f1c836e1f5ba3e8740cc509311f619b0568 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 15:54:06 -0400 Subject: [PATCH 24/95] Eliminated lines of code that were no longer needed --- src/runner.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/runner.cpp b/src/runner.cpp index 3f52f33..78d75fe 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -19,20 +19,13 @@ void ProgramRunner::print_help() { << " -m, --min Minimum value\n" << " -M, --max Maximum value\n" << " -c, --count Number of random numbers to generate\n" - << " -t, --type Specify the type to output\n" - << " -f, --float Generate floating-point random numbers\n" - << " -i, --integer Generate integer random numbers\n"; + << " -t, --type Specify the type to output\n"; } void ProgramRunner::print_version() { std::cout << "Random Number Generator " << version << "\n"; } -bool ProgramRunner::erroneous_flag_args_provided(const ProgramRunner::RawArguments raw_arguments){ - -} - - // Parses a string to uint32_t, returns std::nullopt on error or out-of-range std::optional parse_count_value(const std::string &count_str) { From cca09715b02ca3bf7fd3ff0659bde0b1e5a5b16f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 15:55:11 -0400 Subject: [PATCH 25/95] Added help option to determine program configuration code --- src/runner.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runner.cpp b/src/runner.cpp index 78d75fe..58eac0b 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -90,7 +90,9 @@ void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArgu } else if (raw_args.show_version){ this->behaviour = ProgramBehaviour::Version; return; - } + } else if (raw_args.show_help){ + this->behaviour = ProgramBehaviour::Help; + } if (!raw_args.algorithm_str.has_value()){ this->algorithm = Algorithm::XORShift; // default algorithm to use From cf6a2c679dccd0bdafe1fe67ae2cef2f943e69b9 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 16:07:30 -0400 Subject: [PATCH 26/95] Moved part of determine_program_configuration into determine_generation_configuration --- include/program_runner.hpp | 2 ++ src/runner.cpp | 54 ++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index a5dec42..b52185d 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -79,6 +79,8 @@ class ProgramRunner { RawArguments parse_args(int argc, char **argv); void determine_program_configuration(const RawArguments raw_Arguments); + void determine_generation_configuration(const std::optional &min_str, const std::optional &max_str); + bool erroneous_flag_args_provided(const RawArguments raw_Arguments); void print_help(); void print_version(); diff --git a/src/runner.cpp b/src/runner.cpp index 58eac0b..8fe0a02 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -83,6 +83,34 @@ std::optional> parse_min_and_max_doubles(const std::st return std::nullopt; } +void ProgramRunner::determine_generation_configuration(const std::optional &min_str, const std::optional &max_str){ + bool both_max_and_min_specified = min_str.has_value() && max_str.has_value(); + bool neither_max_or_min_specified = !min_str.has_value() && !max_str.has_value(); + if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ + auto min_and_max = parse_min_and_max_integers(min_str.value(), max_str.value()); + if (!min_and_max.has_value()){ + this->behaviour = ProgramBehaviour::Error; + return; + } + this->min = min_and_max.value().first; + this->max = min_and_max.value().second; + } else if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateFloating){ + auto min_and_max = parse_min_and_max_doubles(min_str.value(), max_str.value()); + if (!min_and_max.has_value()){ + this->behaviour = ProgramBehaviour::Error; + return; + } + this->min = min_and_max.value().first; + this->max = min_and_max.value().second; + } else if (neither_max_or_min_specified && this->behaviour == ProgramBehaviour::GenerateUnitNormal){ + this->min = DefaultMin; + this->max = DefaultMax; + } else { + this->behaviour = ProgramBehaviour::Error; + return; + } +} + void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ if (raw_args.error){ this->behaviour = ProgramBehaviour::Error; @@ -122,33 +150,9 @@ void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArgu this->count = DefaultCount; } + determine_generation_configuration(raw_args.min_str, raw_args.max_str); - bool both_max_and_min_specified = raw_args.min_str.has_value() && raw_args.max_str.has_value(); - bool neither_max_or_min_specified = !raw_args.min_str.has_value() && !raw_args.max_str.has_value(); - if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ - auto min_and_max = parse_min_and_max_integers(raw_args.min_str.value(), raw_args.max_str.value()); - if (!min_and_max.has_value()){ - this->behaviour = ProgramBehaviour::Error; - return; - } - this->min = min_and_max.value().first; - this->max = min_and_max.value().second; - } else if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateFloating){ - auto min_and_max = parse_min_and_max_doubles(raw_args.min_str.value(), raw_args.max_str.value()); - if (!min_and_max.has_value()){ - this->behaviour = ProgramBehaviour::Error; - return; - } - this->min = min_and_max.value().first; - this->max = min_and_max.value().second; - } else if (neither_max_or_min_specified && this->behaviour == ProgramBehaviour::GenerateUnitNormal){ - this->min = DefaultMin; - this->max = DefaultMax; - } else { - this->behaviour = ProgramBehaviour::Error; - return; - } return; } From e3f596533509882e41903f67b9fbdba5b1cfa970 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 16:23:31 -0400 Subject: [PATCH 27/95] Moved parts of determine_program_configuration into determine_user_message_configuration and determine_algorithm_configuration --- include/program_runner.hpp | 2 ++ src/runner.cpp | 33 ++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index b52185d..42510bf 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -79,7 +79,9 @@ class ProgramRunner { RawArguments parse_args(int argc, char **argv); void determine_program_configuration(const RawArguments raw_Arguments); + void determine_user_message_configuration(const bool error, const bool show_version, const bool show_help); void determine_generation_configuration(const std::optional &min_str, const std::optional &max_str); + void ProgramRunner::determine_algorithm_configuration(const std::optional &alg_str); bool erroneous_flag_args_provided(const RawArguments raw_Arguments); void print_help(); diff --git a/src/runner.cpp b/src/runner.cpp index 8fe0a02..57fe7d8 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -111,25 +111,36 @@ void ProgramRunner::determine_generation_configuration(const std::optionalbehaviour = ProgramBehaviour::Error; return; - } else if (raw_args.show_version){ + } else if (show_version){ this->behaviour = ProgramBehaviour::Version; return; - } else if (raw_args.show_help){ + } else if (show_help){ this->behaviour = ProgramBehaviour::Help; + return; } - - if (!raw_args.algorithm_str.has_value()){ +} + +void ProgramRunner::determine_algorithm_configuration(const std::optional &alg_str){ + if (!alg_str.has_value()){ this->algorithm = Algorithm::XORShift; // default algorithm to use - } else if (algorithm_choices.contains(raw_args.algorithm_str.value())) { // TODO: this check/get can be made more efficient using find - this->algorithm = algorithm_choices.at(raw_args.algorithm_str.value()); - } else { + } else if (algorithm_choices.contains(alg_str.value())) { // TODO: this check/get can be made more efficient using find + this->algorithm = algorithm_choices.at(alg_str.value()); + } else { this->behaviour = ProgramBehaviour::Error; - return; - } + return; + } +} + +void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ + + determine_user_message_configuration(raw_args.error, raw_args.show_version, raw_args.show_help); + + determine_algorithm_configuration(raw_args.algorithm_str); + if (!raw_args.type.has_value()){ this->behaviour = ProgramBehaviour::GenerateInteger; From 5e7826dcd9362c99c8f13affdaffa102210ac269 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 16:32:50 -0400 Subject: [PATCH 28/95] Moved some program config code into determine_generation_type_configuration and renamed determine_generation_configuration to determine_generation_range_configuration --- include/program_runner.hpp | 3 ++- src/runner.cpp | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 42510bf..c5dc2b8 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -80,7 +80,8 @@ class ProgramRunner { RawArguments parse_args(int argc, char **argv); void determine_program_configuration(const RawArguments raw_Arguments); void determine_user_message_configuration(const bool error, const bool show_version, const bool show_help); - void determine_generation_configuration(const std::optional &min_str, const std::optional &max_str); + void determine_generation_range_configuration(const std::optional &min_str, const std::optional &max_str); + void determine_generation_type_configuration(const std::optional &generation_type); void ProgramRunner::determine_algorithm_configuration(const std::optional &alg_str); bool erroneous_flag_args_provided(const RawArguments raw_Arguments); diff --git a/src/runner.cpp b/src/runner.cpp index 57fe7d8..50c9249 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -83,7 +83,7 @@ std::optional> parse_min_and_max_doubles(const std::st return std::nullopt; } -void ProgramRunner::determine_generation_configuration(const std::optional &min_str, const std::optional &max_str){ +void ProgramRunner::determine_generation_range_configuration(const std::optional &min_str, const std::optional &max_str){ bool both_max_and_min_specified = min_str.has_value() && max_str.has_value(); bool neither_max_or_min_specified = !min_str.has_value() && !max_str.has_value(); if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ @@ -135,20 +135,24 @@ void ProgramRunner::determine_algorithm_configuration(const std::optional &generation_type){ + if (!generation_type.has_value()){ + this->behaviour = ProgramBehaviour::GenerateInteger; + } else if (generation_types.contains(generation_type.value())){ + this->behaviour = generation_types.at(generation_type.value()); + } else { + this->behaviour = ProgramBehaviour::Error; + } +} + void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ determine_user_message_configuration(raw_args.error, raw_args.show_version, raw_args.show_help); determine_algorithm_configuration(raw_args.algorithm_str); + determine_generation_type_configuration(raw_args.type); - if (!raw_args.type.has_value()){ - this->behaviour = ProgramBehaviour::GenerateInteger; - } else if (generation_types.contains(raw_args.type.value())){ - this->behaviour = generation_types.at(raw_args.type.value()); - } else { - this->behaviour = ProgramBehaviour::Error; - } if (raw_args.count_str.has_value()){ std::optional count_val = parse_count_value(raw_args.count_str.value()); @@ -161,7 +165,7 @@ void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArgu this->count = DefaultCount; } - determine_generation_configuration(raw_args.min_str, raw_args.max_str); + determine_generation_range_configuration(raw_args.min_str, raw_args.max_str); From 6e0d09e05969a07da93ccafbc099da903e638705 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 18 May 2025 16:53:22 -0400 Subject: [PATCH 29/95] Moved logic for determine_program_configuration into determine_count_configuration --- .vscode/settings.json | 3 ++- include/program_runner.hpp | 3 ++- src/runner.cpp | 38 +++++++++++++++++++++++--------------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 223abf0..694dd56 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -76,7 +76,8 @@ "__split_buffer": "cpp", "__tree": "cpp", "queue": "cpp", - "stack": "cpp" + "stack": "cpp", + "filesystem": "cpp" }, "cmake.sourceDirectory": "/home/eric/Projects/Randomizer/" } \ No newline at end of file diff --git a/include/program_runner.hpp b/include/program_runner.hpp index c5dc2b8..105b90f 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -82,7 +82,8 @@ class ProgramRunner { void determine_user_message_configuration(const bool error, const bool show_version, const bool show_help); void determine_generation_range_configuration(const std::optional &min_str, const std::optional &max_str); void determine_generation_type_configuration(const std::optional &generation_type); - void ProgramRunner::determine_algorithm_configuration(const std::optional &alg_str); + void determine_count_configuration(const std::optional &count_str); + void determine_algorithm_configuration(const std::optional &alg_str); bool erroneous_flag_args_provided(const RawArguments raw_Arguments); void print_help(); diff --git a/src/runner.cpp b/src/runner.cpp index 50c9249..d618ece 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -145,31 +145,39 @@ void ProgramRunner::determine_generation_type_configuration(const std::optional< } } -void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ - - determine_user_message_configuration(raw_args.error, raw_args.show_version, raw_args.show_help); - - determine_algorithm_configuration(raw_args.algorithm_str); - - determine_generation_type_configuration(raw_args.type); - - - if (raw_args.count_str.has_value()){ - std::optional count_val = parse_count_value(raw_args.count_str.value()); +void ProgramRunner::determine_count_configuration(const std::optional &count_str){ + if (count_str.has_value()){ + std::optional count_val = parse_count_value(count_str.value()); if (!count_val.has_value()){ this->behaviour = ProgramBehaviour::Error; return; } this->count = count_val; - } else { + } else { this->count = DefaultCount; - } + } +} - determine_generation_range_configuration(raw_args.min_str, raw_args.max_str); +void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArguments raw_args){ + + determine_user_message_configuration(raw_args.error, raw_args.show_version, raw_args.show_help); + if (this->behaviour == ProgramBehaviour::Error || this->behaviour == ProgramBehaviour::Version || this->behaviour == ProgramBehaviour::Help){ + return; + } + determine_algorithm_configuration(raw_args.algorithm_str); + if (this->behaviour == ProgramBehaviour::Error) return - return; + determine_generation_type_configuration(raw_args.type); + if (this->behaviour == ProgramBehaviour::Error) return + + determine_count_configuration(raw_args.count_str); + if (this->behaviour == ProgramBehaviour::Error) return + + determine_generation_range_configuration(raw_args.min_str, raw_args.max_str); + + return; } ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { From e33466ab830237b9156fa6966ce9c79d84d3f422 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 19 May 2025 15:26:34 -0400 Subject: [PATCH 30/95] Templated parse_value and parse_min_and_max_numbers so that seperate functions for int64_t and doubles are no longer necessary. --- CMakeLists.txt | 2 +- src/runner.cpp | 71 +++++++++++++++++++++----------------------------- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6019290..887ea50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.21) project(randomizer) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/src/runner.cpp b/src/runner.cpp index d618ece..174d082 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -19,7 +19,7 @@ void ProgramRunner::print_help() { << " -m, --min Minimum value\n" << " -M, --max Maximum value\n" << " -c, --count Number of random numbers to generate\n" - << " -t, --type Specify the type to output\n"; + << " -t, --type Specify the type to output (default: unit)\n"; } void ProgramRunner::print_version() { @@ -37,57 +37,37 @@ std::optional parse_count_value(const std::string &count_str) { return value; } -std::optional> parse_min_and_max_integers(const std::string &min_str, const std::string &max_str) { - int64_t min_value; - const auto [ptr, ec] = std::from_chars(min_str.data(), min_str.data() + min_str.size(), min_value); - if (ec != std::errc() || ptr != min_str.data() + min_str.size()) { - return std::nullopt; // Parsing failed or extra characters present - } - - int64_t max_value; - const auto [ptr, ec] = std::from_chars(max_str.data(), max_str.data() + max_str.size(), max_value); - if (ec != std::errc() || ptr != max_str.data() + max_str.size()) { - return std::nullopt; // Parsing failed or extra characters present - } - - if (min_value <= max_value) { - std::pair range = {min_value, max_value}; - std::optional> range_opt = range; - return range_opt; - } - - - return std::nullopt; -} -std::optional> parse_min_and_max_doubles(const std::string &min_str, const std::string &max_str) { - double min_value; - const auto [ptr, ec] = std::from_chars(min_str.data(), min_str.data() + min_str.size(), min_value); - if (ec != std::errc() || ptr != min_str.data() + min_str.size()) { - return std::nullopt; // Parsing failed or extra characters present - } +template +concept FromCharsParsable = std::is_integral_v || std::is_floating_point_v; - double max_value; - const auto [ptr, ec] = std::from_chars(max_str.data(), max_str.data() + max_str.size(), max_value); - if (ec != std::errc() || ptr != max_str.data() + max_str.size()) { - return std::nullopt; // Parsing failed or extra characters present - } - if (min_value <= max_value) { - std::pair range = {min_value, max_value}; - std::optional> range_opt = range; - return range_opt; +// Helper function for parsing a string to a numeric type using std::from_chars +template +std::optional parse_value(const std::string& str) { + T value; + auto result = std::from_chars(str.data(), str.data() + str.size(), value); + if (result.ec != std::errc() || result.ptr != str.data() + str.size()) { + return std::nullopt; } + return value; +} - +template +std::optional> parse_min_and_max_numbers(const std::string &min_str, const std::string &max_str) { + std::optional min_value = parse_value(min_str); + std::optional max_value = parse_value(max_str); + if (!min_value.has_value() || !max_value.has_value()) return std::nullopt; + if (min_value.value() <= max_value.value()) return std::make_pair(min_value.value(), max_value.value()); return std::nullopt; } + void ProgramRunner::determine_generation_range_configuration(const std::optional &min_str, const std::optional &max_str){ bool both_max_and_min_specified = min_str.has_value() && max_str.has_value(); bool neither_max_or_min_specified = !min_str.has_value() && !max_str.has_value(); if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ - auto min_and_max = parse_min_and_max_integers(min_str.value(), max_str.value()); + auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); if (!min_and_max.has_value()){ this->behaviour = ProgramBehaviour::Error; return; @@ -95,7 +75,7 @@ void ProgramRunner::determine_generation_range_configuration(const std::optional this->min = min_and_max.value().first; this->max = min_and_max.value().second; } else if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateFloating){ - auto min_and_max = parse_min_and_max_doubles(min_str.value(), max_str.value()); + auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); if (!min_and_max.has_value()){ this->behaviour = ProgramBehaviour::Error; return; @@ -243,3 +223,12 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { return raw_arguments; } + +ProgramRunner::ProgramRunner(int argc, char **argv){ + RawArguments raw_arguments = parse_args(argc, argv); + determine_program_configuration(raw_arguments); + std::cout << (int) this->behaviour << '\n'; + std::cout << (int) this->algorithm.value() << '\n'; + std::cout << count.value() << std::endl; + +} From 5ca4d858b6b4d7bc48fa845be661b181a829f5a8 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 19 May 2025 18:41:48 -0400 Subject: [PATCH 31/95] Got ProgramRunner working to print error, help, and version messages --- .vscode/launch.json | 4 ++-- include/program_runner.hpp | 22 +++++++++---------- src/main.cpp | 33 +++++++++++++++------------- src/runner.cpp | 44 +++++++++++++++++++++++++++----------- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8790b29..bfa34bc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": [3, 4], + "args": [], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], @@ -27,7 +27,7 @@ "ignoreFailures": true } ], - "preLaunchTask": "CMake: build", + "preLaunchTask": "CMake: build" } ] diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 105b90f..9b8b38b 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -18,12 +18,6 @@ class ProgramRunner { private: - // Defaults - - static constexpr uint32_t DefaultCount = 1; - static constexpr int64_t DefaultMin = 1; - static constexpr int64_t DefaultMax = 6; - enum class Algorithm { XORShift, LinearCongruentialGenerator, @@ -77,6 +71,11 @@ class ProgramRunner { std::optional count_str; }; + + // Defaults + + static constexpr uint32_t DefaultCount = 1; + RawArguments parse_args(int argc, char **argv); void determine_program_configuration(const RawArguments raw_Arguments); void determine_user_message_configuration(const bool error, const bool show_version, const bool show_help); @@ -86,14 +85,15 @@ class ProgramRunner { void determine_algorithm_configuration(const std::optional &alg_str); bool erroneous_flag_args_provided(const RawArguments raw_Arguments); + void print_error(); void print_help(); void print_version(); - ProgramBehaviour behaviour; - std::optional algorithm; - std::optional> min; - std::optional> max; - std::optional count; + std::optional behaviour = std::nullopt; + std::optional algorithm = std::nullopt; + std::optional> min = std::nullopt; + std::optional> max = std::nullopt; + std::optional count = std::nullopt; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d700c5c..3ddd5f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "prng.hpp" #include "linear_congruential_generator.hpp" #include "xorshift.hpp" +#include "program_runner.hpp" struct Range { @@ -32,25 +33,27 @@ void print_help_message(){ int main(int argc, char *argv[]){ - Range range; - try { - range = get_range_from_args(argc, argv); - } catch (std::invalid_argument& e){ - print_help_message(); - exit(1); - } catch (std::out_of_range& e){ - print_help_message(); - exit(1); - } + // Range range; + // try { + // range = get_range_from_args(argc, argv); + // } catch (std::invalid_argument& e){ + // print_help_message(); + // exit(1); + // } catch (std::out_of_range& e){ + // print_help_message(); + // exit(1); + // } + + // auto xorshift = XORShift(); - auto xorshift = XORShift(); + // std::cout << "Random number between " << range.min << " and " << range.max << "\n"; - std::cout << "Random number between " << range.min << " and " << range.max << "\n"; + // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; + // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; + // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; + ProgramRunner runner = ProgramRunner(argc, argv); return 0; diff --git a/src/runner.cpp b/src/runner.cpp index 174d082..3a5371d 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -9,6 +9,10 @@ #include "program_runner.hpp" +void ProgramRunner::print_error() { + std::cerr << program_name << ": bad usage\n" + << "Try 'randomizer --help' for more information.\n"; +} void ProgramRunner::print_help() { std::cout << "Usage: " << program_name << " [options]\n" @@ -18,12 +22,12 @@ void ProgramRunner::print_help() { << " -a, --algorithm Specify the algorithm (default: xorshift)\n" << " -m, --min Minimum value\n" << " -M, --max Maximum value\n" - << " -c, --count Number of random numbers to generate\n" + << " -c, --count Number of random numbers to generate (default: 1)\n" << " -t, --type Specify the type to output (default: unit)\n"; } void ProgramRunner::print_version() { - std::cout << "Random Number Generator " << version << "\n"; + std::cout << program_name << " " << version << "\n"; } @@ -83,8 +87,7 @@ void ProgramRunner::determine_generation_range_configuration(const std::optional this->min = min_and_max.value().first; this->max = min_and_max.value().second; } else if (neither_max_or_min_specified && this->behaviour == ProgramBehaviour::GenerateUnitNormal){ - this->min = DefaultMin; - this->max = DefaultMax; + return; } else { this->behaviour = ProgramBehaviour::Error; return; @@ -111,7 +114,6 @@ void ProgramRunner::determine_algorithm_configuration(const std::optionalalgorithm = algorithm_choices.at(alg_str.value()); } else { this->behaviour = ProgramBehaviour::Error; - return; } } @@ -142,18 +144,25 @@ void ProgramRunner::determine_count_configuration(const std::optionalbehaviour == ProgramBehaviour::Error || this->behaviour == ProgramBehaviour::Version || this->behaviour == ProgramBehaviour::Help){ + auto &behave = this->behaviour; + if (behave.has_value() && (behave == ProgramBehaviour::Error || behave == ProgramBehaviour::Version || behave == ProgramBehaviour::Help)){ return; } determine_algorithm_configuration(raw_args.algorithm_str); - if (this->behaviour == ProgramBehaviour::Error) return + if (behave.has_value() && behave == ProgramBehaviour::Error){ + return; + } determine_generation_type_configuration(raw_args.type); - if (this->behaviour == ProgramBehaviour::Error) return + if (behave.has_value() && behave == ProgramBehaviour::Error){ + return; + } determine_count_configuration(raw_args.count_str); - if (this->behaviour == ProgramBehaviour::Error) return + if (behave.has_value() && behave == ProgramBehaviour::Error){ + return; + } determine_generation_range_configuration(raw_args.min_str, raw_args.max_str); @@ -227,8 +236,19 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { ProgramRunner::ProgramRunner(int argc, char **argv){ RawArguments raw_arguments = parse_args(argc, argv); determine_program_configuration(raw_arguments); - std::cout << (int) this->behaviour << '\n'; - std::cout << (int) this->algorithm.value() << '\n'; - std::cout << count.value() << std::endl; + + if (this->behaviour.value() == ProgramBehaviour::Error){ + print_error(); + exit(1); + } else if (this->behaviour.value() == ProgramBehaviour::Help){ + print_help(); + exit(0); + } else if (this->behaviour.value() == ProgramBehaviour::Version){ + print_version(); + exit(0); + } else { + std::cout << "You got to another type of state!" << std::endl; + exit(0); + } } From 18907eef39d646931c0541b574e1dcfe73b9616d Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 19 May 2025 18:44:35 -0400 Subject: [PATCH 32/95] Removed old code from main --- src/main.cpp | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3ddd5f3..16afc13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,57 +5,7 @@ #include "xorshift.hpp" #include "program_runner.hpp" - -struct Range { - int min; - int max; -}; - -Range get_range_from_args(int argc, char **argv){ - if (argc != 3){ - throw std::invalid_argument("Incorrect number of arguments. "); - } - - std::string min_str = std::string(argv[1]); - std::string max_str = std::string(argv[2]); - - // can throw an exception - Range range; - range.min = std::stoi(min_str); - range.max = std::stoi(max_str); - return range; -} - -void print_help_message(){ - std::string_view help = "Usage: randomizer \n"; - std::cout << help << std::endl; -} - - int main(int argc, char *argv[]){ - // Range range; - // try { - // range = get_range_from_args(argc, argv); - // } catch (std::invalid_argument& e){ - // print_help_message(); - // exit(1); - // } catch (std::out_of_range& e){ - // print_help_message(); - // exit(1); - // } - - // auto xorshift = XORShift(); - - - // std::cout << "Random number between " << range.min << " and " << range.max << "\n"; - - // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - // std::cout << xorshift.generateUnitNormalRandomValue() << " was a pseudorandom number generated by the xorshift.\n"; - ProgramRunner runner = ProgramRunner(argc, argv); - - return 0; - } \ No newline at end of file From ff39bb46711bb451c622d7f0ee5753990ef5af82 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Mon, 12 May 2025 12:15:15 -0400 Subject: [PATCH 33/95] Started to add testing infrastructure with CMake --- CMakeLists.txt | 5 ++++- tests/CMakeLists.txt | 22 ++++++++++++++++++++++ tests/test_LinearCongruentialGenerator.cpp | 7 +++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test_LinearCongruentialGenerator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 887ea50..eeadc66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,4 +16,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") file(GLOB_RECURSE HEADERS "include/*.hpp") # Define main executable -add_executable(${PROJECT_NAME} ${SOURCES}) \ No newline at end of file +add_executable(${PROJECT_NAME} ${SOURCES}) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..11f5822 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,22 @@ +# Download and unpack GoogleTest if not present +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +# Collect test sources +file(GLOB_RECURSE TEST_SOURCES "*.cpp") + +# Add test executable +add_executable(RandomizerTests ${TEST_SOURCES}) + +# Link to GoogleTest and your app sources +target_include_directories(RandomizerTests PRIVATE ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(RandomizerTests gtest gtest_main) + +include(GoogleTest) +gtest_discover_tests(RandomizerTests) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp new file mode 100644 index 0000000..83c3c13 --- /dev/null +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -0,0 +1,7 @@ +#include "gtest/gtest.h" +#include "prng.hpp" +#include "linear_congruential_generator.hpp" + +TEST(RandomizerTests, SayHelloDoesNotThrow) { + EXPECT_NO_THROW(); +} \ No newline at end of file From 6f4b222453f0781881daee4d9946533b9c0e83bf Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 11:22:51 -0400 Subject: [PATCH 34/95] Got an example test running with GoogleTest and CMake --- CMakeLists.txt | 9 ++++++++- tests/CMakeLists.txt | 8 +++++--- tests/test_LinearCongruentialGenerator.cpp | 5 +++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eeadc66..3d9d5e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.22) project(randomizer) set(CMAKE_CXX_STANDARD 20) @@ -15,8 +15,15 @@ include_directories(include) file(GLOB_RECURSE SOURCES "src/*.cpp") file(GLOB_RECURSE HEADERS "include/*.hpp") +# Define a library target for the main code +add_library(randomizer_lib ${SOURCES} ${HEADERS}) +target_include_directories(randomizer_lib PUBLIC ${CMAKE_SOURCE_DIR}/include) + + # Define main executable add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} PRIVATE randomizer_lib) + enable_testing() add_subdirectory(tests) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11f5822..57af17c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,6 @@ +cmake_minimum_required(VERSION 3.22) + + # Download and unpack GoogleTest if not present include(FetchContent) FetchContent_Declare( @@ -14,9 +17,8 @@ file(GLOB_RECURSE TEST_SOURCES "*.cpp") # Add test executable add_executable(RandomizerTests ${TEST_SOURCES}) -# Link to GoogleTest and your app sources -target_include_directories(RandomizerTests PRIVATE ${PROJECT_SOURCE_DIR}/include) -target_link_libraries(RandomizerTests gtest gtest_main) +# Link to GoogleTest and the library for the randomizer code +target_link_libraries(RandomizerTests PRIVATE gtest gtest_main randomizer_lib) include(GoogleTest) gtest_discover_tests(RandomizerTests) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index 83c3c13..4eab758 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -1,7 +1,8 @@ #include "gtest/gtest.h" #include "prng.hpp" #include "linear_congruential_generator.hpp" +#include "xorshift.hpp" -TEST(RandomizerTests, SayHelloDoesNotThrow) { - EXPECT_NO_THROW(); +TEST(ArithmeticTest, OnePlusOneEqualsTwo) { + ASSERT_EQ(1+1, 2) << "1+1 is 2"; } \ No newline at end of file From ffb5badfe3f87d4b942346c66870677f74e55539 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 15:49:51 -0400 Subject: [PATCH 35/95] Added simple test case for LCG contructor. --- tests/test_LinearCongruentialGenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index 4eab758..d8bd7d2 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -1,8 +1,8 @@ #include "gtest/gtest.h" #include "prng.hpp" #include "linear_congruential_generator.hpp" -#include "xorshift.hpp" -TEST(ArithmeticTest, OnePlusOneEqualsTwo) { - ASSERT_EQ(1+1, 2) << "1+1 is 2"; +// Test that the lcg constructors do not fail +TEST(TestLinearCongruentialGenerator, Constructor) { + EXPECT_NO_THROW(LinearCongruentialGenerator()); } \ No newline at end of file From 0d3ed5035211c6f82bb37f39a2578987609a2ac0 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 16:20:03 -0400 Subject: [PATCH 36/95] Added test_XORShift.cpp to make sure multiple test files are usable --- tests/test_XORShift.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/test_XORShift.cpp diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp new file mode 100644 index 0000000..f40d8bf --- /dev/null +++ b/tests/test_XORShift.cpp @@ -0,0 +1,8 @@ +#include "gtest/gtest.h" +#include "prng.hpp" +#include "xorshift.hpp" + +// Test that the lcg constructors do not fail +TEST(TestXORShift, Constructor) { + EXPECT_NO_THROW(XORShift()); +} \ No newline at end of file From d6f7b228b1cab16033300bc1cbf215b8dce950d7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 16:44:29 -0400 Subject: [PATCH 37/95] Test cases for the lcg constructors --- tests/test_LinearCongruentialGenerator.cpp | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index d8bd7d2..5df7495 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -1,8 +1,33 @@ #include "gtest/gtest.h" #include "prng.hpp" #include "linear_congruential_generator.hpp" +#include +#include // Test that the lcg constructors do not fail -TEST(TestLinearCongruentialGenerator, Constructor) { +TEST(TestLinearCongruentialGenerator, BlankConstructor) { EXPECT_NO_THROW(LinearCongruentialGenerator()); -} \ No newline at end of file +} + +TEST(TestLinearCongruentialGenerator, SeedConstructor) { + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::min())); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::min() + 1)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() / 10)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() / 2)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() - 1)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max())); +} + +TEST(TestLinearCongruentialGenerator, CustomConstructor) { + EXPECT_NO_THROW(LinearCongruentialGenerator(0x7FFFFFFF, 1103515245, 12345, 0x7FFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(60, 3453983, 897, 0xFFFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(1, std::numeric_limits::max() / 2, -50, 0xFFF)); +} + +TEST(TestLinearCongruentialGenerator, SeedAndCustomConstructor) { + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::min(), 0x7FFFFFFF, 1103515245, 12345, 0x7FFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max(), 60, 3453983, 897, 0xFFFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() / 2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF)); + EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() - 1, 1, std::numeric_limits::max() / 2, 58678575, 0xFFFFFF)); +} From d85b70d612991ad70769a6871dbaee91a8b1a9fd Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 17:20:18 -0400 Subject: [PATCH 38/95] Added test cases for the lcg's generateUnitNormalRangeValu() --- tests/test_LinearCongruentialGenerator.cpp | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index 5df7495..7fc0a6c 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -31,3 +31,33 @@ TEST(TestLinearCongruentialGenerator, SeedAndCustomConstructor) { EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() / 2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF)); EXPECT_NO_THROW(LinearCongruentialGenerator(std::numeric_limits::max() - 1, 1, std::numeric_limits::max() / 2, 58678575, 0xFFFFFF)); } + +TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueHasCorrectRange) { + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + + short enough_iterations_to_be_confident = 1000; + for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + double value = lcg.generateUnitNormalRandomValue(); + EXPECT_GE(value, 0.0); + EXPECT_LE(value, 1.0); + } +} + + +/* This test of course is subject to some randomness +it is possible to fail even if everything is working +properly, but it would be incredibly unlikely to fail. +If this test ever fails it should be investigated further +manually. */ +TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueAverageConvergesToExpected) { + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + double expected_average = 0.5; + double sum = 0.0; + short enough_iterations_to_be_confident = 10000; + for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + sum += lcg.generateUnitNormalRandomValue(); + } + double average = sum / enough_iterations_to_be_confident; + double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + EXPECT_NEAR(average, expected_average, confidence_interval); +} From 201ef650221c6366e6d68a90f1d5cd94d758069f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 17:33:12 -0400 Subject: [PATCH 39/95] Added tests for XORShift --- tests/test_XORShift.cpp | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index f40d8bf..8e3f571 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -1,8 +1,62 @@ #include "gtest/gtest.h" #include "prng.hpp" #include "xorshift.hpp" +#include +#include // Test that the lcg constructors do not fail -TEST(TestXORShift, Constructor) { +TEST(TestXORShift, BlankConstructor) { EXPECT_NO_THROW(XORShift()); +} + +TEST(TestXORShift, SeedConstructor) { + EXPECT_NO_THROW(XORShift(std::numeric_limits::min())); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max())); +} + + +TEST(TestXORShift, SeedAndConstantsConstructor) { + EXPECT_NO_THROW(XORShift(std::numeric_limits::min(), 1, 2, 3)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1, 79, 55555, std::numeric_limits::max())); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5, 4545, 9876567, 9876786)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5, 1432, 6876, 3)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2, 42, 42, 42)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1, 2345678987, 9876543, 123422)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max(), 345679875, 2, 0)); +} + + +TEST(TestXORShift, GenerateUnitNormalRandomValueHasCorrectRange) { + XORShift xor_shift = XORShift(); + + short enough_iterations_to_be_confident = 1000; + for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + double value = xor_shift.generateUnitNormalRandomValue(); + EXPECT_GE(value, 0.0); + EXPECT_LE(value, 1.0); + } +} + + +/* This test of course is subject to some randomness +it is possible to fail even if everything is working +properly, but it would be incredibly unlikely to fail. +If this test ever fails it should be investigated further +manually. */ +TEST(TestXORShift, GenerateUnitNormalRandomValueAverageConvergesToExpected) { + XORShift xor_shift = XORShift(); + double expected_average = 0.5; + double sum = 0.0; + short enough_iterations_to_be_confident = 10000; + for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + sum += xor_shift.generateUnitNormalRandomValue(); + } + double average = sum / enough_iterations_to_be_confident; + double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + EXPECT_NEAR(average, expected_average, confidence_interval); } \ No newline at end of file From 5f3d4b159c3dc19faaf07f9396621f25cd493ae1 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 17:45:51 -0400 Subject: [PATCH 40/95] Renamed occurences of std::uint64_t to uint64_t --- include/linear_congruential_generator.hpp | 30 ++++++++++----------- include/prng.hpp | 16 +++++------ include/xorshift.hpp | 20 +++++++------- src/prngs/linear_congruential_generator.cpp | 20 +++++++------- src/prngs/prng.cpp | 16 +++++------ src/prngs/xorshift.cpp | 10 +++---- tests/test_XORShift.cpp | 28 +++++++++---------- 7 files changed, 70 insertions(+), 70 deletions(-) diff --git a/include/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp index afa6da4..f6d934d 100644 --- a/include/linear_congruential_generator.hpp +++ b/include/linear_congruential_generator.hpp @@ -29,28 +29,28 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { note = "[Online; accessed 10-May-2025]" } */ - static constexpr std::uint64_t DefaultModulus = 0x7FFFFFFF; - static constexpr std::uint64_t DefaultMultiplier = 1103515245; - static constexpr std::uint64_t DefaultIncrement = 12345; - static constexpr std::uint64_t DefaultMask = 0x7FFFFFFF; // bits 0 through 30 + static constexpr uint64_t DefaultModulus = 0x7FFFFFFF; + static constexpr uint64_t DefaultMultiplier = 1103515245; + static constexpr uint64_t DefaultIncrement = 12345; + static constexpr uint64_t DefaultMask = 0x7FFFFFFF; // bits 0 through 30 - const std::uint64_t m_modulus; - const std::uint64_t m_multiplier; - const std::uint64_t m_increment; - const std::uint64_t m_mask; - std::uint64_t m_current_value = 0; + const uint64_t m_modulus; + const uint64_t m_multiplier; + const uint64_t m_increment; + const uint64_t m_mask; + uint64_t m_current_value = 0; - static std::uint64_t getMinimumValue(const std::uint64_t mask); - static std::uint64_t getMaximumValue(const std::uint64_t modulus, std::uint64_t mask); + static uint64_t getMinimumValue(const std::uint64_t mask); + static uint64_t getMaximumValue(const uint64_t modulus, uint64_t mask); public: LinearCongruentialGenerator(); - LinearCongruentialGenerator(const std::uint64_t seed); - LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask); - LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask); + LinearCongruentialGenerator(const uint64_t seed); + LinearCongruentialGenerator(uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask); + LinearCongruentialGenerator(uint64_t seed, uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask); - std::uint64_t generateRandomValue() override; + uint64_t generateRandomValue() override; double generateUnitNormalRandomValue() override; }; diff --git a/include/prng.hpp b/include/prng.hpp index dee8313..b78451f 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -6,20 +6,20 @@ class PseudoRandomNumberGenerator { protected: - const std::uint64_t m_seed; - const std::uint64_t m_minimum_value; - const std::uint64_t m_maximum_value; + const uint64_t m_seed; + const uint64_t m_minimum_value; + const uint64_t m_maximum_value; public: PseudoRandomNumberGenerator(); - PseudoRandomNumberGenerator(const std::uint64_t seed); - PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value); - PseudoRandomNumberGenerator(const std::uint64_t seed, const std::uint64_t minimum_value, const std::uint64_t maximum_value); + PseudoRandomNumberGenerator(const uint64_t seed); + PseudoRandomNumberGenerator(const uint64_t minimum_value, const uint64_t maximum_value); + PseudoRandomNumberGenerator(const uint64_t seed, const uint64_t minimum_value, const uint64_t maximum_value); - std::uint64_t generateCryptographicallyInsecureSeed(); + uint64_t generateCryptographicallyInsecureSeed(); - virtual std::uint64_t generateRandomValue() = 0; + virtual uint64_t generateRandomValue() = 0; virtual double generateUnitNormalRandomValue() = 0; }; diff --git a/include/xorshift.hpp b/include/xorshift.hpp index 8d2d20b..db5aba7 100644 --- a/include/xorshift.hpp +++ b/include/xorshift.hpp @@ -17,23 +17,23 @@ class XORShift : public PseudoRandomNumberGenerator { // Default values from https://en.wikipedia.org/wiki/Xorshift - static constexpr std::uint64_t DefaultA = 13; - static constexpr std::uint64_t DefaultB = 7; - static constexpr std::uint64_t DefaultC = 17; + static constexpr uint64_t DefaultA = 13; + static constexpr uint64_t DefaultB = 7; + static constexpr uint64_t DefaultC = 17; // XORShift has 3 constants that are used for shifting - const std::uint64_t m_a; - const std::uint64_t m_b; - const std::uint64_t m_c; - std::uint64_t m_current_value = 0; + const uint64_t m_a; + const uint64_t m_b; + const uint64_t m_c; + uint64_t m_current_value = 0; public: XORShift(); - XORShift(const std::uint64_t seed); - XORShift(const std::uint64_t seed, const std::uint64_t a, const std::uint64_t b, const std::uint64_t c); + XORShift(const uint64_t seed); + XORShift(const uint64_t seed, const uint64_t a, const uint64_t b, const uint64_t c); - std::uint64_t generateRandomValue() override; + uint64_t generateRandomValue() override; double generateUnitNormalRandomValue() override; }; diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index f12c7d5..f534db4 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -14,7 +14,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator() m_current_value(m_seed) {} // Constructor with seed -LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t seed) +LinearCongruentialGenerator::LinearCongruentialGenerator(const uint64_t seed) : PseudoRandomNumberGenerator(seed, getMinimumValue(DefaultMask), getMaximumValue(DefaultModulus, DefaultMask)), m_modulus(DefaultModulus), m_multiplier(DefaultMultiplier), @@ -23,7 +23,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(const std::uint64_t see m_current_value(m_seed) {} // Constructor with custom parameters -LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) +LinearCongruentialGenerator::LinearCongruentialGenerator(uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask) : PseudoRandomNumberGenerator(getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), @@ -32,7 +32,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t modulus, m_current_value(m_seed) {} // Constructor with seed and custom parameters -LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std::uint64_t modulus, std::uint64_t multiplier, std::uint64_t increment, std::uint64_t mask) +LinearCongruentialGenerator::LinearCongruentialGenerator(uint64_t seed, uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask) : PseudoRandomNumberGenerator(seed, getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), @@ -41,11 +41,11 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(std::uint64_t seed, std m_current_value(m_seed) {} -std::uint64_t LinearCongruentialGenerator::getMinimumValue(const std::uint64_t mask) { +uint64_t LinearCongruentialGenerator::getMinimumValue(const uint64_t mask) { // Find the index of the least significant bit - std::uint64_t least_significant_bit_index = 0; - std::uint64_t least_significant_compare_mask = mask; + uint64_t least_significant_bit_index = 0; + uint64_t least_significant_compare_mask = mask; // We shift bits until we reach a 1, incrementing the counter each time while ((least_significant_compare_mask & 1) == 0) { @@ -55,12 +55,12 @@ std::uint64_t LinearCongruentialGenerator::getMinimumValue(const std::uint64_t m return (1 << least_significant_bit_index) - 1; } -std::uint64_t LinearCongruentialGenerator::getMaximumValue(const std::uint64_t modulus, std::uint64_t mask) { +uint64_t LinearCongruentialGenerator::getMaximumValue(const uint64_t modulus, uint64_t mask) { return std::min(modulus, mask); } // Generate a random value anywhere in the range of the LCG -std::uint64_t LinearCongruentialGenerator::generateRandomValue() { +uint64_t LinearCongruentialGenerator::generateRandomValue() { // Compute the standard LCG formula for the next value m_current_value = (m_multiplier * m_current_value + m_increment) % m_modulus; @@ -76,9 +76,9 @@ std::uint64_t LinearCongruentialGenerator::generateRandomValue() { // Generate a random value normalized to the range [0, 1) double LinearCongruentialGenerator::generateUnitNormalRandomValue() { - std::uint64_t random_value = generateRandomValue(); + uint64_t random_value = generateRandomValue(); - std::uint64_t range = m_maximum_value - m_minimum_value; + uint64_t range = m_maximum_value - m_minimum_value; if (range == 0) { return 0.0; // Avoid division by zero } diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index acd9e39..159567f 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -11,22 +11,22 @@ not generally true and will likely have the PRNG produce highly incorrect values generating uniform random floats. Use with caution! */ PseudoRandomNumberGenerator::PseudoRandomNumberGenerator() : m_seed(generateCryptographicallyInsecureSeed()), - m_minimum_value(std::numeric_limits::min()), - m_maximum_value(std::numeric_limits::max()) {} + m_minimum_value(std::numeric_limits::min()), + m_maximum_value(std::numeric_limits::max()) {} -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed) +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const uint64_t seed) : m_seed(seed), - m_minimum_value(std::numeric_limits::min()), - m_maximum_value(std::numeric_limits::max()) {} + m_minimum_value(std::numeric_limits::min()), + m_maximum_value(std::numeric_limits::max()) {} -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t minimum_value, const std::uint64_t maximum_value) +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const uint64_t minimum_value, const uint64_t maximum_value) : m_seed(generateCryptographicallyInsecureSeed()), m_minimum_value(minimum_value), m_maximum_value(maximum_value) {} -PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const std::uint64_t seed, const std::uint64_t minimum_value, const std::uint64_t maximum_value) +PseudoRandomNumberGenerator::PseudoRandomNumberGenerator(const uint64_t seed, const uint64_t minimum_value, const uint64_t maximum_value) : m_seed(seed), m_minimum_value(minimum_value), m_maximum_value(maximum_value) {} @@ -35,6 +35,6 @@ std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed auto current_time = std::chrono::system_clock::now(); auto current_time_duration = current_time.time_since_epoch(); // convert a bare time to a duration auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds - std::uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed + uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed return seed_from_milliseconds; }; \ No newline at end of file diff --git a/src/prngs/xorshift.cpp b/src/prngs/xorshift.cpp index 15eea9b..241f8c3 100644 --- a/src/prngs/xorshift.cpp +++ b/src/prngs/xorshift.cpp @@ -11,14 +11,14 @@ XORShift::XORShift() m_c(DefaultC), m_current_value(m_seed) {} -XORShift::XORShift(const std::uint64_t seed) +XORShift::XORShift(const uint64_t seed) : PseudoRandomNumberGenerator(seed), m_a(DefaultA), m_b(DefaultB), m_c(DefaultC), m_current_value(m_seed) {} -XORShift::XORShift(const std::uint64_t seed, const std::uint64_t a, const std::uint64_t b, const std::uint64_t c) +XORShift::XORShift(const uint64_t seed, const uint64_t a, const uint64_t b, const uint64_t c) : PseudoRandomNumberGenerator(seed), m_a(a), m_b(b), @@ -28,7 +28,7 @@ XORShift::XORShift(const std::uint64_t seed, const std::uint64_t a, const std::u // Generate a random value anywhere in the range of the XORShift algorithm -std::uint64_t XORShift::generateRandomValue() { +uint64_t XORShift::generateRandomValue() { // Compute the standard LCG formula for the next value m_current_value ^= (m_current_value << m_a); m_current_value ^= (m_current_value >> m_b); @@ -40,9 +40,9 @@ std::uint64_t XORShift::generateRandomValue() { // Generate a random value normalized to the range [0, 1) double XORShift::generateUnitNormalRandomValue() { - std::uint64_t random_value = generateRandomValue(); + uint64_t random_value = generateRandomValue(); - std::uint64_t range = m_maximum_value - m_minimum_value; + uint64_t range = m_maximum_value - m_minimum_value; if (range == 0) { return 0.0; // Avoid division by zero } diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index 8e3f571..664f129 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -10,24 +10,24 @@ TEST(TestXORShift, BlankConstructor) { } TEST(TestXORShift, SeedConstructor) { - EXPECT_NO_THROW(XORShift(std::numeric_limits::min())); - EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max())); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min())); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max())); } TEST(TestXORShift, SeedAndConstantsConstructor) { - EXPECT_NO_THROW(XORShift(std::numeric_limits::min(), 1, 2, 3)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1, 79, 55555, std::numeric_limits::max())); - EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5, 4545, 9876567, 9876786)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5, 1432, 6876, 3)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2, 42, 42, 42)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1, 2345678987, 9876543, 123422)); - EXPECT_NO_THROW(XORShift(std::numeric_limits::max(), 345679875, 2, 0)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min(), 1, 2, 3)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 1, 79, 55555, std::numeric_limits::max())); + EXPECT_NO_THROW(XORShift(std::numeric_limits::min() + 5, 4545, 9876567, 9876786)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 5, 1432, 6876, 3)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() / 2, 42, 42, 42)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max() - 1, 2345678987, 9876543, 123422)); + EXPECT_NO_THROW(XORShift(std::numeric_limits::max(), 345679875, 2, 0)); } From 4f114c8a578fcb384235693f57414b404dacbcc7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 24 May 2025 17:52:24 -0400 Subject: [PATCH 41/95] Added const everywhere appropriate. --- include/linear_congruential_generator.hpp | 6 +++--- src/prngs/linear_congruential_generator.cpp | 6 +++--- tests/test_LinearCongruentialGenerator.cpp | 12 ++++++------ tests/test_XORShift.cpp | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp index f6d934d..eef02fc 100644 --- a/include/linear_congruential_generator.hpp +++ b/include/linear_congruential_generator.hpp @@ -42,13 +42,13 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { static uint64_t getMinimumValue(const std::uint64_t mask); - static uint64_t getMaximumValue(const uint64_t modulus, uint64_t mask); + static uint64_t getMaximumValue(const uint64_t modulus, const uint64_t mask); public: LinearCongruentialGenerator(); LinearCongruentialGenerator(const uint64_t seed); - LinearCongruentialGenerator(uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask); - LinearCongruentialGenerator(uint64_t seed, uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask); + LinearCongruentialGenerator(const uint64_t modulus, const uint64_t multiplier, const uint64_t increment, const uint64_t mask); + LinearCongruentialGenerator(const uint64_t seed, const uint64_t modulus, const uint64_t multiplier, const uint64_t increment, const uint64_t mask); uint64_t generateRandomValue() override; double generateUnitNormalRandomValue() override; diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index f534db4..2326522 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -23,7 +23,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(const uint64_t seed) m_current_value(m_seed) {} // Constructor with custom parameters -LinearCongruentialGenerator::LinearCongruentialGenerator(uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask) +LinearCongruentialGenerator::LinearCongruentialGenerator(const uint64_t modulus, const uint64_t multiplier, const uint64_t increment, const uint64_t mask) : PseudoRandomNumberGenerator(getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), @@ -32,7 +32,7 @@ LinearCongruentialGenerator::LinearCongruentialGenerator(uint64_t modulus, uint6 m_current_value(m_seed) {} // Constructor with seed and custom parameters -LinearCongruentialGenerator::LinearCongruentialGenerator(uint64_t seed, uint64_t modulus, uint64_t multiplier, uint64_t increment, uint64_t mask) +LinearCongruentialGenerator::LinearCongruentialGenerator(const uint64_t seed, const uint64_t modulus, const uint64_t multiplier, const uint64_t increment, const uint64_t mask) : PseudoRandomNumberGenerator(seed, getMinimumValue(mask), getMaximumValue(modulus, mask)), m_modulus(modulus), m_multiplier(multiplier), @@ -55,7 +55,7 @@ uint64_t LinearCongruentialGenerator::getMinimumValue(const uint64_t mask) { return (1 << least_significant_bit_index) - 1; } -uint64_t LinearCongruentialGenerator::getMaximumValue(const uint64_t modulus, uint64_t mask) { +uint64_t LinearCongruentialGenerator::getMaximumValue(const uint64_t modulus, const uint64_t mask) { return std::min(modulus, mask); } diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index 7fc0a6c..7b84083 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -35,9 +35,9 @@ TEST(TestLinearCongruentialGenerator, SeedAndCustomConstructor) { TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueHasCorrectRange) { LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); - short enough_iterations_to_be_confident = 1000; + const short enough_iterations_to_be_confident = 1000; for (short i = 0; i < enough_iterations_to_be_confident; ++i) { - double value = lcg.generateUnitNormalRandomValue(); + const double value = lcg.generateUnitNormalRandomValue(); EXPECT_GE(value, 0.0); EXPECT_LE(value, 1.0); } @@ -51,13 +51,13 @@ If this test ever fails it should be investigated further manually. */ TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueAverageConvergesToExpected) { LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); - double expected_average = 0.5; + const double expected_average = 0.5; double sum = 0.0; - short enough_iterations_to_be_confident = 10000; + const short enough_iterations_to_be_confident = 10000; for (short i = 0; i < enough_iterations_to_be_confident; ++i) { sum += lcg.generateUnitNormalRandomValue(); } - double average = sum / enough_iterations_to_be_confident; - double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + const double average = sum / enough_iterations_to_be_confident; + const double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); EXPECT_NEAR(average, expected_average, confidence_interval); } diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index 664f129..33de36c 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -34,9 +34,9 @@ TEST(TestXORShift, SeedAndConstantsConstructor) { TEST(TestXORShift, GenerateUnitNormalRandomValueHasCorrectRange) { XORShift xor_shift = XORShift(); - short enough_iterations_to_be_confident = 1000; + const short enough_iterations_to_be_confident = 1000; for (short i = 0; i < enough_iterations_to_be_confident; ++i) { - double value = xor_shift.generateUnitNormalRandomValue(); + const double value = xor_shift.generateUnitNormalRandomValue(); EXPECT_GE(value, 0.0); EXPECT_LE(value, 1.0); } @@ -50,13 +50,13 @@ If this test ever fails it should be investigated further manually. */ TEST(TestXORShift, GenerateUnitNormalRandomValueAverageConvergesToExpected) { XORShift xor_shift = XORShift(); - double expected_average = 0.5; + const double expected_average = 0.5; double sum = 0.0; - short enough_iterations_to_be_confident = 10000; + const short enough_iterations_to_be_confident = 10000; for (short i = 0; i < enough_iterations_to_be_confident; ++i) { sum += xor_shift.generateUnitNormalRandomValue(); } - double average = sum / enough_iterations_to_be_confident; - double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + const double average = sum / enough_iterations_to_be_confident; + const double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); EXPECT_NEAR(average, expected_average, confidence_interval); } \ No newline at end of file From c37b56cb5e1005ee4d83c11626759796bc278d50 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 25 May 2025 15:07:21 -0400 Subject: [PATCH 42/95] Finished tests for equidistribution --- tests/test_LinearCongruentialGenerator.cpp | 8 ++++---- tests/test_XORShift.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index 7b84083..e559602 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -49,15 +49,15 @@ it is possible to fail even if everything is working properly, but it would be incredibly unlikely to fail. If this test ever fails it should be investigated further manually. */ -TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueAverageConvergesToExpected) { +TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueEquidistributed) { LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); const double expected_average = 0.5; double sum = 0.0; - const short enough_iterations_to_be_confident = 10000; - for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + const int enough_iterations_to_be_confident = 100000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { sum += lcg.generateUnitNormalRandomValue(); } const double average = sum / enough_iterations_to_be_confident; - const double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + const double confidence_interval = 0.01; EXPECT_NEAR(average, expected_average, confidence_interval); } diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index 33de36c..eb40996 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -48,15 +48,15 @@ it is possible to fail even if everything is working properly, but it would be incredibly unlikely to fail. If this test ever fails it should be investigated further manually. */ -TEST(TestXORShift, GenerateUnitNormalRandomValueAverageConvergesToExpected) { +TEST(TestXORShift, GenerateUnitNormalRandomValueEquidistributed) { XORShift xor_shift = XORShift(); const double expected_average = 0.5; double sum = 0.0; - const short enough_iterations_to_be_confident = 10000; - for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + const int enough_iterations_to_be_confident = 100000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { sum += xor_shift.generateUnitNormalRandomValue(); } const double average = sum / enough_iterations_to_be_confident; - const double confidence_interval = 1 / (enough_iterations_to_be_confident / 10); + const double confidence_interval = 0.01; EXPECT_NEAR(average, expected_average, confidence_interval); } \ No newline at end of file From 61b519172fee86061304a917aa490ec54c34bc74 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 25 May 2025 15:12:12 -0400 Subject: [PATCH 43/95] Increased the number of iterations to 10000 fir GenerateUntNormalRandomValueHasCorrectRange --- tests/test_LinearCongruentialGenerator.cpp | 4 ++-- tests/test_XORShift.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index e559602..cb5cb95 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -35,8 +35,8 @@ TEST(TestLinearCongruentialGenerator, SeedAndCustomConstructor) { TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueHasCorrectRange) { LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); - const short enough_iterations_to_be_confident = 1000; - for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + const int enough_iterations_to_be_confident = 10000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { const double value = lcg.generateUnitNormalRandomValue(); EXPECT_GE(value, 0.0); EXPECT_LE(value, 1.0); diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index eb40996..69b5a6e 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -34,8 +34,8 @@ TEST(TestXORShift, SeedAndConstantsConstructor) { TEST(TestXORShift, GenerateUnitNormalRandomValueHasCorrectRange) { XORShift xor_shift = XORShift(); - const short enough_iterations_to_be_confident = 1000; - for (short i = 0; i < enough_iterations_to_be_confident; ++i) { + const int enough_iterations_to_be_confident = 10000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { const double value = xor_shift.generateUnitNormalRandomValue(); EXPECT_GE(value, 0.0); EXPECT_LE(value, 1.0); From c67d441bf579eb11a6e7327239b993fa2eded0e8 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 25 May 2025 15:55:28 -0400 Subject: [PATCH 44/95] Reconfigured the print_error method to use he program_name variable instead of a hardcoded string --- src/runner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runner.cpp b/src/runner.cpp index 3a5371d..95d8739 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -11,7 +11,7 @@ void ProgramRunner::print_error() { std::cerr << program_name << ": bad usage\n" - << "Try 'randomizer --help' for more information.\n"; + << "Try '" << program_name << " --help' for more information.\n"; } void ProgramRunner::print_help() { From e24f165019bbb367b7be6531ca9cdb1b7431a2c4 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 25 May 2025 21:29:49 -0400 Subject: [PATCH 45/95] Test declarations for ProgramRunner --- tests/test_ProgramRunner.cpp | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 tests/test_ProgramRunner.cpp diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp new file mode 100644 index 0000000..feb7bc8 --- /dev/null +++ b/tests/test_ProgramRunner.cpp @@ -0,0 +1,132 @@ +#include +#include "program_runner.hpp" + +TEST(TestProgramRunner, NoArgs){ + +} + +TEST(TestProgramRunner, JustHelp){ + +} + +TEST(TestProgramRunner, JustVersion){ + +} + +TEST(TestProgramRunner, HelpThenVersion){ + +} + +TEST(TestProgramRunner, VersionThenHelp){ + +} + +TEST(TestProgramRunner, HelpThenOther){ + +} + +TEST(TestProgramRunner, VersionThenOther){ + +} + +TEST(TestProgramRunner, BadSubcommands){ + +} + +TEST(TestProgramRunner, DefaultAlgorithm){ + +} + +TEST(TestProgramRunner, ExplicitAlgorithmXOR){ + +} + +TEST(TestProgramRunner, ExplicitAlgorithmLCG){ + +} + +TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ + +} + +TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ + +} + +TEST(TestProgramRunner, NoMinOrMax){ + +} + +TEST(TestProgramRunner, MinNoMax){ + +} + +TEST(TestProgramRunner, MaxNoMin){ + +} + +TEST(TestProgramRunner, MinAndMax){ + +} + +TEST(TestProgramRunner, MinExcessiveNegativeMagnitude){ + +} + +TEST(TestProgramRunner, MinExcessivePositiveMagnitude){ + +} + +TEST(TestProgramRunner, MinAndMaxExcessiveNegativeMagnitude){ + +} + +TEST(TestProgramRunner, MinAndMaxExcessivePositiveMagnitude){ + +} + +TEST(TestProgramRunner, DefaultCount){ + +} + +TEST(TestProgramRunner, ExplicitCount){ + +} + +TEST(TestProgramRunner, CountIsZero){ + +} + +TEST(TestProgramRunner, CountIsOne){ + +} + +TEST(TestProgramRunner, CountIsNegative){ + +} + +TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ + +} + +TEST(TestProgramRunner, DefaultType){ + +} + +TEST(TestProgramRunner, ExplicitTypeUnit){ + +} + +TEST(TestProgramRunner, ExplicitTypeFloat){ + +} + +TEST(TestProgramRunner, ExplicitTypeInt){ + +} + +TEST(TestProgramRunner, ExplicitTypeInvalid){ + +} + + From f605eaec95f8d80db13861323b21676945eed787 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 25 May 2025 22:06:20 -0400 Subject: [PATCH 46/95] Changed the determine_program_configuration method to take a reference instead to remove a copy --- .vscode/launch.json | 2 +- include/program_runner.hpp | 3 +-- src/runner.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bfa34bc..c3e0bb5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": [], + "args": ["--help", "--version", "this is another argument hope you can find it"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 9b8b38b..91262a4 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -77,14 +77,13 @@ class ProgramRunner { static constexpr uint32_t DefaultCount = 1; RawArguments parse_args(int argc, char **argv); - void determine_program_configuration(const RawArguments raw_Arguments); + void determine_program_configuration(const RawArguments &raw_arguments); void determine_user_message_configuration(const bool error, const bool show_version, const bool show_help); void determine_generation_range_configuration(const std::optional &min_str, const std::optional &max_str); void determine_generation_type_configuration(const std::optional &generation_type); void determine_count_configuration(const std::optional &count_str); void determine_algorithm_configuration(const std::optional &alg_str); - bool erroneous_flag_args_provided(const RawArguments raw_Arguments); void print_error(); void print_help(); void print_version(); diff --git a/src/runner.cpp b/src/runner.cpp index 95d8739..1515069 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -141,7 +141,7 @@ void ProgramRunner::determine_count_configuration(const std::optionalbehaviour; From 5659cef1e714f28541a4fc4e10c7cd0bb0582604 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Wed, 28 May 2025 20:12:08 -0400 Subject: [PATCH 47/95] Added ArgvBuilder in the ProgramRunner tests to simulate cli arguments --- tests/test_ProgramRunner.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index feb7bc8..2e3d7d0 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -1,8 +1,26 @@ #include #include "program_runner.hpp" -TEST(TestProgramRunner, NoArgs){ +struct ArgvBuilder { + std::vector args; + std::vector argv; + + ArgvBuilder(std::initializer_list init) : args(init) { + std::string program_name = "randomizer"; + argv.push_back(const_cast(program_name.c_str())); + for (auto& s : args) { + argv.push_back(const_cast(s.c_str())); + } + argv.push_back(nullptr); // argv must be null-terminated + } + + int argc() const { return static_cast(args.size()); } + char** argv_ptr() { return argv.data(); } +}; +TEST(TestProgramRunner, NoArgs){ + ArgvBuilder builder({}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); } TEST(TestProgramRunner, JustHelp){ From 49f02fc872d4dedcd53b7b02c3d3a1afd755f599 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Thu, 29 May 2025 17:45:04 -0400 Subject: [PATCH 48/95] Added the ProgramStatus struct to be returned by iterate() and run() in ProgramRunner --- include/program_runner.hpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 91262a4..f312658 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -9,13 +9,25 @@ class ProgramRunner { public: ProgramRunner(int argc, char **argv); - void iterate(); - void iterate(std::uint64_t iterations); - void run(); + struct ProgramStatus { + const std::optional stderr_message; + const std::optional stdout_message; + const std::optional exit_code; + }; + + ProgramStatus iterate(); + + // will return the ProgramStatus of the last iteration + ProgramStatus iterate(uint64_t iterations); + + // runs until the program runner finishes, will return the ProgramStatus of the last iteration + ProgramStatus run(); const std::string version = "0.1"; const std::string program_name = "randomizer"; + + private: enum class Algorithm { From 224ed0cbfaec00ddbe8641bbd2ea8e44386d5c23 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Thu, 29 May 2025 17:45:45 -0400 Subject: [PATCH 49/95] Added the test cases for the cli args --- tests/test_ProgramRunner.cpp | 264 +++++++++++++++++++++++++++++++---- 1 file changed, 234 insertions(+), 30 deletions(-) diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index 2e3d7d0..72a496a 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -21,130 +21,334 @@ struct ArgvBuilder { TEST(TestProgramRunner, NoArgs){ ArgvBuilder builder({}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, JustHelp){ - + ArgvBuilder builder({"--help"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, JustVersion){ - + ArgvBuilder builder({"--version"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, HelpThenVersion){ - + ArgvBuilder builder({"--help", "--version"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, VersionThenHelp){ - + ArgvBuilder builder({"--version", "--help"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, HelpThenOther){ - + ArgvBuilder builder({"--help", "other", "stuff", "which", "is", "ignored"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, VersionThenOther){ - + ArgvBuilder builder({"--version", "ignore", "this", "stuff", "please", "and", "thank", "you"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, BadSubcommands){ - + ArgvBuilder builder({"badsubcommand", "--help"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, DefaultAlgorithm){ - + ArgvBuilder builder({"--min", "0", "--max=10", "-c", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitAlgorithmXOR){ - + ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitAlgorithmLCG){ - + ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m=0", "-M", "10", "--count=5", "-t=unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ - + ArgvBuilder builder({"-a", "mersenne", "-m=0", "-M", "10", "--count=5", "--type=unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ - + ArgvBuilder builder({"--algorithm", "invalid-algorithm", "-m", "0", "-M=10", "--count", "5", "-t", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, NoMinOrMax){ - + ArgvBuilder builder({"--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MinNoMax){ - + ArgvBuilder builder({"--min", "0", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MaxNoMin){ - + ArgvBuilder builder({"-M", "10", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MinAndMax){ - + ArgvBuilder builder({"--min", "0", "--max", "6", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, MinExcessiveNegativeMagnitude){ - + constexpr char* lowest_64_bit_signed_minus_one = "-9223372036854775809"; + ArgvBuilder builder({"--min", lowest_64_bit_signed_minus_one, "--max", "10", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MinExcessivePositiveMagnitude){ - + constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; + ArgvBuilder builder({"--min", highest_64_bit_signed_plus_one, "--max", "10", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MinAndMaxExcessiveNegativeMagnitude){ - + constexpr char* lowest_64_bit_signed_minus_one = "-9223372036854775809"; + ArgvBuilder builder({"--min", lowest_64_bit_signed_minus_one, "--max", lowest_64_bit_signed_minus_one, "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, MinAndMaxExcessivePositiveMagnitude){ - + constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; + ArgvBuilder builder({"--min", highest_64_bit_signed_plus_one, "--max", highest_64_bit_signed_plus_one, "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, DefaultCount){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitCount){ + int explicit_count = 34539; // Arbitrary positive count for testing + std::string explicit_count_str = std::to_string(explicit_count); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", explicit_count_str, "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + for (int i = 0; i < explicit_count; ++i) { + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_FALSE(status.exit_code.has_value()); + } + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, CountIsZero){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "0", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, CountIsOne){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "1", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, CountIsNegative){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "-5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ - + constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_64_bit_signed_plus_one, "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.iterate(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } TEST(TestProgramRunner, DefaultType){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--algorithm", "xorshift"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitTypeUnit){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "unit"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitTypeFloat){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "float"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitTypeInt){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "int"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_FALSE(status.stderr_message.has_value()); + EXPECT_TRUE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, ExplicitTypeInvalid){ - + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "invalid"}); + ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); + ProgramRunner::ProgramStatus status = program_runner.run(); + EXPECT_TRUE(status.stderr_message.has_value()); + EXPECT_FALSE(status.stdout_message.has_value()); + EXPECT_TRUE(status.exit_code.has_value()); + EXPECT_NE(status.exit_code, 0); } From 4ba4f08dcf972e9a1dbb862faff56690b5c4117d Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Thu, 29 May 2025 18:55:51 -0400 Subject: [PATCH 50/95] Added the pointer to prng in the program runner --- include/program_runner.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index f312658..7058d86 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include "prng.hpp" class ProgramRunner { public: @@ -106,5 +108,7 @@ class ProgramRunner { std::optional> max = std::nullopt; std::optional count = std::nullopt; + std::unique_ptr prng = nullptr; + }; \ No newline at end of file From 26cecd55bfab94fe437ca991204eb0c5dad146cb Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 30 May 2025 15:03:02 -0400 Subject: [PATCH 51/95] Added the virtual methods for generating floats and ints to the prng --- include/prng.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/prng.hpp b/include/prng.hpp index b78451f..66b15f8 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -21,6 +21,8 @@ class PseudoRandomNumberGenerator virtual uint64_t generateRandomValue() = 0; virtual double generateUnitNormalRandomValue() = 0; + virtual double generateFloatingPointRandomValue(double min, double max) = 0; + virtual int64_t generateIntegerRandomValue(int64_t min, int64_t max) = 0; }; #endif \ No newline at end of file From 4f1af2ecf7452b642bfeee42a52744dc059c19a6 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 30 May 2025 16:01:06 -0400 Subject: [PATCH 52/95] Changed the min and max's to be floats and i32s instead of doubles and i64s to and write the generateFloatingPointRandomeValue and generateIntegerRandomValue prng methods --- include/prng.hpp | 4 +- include/program_runner.hpp | 23 ++++++-- src/prngs/prng.cpp | 14 ++++- src/runner.cpp | 113 +++++++++++++++++++++++++++---------- 4 files changed, 115 insertions(+), 39 deletions(-) diff --git a/include/prng.hpp b/include/prng.hpp index 66b15f8..4386948 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -21,8 +21,8 @@ class PseudoRandomNumberGenerator virtual uint64_t generateRandomValue() = 0; virtual double generateUnitNormalRandomValue() = 0; - virtual double generateFloatingPointRandomValue(double min, double max) = 0; - virtual int64_t generateIntegerRandomValue(int64_t min, int64_t max) = 0; + double generateFloatingPointRandomValue(float min, float max); + int64_t generateIntegerRandomValue(int32_t min, int32_t max); }; #endif \ No newline at end of file diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 7058d86..05b5d46 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -12,8 +12,8 @@ class ProgramRunner { ProgramRunner(int argc, char **argv); struct ProgramStatus { - const std::optional stderr_message; const std::optional stdout_message; + const std::optional stderr_message; const std::optional exit_code; }; @@ -25,6 +25,8 @@ class ProgramRunner { // runs until the program runner finishes, will return the ProgramStatus of the last iteration ProgramStatus run(); + bool is_finished(); + const std::string version = "0.1"; const std::string program_name = "randomizer"; @@ -97,18 +99,27 @@ class ProgramRunner { void determine_generation_type_configuration(const std::optional &generation_type); void determine_count_configuration(const std::optional &count_str); void determine_algorithm_configuration(const std::optional &alg_str); + void create_prng(); - void print_error(); - void print_help(); - void print_version(); + std::string error_string(); + std::string help_string(); + std::string version_string(); std::optional behaviour = std::nullopt; std::optional algorithm = std::nullopt; - std::optional> min = std::nullopt; - std::optional> max = std::nullopt; + std::optional> min = std::nullopt; + std::optional> max = std::nullopt; std::optional count = std::nullopt; + uint32_t iteration = 0; + bool finished = false; + std::unique_ptr prng = nullptr; + // Exit codes + static constexpr int ExitCodeSuccess = 0; + static constexpr int ExitCodeError = 1; + + }; \ No newline at end of file diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 159567f..f56beff 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -37,4 +37,16 @@ std::uint64_t PseudoRandomNumberGenerator::generateCryptographicallyInsecureSeed auto time_as_milliseconds = std::chrono::duration_cast(current_time_duration); // get the duration as a value in milliseconds uint64_t seed_from_milliseconds = time_as_milliseconds.count(); // convert the milliseconds to a bare integer and use that as our seed return seed_from_milliseconds; -}; \ No newline at end of file +}; + +double PseudoRandomNumberGenerator::generateFloatingPointRandomValue(float min, float max) { + double random_val = generateUnitNormalRandomValue(); + double scaled_value = min + (random_val * (max - min)); + return scaled_value; +} + +int64_t PseudoRandomNumberGenerator::generateIntegerRandomValue(int32_t min, int32_t max) { + int64_t random_val = generateUnitNormalRandomValue(); + int64_t scaled_value = min + (random_val * (max - min)); + return scaled_value; +} \ No newline at end of file diff --git a/src/runner.cpp b/src/runner.cpp index 1515069..0bf1cba 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -7,27 +7,33 @@ #include #include #include "program_runner.hpp" +#include "prng.hpp" +#include "xorshift.hpp" +#include "linear_congruential_generator.hpp" -void ProgramRunner::print_error() { - std::cerr << program_name << ": bad usage\n" - << "Try '" << program_name << " --help' for more information.\n"; +std::string ProgramRunner::error_string() { + std::string error_string = program_name + ": bad usage\n" + + "Try '" + program_name + " --help' for more information.\n"; + return error_string; } -void ProgramRunner::print_help() { - std::cout << "Usage: " << program_name << " [options]\n" - << "Options:\n" - << " -h, --help Show this help message\n" - << " -v, --version Show version information\n" - << " -a, --algorithm Specify the algorithm (default: xorshift)\n" - << " -m, --min Minimum value\n" - << " -M, --max Maximum value\n" - << " -c, --count Number of random numbers to generate (default: 1)\n" - << " -t, --type Specify the type to output (default: unit)\n"; +std::string ProgramRunner::help_string() { + std::string help_string = "Usage: " + program_name + " [options]\n" + + "Options:\n" + + " -h, --help Show this help message\n" + + " -v, --version Show version information\n" + + " -a, --algorithm Specify the algorithm (default: xorshift)\n" + + " -m, --min Minimum value\n" + + " -M, --max Maximum value\n" + + " -c, --count Number of random numbers to generate (default: 1)\n" + + " -t, --type Specify the type to output (default: unit)\n"; + return help_string; } -void ProgramRunner::print_version() { - std::cout << program_name << " " << version << "\n"; +std::string ProgramRunner::version_string() { + std::string version_string = program_name + " " + version + "\n"; + return version_string; } @@ -57,6 +63,7 @@ std::optional parse_value(const std::string& str) { return value; } + template std::optional> parse_min_and_max_numbers(const std::string &min_str, const std::string &max_str) { std::optional min_value = parse_value(min_str); @@ -71,7 +78,7 @@ void ProgramRunner::determine_generation_range_configuration(const std::optional bool both_max_and_min_specified = min_str.has_value() && max_str.has_value(); bool neither_max_or_min_specified = !min_str.has_value() && !max_str.has_value(); if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateInteger){ - auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); + auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); if (!min_and_max.has_value()){ this->behaviour = ProgramBehaviour::Error; return; @@ -79,7 +86,7 @@ void ProgramRunner::determine_generation_range_configuration(const std::optional this->min = min_and_max.value().first; this->max = min_and_max.value().second; } else if (both_max_and_min_specified && this->behaviour == ProgramBehaviour::GenerateFloating){ - auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); + auto min_and_max = parse_min_and_max_numbers(min_str.value(), max_str.value()); if (!min_and_max.has_value()){ this->behaviour = ProgramBehaviour::Error; return; @@ -233,22 +240,68 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { return raw_arguments; } +void ProgramRunner::create_prng() { + switch (this->algorithm.value()) { + case ProgramRunner::Algorithm::XORShift: + this->prng = std::make_unique(); + break; + case ProgramRunner::Algorithm::LinearCongruentialGenerator: + this->prng = std::make_unique(); + break; + default: + throw std::runtime_error("Unsupported algorithm"); + } + + if (this->prng == nullptr) { + throw std::runtime_error("Failed to create PseudoRandomNumberGenerator instance"); + } + + return; +} + ProgramRunner::ProgramRunner(int argc, char **argv){ RawArguments raw_arguments = parse_args(argc, argv); determine_program_configuration(raw_arguments); - - if (this->behaviour.value() == ProgramBehaviour::Error){ - print_error(); - exit(1); - } else if (this->behaviour.value() == ProgramBehaviour::Help){ - print_help(); - exit(0); - } else if (this->behaviour.value() == ProgramBehaviour::Version){ - print_version(); - exit(0); - } else { - std::cout << "You got to another type of state!" << std::endl; - exit(0); + create_prng(); +} + +bool ProgramRunner::is_finished() { + if (this->finished) { + return true; + } else if ((this->count.has_value()) && (this->iteration >= this->count)) { + this->finished = true; + return true; + } else if (!this->count.has_value() || this->count.value() == 0) { + throw std::runtime_error("ProgramRunner is not configured properly, count is not set or is zero"); } + return false; +} + +ProgramRunner::ProgramStatus ProgramRunner::iterate() { + + if (is_finished()) { + throw std::runtime_error("ProgramRunner has finished, cannot iterate further"); + } + + if (!this->behaviour.has_value()) { + throw std::runtime_error("ProgramRunner not configured properly"); + } + + if (this->behaviour == ProgramBehaviour::Error) { + this->finished = true; + return {std::nullopt, error_string(), ProgramRunner::ExitCodeError}; + } else if (this->behaviour == ProgramBehaviour::Help) { + this->finished = true; + return {help_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; + } else if (this->behaviour == ProgramBehaviour::Version) { + this->finished = true; + return {version_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; + } + + + + + + } From 801e911f18e0f713cba178d0024c504f94faafcf Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 30 May 2025 16:25:45 -0400 Subject: [PATCH 53/95] Finished the iterate() method in runner --- src/runner.cpp | 56 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/runner.cpp b/src/runner.cpp index 0bf1cba..d8ccb1e 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -284,24 +284,52 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { throw std::runtime_error("ProgramRunner has finished, cannot iterate further"); } - if (!this->behaviour.has_value()) { + if (!this->behaviour.has_value() || !this->count.has_value() || !this->prng) { throw std::runtime_error("ProgramRunner not configured properly"); } - if (this->behaviour == ProgramBehaviour::Error) { - this->finished = true; - return {std::nullopt, error_string(), ProgramRunner::ExitCodeError}; - } else if (this->behaviour == ProgramBehaviour::Help) { - this->finished = true; - return {help_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; - } else if (this->behaviour == ProgramBehaviour::Version) { - this->finished = true; - return {version_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; - } - - - + switch (this->behaviour.value()) { + + case ProgramBehaviour::Error: + this->finished = true; + return {std::nullopt, error_string(), ProgramRunner::ExitCodeError}; + + case ProgramBehaviour::Help: + this->finished = true; + return {help_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; + + case ProgramBehaviour::Version: + this->finished = true; + return {version_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; + + case ProgramBehaviour::GenerateUnitNormal: + this->iteration++; + double random_value = this->prng->generateUnitNormalRandomValue(); + auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; + return {std::to_string(random_value), std::nullopt, exit_code_based_on_count}; + + case ProgramBehaviour::GenerateFloating: + this->iteration++; + float random_float = this->prng->generateFloatingPointRandomValue( + std::get(this->min.value_or(0.0f)), + std::get(this->max.value_or(1.0f)) + ); + auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; + return {std::to_string(random_float), std::nullopt, exit_code_based_on_count}; + + case ProgramBehaviour::GenerateInteger: + this->iteration++; + int32_t random_int = this->prng->generateIntegerRandomValue( + std::get(this->min.value_or(0)), + std::get(this->max.value_or(1)) + ); + auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; + return {std::to_string(random_int), std::nullopt, exit_code_based_on_count}; + default: + throw std::runtime_error("ProgramRunner not configured properly, behaviour is not set"); + } + return {std::nullopt, std::nullopt, ProgramRunner::ExitCodeError}; // Should never reach here } From 0b14474ee39a3fa3e380261a84ac00ced02ce78a Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 30 May 2025 17:07:54 -0400 Subject: [PATCH 54/95] Completed the iterate() and run() methods --- include/program_runner.hpp | 6 +++--- src/runner.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 05b5d46..dd832af 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -12,9 +12,9 @@ class ProgramRunner { ProgramRunner(int argc, char **argv); struct ProgramStatus { - const std::optional stdout_message; - const std::optional stderr_message; - const std::optional exit_code; + std::optional stdout_message; + std::optional stderr_message; + std::optional exit_code; }; ProgramStatus iterate(); diff --git a/src/runner.cpp b/src/runner.cpp index d8ccb1e..4d576fc 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -333,3 +333,30 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { return {std::nullopt, std::nullopt, ProgramRunner::ExitCodeError}; // Should never reach here } + +ProgramRunner::ProgramStatus ProgramRunner::iterate(uint64_t iterations) { + if (iterations == 0) { + throw std::invalid_argument("Number of iterations must be greater than zero"); + } + + ProgramStatus last_status; + for (uint64_t i = 0; i < iterations; ++i) { + last_status = iterate(); + if (last_status.exit_code.has_value()) { + return last_status; // Return immediately if an exit code is present + } + } + return last_status; // Return the status of the last iteration +} + +ProgramRunner::ProgramStatus ProgramRunner::run() { + while (!is_finished()) { + auto status = iterate(); + if (status.exit_code.has_value()) { + return status; + } + } + throw std::runtime_error("ProgramRunner has finished without returning an exit code. This should not happen."); +} + + From 42246ea216235dd2f239a3ca4a146fc5bb313dc5 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 30 May 2025 17:25:41 -0400 Subject: [PATCH 55/95] Fixed up tests to reflect changes to min and max types --- tests/test_ProgramRunner.cpp | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index 72a496a..86d990d 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -1,6 +1,11 @@ #include #include "program_runner.hpp" +constexpr char* highest_i32_plus_one = "2147483648"; +constexpr char* lowest_i32_minus_one = "-2147483649"; +constexpr char* highest_float_plus_more = "3.402823467e+38"; +constexpr char* lowest_float_minus_more = "-3.402823467e+38"; + struct ArgvBuilder { std::vector args; std::vector argv; @@ -98,7 +103,7 @@ TEST(TestProgramRunner, BadSubcommands){ } TEST(TestProgramRunner, DefaultAlgorithm){ - ArgvBuilder builder({"--min", "0", "--max=10", "-c", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max=10", "-c", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -108,7 +113,7 @@ TEST(TestProgramRunner, DefaultAlgorithm){ } TEST(TestProgramRunner, ExplicitAlgorithmXOR){ - ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "unit"}); + ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -118,7 +123,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmXOR){ } TEST(TestProgramRunner, ExplicitAlgorithmLCG){ - ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m=0", "-M", "10", "--count=5", "-t=unit"}); + ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m=0", "-M", "10", "--count=5", "-t=int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -128,7 +133,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmLCG){ } TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ - ArgvBuilder builder({"-a", "mersenne", "-m=0", "-M", "10", "--count=5", "--type=unit"}); + ArgvBuilder builder({"-a", "mersenne", "-m=0", "-M", "10", "--count=5", "--type=int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -138,7 +143,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ } TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ - ArgvBuilder builder({"--algorithm", "invalid-algorithm", "-m", "0", "-M=10", "--count", "5", "-t", "unit"}); + ArgvBuilder builder({"--algorithm", "invalid-algorithm", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -148,7 +153,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ } TEST(TestProgramRunner, NoMinOrMax){ - ArgvBuilder builder({"--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -158,7 +163,7 @@ TEST(TestProgramRunner, NoMinOrMax){ } TEST(TestProgramRunner, MinNoMax){ - ArgvBuilder builder({"--min", "0", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -168,7 +173,7 @@ TEST(TestProgramRunner, MinNoMax){ } TEST(TestProgramRunner, MaxNoMin){ - ArgvBuilder builder({"-M", "10", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"-M", "11.1", "--count", "5", "--type", "float"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -178,7 +183,7 @@ TEST(TestProgramRunner, MaxNoMin){ } TEST(TestProgramRunner, MinAndMax){ - ArgvBuilder builder({"--min", "0", "--max", "6", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "6", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -188,8 +193,7 @@ TEST(TestProgramRunner, MinAndMax){ } TEST(TestProgramRunner, MinExcessiveNegativeMagnitude){ - constexpr char* lowest_64_bit_signed_minus_one = "-9223372036854775809"; - ArgvBuilder builder({"--min", lowest_64_bit_signed_minus_one, "--max", "10", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", lowest_i32_minus_one, "--max", "10", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -199,8 +203,7 @@ TEST(TestProgramRunner, MinExcessiveNegativeMagnitude){ } TEST(TestProgramRunner, MinExcessivePositiveMagnitude){ - constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; - ArgvBuilder builder({"--min", highest_64_bit_signed_plus_one, "--max", "10", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", highest_i32_plus_one, "--max", "10", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -210,8 +213,7 @@ TEST(TestProgramRunner, MinExcessivePositiveMagnitude){ } TEST(TestProgramRunner, MinAndMaxExcessiveNegativeMagnitude){ - constexpr char* lowest_64_bit_signed_minus_one = "-9223372036854775809"; - ArgvBuilder builder({"--min", lowest_64_bit_signed_minus_one, "--max", lowest_64_bit_signed_minus_one, "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", lowest_i32_minus_one, "--max", lowest_i32_minus_one, "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -221,8 +223,7 @@ TEST(TestProgramRunner, MinAndMaxExcessiveNegativeMagnitude){ } TEST(TestProgramRunner, MinAndMaxExcessivePositiveMagnitude){ - constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; - ArgvBuilder builder({"--min", highest_64_bit_signed_plus_one, "--max", highest_64_bit_signed_plus_one, "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--min", highest_i32_plus_one, "--max", highest_i32_plus_one, "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -291,8 +292,7 @@ TEST(TestProgramRunner, CountIsNegative){ } TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ - constexpr char* highest_64_bit_signed_plus_one = "9223372036854775808"; - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_64_bit_signed_plus_one, "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_i32_plus_one, "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); EXPECT_TRUE(status.stderr_message.has_value()); @@ -302,7 +302,7 @@ TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ } TEST(TestProgramRunner, DefaultType){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--algorithm", "xorshift"}); + ArgvBuilder builder({"--count", "5", "--algorithm", "xorshift"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -312,7 +312,7 @@ TEST(TestProgramRunner, DefaultType){ } TEST(TestProgramRunner, ExplicitTypeUnit){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "unit"}); + ArgvBuilder builder({"--count", "5", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); @@ -322,7 +322,7 @@ TEST(TestProgramRunner, ExplicitTypeUnit){ } TEST(TestProgramRunner, ExplicitTypeFloat){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "float"}); + ArgvBuilder builder({"--min", "-4.3", "--max", "10.5", "--count", "5", "--type", "float"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); EXPECT_FALSE(status.stderr_message.has_value()); From c9c5718c298a047df9019c7f5dfeedcecc5f41b0 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 19:05:55 -0400 Subject: [PATCH 56/95] Fixed up code to use the POSIXly correct option in getopt which allows more test cases to pass. --- .vscode/launch.json | 2 +- src/main.cpp | 1 + src/runner.cpp | 22 +-- tests/test_ProgramRunner.cpp | 271 ++++++++++++++++++----------------- 4 files changed, 152 insertions(+), 144 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c3e0bb5..26f71ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": ["--help", "--version", "this is another argument hope you can find it"], + "args": ["badsubcommand", "--help"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/src/main.cpp b/src/main.cpp index 16afc13..b53f8f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,5 +7,6 @@ int main(int argc, char *argv[]){ ProgramRunner runner = ProgramRunner(argc, argv); + ProgramRunner::ProgramStatus status = runner.run(); return 0; } \ No newline at end of file diff --git a/src/runner.cpp b/src/runner.cpp index 4d576fc..533e608 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -177,7 +177,7 @@ void ProgramRunner::determine_program_configuration(const ProgramRunner::RawArgu } ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { - const char* const short_opts = "hva:m:M:c:t:"; + const char* const short_opts = "+hva:m:M:c:t:"; const ::option long_opts[] = { {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, @@ -241,6 +241,11 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { } void ProgramRunner::create_prng() { + + if (!this->algorithm.has_value()) { + return; + } + switch (this->algorithm.value()) { case ProgramRunner::Algorithm::XORShift: this->prng = std::make_unique(); @@ -271,9 +276,7 @@ bool ProgramRunner::is_finished() { } else if ((this->count.has_value()) && (this->iteration >= this->count)) { this->finished = true; return true; - } else if (!this->count.has_value() || this->count.value() == 0) { - throw std::runtime_error("ProgramRunner is not configured properly, count is not set or is zero"); - } + } return false; } @@ -284,7 +287,7 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { throw std::runtime_error("ProgramRunner has finished, cannot iterate further"); } - if (!this->behaviour.has_value() || !this->count.has_value() || !this->prng) { + if (!this->behaviour.has_value()) { throw std::runtime_error("ProgramRunner not configured properly"); } @@ -303,13 +306,14 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { this->finished = true; return {version_string(), std::nullopt, ProgramRunner::ExitCodeSuccess}; - case ProgramBehaviour::GenerateUnitNormal: + case ProgramBehaviour::GenerateUnitNormal: { this->iteration++; double random_value = this->prng->generateUnitNormalRandomValue(); auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; return {std::to_string(random_value), std::nullopt, exit_code_based_on_count}; + } - case ProgramBehaviour::GenerateFloating: + case ProgramBehaviour::GenerateFloating: { this->iteration++; float random_float = this->prng->generateFloatingPointRandomValue( std::get(this->min.value_or(0.0f)), @@ -317,8 +321,9 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { ); auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; return {std::to_string(random_float), std::nullopt, exit_code_based_on_count}; + } - case ProgramBehaviour::GenerateInteger: + case ProgramBehaviour::GenerateInteger: { this->iteration++; int32_t random_int = this->prng->generateIntegerRandomValue( std::get(this->min.value_or(0)), @@ -326,6 +331,7 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { ); auto exit_code_based_on_count = is_finished() ? std::optional(ProgramRunner::ExitCodeSuccess) : std::nullopt; return {std::to_string(random_int), std::nullopt, exit_code_based_on_count}; + } default: throw std::runtime_error("ProgramRunner not configured properly, behaviour is not set"); diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index 86d990d..ae50968 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -1,10 +1,10 @@ #include #include "program_runner.hpp" -constexpr char* highest_i32_plus_one = "2147483648"; -constexpr char* lowest_i32_minus_one = "-2147483649"; -constexpr char* highest_float_plus_more = "3.402823467e+38"; -constexpr char* lowest_float_minus_more = "-3.402823467e+38"; +constexpr const char* highest_i32_plus_one = "2147483648"; +constexpr const char* lowest_i32_minus_one = "-2147483649"; +constexpr const char* highest_float_plus_more = "3.402823467e+38"; +constexpr const char* lowest_float_minus_more = "-3.402823467e+38"; struct ArgvBuilder { std::vector args; @@ -19,7 +19,7 @@ struct ArgvBuilder { argv.push_back(nullptr); // argv must be null-terminated } - int argc() const { return static_cast(args.size()); } + int argc() const { return static_cast(argv.size() - 1); } char** argv_ptr() { return argv.data(); } }; @@ -27,219 +27,220 @@ TEST(TestProgramRunner, NoArgs){ ArgvBuilder builder({}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code, 0); } TEST(TestProgramRunner, JustHelp){ ArgvBuilder builder({"--help"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, JustVersion){ ArgvBuilder builder({"--version"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code, 0); } TEST(TestProgramRunner, HelpThenVersion){ ArgvBuilder builder({"--help", "--version"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, VersionThenHelp){ ArgvBuilder builder({"--version", "--help"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, HelpThenOther){ ArgvBuilder builder({"--help", "other", "stuff", "which", "is", "ignored"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, VersionThenOther){ ArgvBuilder builder({"--version", "ignore", "this", "stuff", "please", "and", "thank", "you"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, BadSubcommands){ ArgvBuilder builder({"badsubcommand", "--help"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, DefaultAlgorithm){ ArgvBuilder builder({"--min", "0", "--max=10", "-c", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitAlgorithmXOR){ ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitAlgorithmLCG){ ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m=0", "-M", "10", "--count=5", "-t=int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ ArgvBuilder builder({"-a", "mersenne", "-m=0", "-M", "10", "--count=5", "--type=int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ ArgvBuilder builder({"--algorithm", "invalid-algorithm", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, NoMinOrMax){ ArgvBuilder builder({"--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinNoMax){ ArgvBuilder builder({"--min", "0", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MaxNoMin){ ArgvBuilder builder({"-M", "11.1", "--count", "5", "--type", "float"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinAndMax){ ArgvBuilder builder({"--min", "0", "--max", "6", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinExcessiveNegativeMagnitude){ ArgvBuilder builder({"--min", lowest_i32_minus_one, "--max", "10", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinExcessivePositiveMagnitude){ ArgvBuilder builder({"--min", highest_i32_plus_one, "--max", "10", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinAndMaxExcessiveNegativeMagnitude){ ArgvBuilder builder({"--min", lowest_i32_minus_one, "--max", lowest_i32_minus_one, "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, MinAndMaxExcessivePositiveMagnitude){ ArgvBuilder builder({"--min", highest_i32_plus_one, "--max", highest_i32_plus_one, "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, DefaultCount){ ArgvBuilder builder({"--min", "0", "--max", "10", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitCount){ @@ -249,106 +250,106 @@ TEST(TestProgramRunner, ExplicitCount){ ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); for (int i = 0; i < explicit_count; ++i) { ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_FALSE(status.exit_code.has_value()); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_FALSE(status.exit_code.has_value()); } ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, CountIsZero){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "0", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, CountIsOne){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "1", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, CountIsNegative){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "-5", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_i32_plus_one, "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } TEST(TestProgramRunner, DefaultType){ ArgvBuilder builder({"--count", "5", "--algorithm", "xorshift"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitTypeUnit){ ArgvBuilder builder({"--count", "5", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitTypeFloat){ ArgvBuilder builder({"--min", "-4.3", "--max", "10.5", "--count", "5", "--type", "float"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitTypeInt){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_FALSE(status.stderr_message.has_value()); - EXPECT_TRUE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_EQ(status.exit_code, 0); + ASSERT_FALSE(status.stderr_message.has_value()); + ASSERT_TRUE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_EQ(status.exit_code.value(), 0); } TEST(TestProgramRunner, ExplicitTypeInvalid){ ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "5", "--type", "invalid"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); - EXPECT_TRUE(status.stderr_message.has_value()); - EXPECT_FALSE(status.stdout_message.has_value()); - EXPECT_TRUE(status.exit_code.has_value()); - EXPECT_NE(status.exit_code, 0); + ASSERT_TRUE(status.stderr_message.has_value()); + ASSERT_FALSE(status.stdout_message.has_value()); + ASSERT_TRUE(status.exit_code.has_value()); + ASSERT_NE(status.exit_code.value(), 0); } From 077d4b64cbb5f11d8443b593a81698378196d99f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 19:37:32 -0400 Subject: [PATCH 57/95] Fixed bug where there was no_argument given as a setting for the type flag, causing the type to always fail when specified --- .vscode/launch.json | 2 +- src/runner.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 26f71ce..3bff422 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": ["badsubcommand", "--help"], + "args": ["--min", "0", "--max=10", "-c", "5", "--type", "int"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/src/runner.cpp b/src/runner.cpp index 533e608..e2df315 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -185,7 +185,7 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { {"min", required_argument, nullptr, 'm'}, {"max", required_argument, nullptr, 'M'}, {"count", required_argument, nullptr, 'c'}, - {"type", no_argument, nullptr, 't'}, + {"type", required_argument, nullptr, 't'}, {nullptr, 0, nullptr, 0} }; From 37af76dd7cb815516ffbaaeb1639b3ca2ff40dc9 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 20:07:38 -0400 Subject: [PATCH 58/95] Fixed some test cases with erroneous inputs of specifying a min and max while using the unit type --- .vscode/launch.json | 2 +- tests/test_ProgramRunner.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3bff422..d9f4e26 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": ["--min", "0", "--max=10", "-c", "5", "--type", "int"], + "args": ["--min", "0", "--max", "10", "--type", "unit"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index ae50968..641f1a1 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -234,7 +234,7 @@ TEST(TestProgramRunner, MinAndMaxExcessivePositiveMagnitude){ } TEST(TestProgramRunner, DefaultCount){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--type", "floating"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_FALSE(status.stderr_message.has_value()); @@ -246,7 +246,7 @@ TEST(TestProgramRunner, DefaultCount){ TEST(TestProgramRunner, ExplicitCount){ int explicit_count = 34539; // Arbitrary positive count for testing std::string explicit_count_str = std::to_string(explicit_count); - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", explicit_count_str, "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", explicit_count_str, "--type", "integer"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); for (int i = 0; i < explicit_count; ++i) { ProgramRunner::ProgramStatus status = program_runner.iterate(); @@ -263,7 +263,7 @@ TEST(TestProgramRunner, ExplicitCount){ } TEST(TestProgramRunner, CountIsZero){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "0", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "0", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_TRUE(status.stderr_message.has_value()); @@ -273,7 +273,7 @@ TEST(TestProgramRunner, CountIsZero){ } TEST(TestProgramRunner, CountIsOne){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "1", "--type", "unit"}); + ArgvBuilder builder({"--count", "1", "--type", "unit"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_FALSE(status.stderr_message.has_value()); @@ -283,7 +283,7 @@ TEST(TestProgramRunner, CountIsOne){ } TEST(TestProgramRunner, CountIsNegative){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "-5", "--type", "unit"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", "-5", "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_TRUE(status.stderr_message.has_value()); From 290dcbb43c82ca0bca936c40b591207cafe015d0 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 20:11:26 -0400 Subject: [PATCH 59/95] Fixed error in TestProgramRunner.ExplicitCount test where it over looped and looped over the full count expected no exit code, when it should have looped over all but the last iteration --- tests/test_ProgramRunner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index 641f1a1..f3fcb60 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -245,10 +245,11 @@ TEST(TestProgramRunner, DefaultCount){ TEST(TestProgramRunner, ExplicitCount){ int explicit_count = 34539; // Arbitrary positive count for testing + int all_but_last = explicit_count - 1; std::string explicit_count_str = std::to_string(explicit_count); ArgvBuilder builder({"--min", "0", "--max", "10", "--count", explicit_count_str, "--type", "integer"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); - for (int i = 0; i < explicit_count; ++i) { + for (int i = 0; i < all_but_last; ++i) { ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_FALSE(status.stderr_message.has_value()); ASSERT_TRUE(status.stdout_message.has_value()); From 84ddd7bffb7bc925fa75121f0acbba0c88d78ef3 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 20:19:28 -0400 Subject: [PATCH 60/95] Added check to return an error if the user supplied a count value of 0 --- src/runner.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/runner.cpp b/src/runner.cpp index e2df315..d030835 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -36,18 +36,6 @@ std::string ProgramRunner::version_string() { return version_string; } - -// Parses a string to uint32_t, returns std::nullopt on error or out-of-range -std::optional parse_count_value(const std::string &count_str) { - uint32_t value; - const auto [ptr, ec] = std::from_chars(count_str.data(), count_str.data() + count_str.size(), value); - if (ec != std::errc() || ptr != count_str.data() + count_str.size()) { - return std::nullopt; // Parsing failed or extra characters present - } - return value; -} - - template concept FromCharsParsable = std::is_integral_v || std::is_floating_point_v; @@ -136,8 +124,8 @@ void ProgramRunner::determine_generation_type_configuration(const std::optional< void ProgramRunner::determine_count_configuration(const std::optional &count_str){ if (count_str.has_value()){ - std::optional count_val = parse_count_value(count_str.value()); - if (!count_val.has_value()){ + std::optional count_val = parse_value(count_str.value()); + if (!count_val.has_value() || count_val.value() <= 0){ this->behaviour = ProgramBehaviour::Error; return; } From 77f1fb9adc5c6e347e785daf0ea03b1adabdf624 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 20:23:08 -0400 Subject: [PATCH 61/95] Fixed test case TestProgramRunner.CountExcessvePositiveMagnitude to use the highest value for a u32 instead of an i32 --- tests/test_ProgramRunner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index f3fcb60..0e99beb 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -3,6 +3,7 @@ constexpr const char* highest_i32_plus_one = "2147483648"; constexpr const char* lowest_i32_minus_one = "-2147483649"; +constexpr const char* highest_u32_plus_one = "4294967296"; constexpr const char* highest_float_plus_more = "3.402823467e+38"; constexpr const char* lowest_float_minus_more = "-3.402823467e+38"; @@ -294,7 +295,7 @@ TEST(TestProgramRunner, CountIsNegative){ } TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ - ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_i32_plus_one, "--type", "int"}); + ArgvBuilder builder({"--min", "0", "--max", "10", "--count", highest_u32_plus_one, "--type", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.iterate(); ASSERT_TRUE(status.stderr_message.has_value()); From d8d4ef2dbc7e6e6a2c701479da8b21d99c055f45 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 20:50:26 -0400 Subject: [PATCH 62/95] Fixed the misformatting a short args in the DefaultType test case --- .vscode/launch.json | 2 +- include/program_runner.hpp | 1 + src/runner.cpp | 4 ++-- tests/test_ProgramRunner.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d9f4e26..bb4a5d7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": ["--min", "0", "--max", "10", "--type", "unit"], + "args": ["-m=0", "-M=1", "--count", "5", "--algorithm", "xorshift"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/include/program_runner.hpp b/include/program_runner.hpp index dd832af..cec8223 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -91,6 +91,7 @@ class ProgramRunner { // Defaults static constexpr uint32_t DefaultCount = 1; + static constexpr ProgramBehaviour DefaultGenerationType = ProgramBehaviour::GenerateInteger; RawArguments parse_args(int argc, char **argv); void determine_program_configuration(const RawArguments &raw_arguments); diff --git a/src/runner.cpp b/src/runner.cpp index d030835..a899932 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -27,7 +27,7 @@ std::string ProgramRunner::help_string() { + " -m, --min Minimum value\n" + " -M, --max Maximum value\n" + " -c, --count Number of random numbers to generate (default: 1)\n" - + " -t, --type Specify the type to output (default: unit)\n"; + + " -t, --type Specify the type to output (default: int)\n"; return help_string; } @@ -114,7 +114,7 @@ void ProgramRunner::determine_algorithm_configuration(const std::optional &generation_type){ if (!generation_type.has_value()){ - this->behaviour = ProgramBehaviour::GenerateInteger; + this->behaviour = DefaultGenerationType; } else if (generation_types.contains(generation_type.value())){ this->behaviour = generation_types.at(generation_type.value()); } else { diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index 0e99beb..e19fb8c 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -305,7 +305,7 @@ TEST(TestProgramRunner, CountExcessivePositiveMagnitude){ } TEST(TestProgramRunner, DefaultType){ - ArgvBuilder builder({"--count", "5", "--algorithm", "xorshift"}); + ArgvBuilder builder({"-m0", "-M1", "--count", "5", "--algorithm", "xorshift"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); ASSERT_FALSE(status.stderr_message.has_value()); From 702d663efc20c74c7297b84b1270c9e2f864e2ad Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 21:01:13 -0400 Subject: [PATCH 63/95] Fixed invald command line argument in the TestProgramRunner.ExplicitAlgorithm tests --- .vscode/launch.json | 2 +- tests/test_ProgramRunner.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bb4a5d7..fcd947e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": ["-m=0", "-M=1", "--count", "5", "--algorithm", "xorshift"], + "args": ["--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "int"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/tests/test_ProgramRunner.cpp b/tests/test_ProgramRunner.cpp index e19fb8c..1e9a2a5 100644 --- a/tests/test_ProgramRunner.cpp +++ b/tests/test_ProgramRunner.cpp @@ -115,7 +115,7 @@ TEST(TestProgramRunner, DefaultAlgorithm){ } TEST(TestProgramRunner, ExplicitAlgorithmXOR){ - ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); + ArgvBuilder builder({"--algorithm", "xor", "-m", "0", "-M10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); ASSERT_FALSE(status.stderr_message.has_value()); @@ -125,7 +125,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmXOR){ } TEST(TestProgramRunner, ExplicitAlgorithmLCG){ - ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m=0", "-M", "10", "--count=5", "-t=int"}); + ArgvBuilder builder({"--algorithm=linear-congruential-generator", "-m0", "-M", "10", "--count=5", "-tint"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); ASSERT_FALSE(status.stderr_message.has_value()); @@ -135,7 +135,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmLCG){ } TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ - ArgvBuilder builder({"-a", "mersenne", "-m=0", "-M", "10", "--count=5", "--type=int"}); + ArgvBuilder builder({"-a", "mersenne", "-m", "0", "-M", "10", "--count=5", "--type=int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); ASSERT_FALSE(status.stderr_message.has_value()); @@ -145,7 +145,7 @@ TEST(TestProgramRunner, ExplicitAlgorithmMersenne){ } TEST(TestProgramRunner, ExplicitAlgorithmInvalid){ - ArgvBuilder builder({"--algorithm", "invalid-algorithm", "-m", "0", "-M=10", "--count", "5", "-t", "int"}); + ArgvBuilder builder({"--algorithm", "invalid-algorithm", "--min", "0", "-M" "10", "--count", "5", "-t", "int"}); ProgramRunner program_runner = ProgramRunner(builder.argc(), builder.argv_ptr()); ProgramRunner::ProgramStatus status = program_runner.run(); ASSERT_TRUE(status.stderr_message.has_value()); From 14fde31be2d669b762936a3d26b63a20198c7810 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 21:21:50 -0400 Subject: [PATCH 64/95] Added github actions workflow for testing with ctest --- .github/workflows/testing.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/testing.yml diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..86817a2 --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,27 @@ +name: CMake & CTest + +on: + push: + branches: ["main", "dev"] + pull_request: + branches: ["main", "dev"] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y cmake g++ + + - name: Configure CMake + run: cmake -S . -B build + + - name: Build + run: cmake --build build + + - name: Run tests + run: ctest --test-dir build --output-on-failure \ No newline at end of file From 5b135c606cf883f2419ca8fdc921bea2e6ee2a82 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 21:25:10 -0400 Subject: [PATCH 65/95] Renamed the testing workflow to CTest --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 86817a2..fb0d976 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,4 +1,4 @@ -name: CMake & CTest +name: CTest on: push: From 26a440d2b24b35648178380fa066a73fae039d03 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 21:41:16 -0400 Subject: [PATCH 66/95] Default codeql.yml from gh --- .github/workflows/codeql.yml | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..246f319 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,98 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main", "dev" ] + pull_request: + branches: [ "main", "dev" ] + schedule: + - cron: '34 0 * * 2' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: c-cpp + build-mode: autobuild + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file From 5a82d6c966aee176fdb1a6216a96ff11dff83817 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 31 May 2025 21:47:38 -0400 Subject: [PATCH 67/95] Updated code scanning workflow --- .github/workflows/codeql.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 246f319..a2e4982 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,14 +1,3 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL Advanced" on: @@ -27,7 +16,7 @@ jobs: # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners (GitHub.com only) # Consider using larger runners or machines with greater resources for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + runs-on: ubuntu-latest permissions: # required for all workflows security-events: write From 3c39552cc60b53a1d35278de1a65022b3d2d5476 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 10:57:15 -0400 Subject: [PATCH 68/95] Added -Wall -Wextra -Wshadow -Wconversion -Wpedantic -Werror to all compilation targets with GCC and clang. --- CMakeLists.txt | 21 +++++++++++++++++++++ tests/CMakeLists.txt | 11 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d9d5e2..f75d91e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,5 +25,26 @@ add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} PRIVATE randomizer_lib) +# Add compiler warnings only for GCC and Clang +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(randomizer_lib PRIVATE + -Wall + -Wextra + -Wshadow + -Wconversion + -Wpedantic + -Werror + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -Wextra + -Wshadow + -Wconversion + -Wpedantic + -Werror + ) +endif() + + enable_testing() add_subdirectory(tests) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 57af17c..a9db20c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,5 +20,16 @@ add_executable(RandomizerTests ${TEST_SOURCES}) # Link to GoogleTest and the library for the randomizer code target_link_libraries(RandomizerTests PRIVATE gtest gtest_main randomizer_lib) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(RandomizerTests PRIVATE + -Wall + -Wextra + -Wshadow + -Wconversion + -Wpedantic + -Werror + ) +endif() + include(GoogleTest) gtest_discover_tests(RandomizerTests) From d6ff342ca35e0cee3f6beb78405ed5da2826270a Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 11:09:00 -0400 Subject: [PATCH 69/95] Made modifications to source code to fix warnings with type conversions --- src/prngs/linear_congruential_generator.cpp | 2 +- src/prngs/prng.cpp | 5 +++-- src/prngs/xorshift.cpp | 2 +- src/runner.cpp | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 2326522..350165e 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -84,7 +84,7 @@ double LinearCongruentialGenerator::generateUnitNormalRandomValue() { } // Normalize the random value to [0, 1) - double normalized_value = static_cast(random_value - m_minimum_value) / range; + double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); return normalized_value; } diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index f56beff..507e27e 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -46,7 +46,8 @@ double PseudoRandomNumberGenerator::generateFloatingPointRandomValue(float min, } int64_t PseudoRandomNumberGenerator::generateIntegerRandomValue(int32_t min, int32_t max) { - int64_t random_val = generateUnitNormalRandomValue(); - int64_t scaled_value = min + (random_val * (max - min)); + double random_val = generateUnitNormalRandomValue(); + double random_val_magnitude = random_val * (max - min); + int64_t scaled_value = static_cast(min + random_val_magnitude); return scaled_value; } \ No newline at end of file diff --git a/src/prngs/xorshift.cpp b/src/prngs/xorshift.cpp index 241f8c3..947fed1 100644 --- a/src/prngs/xorshift.cpp +++ b/src/prngs/xorshift.cpp @@ -48,7 +48,7 @@ double XORShift::generateUnitNormalRandomValue() { } // Normalize the random value to [0, 1) - double normalized_value = static_cast(random_value - m_minimum_value) / range; + double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); return normalized_value; } diff --git a/src/runner.cpp b/src/runner.cpp index a899932..6541a04 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -303,7 +303,7 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { case ProgramBehaviour::GenerateFloating: { this->iteration++; - float random_float = this->prng->generateFloatingPointRandomValue( + double random_float = this->prng->generateFloatingPointRandomValue( std::get(this->min.value_or(0.0f)), std::get(this->max.value_or(1.0f)) ); @@ -313,7 +313,7 @@ ProgramRunner::ProgramStatus ProgramRunner::iterate() { case ProgramBehaviour::GenerateInteger: { this->iteration++; - int32_t random_int = this->prng->generateIntegerRandomValue( + int64_t random_int = this->prng->generateIntegerRandomValue( std::get(this->min.value_or(0)), std::get(this->max.value_or(1)) ); From ed9777f05e4b624ed3cb355222a9b68f5fc4af4f Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 12:00:33 -0400 Subject: [PATCH 70/95] Made it so that main() prints out status. --- src/main.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b53f8f3..9b6d176 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,18 @@ int main(int argc, char *argv[]){ ProgramRunner runner = ProgramRunner(argc, argv); - ProgramRunner::ProgramStatus status = runner.run(); - return 0; + ProgramRunner::ProgramStatus status; + while (!runner.is_finished()) { + status = runner.iterate(); + if (status.stdout_message.has_value()) { + std::cout << status.stdout_message.value() << std::endl; + } + if (status.stderr_message.has_value()) { + std::cerr << status.stderr_message.value() << std::endl; + } + if (status.exit_code.has_value()) { + exit(status.exit_code.value()); + } + } + exit(1); // If we reach here, something went wrong } \ No newline at end of file From 691cbb6ecb87c41b75d94afd6466e4e8be3c2f78 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 16:28:37 -0400 Subject: [PATCH 71/95] Fixed problem with the inclusive range for integer generation --- src/prngs/prng.cpp | 2 +- tests/test_LinearCongruentialGenerator.cpp | 88 +++++++++++++++++++++ tests/test_XORShift.cpp | 89 +++++++++++++++++++++- 3 files changed, 177 insertions(+), 2 deletions(-) diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 507e27e..d6fb66a 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -47,7 +47,7 @@ double PseudoRandomNumberGenerator::generateFloatingPointRandomValue(float min, int64_t PseudoRandomNumberGenerator::generateIntegerRandomValue(int32_t min, int32_t max) { double random_val = generateUnitNormalRandomValue(); - double random_val_magnitude = random_val * (max - min); + double random_val_magnitude = random_val * (max - min + 1); int64_t scaled_value = static_cast(min + random_val_magnitude); return scaled_value; } \ No newline at end of file diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index cb5cb95..bd5c6ca 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -3,6 +3,7 @@ #include "linear_congruential_generator.hpp" #include #include +#include // Test that the lcg constructors do not fail TEST(TestLinearCongruentialGenerator, BlankConstructor) { @@ -61,3 +62,90 @@ TEST(TestLinearCongruentialGenerator, GenerateUnitNormalRandomValueEquidistribut const double confidence_interval = 0.01; EXPECT_NEAR(average, expected_average, confidence_interval); } + + +TEST(TestLinearCongruentialGenerator, GenerateFloatingPointRandomValueInRange){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const float min = -25.678f; + const float max = 3242.342f; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + double value = lcg.generateFloatingPointRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestLinearCongruentialGenerator, GenerateFloatingPointRandomValueEquidistributed){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const double min = -300.0; + const double max = 700.0; + const int num_iterations = 10000; + double sum = 0.0; + double variance_sum = 0.0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + double value = lcg.generateFloatingPointRandomValue(min, max); + sum += value; + variance_sum += (value - expected_value) * (value - expected_value); + } + double average = sum / num_iterations; + double variance = variance_sum / num_iterations; + double standard_deviation = std::sqrt(variance); + double three_sigma = 3 * standard_deviation; + ASSERT_NEAR(expected_value, average, three_sigma);; + +} + +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueInRange){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const int32_t min = -70; + const int32_t max = 141; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + int64_t value = lcg.generateIntegerRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueEquidistributed){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const int32_t min = 1; + const int32_t max = 6; + const int num_iterations = 10000; + double sum = 0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + sum += static_cast(lcg.generateIntegerRandomValue(min, max)); + } + double average = sum / num_iterations; + double margin_of_error = 0.02 * expected_value; + ASSERT_NEAR(expected_value, average, margin_of_error); +} + + +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiveRange){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const int32_t min = 1; + const int32_t max = 6; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = lcg.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} \ No newline at end of file diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index 69b5a6e..0f0c662 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -3,6 +3,7 @@ #include "xorshift.hpp" #include #include +#include // Test that the lcg constructors do not fail TEST(TestXORShift, BlankConstructor) { @@ -34,7 +35,7 @@ TEST(TestXORShift, SeedAndConstantsConstructor) { TEST(TestXORShift, GenerateUnitNormalRandomValueHasCorrectRange) { XORShift xor_shift = XORShift(); - const int enough_iterations_to_be_confident = 10000; + const int enough_iterations_to_be_confident = 1000; for (int i = 0; i < enough_iterations_to_be_confident; ++i) { const double value = xor_shift.generateUnitNormalRandomValue(); EXPECT_GE(value, 0.0); @@ -59,4 +60,90 @@ TEST(TestXORShift, GenerateUnitNormalRandomValueEquidistributed) { const double average = sum / enough_iterations_to_be_confident; const double confidence_interval = 0.01; EXPECT_NEAR(average, expected_average, confidence_interval); +} + + +TEST(TestXORShift, GenerateFloatingPointRandomValueInRange){ + XORShift xor_shift = XORShift(); + const float min = -25.678f; + const float max = 3242.342f; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + double value = xor_shift.generateFloatingPointRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestXORShift, GenerateFloatingPointRandomValueEquidistributed){ + XORShift xor_shift = XORShift(); + const double min = -300.0; + const double max = 700.0; + const int num_iterations = 10000; + double sum = 0.0; + double variance_sum = 0.0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + double value = xor_shift.generateFloatingPointRandomValue(min, max); + sum += value; + variance_sum += (value - expected_value) * (value - expected_value); + } + double average = sum / num_iterations; + double variance = variance_sum / num_iterations; + double standard_deviation = std::sqrt(variance); + double three_sigma = 3 * standard_deviation; + ASSERT_NEAR(expected_value, average, three_sigma); + +} + +TEST(TestXORShift, GenerateIntegerRandomValueInRange){ + XORShift xor_shift = XORShift(); + const int32_t min = -70; + const int32_t max = 141; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + int64_t value = xor_shift.generateIntegerRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestXORShift, GenerateIntegerRandomValueEquidistributed){ + XORShift xor_shift = XORShift(); + const int32_t min = 1; + const int32_t max = 6; + const int num_iterations = 10000; + double sum = 0.0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + sum += static_cast(xor_shift.generateIntegerRandomValue(min, max)); + } + double average = sum / num_iterations; + double margin_of_error = 0.02 * expected_value; + ASSERT_NEAR(expected_value, average, margin_of_error); +} + +TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRange){ + XORShift xor_shift = XORShift(); + const int32_t min = 1; + const int32_t max = 6; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = xor_shift.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); } \ No newline at end of file From 6e203ca9553902d94c4898aaca98182c965a533c Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 18:55:49 -0400 Subject: [PATCH 72/95] Added the warmup function --- include/program_runner.hpp | 4 ++++ src/runner.cpp | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index cec8223..0610b02 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -27,9 +27,13 @@ class ProgramRunner { bool is_finished(); + void warmup(uint64_t iterations = 1000); + const std::string version = "0.1"; const std::string program_name = "randomizer"; + + private: diff --git a/src/runner.cpp b/src/runner.cpp index 6541a04..f99b960 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -353,4 +353,10 @@ ProgramRunner::ProgramStatus ProgramRunner::run() { throw std::runtime_error("ProgramRunner has finished without returning an exit code. This should not happen."); } +void ProgramRunner::warmup(uint64_t iterations) { + for (uint64_t i = 0; i < iterations; ++i) { + this->prng->generateRandomValue(); // Warmup the PRNG + } +} + From 7eda7de9c9fe1225a4abc5353d559064b11796e7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 1 Jun 2025 18:56:30 -0400 Subject: [PATCH 73/95] Added the call to warmup in main --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index 9b6d176..593e5e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ int main(int argc, char *argv[]){ ProgramRunner runner = ProgramRunner(argc, argv); + runner.warmup(); ProgramRunner::ProgramStatus status; while (!runner.is_finished()) { status = runner.iterate(); From b7a6ed73cd3a86dd792ed07db02e49c7a2d0a871 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Thu, 5 Jun 2025 18:37:27 -0400 Subject: [PATCH 74/95] Added test file for Mersenne Twister --- tests/test_MersenneTwister.cpp | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/test_MersenneTwister.cpp diff --git a/tests/test_MersenneTwister.cpp b/tests/test_MersenneTwister.cpp new file mode 100644 index 0000000..5cbe701 --- /dev/null +++ b/tests/test_MersenneTwister.cpp @@ -0,0 +1,137 @@ +#include "gtest/gtest.h" +#include "prng.hpp" +#include "mersenne_twister.hpp" +#include +#include +#include + +// Test that the mt constructors do not fail +TEST(TestMersenneTwister, BlankConstructor) { + EXPECT_NO_THROW(MersenneTwister()); +} + +TEST(TestMersenneTwister, SeedConstructor) { + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::min())); + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::min() + 1)); + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::max() / 10)); + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::max() / 2)); + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::max() - 1)); + EXPECT_NO_THROW(MersenneTwister(std::numeric_limits::max())); +} + +TEST(TestMersenneTwister, GenerateUnitNormalRandomValueHasCorrectRange) { + MersenneTwister mt = MersenneTwister(); + + const int enough_iterations_to_be_confident = 10000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { + const double value = mt.generateUnitNormalRandomValue(); + EXPECT_GE(value, 0.0); + EXPECT_LE(value, 1.0); + } +} + + +/* This test of course is subject to some randomness +it is possible to fail even if everything is working +properly, but it would be incredibly unlikely to fail. +If this test ever fails it should be investigated further +manually. */ +TEST(TestMersenneTwister, GenerateUnitNormalRandomValueEquidistributed) { + MersenneTwister mt = MersenneTwister(); + const double expected_average = 0.5; + double sum = 0.0; + const int enough_iterations_to_be_confident = 100000; + for (int i = 0; i < enough_iterations_to_be_confident; ++i) { + sum += mt.generateUnitNormalRandomValue(); + } + const double average = sum / enough_iterations_to_be_confident; + const double confidence_interval = 0.01; + EXPECT_NEAR(average, expected_average, confidence_interval); +} + + +TEST(TestMersenneTwister, GenerateFloatingPointRandomValueInRange){ + MersenneTwister mt = MersenneTwister(); + const float min = -25.678f; + const float max = 3242.342f; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + double value = mt.generateFloatingPointRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestMersenneTwister, GenerateFloatingPointRandomValueEquidistributed){ + MersenneTwister mt = MersenneTwister(); + const double min = -300.0; + const double max = 700.0; + const int num_iterations = 10000; + double sum = 0.0; + double variance_sum = 0.0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + double value = mt.generateFloatingPointRandomValue(min, max); + sum += value; + variance_sum += (value - expected_value) * (value - expected_value); + } + double average = sum / num_iterations; + double variance = variance_sum / num_iterations; + double standard_deviation = std::sqrt(variance); + double three_sigma = 3 * standard_deviation; + ASSERT_NEAR(expected_value, average, three_sigma);; + +} + +TEST(TestMersenneTwister, GenerateIntegerRandomValueInRange){ + MersenneTwister mt = MersenneTwister(); + const int32_t min = -70; + const int32_t max = 141; + const int num_iterations = 1000; + for (int i = 0; i < num_iterations; ++i){ + int64_t value = mt.generateIntegerRandomValue(min, max); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} + +TEST(TestMersenneTwister, GenerateIntegerRandomValueEquidistributed){ + MersenneTwister mt = MersenneTwister(); + const int32_t min = 1; + const int32_t max = 6; + const int num_iterations = 10000; + double sum = 0; + double expected_value = (min + max) / 2.0; + for (int i = 0; i < num_iterations; ++i){ + sum += static_cast(mt.generateIntegerRandomValue(min, max)); + } + double average = sum / num_iterations; + double margin_of_error = 0.02 * expected_value; + ASSERT_NEAR(expected_value, average, margin_of_error); +} + + +TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRange){ + MersenneTwister mt = MersenneTwister(); + const int32_t min = 1; + const int32_t max = 6; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = mt.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} \ No newline at end of file From e5e8db933b06576c457f1e06cb6d033e54400c34 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Thu, 5 Jun 2025 18:57:32 -0400 Subject: [PATCH 75/95] Added the mersenne twister header --- include/mersenne_twister.hpp | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 include/mersenne_twister.hpp diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp new file mode 100644 index 0000000..31f2e29 --- /dev/null +++ b/include/mersenne_twister.hpp @@ -0,0 +1,62 @@ +#ifndef MERSENNE_TWISTER_H +#define MERSENNE_TWISTER_H + +#include +#include "prng.hpp" + +class MersenneTwister : public PseudoRandomNumberGenerator { + +private: + + /* MT19937-64 coefficients: + w = 64 , word size in bits + n = 312, degree of recurrence + m = 156, middle word + r = 31, number of bits of the lower bit mask + a = 0xb5026f5aa96619e9, coefficients + u = 29, tempering bit shift/mask + d = 0x5555555555555555 + s = 17, tempering bit shift/mask + b = 0x71d67fffeda60000, tempering bit mask + t = 37, tempering bit shift/mask + c = 0xfff7eee000000000, tempering bit mask + l = 43, tempering bit shift/mask + f = 6364136223846793005 + taken from @misc{ enwiki:1290406357, + author = "{Wikipedia contributors}", + title = "Mersenne Twister --- {Wikipedia}{,} The Free Encyclopedia", + year = "2025", + url = "https://en.wikipedia.org/w/index.php?title=Mersenne_Twister&oldid=1290406357", + note = "[Online; accessed 5-June-2025]" + } + */ + + static constexpr uint64_t Default_w = 64; + static constexpr uint64_t Default_n = 312; + static constexpr uint64_t Default_m = 156; + static constexpr uint64_t Default_r = 31; + static constexpr uint64_t Default_a = 0xb5026f5aa96619e9; + static constexpr uint64_t Default_u = 29; + static constexpr uint64_t Default_d = 0x5555555555555555; + static constexpr uint64_t Default_s = 17; + static constexpr uint64_t Default_b = 0x71d67fffeda60000; + static constexpr uint64_t Default_t = 37; + static constexpr uint64_t Default_c = 0xfff7eee000000000; + static constexpr uint64_t Default_l = 43; + static constexpr uint64_t Default_f = 6364136223846793005; + + + uint64_t m_current_value = 0; + std::vector recurrence_state; + + std::vector initializeRecurrenceState(const uint64_t seed); + +public: + MersenneTwister(); + MersenneTwister(const uint64_t seed); + + uint64_t generateRandomValue() override; + double generateUnitNormalRandomValue() override; +}; + +#endif \ No newline at end of file From 79526986fd09c29f11698d2af100922cad482bc2 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 6 Jun 2025 13:39:29 -0400 Subject: [PATCH 76/95] Updated the method signature for initializeRecurrenceState in the mersenne twister header --- include/mersenne_twister.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp index 31f2e29..870611b 100644 --- a/include/mersenne_twister.hpp +++ b/include/mersenne_twister.hpp @@ -2,6 +2,7 @@ #define MERSENNE_TWISTER_H #include +#include #include "prng.hpp" class MersenneTwister : public PseudoRandomNumberGenerator { @@ -47,9 +48,9 @@ class MersenneTwister : public PseudoRandomNumberGenerator { uint64_t m_current_value = 0; - std::vector recurrence_state; + std::vector m_recurrence_state; - std::vector initializeRecurrenceState(const uint64_t seed); + std::vector initializeRecurrenceState(const uint64_t seed, const uint64_t n, const uint64_t f, const uint64_t w); public: MersenneTwister(); From 9110079fff9f3fb38fa01a3bc0991b7a804ee8f4 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 6 Jun 2025 13:39:52 -0400 Subject: [PATCH 77/95] Added the initializeRecurrenceState method definition --- src/prngs/mersenne_twister.cpp | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp index 8ee4302..d8d84a2 100644 --- a/src/prngs/mersenne_twister.cpp +++ b/src/prngs/mersenne_twister.cpp @@ -1,23 +1,25 @@ +#include +#include #include "prng.hpp" +#include "mersenne_twister.hpp" -class MersenneTwister : PseudoRandomNumberGenerator { - /* MT19937-64 coefficients: - w = 64 - n = 312 - m = 156 - r = 31 - a = 0xb5026f5aa96619e9 - u = 29 - d = 0x5555555555555555 - s = 17 - b = 0x71d67fffeda60000 - t = 37 - c = 0xfff7eee000000000 - l = 43 - f = 6364136223846793005 - */ - MersenneTwister(){ +MersenneTwister::MersenneTwister(){ +} + +MersenneTwister::MersenneTwister(const uint64_t seed) { + m_recurrence_state = initializeRecurrenceState(seed); + m_current_value = m_recurrence_state[0]; +} + +std::vector MersenneTwister::initializeRecurrenceState(const uint64_t seed, const uint64_t n, const uint64_t f, const uint64_t w) { + std::vector state(n); + state[0] = seed; + for (uint64_t i = 1; i < n; ++i) { + state[i] = (f * (state[i - 1] ^ (state[i - 1] >> (w - 2)))) + i; } -}; \ No newline at end of file + return state; +} + + From 3cbe85f2d9189b68d84c1d32c6df53ab77e96f69 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Fri, 6 Jun 2025 13:43:28 -0400 Subject: [PATCH 78/95] Changed constants to be class members --- include/mersenne_twister.hpp | 16 +++++++++++++++- src/prngs/mersenne_twister.cpp | 23 ++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp index 870611b..3d8fd5c 100644 --- a/include/mersenne_twister.hpp +++ b/include/mersenne_twister.hpp @@ -48,9 +48,23 @@ class MersenneTwister : public PseudoRandomNumberGenerator { uint64_t m_current_value = 0; + uint64_t m_w; + uint64_t m_n; + uint64_t m_m; + uint64_t m_r; + uint64_t m_a; + uint64_t m_u; + uint64_t m_d; + uint64_t m_s; + uint64_t m_b; + uint64_t m_t; + uint64_t m_c; + uint64_t m_l; + uint64_t m_f; std::vector m_recurrence_state; + - std::vector initializeRecurrenceState(const uint64_t seed, const uint64_t n, const uint64_t f, const uint64_t w); + std::vector initializeRecurrenceState(const uint64_t seed); public: MersenneTwister(); diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp index d8d84a2..2a1ffe3 100644 --- a/src/prngs/mersenne_twister.cpp +++ b/src/prngs/mersenne_twister.cpp @@ -9,15 +9,28 @@ MersenneTwister::MersenneTwister(){ } MersenneTwister::MersenneTwister(const uint64_t seed) { + m_w = Default_w; + m_n = Default_n; + m_m = Default_m; + m_r = Default_r; + m_a = Default_a; + m_u = Default_u; + m_d = Default_d; + m_s = Default_s; + m_b = Default_b; + m_t = Default_t; + m_c = Default_c; + m_l = Default_l; + m_f = Default_f; m_recurrence_state = initializeRecurrenceState(seed); - m_current_value = m_recurrence_state[0]; + m_current_value = seed; } -std::vector MersenneTwister::initializeRecurrenceState(const uint64_t seed, const uint64_t n, const uint64_t f, const uint64_t w) { - std::vector state(n); +std::vector MersenneTwister::initializeRecurrenceState(const uint64_t seed) { + std::vector state(m_n); state[0] = seed; - for (uint64_t i = 1; i < n; ++i) { - state[i] = (f * (state[i - 1] ^ (state[i - 1] >> (w - 2)))) + i; + for (uint64_t i = 1; i < m_n; ++i) { + state[i] = (m_f * (state[i - 1] ^ (state[i - 1] >> (m_w - 2)))) + i; } return state; } From f9664462c54cf19f2fa5681212902b6ef3b6c756 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 28 Jun 2025 17:35:26 -0400 Subject: [PATCH 79/95] Private helper methods for computing random values with the Mersenne Twister. --- include/mersenne_twister.hpp | 33 ++++++++++++++++++-- src/prngs/mersenne_twister.cpp | 55 +++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp index 3d8fd5c..981ea83 100644 --- a/include/mersenne_twister.hpp +++ b/include/mersenne_twister.hpp @@ -46,25 +46,54 @@ class MersenneTwister : public PseudoRandomNumberGenerator { static constexpr uint64_t Default_l = 43; static constexpr uint64_t Default_f = 6364136223846793005; - - uint64_t m_current_value = 0; + // word size uint64_t m_w; + + // degree of the recurrence uint64_t m_n; + + // middle word offset uint64_t m_m; + + // separation point where the upper bitmask begins uint64_t m_r; + + // twist matrix coefficient uint64_t m_a; + + // MT bitwise constant uint64_t m_u; + + // MT bitwise constant uint64_t m_d; + + // bit shift used for tempering uint64_t m_s; + + // bit mask used for tempering uint64_t m_b; + + // bit shift used for tempering uint64_t m_t; + + // bit mask used for tempering uint64_t m_c; + + // MT bitwise constant uint64_t m_l; + + // initialization constant uint64_t m_f; + + uint64_t m_upper_bit_mask; + uint64_t m_lower_bit_mask; std::vector m_recurrence_state; + int m_state_index; std::vector initializeRecurrenceState(const uint64_t seed); + uint64_t generateNextStateValue(); + uint64_t tempering(uint64_t); public: MersenneTwister(); diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp index 2a1ffe3..f278aec 100644 --- a/src/prngs/mersenne_twister.cpp +++ b/src/prngs/mersenne_twister.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "prng.hpp" #include "mersenne_twister.hpp" @@ -22,8 +23,10 @@ MersenneTwister::MersenneTwister(const uint64_t seed) { m_c = Default_c; m_l = Default_l; m_f = Default_f; + m_lower_bit_mask = std::numeric_limits::max() >> (m_w - m_r); + m_upper_bit_mask = std::numeric_limits::max() << (m_r); m_recurrence_state = initializeRecurrenceState(seed); - m_current_value = seed; + m_state_index = 0; } std::vector MersenneTwister::initializeRecurrenceState(const uint64_t seed) { @@ -35,4 +38,54 @@ std::vector MersenneTwister::initializeRecurrenceState(const uint64_t return state; } +uint64_t MersenneTwister::generateNextStateValue(){ + + int k = m_state_index; + + // x_{k-n}, but since we are using circular indexing it is actually just the same as x_k! + int upper_index = k; + + // x_{k-(n-1)} + int lower_index = k - (m_n - 1); + if (lower_index < 0){ // wraparound the index for the buffer + lower_index += m_n; + } + + uint64_t upper_part = m_recurrence_state[upper_index] & m_upper_bit_mask; + uint64_t lower_part = m_recurrence_state[lower_index] & m_lower_bit_mask; + uint64_t concatenated_value = upper_part | lower_part; + + uint64_t matrix_mul_result = concatenated_value >> 1; + if (concatenated_value & 0b1){ + matrix_mul_result ^= m_a; + } + + + uint64_t middle_index = k - (m_n - m_m); + if (middle_index < 0){ + middle_index += m_n; + } + uint64_t middle_value = m_recurrence_state[middle_index]; + + uint64_t state_value = matrix_mul_result ^ middle_value; + + m_recurrence_state[k] = state_value; + + m_state_index++; + if (m_state_index >= m_n){ + m_state_index = 0; + } + + return state_value; + +} + +uint64_t MersenneTwister::tempering(uint64_t val){ + uint64_t tempered_value = val ^ (val >> m_u); + tempered_value ^= ((tempered_value << m_s) & m_b); + tempered_value ^= ((tempered_value << m_t) & m_c); + tempered_value ^= (tempered_value >> 1); + return tempered_value; +} + From acfbb6bc23efb6bd9f46063a44fd37c0dc8dba54 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 28 Jun 2025 18:04:02 -0400 Subject: [PATCH 80/95] Completed the Mersenne Twister class. --- include/mersenne_twister.hpp | 16 +++++------ src/prngs/mersenne_twister.cpp | 49 +++++++++++++++++++++++++++++----- src/prngs/prng.cpp | 15 +++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp index 981ea83..3a75b6d 100644 --- a/include/mersenne_twister.hpp +++ b/include/mersenne_twister.hpp @@ -32,10 +32,10 @@ class MersenneTwister : public PseudoRandomNumberGenerator { } */ - static constexpr uint64_t Default_w = 64; - static constexpr uint64_t Default_n = 312; - static constexpr uint64_t Default_m = 156; - static constexpr uint64_t Default_r = 31; + static constexpr int Default_w = 64; + static constexpr int Default_n = 312; + static constexpr int Default_m = 156; + static constexpr int Default_r = 31; static constexpr uint64_t Default_a = 0xb5026f5aa96619e9; static constexpr uint64_t Default_u = 29; static constexpr uint64_t Default_d = 0x5555555555555555; @@ -47,16 +47,16 @@ class MersenneTwister : public PseudoRandomNumberGenerator { static constexpr uint64_t Default_f = 6364136223846793005; // word size - uint64_t m_w; + int m_w; // degree of the recurrence - uint64_t m_n; + int m_n; // middle word offset - uint64_t m_m; + int m_m; // separation point where the upper bitmask begins - uint64_t m_r; + int m_r; // twist matrix coefficient uint64_t m_a; diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp index f278aec..70702d0 100644 --- a/src/prngs/mersenne_twister.cpp +++ b/src/prngs/mersenne_twister.cpp @@ -5,11 +5,29 @@ #include "mersenne_twister.hpp" -MersenneTwister::MersenneTwister(){ - +MersenneTwister::MersenneTwister() + : PseudoRandomNumberGenerator() { + m_w = Default_w; + m_n = Default_n; + m_m = Default_m; + m_r = Default_r; + m_a = Default_a; + m_u = Default_u; + m_d = Default_d; + m_s = Default_s; + m_b = Default_b; + m_t = Default_t; + m_c = Default_c; + m_l = Default_l; + m_f = Default_f; + m_lower_bit_mask = std::numeric_limits::max() >> (m_w - m_r); + m_upper_bit_mask = std::numeric_limits::max() << (m_r); + m_recurrence_state = initializeRecurrenceState(m_seed); + m_state_index = 0; } -MersenneTwister::MersenneTwister(const uint64_t seed) { +MersenneTwister::MersenneTwister(const uint64_t seed) + : PseudoRandomNumberGenerator(seed) { m_w = Default_w; m_n = Default_n; m_m = Default_m; @@ -25,14 +43,14 @@ MersenneTwister::MersenneTwister(const uint64_t seed) { m_f = Default_f; m_lower_bit_mask = std::numeric_limits::max() >> (m_w - m_r); m_upper_bit_mask = std::numeric_limits::max() << (m_r); - m_recurrence_state = initializeRecurrenceState(seed); + m_recurrence_state = initializeRecurrenceState(m_seed); m_state_index = 0; } std::vector MersenneTwister::initializeRecurrenceState(const uint64_t seed) { std::vector state(m_n); state[0] = seed; - for (uint64_t i = 1; i < m_n; ++i) { + for (int i = 1; i < m_n; ++i) { state[i] = (m_f * (state[i - 1] ^ (state[i - 1] >> (m_w - 2)))) + i; } return state; @@ -61,7 +79,7 @@ uint64_t MersenneTwister::generateNextStateValue(){ } - uint64_t middle_index = k - (m_n - m_m); + int middle_index = k - (m_n - m_m); if (middle_index < 0){ middle_index += m_n; } @@ -88,4 +106,23 @@ uint64_t MersenneTwister::tempering(uint64_t val){ return tempered_value; } +uint64_t MersenneTwister::generateRandomValue(){ + uint64_t raw_value = generateNextStateValue(); + uint64_t tempered_value = tempering(raw_value); + return tempered_value; +} + +// Generate a random value normalized to the range [0, 1) +double MersenneTwister::generateUnitNormalRandomValue() { + + uint64_t random_value = generateRandomValue(); + uint64_t range = m_maximum_value - m_minimum_value; + if (range == 0) { + return 0.0; // Avoid division by zero + } + + // Normalize the random value to [0, 1) + double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); + return normalized_value; +} diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index d6fb66a..f6623dd 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -50,4 +50,19 @@ int64_t PseudoRandomNumberGenerator::generateIntegerRandomValue(int32_t min, int double random_val_magnitude = random_val * (max - min + 1); int64_t scaled_value = static_cast(min + random_val_magnitude); return scaled_value; +} + +// Generate a random value normalized to the range [0, 1) +double PseudoRandomNumberGenerator::generateUnitNormalRandomValue() { + + uint64_t random_value = generateRandomValue(); + + uint64_t range = m_maximum_value - m_minimum_value; + if (range == 0) { + return 0.0; // Avoid division by zero + } + + // Normalize the random value to [0, 1) + double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); + return normalized_value; } \ No newline at end of file From 973c559631ef5fd06de4cbeabc4b1861cc17cddd Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 28 Jun 2025 18:16:33 -0400 Subject: [PATCH 81/95] Added Mersenne Twister algorithm to be runnable at the command line by the ProgramRunner. --- include/program_runner.hpp | 6 +++++- src/runner.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index 0610b02..c6e611d 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -41,6 +41,7 @@ class ProgramRunner { enum class Algorithm { XORShift, LinearCongruentialGenerator, + MersenneTwister // Add other algorithms here }; @@ -48,7 +49,10 @@ class ProgramRunner { {"xorshift", Algorithm::XORShift}, {"xor", Algorithm::XORShift}, {"linear-congruential-generator", Algorithm::LinearCongruentialGenerator}, - {"lcg", Algorithm::LinearCongruentialGenerator} + {"lcg", Algorithm::LinearCongruentialGenerator}, + {"mersenne", Algorithm::MersenneTwister}, + {"mersenne-twister", Algorithm::MersenneTwister}, + {"mt", Algorithm::MersenneTwister} }; diff --git a/src/runner.cpp b/src/runner.cpp index f99b960..4dccf98 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -10,6 +10,7 @@ #include "prng.hpp" #include "xorshift.hpp" #include "linear_congruential_generator.hpp" +#include "mersenne_twister.hpp" std::string ProgramRunner::error_string() { @@ -241,6 +242,9 @@ void ProgramRunner::create_prng() { case ProgramRunner::Algorithm::LinearCongruentialGenerator: this->prng = std::make_unique(); break; + case ProgramRunner::Algorithm::MersenneTwister: + this->prng = std::make_unique(); + break; default: throw std::runtime_error("Unsupported algorithm"); } From 9a906186e4fce89f7d38f847f1f95a85fbe2769b Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 28 Jun 2025 19:22:08 -0400 Subject: [PATCH 82/95] Fixed the problem with segfaulting due to warming up when the user needed the version or help displayed instead of numbers generated. --- include/program_runner.hpp | 6 +++--- src/main.cpp | 3 +-- src/runner.cpp | 14 +++++++++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/program_runner.hpp b/include/program_runner.hpp index c6e611d..563a73c 100644 --- a/include/program_runner.hpp +++ b/include/program_runner.hpp @@ -9,7 +9,7 @@ class ProgramRunner { public: - ProgramRunner(int argc, char **argv); + ProgramRunner(int argc, char **argv, int warmup_iterations = 0); struct ProgramStatus { std::optional stdout_message; @@ -27,8 +27,6 @@ class ProgramRunner { bool is_finished(); - void warmup(uint64_t iterations = 1000); - const std::string version = "0.1"; const std::string program_name = "randomizer"; @@ -109,6 +107,8 @@ class ProgramRunner { void determine_count_configuration(const std::optional &count_str); void determine_algorithm_configuration(const std::optional &alg_str); void create_prng(); + void warmup(uint64_t iterations); + std::string error_string(); std::string help_string(); diff --git a/src/main.cpp b/src/main.cpp index 593e5e8..73b6635 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,8 +6,7 @@ #include "program_runner.hpp" int main(int argc, char *argv[]){ - ProgramRunner runner = ProgramRunner(argc, argv); - runner.warmup(); + ProgramRunner runner = ProgramRunner(argc, argv, 1000); ProgramRunner::ProgramStatus status; while (!runner.is_finished()) { status = runner.iterate(); diff --git a/src/runner.cpp b/src/runner.cpp index 4dccf98..cc92104 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -232,7 +232,7 @@ ProgramRunner::RawArguments ProgramRunner::parse_args(int argc, char **argv) { void ProgramRunner::create_prng() { if (!this->algorithm.has_value()) { - return; + throw std::runtime_error("Algorithm not set, cannot create PseudoRandomNumberGenerator instance"); } switch (this->algorithm.value()) { @@ -256,10 +256,14 @@ void ProgramRunner::create_prng() { return; } -ProgramRunner::ProgramRunner(int argc, char **argv){ +ProgramRunner::ProgramRunner(int argc, char **argv, int warmup_iterations){ RawArguments raw_arguments = parse_args(argc, argv); determine_program_configuration(raw_arguments); - create_prng(); + if (this->algorithm.has_value()) { + create_prng(); + warmup(warmup_iterations); + } + } bool ProgramRunner::is_finished() { @@ -358,6 +362,10 @@ ProgramRunner::ProgramStatus ProgramRunner::run() { } void ProgramRunner::warmup(uint64_t iterations) { + if (this->prng == nullptr) { + throw std::runtime_error("PRNG is not initialized, cannot warmup"); + } + for (uint64_t i = 0; i < iterations; ++i) { this->prng->generateRandomValue(); // Warmup the PRNG } From 84ac79d7bd86448f3281e960fd4df41adda9860b Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 29 Jun 2025 11:53:40 -0400 Subject: [PATCH 83/95] Added test cases for inclusive ranges when generating integers --- tests/test_LinearCongruentialGenerator.cpp | 52 +++++++++++++++++++- tests/test_MersenneTwister.cpp | 52 +++++++++++++++++++- tests/test_XORShift.cpp | 56 +++++++++++++++++++++- 3 files changed, 156 insertions(+), 4 deletions(-) diff --git a/tests/test_LinearCongruentialGenerator.cpp b/tests/test_LinearCongruentialGenerator.cpp index bd5c6ca..7911e1d 100644 --- a/tests/test_LinearCongruentialGenerator.cpp +++ b/tests/test_LinearCongruentialGenerator.cpp @@ -125,7 +125,7 @@ TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueEquidistributed) } -TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiveRange){ +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiveRangePositive){ LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); const int32_t min = 1; const int32_t max = 6; @@ -146,6 +146,56 @@ TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiv } } + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiveRangeNegative){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const int32_t min = -6; + const int32_t max = -1; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = lcg.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + +TEST(TestLinearCongruentialGenerator, GenerateIntegerRandomValueProducesInclusiveRangeNegativeAndPositive){ + LinearCongruentialGenerator lcg = LinearCongruentialGenerator(); + const int32_t min = -10; + const int32_t max = 10; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = lcg.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + ASSERT_TRUE(found_min); ASSERT_TRUE(found_max); } \ No newline at end of file diff --git a/tests/test_MersenneTwister.cpp b/tests/test_MersenneTwister.cpp index 5cbe701..4f5b69b 100644 --- a/tests/test_MersenneTwister.cpp +++ b/tests/test_MersenneTwister.cpp @@ -111,7 +111,7 @@ TEST(TestMersenneTwister, GenerateIntegerRandomValueEquidistributed){ } -TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRange){ +TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRangePositive){ MersenneTwister mt = MersenneTwister(); const int32_t min = 1; const int32_t max = 6; @@ -132,6 +132,56 @@ TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRange){ } } + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + +TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRangeNegative){ + MersenneTwister mt = MersenneTwister(); + const int32_t min = -6; + const int32_t max = -1; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = mt.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + +TEST(TestMersenneTwister, GenerateIntegerRandomValueProducesInclusiveRangeNegativeAndPositive){ + MersenneTwister mt = MersenneTwister(); + const int32_t min = -10; + const int32_t max = 10; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = mt.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + ASSERT_TRUE(found_min); ASSERT_TRUE(found_max); } \ No newline at end of file diff --git a/tests/test_XORShift.cpp b/tests/test_XORShift.cpp index 0f0c662..56488c4 100644 --- a/tests/test_XORShift.cpp +++ b/tests/test_XORShift.cpp @@ -123,7 +123,7 @@ TEST(TestXORShift, GenerateIntegerRandomValueEquidistributed){ ASSERT_NEAR(expected_value, average, margin_of_error); } -TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRange){ +TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRangePositive){ XORShift xor_shift = XORShift(); const int32_t min = 1; const int32_t max = 6; @@ -146,4 +146,56 @@ TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRange){ ASSERT_TRUE(found_min); ASSERT_TRUE(found_max); -} \ No newline at end of file +} + +TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRangeNegative){ + XORShift xor_shift = XORShift(); + const int32_t min = -6; + const int32_t max = -1; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = xor_shift.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + +TEST(TestXORShift, GenerateIntegerRandomValueProducesInclusiveRangeNegativeAndPositive){ + XORShift xor_shift = XORShift(); + const int32_t min = -10; + const int32_t max = 10; + const int max_iterations = 1000; + bool found_min = false; + bool found_max = false; + + for (int i = 0; i < max_iterations; ++i) { + int64_t value = xor_shift.generateIntegerRandomValue(min, max); + if (value == min) { + found_min = true; + } + if (value == max) { + found_max = true; + } + if (found_min && found_max) { + break; // No need to continue if both values are found + } + } + + ASSERT_TRUE(found_min); + ASSERT_TRUE(found_max); +} + + From 3340c0fe4035af4dee6ac3321bcaa2aee81afaa7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 29 Jun 2025 14:32:17 -0400 Subject: [PATCH 84/95] Fixed the problem with the inclusive range when engative numbers are used in generateIntegerRandomValue. --- src/prngs/prng.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index f6623dd..6628e72 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -47,8 +47,9 @@ double PseudoRandomNumberGenerator::generateFloatingPointRandomValue(float min, int64_t PseudoRandomNumberGenerator::generateIntegerRandomValue(int32_t min, int32_t max) { double random_val = generateUnitNormalRandomValue(); - double random_val_magnitude = random_val * (max - min + 1); - int64_t scaled_value = static_cast(min + random_val_magnitude); + int64_t inclusive_range = max - min + 1; + double random_val_magnitude = random_val * static_cast(inclusive_range); + int64_t scaled_value = static_cast(random_val_magnitude) + min; return scaled_value; } From ae0d5e5328769d9d1d0cfc91a87f4234e5af37e5 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 29 Jun 2025 15:39:02 -0400 Subject: [PATCH 85/95] Removed virtual function definition and implementation for generateUnitNormalRandomValue from concrete classes of prng. --- include/linear_congruential_generator.hpp | 1 - include/mersenne_twister.hpp | 1 - include/prng.hpp | 2 +- include/xorshift.hpp | 1 - src/prngs/linear_congruential_generator.cpp | 16 ---------------- src/prngs/mersenne_twister.cpp | 15 --------------- src/prngs/prng.cpp | 2 +- src/prngs/xorshift.cpp | 16 ---------------- 8 files changed, 2 insertions(+), 52 deletions(-) diff --git a/include/linear_congruential_generator.hpp b/include/linear_congruential_generator.hpp index eef02fc..6b5f614 100644 --- a/include/linear_congruential_generator.hpp +++ b/include/linear_congruential_generator.hpp @@ -51,7 +51,6 @@ class LinearCongruentialGenerator : public PseudoRandomNumberGenerator { LinearCongruentialGenerator(const uint64_t seed, const uint64_t modulus, const uint64_t multiplier, const uint64_t increment, const uint64_t mask); uint64_t generateRandomValue() override; - double generateUnitNormalRandomValue() override; }; #endif \ No newline at end of file diff --git a/include/mersenne_twister.hpp b/include/mersenne_twister.hpp index 3a75b6d..ffabd77 100644 --- a/include/mersenne_twister.hpp +++ b/include/mersenne_twister.hpp @@ -100,7 +100,6 @@ class MersenneTwister : public PseudoRandomNumberGenerator { MersenneTwister(const uint64_t seed); uint64_t generateRandomValue() override; - double generateUnitNormalRandomValue() override; }; #endif \ No newline at end of file diff --git a/include/prng.hpp b/include/prng.hpp index 4386948..69ccf65 100644 --- a/include/prng.hpp +++ b/include/prng.hpp @@ -20,7 +20,7 @@ class PseudoRandomNumberGenerator uint64_t generateCryptographicallyInsecureSeed(); virtual uint64_t generateRandomValue() = 0; - virtual double generateUnitNormalRandomValue() = 0; + double generateUnitNormalRandomValue(); double generateFloatingPointRandomValue(float min, float max); int64_t generateIntegerRandomValue(int32_t min, int32_t max); }; diff --git a/include/xorshift.hpp b/include/xorshift.hpp index db5aba7..b936e9c 100644 --- a/include/xorshift.hpp +++ b/include/xorshift.hpp @@ -34,7 +34,6 @@ class XORShift : public PseudoRandomNumberGenerator { uint64_t generateRandomValue() override; - double generateUnitNormalRandomValue() override; }; #endif \ No newline at end of file diff --git a/src/prngs/linear_congruential_generator.cpp b/src/prngs/linear_congruential_generator.cpp index 350165e..e405f90 100644 --- a/src/prngs/linear_congruential_generator.cpp +++ b/src/prngs/linear_congruential_generator.cpp @@ -72,19 +72,3 @@ uint64_t LinearCongruentialGenerator::generateRandomValue() { return m_current_value; } - -// Generate a random value normalized to the range [0, 1) -double LinearCongruentialGenerator::generateUnitNormalRandomValue() { - - uint64_t random_value = generateRandomValue(); - - uint64_t range = m_maximum_value - m_minimum_value; - if (range == 0) { - return 0.0; // Avoid division by zero - } - - // Normalize the random value to [0, 1) - double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); - return normalized_value; -} - diff --git a/src/prngs/mersenne_twister.cpp b/src/prngs/mersenne_twister.cpp index 70702d0..61ccd6d 100644 --- a/src/prngs/mersenne_twister.cpp +++ b/src/prngs/mersenne_twister.cpp @@ -111,18 +111,3 @@ uint64_t MersenneTwister::generateRandomValue(){ uint64_t tempered_value = tempering(raw_value); return tempered_value; } - -// Generate a random value normalized to the range [0, 1) -double MersenneTwister::generateUnitNormalRandomValue() { - - uint64_t random_value = generateRandomValue(); - - uint64_t range = m_maximum_value - m_minimum_value; - if (range == 0) { - return 0.0; // Avoid division by zero - } - - // Normalize the random value to [0, 1) - double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); - return normalized_value; -} diff --git a/src/prngs/prng.cpp b/src/prngs/prng.cpp index 6628e72..b22693b 100644 --- a/src/prngs/prng.cpp +++ b/src/prngs/prng.cpp @@ -66,4 +66,4 @@ double PseudoRandomNumberGenerator::generateUnitNormalRandomValue() { // Normalize the random value to [0, 1) double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); return normalized_value; -} \ No newline at end of file +} diff --git a/src/prngs/xorshift.cpp b/src/prngs/xorshift.cpp index 947fed1..ce3a3f7 100644 --- a/src/prngs/xorshift.cpp +++ b/src/prngs/xorshift.cpp @@ -36,19 +36,3 @@ uint64_t XORShift::generateRandomValue() { return m_current_value; } - -// Generate a random value normalized to the range [0, 1) -double XORShift::generateUnitNormalRandomValue() { - - uint64_t random_value = generateRandomValue(); - - uint64_t range = m_maximum_value - m_minimum_value; - if (range == 0) { - return 0.0; // Avoid division by zero - } - - // Normalize the random value to [0, 1) - double normalized_value = static_cast(random_value - m_minimum_value) / static_cast(range); - return normalized_value; -} - From 71685c1121076e2db76b2c0a1f6dd124687b5fa2 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Wed, 2 Jul 2025 19:00:03 -0400 Subject: [PATCH 86/95] packaging as .deb and .rpm with cpack. --- CMakeLists.txt | 5 +++-- LICENSE | 1 + packaging/CMakeLists.txt | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 LICENSE create mode 100644 packaging/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index f75d91e..662ecdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.22) -project(randomizer) +project(randomizer VERSION 0.1.0) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -47,4 +47,5 @@ endif() enable_testing() -add_subdirectory(tests) \ No newline at end of file +add_subdirectory(tests) +add_subdirectory(packaging) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..706584b --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +Copyright (c) 2025, Eric Butcher \ No newline at end of file diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt new file mode 100644 index 0000000..258d7ad --- /dev/null +++ b/packaging/CMakeLists.txt @@ -0,0 +1,20 @@ + +set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A UNIX command line utility written in C++ for generating pseudorandom numbers and sequences.") +set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") + +set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/out") + +set(CPACK_GENERATOR "DEB;RPM") + +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) +set(CPACK_DEBIAN_PACKAGE_SECTION Miscellaneous) +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Eric Butcher") + +set(CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + +include(CPack) From 0a58aa32f04e74747d574253581d1450c5c1c03a Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 5 Jul 2025 10:08:11 -0400 Subject: [PATCH 87/95] CMake configuration for cpack to crate .deb and .rpm. --- CMakeLists.txt | 2 ++ packaging/CMakeLists.txt | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 662ecdc..acaa890 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,8 @@ target_include_directories(randomizer_lib PUBLIC ${CMAKE_SOURCE_DIR}/include) add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} PRIVATE randomizer_lib) +# Make CPack install the binary in /usr/bin +install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) # Add compiler warnings only for GCC and Clang if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 258d7ad..dc103b0 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -11,6 +11,14 @@ set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/out") set(CPACK_GENERATOR "DEB;RPM") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64") +else() + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") +endif() set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set(CPACK_DEBIAN_PACKAGE_SECTION Miscellaneous) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Eric Butcher") From 98dae05ee85c98f24840befeae6790eff49490ca Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sat, 5 Jul 2025 10:08:31 -0400 Subject: [PATCH 88/95] Containers for ubuntu and fedora with the program installed. --- containers/Containerfile.fedora | 9 +++++++++ containers/Containerfile.ubuntu | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 containers/Containerfile.fedora create mode 100644 containers/Containerfile.ubuntu diff --git a/containers/Containerfile.fedora b/containers/Containerfile.fedora new file mode 100644 index 0000000..30ea0f0 --- /dev/null +++ b/containers/Containerfile.fedora @@ -0,0 +1,9 @@ +FROM fedora:42 + +COPY ../build/out/*.rpm . +RUN dnf install -y $(ls *.rpm | head -n1) && rm -rf ./*.rpm + +RUN groupadd unprivileged-group && \ + useradd -g unprivileged-group -N -m unprivileged-user +WORKDIR /home/unprivileged-user +USER unprivileged-user:unprivileged-group diff --git a/containers/Containerfile.ubuntu b/containers/Containerfile.ubuntu new file mode 100644 index 0000000..2333dba --- /dev/null +++ b/containers/Containerfile.ubuntu @@ -0,0 +1,9 @@ +FROM ubuntu:24.04 + +COPY ../build/out/*.deb . +RUN dpkg -i $(ls *.deb | head -n1) && rm -f ./*.deb + +RUN groupadd unprivileged-group && \ + useradd -g unprivileged-group -N -m unprivileged-user +WORKDIR /home/unprivileged-user +USER unprivileged-user:unprivileged-group From cc3f5ee90e99143c67353258f3ad91ae0e1968d3 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 16:03:47 -0400 Subject: [PATCH 89/95] Replaced ubuntu container with a debian one becuase ubuntu container was difficult to get man pages set up and working. --- containers/{Containerfile.ubuntu => Containerfile.debian} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename containers/{Containerfile.ubuntu => Containerfile.debian} (77%) diff --git a/containers/Containerfile.ubuntu b/containers/Containerfile.debian similarity index 77% rename from containers/Containerfile.ubuntu rename to containers/Containerfile.debian index 2333dba..a665e07 100644 --- a/containers/Containerfile.ubuntu +++ b/containers/Containerfile.debian @@ -1,4 +1,6 @@ -FROM ubuntu:24.04 +FROM debian:12 + +RUN apt-get update && apt-get install -y man man-db less COPY ../build/out/*.deb . RUN dpkg -i $(ls *.deb | head -n1) && rm -f ./*.deb From 17bc7266d83b1270108ae390a107e91ec303ca46 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 16:04:28 -0400 Subject: [PATCH 90/95] Fedora image Containerfile. --- containers/Containerfile.fedora | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/containers/Containerfile.fedora b/containers/Containerfile.fedora index 30ea0f0..414c380 100644 --- a/containers/Containerfile.fedora +++ b/containers/Containerfile.fedora @@ -1,7 +1,9 @@ FROM fedora:42 +RUN dnf install -y man man-pages man-db less --setopt='tsflags=' + COPY ../build/out/*.rpm . -RUN dnf install -y $(ls *.rpm | head -n1) && rm -rf ./*.rpm +RUN dnf install -y $(ls *.rpm | head -n1) --setopt='tsflags=' && rm -rf ./*.rpm RUN groupadd unprivileged-group && \ useradd -g unprivileged-group -N -m unprivileged-user From cafd105d53887c2023f83077db967ef12895d551 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 16:04:59 -0400 Subject: [PATCH 91/95] Reworded and formatted the help message. --- src/runner.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/runner.cpp b/src/runner.cpp index cc92104..fa13db5 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -15,25 +15,25 @@ std::string ProgramRunner::error_string() { std::string error_string = program_name + ": bad usage\n" - + "Try '" + program_name + " --help' for more information.\n"; + + "Try '" + program_name + " --help' for more information."; return error_string; } std::string ProgramRunner::help_string() { - std::string help_string = "Usage: " + program_name + " [options]\n" - + "Options:\n" - + " -h, --help Show this help message\n" - + " -v, --version Show version information\n" - + " -a, --algorithm Specify the algorithm (default: xorshift)\n" - + " -m, --min Minimum value\n" - + " -M, --max Maximum value\n" - + " -c, --count Number of random numbers to generate (default: 1)\n" - + " -t, --type Specify the type to output (default: int)\n"; + std::string help_string = "Usage: " + program_name + " [OPTIONS]\n\n" + + "Options:\n" + + " -a ALGORITHM, --algorithm=ALGORITHM Specify the algorithm [default: xorshift]\n" + + " -c AMOUNT, --count=AMOUNT Amount of random numbers to generate [default: 1]\n" + + " -M NUMBER, --max=NUMBER Maximum value\n" + + " -m NUMBER, --min=NUMBER Minimum value\n" + + " -t TYPE, --type=TYPE Specify the output type [default: int]\n" + + " -h, --help Display this help and exit\n" + + " -v, --version Output version information and exit"; return help_string; } std::string ProgramRunner::version_string() { - std::string version_string = program_name + " " + version + "\n"; + std::string version_string = program_name + " " + version; return version_string; } From a5296b50731da86088c8f16a22b13f8c8bab0de7 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 16:05:10 -0400 Subject: [PATCH 92/95] Added the man page. --- docs/randomizer.1 | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/randomizer.1 diff --git a/docs/randomizer.1 b/docs/randomizer.1 new file mode 100644 index 0000000..e41d9ce --- /dev/null +++ b/docs/randomizer.1 @@ -0,0 +1,50 @@ +.TH RANDOMIZER 1 "2025-07-05" GNU +.SH NAME +randomizer \- a tool to generate pseudorandom numbers and sequences +.SH SYNOPSIS +.B randomizer +.RI [ options ] + +.SH DESCRIPTION +.B randomizer +is a UNIX command line utility for generating pseudorandom numbers and sequences. +A variety of different pseudorandom number generators can be used. This program is not +necessarily intended to generate pseudorandom values quickly, in bulk quantities, or for scientific purposes. +.SH OPTIONS + +.TP +.BR \-a ", " \-\-algorithm " " \fIalgorithm\fP +Specify the algorithm. Can be one of xorshift, linear-congruential-generator, or mersenne-twister. +If no algorithm is specified then xorshift will be used by default. +The shorthands xor, lcg, and mt can also be used for their respective algorithms. + +.TP +.BR \-c ", " \-\-count " " \fIamount\fP +Specify the amount of random numbers to generate. If no amount is specified only one number will be generated. + +.TP +.BR \-h ", " \-\-help +Show help message and exit. + +.TP +.BR \-M ", " \-\-max " " \fInumber\fP +Specify the maximum value that can be generated. +If generating an integer type the bound will be inclusive; the bound will be exclusive if generating a floating point type. +This must always be used in conjunction with \-\-min. Ranges cannot be specified when generating unit-normal type values. + +.TP +.BR \-m ", " \-\-min " " \fInumber\fP +Specify the minimum value that can be generated. +The minimum value will always be an inclusive bound regardless of the type being generated. +This must always be used in conjunction with \-\-max. Ranges cannot be specified when generating unit-normal type values. + +.TP +.BR \-t ", " \-\-type " " \fItype\fP +Specify the type of numbers to be generated. Can be unit-normal, floating, or integer. +If generating floating or integer values the user must also specify a range using min and max. +Do not specify a range when creating unit-normal values. +The shorthands unit, float, and int can also be used for their respective values. + +.TP +.BR \-v ", " \-\-version +Show version information. From 23843b36fa84ee48516f74292309479f541a6b4e Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 16:06:13 -0400 Subject: [PATCH 93/95] Updated root CMakeLists.txt to create the .deb and .rpm packages with the executable and the man page. --- CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index acaa890..f586ce8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,10 @@ target_link_libraries(${PROJECT_NAME} PRIVATE randomizer_lib) # Make CPack install the binary in /usr/bin install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) +# Make CPack install the man page +include(GNUInstallDirs) +install(FILES "${CMAKE_SOURCE_DIR}/docs/randomizer.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") + # Add compiler warnings only for GCC and Clang if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(randomizer_lib PRIVATE @@ -49,5 +53,10 @@ endif() enable_testing() -add_subdirectory(tests) + +# Do not want to include Google Test in the installation packages +if(BUILD_TESTING) + add_subdirectory(tests) +endif() + add_subdirectory(packaging) \ No newline at end of file From 8054d8e2a6dbff3ae638e225d8672ba9eeaf2826 Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 18:22:21 -0400 Subject: [PATCH 94/95] Wrote README.md. --- README.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5a173d..27da911 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,79 @@ # Randomizer -A UNIX command line utility written in C++ for generating psuedorandom numbers and sequences. +A UNIX command line utility written in C++ for generating pseudorandom numbers and sequences. A variety of different pseudorandom number generators can be used including XORShift, a Linear-Congruential-Generator, and the famous Mersenne-Twister. This program is not necessarily intended to generate pseudorandom values quickly, in bulk quantities, or for scientific purposes. + +This program was mainly written so that I could learn modern C++ and how to build C++ projects with CMake. The build scripts are setup to package this utility as a `.deb` or a `.rpm` for easy installation alongside the included man page on most popular and modern linux distributions. There are also container files included that come with the utility installed inside of them in case you just want to try it out without installing it on your host machine. + +For usage instructions, use `randomizer --help` or view the [included man page](./docs/randomizer.1) using `man randomizer` once the application is installed. + +## Building + +The project is built using CMake. First, make sure you have cmake >= 3.22 installed on your machine: + +```shell +cmake --version +``` + +If you do not have CMake installed, or your version is <3.22, you can install it using [these instructions](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Getting%20Started.html#getting-and-installing-cmake-on-your-computer). + +Also ensure that you have a modern version of `gcc` or `clang` installed that supports C++ versions 20 or later. CMake will also need to use a build tool such as `make` or `ninja-build`, make sure one of those is installed on your machine. These packages should be included by most Linux distribution app repositories when you install CMake with tools like `apt` or `dnf`. + +Once CMake is installed, clone or download this repo and open the root directory of this repo. You can then configure the project using: +```shell +cmake -S . -B build +``` + +and then build the project using: +```shell +cmake --build build +``` + +You should now find that there is a binary called `randomizer` in `./build/out/`. This is the binary executable for the program. You can run the program by using `./randomizer`. + + +## Testing + +When the project was built there should have also been a binary called `RandomizerTests` which contains the executable for unit testing. You can run all of the unit tests by using the command `ctest` from the `./build/` directory. + +## Packaging + +To package the program in `.deb` and `.rpm` formats change into the `./build/` directory and use the command `cpack`. This will create a `.deb` archive for installing on Debian based systems and a `.rpm` archive for installing on RedHat based systems. Both packages will install the binary executable and the included man page. These archives will be placed in the `./build/out/` directory. + +Once packaged, you can install the `.deb` archive on any modern Debian-based system with: +```shell +sudo dpkg -i ./build/out/randomizer-.deb +``` + +To install the `.rpm` archive on any modern RedHat-based distribution, use: +```shell +sudo dnf install ./build/out/randomizer-.rpm +``` + +> These packages were tested on Ubuntu 24.04, Debian 12, and Fedora 42. + +## Containers + +If you do not wish to install the application directly on your host machine, container files are included which will install the application to be used in the container. One image is Debian based and installs the `.deb` archive and the other is Fedora based and installs the `.rpm` archive. Make sure you go through the steps to build and package the application with CMake before building the container(s). + +The containers can be created using [Podman](https://podman.io/) or [Docker](https://www.docker.com/). You must have the CLI for either program installed on your computer. The commands should be the same regardless of what container application you choose to use, just use the appropriate command name. + +The Debian image can be created using the following command from the project's root directory: +```shell +podman build -f containers/Containerfile.debian -t randomizer:debian +``` +and the Fedora image can be created using the command: +```shell +podman build -f containers/Containerfile.fedora -t randomizer:fedora +``` +also from the project's root directory. + +The container can then be accessed via shell using the command: +```shell +podman run --rm -it randomizer:debian +``` +or +```shell +podman run --rm -it randomizer:fedora +``` + +Once inside the container, the application can be ran like normal from the command line using the command `randomizer` and the manual page can be viewed using `man randomizer`. From 86caa019e7a9b5bf30fdbc39274e260218a7247e Mon Sep 17 00:00:00 2001 From: Eric-Butcher Date: Sun, 6 Jul 2025 18:36:43 -0400 Subject: [PATCH 95/95] Fixed CMakeLists so that tests always compile but are no included in the packages. --- CMakeLists.txt | 5 +---- tests/CMakeLists.txt | 5 +++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f586ce8..1b218fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,9 +54,6 @@ endif() enable_testing() -# Do not want to include Google Test in the installation packages -if(BUILD_TESTING) - add_subdirectory(tests) -endif() +add_subdirectory(tests) add_subdirectory(packaging) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a9db20c..e5deeca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,11 @@ FetchContent_Declare( ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# Prevent GoogleTest from being installed by CPack +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set(INSTALL_GMOCK OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) # Collect test sources