Files
klubhaus-doorbell/libraries/FastLED/tests/test_algorithm.cpp
2026-02-12 00:45:31 -08:00

1069 lines
30 KiB
C++

// g++ --std=c++11 test.cpp
#include "test.h"
#include "fl/algorithm.h"
#include "fl/dbg.h"
#include "fl/vector.h"
#include "fl/functional.h"
#include "fl/map.h"
#include "fl/random.h"
#include "test.h"
using namespace fl;
TEST_CASE("reverse an int list") {
fl::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
fl::reverse(vec.begin(), vec.end());
CHECK_EQ(vec[0], 5);
CHECK_EQ(vec[1], 4);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 2);
CHECK_EQ(vec[4], 1);
}
// This test should NOT compile - trying to sort a map should fail
// because it would break the tree's internal ordering invariant
#ifdef COMPILE_ERROR_TEST_MAP_SORT
TEST_CASE("fl::sort on fl::fl_map should fail to compile") {
fl::fl_map<int, fl::string> map;
map[3] = "three";
map[1] = "one";
map[2] = "two";
// This CORRECTLY produces a compilation error!
// ERROR: no match for 'operator+' on map iterators
//
// fl::fl_map iterators are bidirectional (not random access)
// fl::sort requires random access iterators for:
// - Iterator arithmetic: first + 1, last - first
// - Efficient pivot selection and partitioning
//
// This prevents users from accidentally corrupting the tree structure
// by calling sort on a map, which would break ordering invariants.
fl::sort(map.begin(), map.end());
}
#endif
TEST_CASE("fl::sort with default comparator") {
SUBCASE("Sort integers") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 5);
CHECK_EQ(vec[4], 8);
CHECK_EQ(vec[5], 9);
}
SUBCASE("Sort empty container") {
fl::vector<int> vec;
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec.size(), 0);
}
SUBCASE("Sort single element") {
fl::vector<int> vec;
vec.push_back(42);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec.size(), 1);
CHECK_EQ(vec[0], 42);
}
SUBCASE("Sort two elements") {
fl::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 3);
}
SUBCASE("Sort already sorted array") {
fl::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 4);
CHECK_EQ(vec[4], 5);
}
SUBCASE("Sort reverse sorted array") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(4);
vec.push_back(3);
vec.push_back(2);
vec.push_back(1);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 4);
CHECK_EQ(vec[4], 5);
}
SUBCASE("Sort with duplicates") {
fl::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
vec.push_back(4);
vec.push_back(1);
vec.push_back(5);
vec.push_back(3);
vec.push_back(1);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 1);
CHECK_EQ(vec[2], 1);
CHECK_EQ(vec[3], 3);
CHECK_EQ(vec[4], 3);
CHECK_EQ(vec[5], 4);
CHECK_EQ(vec[6], 5);
}
}
TEST_CASE("fl::sort with custom comparator") {
SUBCASE("Sort integers in descending order") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
CHECK_EQ(vec[0], 9);
CHECK_EQ(vec[1], 8);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 3);
CHECK_EQ(vec[4], 2);
CHECK_EQ(vec[5], 1);
}
SUBCASE("Sort using fl::less") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
fl::sort(vec.begin(), vec.end(), fl::less<int>());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 8);
}
SUBCASE("Sort custom struct by member") {
struct Point {
int x, y;
Point() : x(0), y(0) {} // Add default constructor
Point(int x, int y) : x(x), y(y) {}
};
fl::vector<Point> vec;
vec.push_back(Point(3, 1));
vec.push_back(Point(1, 3));
vec.push_back(Point(2, 2));
// Sort by x coordinate
fl::sort(vec.begin(), vec.end(), [](const Point& a, const Point& b) {
return a.x < b.x;
});
CHECK_EQ(vec[0].x, 1);
CHECK_EQ(vec[1].x, 2);
CHECK_EQ(vec[2].x, 3);
}
}
TEST_CASE("fl::sort with different container types") {
SUBCASE("Sort FixedVector") {
fl::FixedVector<int, 6> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 5);
CHECK_EQ(vec[4], 8);
CHECK_EQ(vec[5], 9);
}
SUBCASE("Sort HeapVector") {
fl::HeapVector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 8);
}
}
TEST_CASE("fl::sort stability and performance") {
SUBCASE("Large array sort") {
fl::vector<int> vec;
// Create a larger array for testing
for (int i = 100; i >= 1; --i) {
vec.push_back(i);
}
fl::sort(vec.begin(), vec.end());
// Verify all elements are in order
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], static_cast<int>(i + 1));
}
}
SUBCASE("Partial sort") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
// Sort only first 3 elements
fl::sort(vec.begin(), vec.begin() + 3);
CHECK_EQ(vec[0], 2);
CHECK_EQ(vec[1], 5);
CHECK_EQ(vec[2], 8);
// Rest should be unchanged
CHECK_EQ(vec[3], 1);
CHECK_EQ(vec[4], 9);
CHECK_EQ(vec[5], 3);
}
}
TEST_CASE("fl::sort edge cases") {
SUBCASE("All elements equal") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(5);
vec.push_back(5);
vec.push_back(5);
vec.push_back(5);
fl::sort(vec.begin(), vec.end());
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], 5);
}
}
SUBCASE("Mixed positive and negative") {
fl::vector<int> vec;
vec.push_back(-5);
vec.push_back(2);
vec.push_back(-8);
vec.push_back(1);
vec.push_back(0);
vec.push_back(-1);
fl::sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], -8);
CHECK_EQ(vec[1], -5);
CHECK_EQ(vec[2], -1);
CHECK_EQ(vec[3], 0);
CHECK_EQ(vec[4], 1);
CHECK_EQ(vec[5], 2);
}
}
TEST_CASE("fl::stable_sort with default comparator") {
SUBCASE("Sort integers") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 5);
CHECK_EQ(vec[4], 8);
CHECK_EQ(vec[5], 9);
}
SUBCASE("Sort empty container") {
fl::vector<int> vec;
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec.size(), 0);
}
SUBCASE("Sort single element") {
fl::vector<int> vec;
vec.push_back(42);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec.size(), 1);
CHECK_EQ(vec[0], 42);
}
SUBCASE("Sort two elements") {
fl::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 3);
}
SUBCASE("Sort already sorted array") {
fl::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 4);
CHECK_EQ(vec[4], 5);
}
SUBCASE("Sort with duplicates") {
fl::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
vec.push_back(4);
vec.push_back(1);
vec.push_back(5);
vec.push_back(3);
vec.push_back(1);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 1);
CHECK_EQ(vec[2], 1);
CHECK_EQ(vec[3], 3);
CHECK_EQ(vec[4], 3);
CHECK_EQ(vec[5], 4);
CHECK_EQ(vec[6], 5);
}
}
TEST_CASE("fl::stable_sort stability test") {
// Test that stable_sort maintains relative order of equal elements
struct NumberWithIndex {
int value;
int original_index;
NumberWithIndex() : value(0), original_index(0) {}
NumberWithIndex(int v, int idx) : value(v), original_index(idx) {}
};
SUBCASE("Maintain order of equal elements") {
fl::vector<NumberWithIndex> vec;
vec.push_back(NumberWithIndex(3, 0));
vec.push_back(NumberWithIndex(1, 1));
vec.push_back(NumberWithIndex(3, 2)); // Same value as index 0
vec.push_back(NumberWithIndex(1, 3)); // Same value as index 1
vec.push_back(NumberWithIndex(2, 4));
vec.push_back(NumberWithIndex(3, 5)); // Same value as indices 0 and 2
// Sort by value only (ignore original_index in comparison)
fl::stable_sort(vec.begin(), vec.end(),
[](const NumberWithIndex& a, const NumberWithIndex& b) {
return a.value < b.value;
});
// Verify sorted by value
CHECK_EQ(vec[0].value, 1);
CHECK_EQ(vec[1].value, 1);
CHECK_EQ(vec[2].value, 2);
CHECK_EQ(vec[3].value, 3);
CHECK_EQ(vec[4].value, 3);
CHECK_EQ(vec[5].value, 3);
// Verify stability: equal elements maintain their relative original order
// First 1 should be from original index 1, second 1 from original index 3
CHECK_EQ(vec[0].original_index, 1);
CHECK_EQ(vec[1].original_index, 3);
// The 3's should be in order: original indices 0, 2, 5
CHECK_EQ(vec[3].original_index, 0);
CHECK_EQ(vec[4].original_index, 2);
CHECK_EQ(vec[5].original_index, 5);
}
SUBCASE("Large stability test") {
fl::vector<NumberWithIndex> vec;
// Create pattern: values 1,2,3,1,2,3,1,2,3...
for (int i = 0; i < 30; ++i) {
vec.push_back(NumberWithIndex((i % 3) + 1, i));
}
fl::stable_sort(vec.begin(), vec.end(),
[](const NumberWithIndex& a, const NumberWithIndex& b) {
return a.value < b.value;
});
// Verify all 1's come first, then 2's, then 3's
for (int i = 0; i < 10; ++i) {
CHECK_EQ(vec[i].value, 1);
}
for (int i = 10; i < 20; ++i) {
CHECK_EQ(vec[i].value, 2);
}
for (int i = 20; i < 30; ++i) {
CHECK_EQ(vec[i].value, 3);
}
// Verify stability within each group
// 1's should have original indices: 0, 3, 6, 9, 12, 15, 18, 21, 24, 27
int expected_1_indices[] = {0, 3, 6, 9, 12, 15, 18, 21, 24, 27};
for (int i = 0; i < 10; ++i) {
CHECK_EQ(vec[i].original_index, expected_1_indices[i]);
}
// 2's should have original indices: 1, 4, 7, 10, 13, 16, 19, 22, 25, 28
int expected_2_indices[] = {1, 4, 7, 10, 13, 16, 19, 22, 25, 28};
for (int i = 0; i < 10; ++i) {
CHECK_EQ(vec[i + 10].original_index, expected_2_indices[i]);
}
// 3's should have original indices: 2, 5, 8, 11, 14, 17, 20, 23, 26, 29
int expected_3_indices[] = {2, 5, 8, 11, 14, 17, 20, 23, 26, 29};
for (int i = 0; i < 10; ++i) {
CHECK_EQ(vec[i + 20].original_index, expected_3_indices[i]);
}
}
}
TEST_CASE("fl::stable_sort with custom comparator") {
SUBCASE("Sort integers in descending order") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::stable_sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
CHECK_EQ(vec[0], 9);
CHECK_EQ(vec[1], 8);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 3);
CHECK_EQ(vec[4], 2);
CHECK_EQ(vec[5], 1);
}
SUBCASE("Sort using fl::less") {
fl::vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
fl::stable_sort(vec.begin(), vec.end(), fl::less<int>());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 8);
}
}
TEST_CASE("fl::stable_sort with different container types") {
SUBCASE("Sort FixedVector") {
fl::FixedVector<int, 6> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
vec.push_back(9);
vec.push_back(3);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 3);
CHECK_EQ(vec[3], 5);
CHECK_EQ(vec[4], 8);
CHECK_EQ(vec[5], 9);
}
SUBCASE("Sort HeapVector") {
fl::HeapVector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(8);
vec.push_back(1);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
CHECK_EQ(vec[2], 5);
CHECK_EQ(vec[3], 8);
}
}
TEST_CASE("fl::stable_sort edge cases and stress tests") {
SUBCASE("Very large array with many duplicates") {
fl::vector<int> vec;
// Create a pattern: 1,2,3,1,2,3,... for 100 elements
for (int i = 0; i < 100; ++i) {
vec.push_back((i % 3) + 1);
}
fl::stable_sort(vec.begin(), vec.end());
// Verify all 1's come first (should be ~33 of them)
int ones_count = 0;
int twos_count = 0;
int threes_count = 0;
for (size_t i = 0; i < vec.size(); ++i) {
if (vec[i] == 1) {
ones_count++;
// All 1's should come before any 2's or 3's
CHECK_LT(i, 34); // Should be in first ~33 positions
} else if (vec[i] == 2) {
twos_count++;
// All 2's should come after 1's but before 3's
CHECK_GE(i, 33);
CHECK_LT(i, 67);
} else if (vec[i] == 3) {
threes_count++;
// All 3's should come last
CHECK_GE(i, 66);
}
}
CHECK_EQ(ones_count, 34); // 0,3,6,9,... up to 99 = 34 elements
CHECK_EQ(twos_count, 33); // 1,4,7,10,... up to 97 = 33 elements
CHECK_EQ(threes_count, 33); // 2,5,8,11,... up to 98 = 33 elements
}
SUBCASE("Array with all identical elements") {
fl::vector<int> vec;
for (int i = 0; i < 50; ++i) {
vec.push_back(42);
}
fl::stable_sort(vec.begin(), vec.end());
// All elements should remain 42
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], 42);
}
}
SUBCASE("Reverse sorted large array") {
fl::vector<int> vec;
for (int i = 100; i >= 1; --i) {
vec.push_back(i);
}
fl::stable_sort(vec.begin(), vec.end());
// Should be sorted 1,2,3,...,100
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], static_cast<int>(i + 1));
}
}
SUBCASE("Array with extreme values") {
fl::vector<int> vec;
vec.push_back(2147483647); // INT_MAX
vec.push_back(-2147483648); // INT_MIN
vec.push_back(0);
vec.push_back(1);
vec.push_back(-1);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], -2147483648);
CHECK_EQ(vec[1], -1);
CHECK_EQ(vec[2], 0);
CHECK_EQ(vec[3], 1);
CHECK_EQ(vec[4], 2147483647);
}
}
TEST_CASE("fl::stable_sort with complex objects") {
struct Person {
fl::string name;
int age;
int id; // for tracking original order
Person() : name(""), age(0), id(0) {}
Person(const fl::string& n, int a, int i) : name(n), age(a), id(i) {}
};
SUBCASE("Sort by age, maintain name order for same age") {
fl::vector<Person> people;
people.push_back(Person("Alice", 25, 0));
people.push_back(Person("Bob", 30, 1));
people.push_back(Person("Charlie", 25, 2)); // Same age as Alice
people.push_back(Person("David", 20, 3));
people.push_back(Person("Eve", 30, 4)); // Same age as Bob
// Sort by age, stable sort should maintain relative order for same ages
fl::stable_sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
// Verify age order
CHECK_EQ(people[0].age, 20); // David
CHECK_EQ(people[1].age, 25); // Alice (should come before Charlie)
CHECK_EQ(people[2].age, 25); // Charlie
CHECK_EQ(people[3].age, 30); // Bob (should come before Eve)
CHECK_EQ(people[4].age, 30); // Eve
// Verify stability within age groups
CHECK_EQ(people[0].id, 3); // David
CHECK_EQ(people[1].id, 0); // Alice came before Charlie originally
CHECK_EQ(people[2].id, 2); // Charlie
CHECK_EQ(people[3].id, 1); // Bob came before Eve originally
CHECK_EQ(people[4].id, 4); // Eve
}
}
TEST_CASE("fl::stable_sort algorithm boundaries") {
SUBCASE("Test small array threshold (≤32 elements)") {
// Test exactly 32 elements (boundary case)
fl::vector<int> vec;
for (int i = 32; i >= 1; --i) {
vec.push_back(i);
}
fl::stable_sort(vec.begin(), vec.end());
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], static_cast<int>(i + 1));
}
}
SUBCASE("Test large array (>32 elements)") {
// Test 33 elements (just over boundary)
fl::vector<int> vec;
for (int i = 33; i >= 1; --i) {
vec.push_back(i);
}
fl::stable_sort(vec.begin(), vec.end());
for (size_t i = 0; i < vec.size(); ++i) {
CHECK_EQ(vec[i], static_cast<int>(i + 1));
}
}
SUBCASE("Test rotation edge cases") {
// Single element rotations
fl::vector<int> vec;
vec.push_back(2);
vec.push_back(1);
fl::stable_sort(vec.begin(), vec.end());
CHECK_EQ(vec[0], 1);
CHECK_EQ(vec[1], 2);
}
}
TEST_CASE("fl::stable_sort vs fl::sort comparison") {
struct IndexedValue {
int value;
int original_index;
IndexedValue() : value(0), original_index(0) {}
IndexedValue(int v, int idx) : value(v), original_index(idx) {}
};
SUBCASE("Compare stability between sort and stable_sort") {
// Create two identical arrays
fl::vector<IndexedValue> vec_stable;
fl::vector<IndexedValue> vec_unstable;
// Add elements with same values but different original indices
for (int i = 0; i < 20; ++i) {
int value = (i % 5); // Creates many duplicates: 0,1,2,3,4,0,1,2,3,4,...
vec_stable.push_back(IndexedValue(value, i));
vec_unstable.push_back(IndexedValue(value, i));
}
auto comparator = [](const IndexedValue& a, const IndexedValue& b) {
return a.value < b.value;
};
fl::stable_sort(vec_stable.begin(), vec_stable.end(), comparator);
fl::sort(vec_unstable.begin(), vec_unstable.end(), comparator);
// Both should be sorted by value
for (size_t i = 0; i < vec_stable.size(); ++i) {
CHECK_EQ(vec_stable[i].value, vec_unstable[i].value);
}
// stable_sort should preserve relative order of equal elements
// Check that for each value group, original indices are in increasing order
for (int target_value = 0; target_value < 5; ++target_value) {
fl::vector<int> stable_indices;
for (size_t i = 0; i < vec_stable.size(); ++i) {
if (vec_stable[i].value == target_value) {
stable_indices.push_back(vec_stable[i].original_index);
}
}
// In stable sort, indices should be in original order: 0,5,10,15 for value 0, etc.
for (size_t i = 1; i < stable_indices.size(); ++i) {
CHECK_LT(stable_indices[i-1], stable_indices[i]);
}
}
}
}
TEST_CASE("fl::fl_random basic functionality") {
SUBCASE("Default constructor") {
fl::fl_random rng;
// Should be able to generate numbers
uint32_t val1 = rng();
uint32_t val2 = rng();
// Values should be in valid range
CHECK_GE(val1, fl::fl_random::minimum());
CHECK_LE(val1, fl::fl_random::maximum());
CHECK_GE(val2, fl::fl_random::minimum());
CHECK_LE(val2, fl::fl_random::maximum());
}
SUBCASE("Constructor with seed") {
fl::fl_random rng1(12345);
fl::fl_random rng2(12345); // Same seed
fl::fl_random rng3(54321); // Different seed
// Same seed should produce same sequence
uint32_t val1a = rng1();
uint32_t val1b = rng1();
uint32_t val2a = rng2();
uint32_t val2b = rng2();
CHECK_EQ(val1a, val2a);
CHECK_EQ(val1b, val2b);
// Different seed should likely produce different values
uint32_t val3a = rng3();
CHECK_NE(val1a, val3a); // Very unlikely to be equal
}
SUBCASE("Range generation with single parameter") {
fl::fl_random rng(12345);
// Test range [0, 10)
for (int i = 0; i < 100; ++i) {
uint32_t val = rng(10);
CHECK_GE(val, 0);
CHECK_LT(val, 10);
}
// Test range [0, 1) - should always be 0
for (int i = 0; i < 10; ++i) {
uint32_t val = rng(1);
CHECK_EQ(val, 0);
}
}
SUBCASE("Range generation with min and max") {
fl::fl_random rng(12345);
// Test range [5, 15)
for (int i = 0; i < 100; ++i) {
uint32_t val = rng(5, 15);
CHECK_GE(val, 5);
CHECK_LT(val, 15);
}
// Test range [100, 101) - should always be 100
for (int i = 0; i < 10; ++i) {
uint32_t val = rng(100, 101);
CHECK_EQ(val, 100);
}
}
SUBCASE("8-bit random generation") {
fl::fl_random rng(12345);
// Test random8()
for (int i = 0; i < 50; ++i) {
uint8_t val = rng.random8();
CHECK_GE(val, 0);
CHECK_LE(val, 255);
}
// Test random8(n)
for (int i = 0; i < 50; ++i) {
uint8_t val = rng.random8(50);
CHECK_GE(val, 0);
CHECK_LT(val, 50);
}
// Test random8(min, max)
for (int i = 0; i < 50; ++i) {
uint8_t val = rng.random8(10, 20);
CHECK_GE(val, 10);
CHECK_LT(val, 20);
}
}
SUBCASE("16-bit random generation") {
fl::fl_random rng(12345);
// Test random16()
for (int i = 0; i < 50; ++i) {
uint16_t val = rng.random16();
CHECK_GE(val, 0);
CHECK_LE(val, 65535);
}
// Test random16(n)
for (int i = 0; i < 50; ++i) {
uint16_t val = rng.random16(1000);
CHECK_GE(val, 0);
CHECK_LT(val, 1000);
}
// Test random16(min, max)
for (int i = 0; i < 50; ++i) {
uint16_t val = rng.random16(500, 1500);
CHECK_GE(val, 500);
CHECK_LT(val, 1500);
}
}
SUBCASE("Seed management") {
fl::fl_random rng;
// Set and get seed
rng.set_seed(42);
CHECK_EQ(rng.get_seed(), 42);
// Add entropy
rng.add_entropy(100);
CHECK_EQ(rng.get_seed(), 142); // 42 + 100
}
SUBCASE("Static min/max methods") {
CHECK_EQ(fl::fl_random::minimum(), 0);
CHECK_EQ(fl::fl_random::maximum(), 4294967295U);
}
}
TEST_CASE("fl::fl_random deterministic behavior") {
SUBCASE("Same seed produces same sequence") {
fl::fl_random rng1(12345);
fl::fl_random rng2(12345);
fl::vector<uint32_t> seq1;
fl::vector<uint32_t> seq2;
// Generate same sequence from both generators
for (int i = 0; i < 20; ++i) {
seq1.push_back(rng1());
seq2.push_back(rng2());
}
// Sequences should be identical
for (size_t i = 0; i < seq1.size(); ++i) {
CHECK_EQ(seq1[i], seq2[i]);
}
}
SUBCASE("Different seeds produce different sequences") {
fl::fl_random rng1(12345);
fl::fl_random rng2(54321);
fl::vector<uint32_t> seq1;
fl::vector<uint32_t> seq2;
// Generate sequences from both generators
for (int i = 0; i < 20; ++i) {
seq1.push_back(rng1());
seq2.push_back(rng2());
}
// Sequences should be different (very unlikely to be identical)
bool different = false;
for (size_t i = 0; i < seq1.size(); ++i) {
if (seq1[i] != seq2[i]) {
different = true;
break;
}
}
CHECK(different);
}
}
TEST_CASE("fl::default_random global instance") {
SUBCASE("Global instance is accessible") {
// Should be able to use the global instance
uint32_t val1 = fl::default_random()();
uint32_t val2 = fl::default_random()();
// Values should be in valid range
CHECK_GE(val1, fl::fl_random::minimum());
CHECK_LE(val1, fl::fl_random::maximum());
CHECK_GE(val2, fl::fl_random::minimum());
CHECK_LE(val2, fl::fl_random::maximum());
}
SUBCASE("Global instance can be seeded") {
fl::default_random().set_seed(12345);
CHECK_EQ(fl::default_random().get_seed(), 12345);
uint32_t val1 = fl::default_random()();
// Reset to same seed and verify same value
fl::default_random().set_seed(12345);
uint32_t val2 = fl::default_random()();
CHECK_EQ(val1, val2);
}
}
TEST_CASE("fl::shuffle with fl::fl_random") {
SUBCASE("Shuffle with explicit fl::fl_random instance") {
fl::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
fl::fl_random rng(12345);
fl::shuffle(vec.begin(), vec.end(), rng);
// All elements should still be present
CHECK_EQ(vec.size(), 5);
// Check each original element is still present
for (int i = 1; i <= 5; ++i) {
bool found = false;
for (size_t j = 0; j < vec.size(); ++j) {
if (vec[j] == i) {
found = true;
break;
}
}
CHECK(found);
}
}
SUBCASE("Deterministic shuffle with same seed") {
fl::vector<int> vec1;
fl::vector<int> vec2;
for (int i = 1; i <= 10; ++i) {
vec1.push_back(i);
vec2.push_back(i);
}
fl::fl_random rng1(12345);
fl::fl_random rng2(12345); // Same seed
fl::shuffle(vec1.begin(), vec1.end(), rng1);
fl::shuffle(vec2.begin(), vec2.end(), rng2);
// Same seed should produce same shuffle
CHECK_EQ(vec1.size(), vec2.size());
for (size_t i = 0; i < vec1.size(); ++i) {
CHECK_EQ(vec1[i], vec2[i]);
}
}
SUBCASE("Shuffle with global default_random") {
fl::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
vec.push_back(40);
vec.push_back(50);
// This should use the global default_random instance
fl::shuffle(vec.begin(), vec.end());
// All elements should still be present
CHECK_EQ(vec.size(), 5);
// Check each original element is still present
int expected[] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; ++i) {
bool found = false;
for (size_t j = 0; j < vec.size(); ++j) {
if (vec[j] == expected[i]) {
found = true;
break;
}
}
CHECK(found);
}
}
}