// // random_test.cpp // shared_random_generator-test // // Created by Sam Jaffe on 8/14/20. // Copyright © 2020 Sam Jaffe. All rights reserved. // #include "random/random.h" #include "random/device.h" #include "xcode_gtest_helper.h" using testing::DoubleNear; struct MockDevice : engine::random::Device { MOCK_METHOD2(s_inclusive, int32_t(int32_t, int32_t)); MOCK_METHOD2(u_inclusive, uint32_t(uint32_t, uint32_t)); MOCK_METHOD2(exclusive, double(double, double)); int32_t inclusive(int32_t min, int32_t max) { return s_inclusive(min, max); } uint32_t inclusive(uint32_t min, uint32_t max) { return u_inclusive(min, max); } }; TEST(RandomTest, ExclusiveIntegerPassedAsInclusive) { auto mock = std::make_shared(); engine::random::Random generator{mock}; EXPECT_CALL(*mock, s_inclusive(-10, 9)).Times(1); generator.exclusive(-10, 10); } TEST(RandomTest, ExclusiveUIntegerPassedAsInclusive) { auto mock = std::make_shared(); engine::random::Random generator{mock}; EXPECT_CALL(*mock, u_inclusive(0, 9)).Times(1); generator.exclusive(0u, 10u); } TEST(RandomTest, PassesThroughExclusiveDoubleCall) { auto mock = std::make_shared(); engine::random::Random generator{mock}; EXPECT_CALL(*mock, exclusive(1.0, 10.0)).Times(1); generator.exclusive(1.0, 10.0); } TEST(RandomTest, DoctorsInclusiveDoubleCall) { auto mock = std::make_shared(); engine::random::Random generator{mock}; EXPECT_CALL(*mock, exclusive(1.0, DoubleNear(10.0, 1e-7))).Times(1); generator.inclusive(1.0, 10.0); } class kahan_summation { private: double sum_{0.0}; double carry_{0.0}; public: kahan_summation() = default; kahan_summation & operator+=(double d); explicit operator double() const { return sum_; } }; kahan_summation & kahan_summation::operator+=(double num) { double const yield = num - carry_; double const total = sum_ + yield; carry_ = (total - sum_) - yield; sum_ = total; return *this; } TEST(DefaultRandomTest, RandomDistributionIntIsUniform) { engine::random::Random generator{}; kahan_summation sum{}; size_t const iters = 2000000; for (size_t i = 0; i < iters; ++i) { // All values from 0 - 100 are allowable sum += generator.inclusive(0, 100); } // Expected result is (0+99)/2 +/- 0.1% EXPECT_THAT(double(sum) / iters, DoubleNear(50, 0.05)); } TEST(DefaultRandomTest, RandomDistributionDblIsUniform) { engine::random::Random generator{}; kahan_summation sum{}; size_t const iters = 2000000; for (size_t i = 0; i < iters; ++i) { // All values from 0 - 99 are allowable sum += generator.exclusive(0.0, 100.0); } // Expected result is [0.0, 100.0) +/- 0.1% EXPECT_THAT(double(sum) / iters, DoubleNear(50, 0.05)); } TEST(DefaultRandomTest, InclusiveRangeMayIncludeValue) { engine::random::Random generator{}; EXPECT_THAT(generator.inclusive(1.0, 1.0), DoubleNear(1.0, 1E-6)); } TEST(DefaultRandomTest, RandomDistributionDblInclIsUniform) { engine::random::Random generator{}; kahan_summation sum{}; size_t const iters = 2000000; for (size_t i = 0; i < iters; ++i) { // All values in [0.0, 100.0] are allowable sum += generator.inclusive(0.0, 100.0); } // Expected result is (0+100)/2 +/- 0.1% EXPECT_THAT(double(sum) / iters, DoubleNear(50, 0.05)); }