#include #include #include #include #include // // If the default clang follows the the one true brace style is that not // obviously the better brace style??? // class DistributionPair { public: DistributionPair(std::uint32_t minValue, std::uint32_t maxValue) : minValue(minValue), maxValue(maxValue), count(0) {} std::uint32_t minValue; std::uint32_t maxValue; std::uint32_t count; }; std::vector generateUniformDistribution(std::uint32_t howMany, std::uint32_t min, std::uint32_t max, std::uint8_t numberBins); std::vector generateNormalDistribution(std::uint32_t howMany, float mean, float stdev, std::uint8_t numberBins); std::vector generatePoissonDistribution(std::uint32_t howMany, std::uint8_t howOften, std::uint8_t numberBins); void plotDistribution(std::string title, const std::vector& distribution, const std::uint8_t maxPlotLineSize); std::vector createBins(int min, int max, int binCount); std::random_device randomDevice; std::default_random_engine randomEngine(randomDevice()); // ------------------------------------------------------------------ // // Testing Code // // ------------------------------------------------------------------ #include #include #include #include namespace testing::detail { using namespace std::string_literals; using Bins = std::vector>; using DistFunc = std::function()>; #define CS3460_ASSERT_EQ(expected, actual, message) \ if (expected != actual) \ { \ fail(message, "[ Expected", expected, "but got", actual, "]"); \ return; \ } #define CS3460_CASE(x) \ [] { return x; }; \ std::cout << " Case " << #x << "\n"; template void failInternal(const Message& message) { std::cout << message << " "; } template void failInternal(const Message& message, const Messages&... messages) { failInternal(message); failInternal(messages...); } template void fail(const Messages&... messages) { std::cout << " Assertion failed: "; failInternal(messages...); std::cout << "\n"; } Bins generateBins(const std::uint32_t min, const std::uint32_t max, const std::uint8_t numberBins) { const auto binRange = (max - min) / numberBins; auto minBin = min; auto maxBin = min + binRange; Bins results(numberBins); for (std::uint8_t bin = 0u; bin < numberBins; bin++) { results[bin] = {minBin, maxBin}; minBin = maxBin + 1; maxBin = minBin + binRange; } return results; } void returnsTheExpectedBins(const DistFunc& func, const Bins& bins) { const auto result = func(); CS3460_ASSERT_EQ(bins.size(), result.size(), "Wrong number of bins"); for (auto i = 0u; i < bins.size(); i++) { CS3460_ASSERT_EQ(bins[i].first, result[i].minValue, "Wrong minimum value for bin "s + std::to_string(i)); CS3460_ASSERT_EQ(bins[i].second, result[i].maxValue, "Wrong maximum value for bin "s + std::to_string(i)); } } void hasTheCorrectTotalAcrossAllBins(const DistFunc& func, const std::uint32_t howMany) { const auto result = func(); const auto add_counts = [](std::uint32_t total, const DistributionPair& bin) { return total + bin.count; }; CS3460_ASSERT_EQ(howMany, std::accumulate(result.cbegin(), result.cend(), 0u, add_counts), "Wrong number of elements across all bins"); } void testUniformDistribution() { std::cout << "Testing generateUniformDistribution\n"; auto func = CS3460_CASE(generateUniformDistribution(100000, 0, 79, 40)); returnsTheExpectedBins(func, generateBins(0, 79, 40)); hasTheCorrectTotalAcrossAllBins(func, 100000); } void testNormalDistribution() { std::cout << "Testing generateNormalDistribution\n"; auto func = CS3460_CASE(generateNormalDistribution(100000, 50, 5, 40)); returnsTheExpectedBins(func, generateBins(30, 69, 40)); hasTheCorrectTotalAcrossAllBins(func, 100000); } void testPoissonDistribution() { std::cout << "Testing generatePoissonDistribution\n"; auto func = CS3460_CASE(generatePoissonDistribution(100000, 6, 40)); returnsTheExpectedBins(func, generateBins(0, 39, 40)); hasTheCorrectTotalAcrossAllBins(func, 100000); } } // namespace testing::detail void test() { using namespace testing::detail; testUniformDistribution(); testNormalDistribution(); testPoissonDistribution(); std::cout << "\n\n"; } int main() { auto uniform = generateUniformDistribution(100000, 0, 79, 40); plotDistribution("--- Uniform ---", uniform, 80); auto normal = generateNormalDistribution(100000, 50, 5, 40); plotDistribution("--- Normal ---", normal, 80); auto poisson = generatePoissonDistribution(100000, 6, 40); plotDistribution("--- Poisson ---", poisson, 80); test(); return 0; } std::vector generateUniformDistribution(std::uint32_t howMany, std::uint32_t min, std::uint32_t max, std::uint8_t numberBins) { std::uniform_int_distribution distribution(min, max); auto bins = createBins(min, max, numberBins); for (int i = 0; i < static_cast(howMany); i++) { float number = distribution(randomEngine); for(int i = 0; i <= numberBins; i++) { if(bins[i].maxValue >= number && bins[i].minValue <= number) bins[i].count++; } } return bins; } std::vector generateNormalDistribution(std::uint32_t howMany, float mean, float stdev, std::uint8_t numberBins) { int min = static_cast(mean - (4 * stdev)); int max = static_cast(mean + (4 * stdev) - 1); std::normal_distribution distribution(mean, stdev); auto bins = createBins(min, max, numberBins); for (int i = 0; i < static_cast(howMany); i++) { double number = distribution(randomEngine); int position = static_cast(number - min / ((max - min) / float(numberBins))); if (position > numberBins - 1) bins[numberBins - 1].count++; else if (position < 0) bins[0].count++; else bins[position].count++; } return bins; } std::vector generatePoissonDistribution(std::uint32_t howMany, std::uint8_t howOften, std::uint8_t numberBins) { std::poisson_distribution distribution(howOften); int min = 0; int max = numberBins - 1; auto bins = createBins(min, max, numberBins); for (int i = 0; i < static_cast(howMany); i++) { int number = distribution(randomEngine); if (number > numberBins - 1) bins[numberBins - 1].count++; else if (number < 0) bins[0].count++; else bins[number].count++; } return bins; } std::vector createBins(int min, int max, int binCount) { std::vector myVector; int difference = static_cast(float(max - min + 1) / binCount); for (int i = 0; i < binCount; i++) { DistributionPair dpair((min + i) * difference, (min + i) * difference + difference - 1); myVector.push_back(dpair); } return myVector; } void plotDistribution(std::string title, const std::vector& distribution, const std::uint8_t maxPlotLineSize) { std::cout << title << std::endl; auto max = 0; for (auto currentBin : distribution) { if (static_cast(currentBin.count) > static_cast(max)) max = currentBin.count; } for (auto currentBin : distribution) { int starCount = static_cast(currentBin.count / float(max) * maxPlotLineSize); std::string info = "[ " + std::to_string(currentBin.minValue) + ", " + std::to_string(currentBin.maxValue) + " ] : "; std::string starString; for (int i = 0; i < starCount; i++) starString += "*"; std::cout << std::setw(15) << info << starString; std::cout << std::endl; } }