62 lines
1.6 KiB
C++
62 lines
1.6 KiB
C++
|
#include <cruft/util/geom/sample/edge.hpp>
|
||
|
#include <cruft/util/tap.hpp>
|
||
|
#include <cruft/util/coord/iostream.hpp>
|
||
|
#include <cruft/util/coord/comparator.hpp>
|
||
|
|
||
|
#include <map>
|
||
|
#include <iostream>
|
||
|
|
||
|
int main ()
|
||
|
{
|
||
|
static constexpr int ITERATIONS = 1'000'000;
|
||
|
static const cruft::extent2i AREA { 3, 7 };
|
||
|
|
||
|
std::map<cruft::point2i, int, cruft::coord::ordering<>> counts;
|
||
|
|
||
|
auto sampler = cruft::geom::sample::edge (AREA);
|
||
|
std::mt19937_64 gen;
|
||
|
|
||
|
cruft::TAP::logger tap;
|
||
|
|
||
|
for (int i = 0; i < ITERATIONS; ++i) {
|
||
|
auto const pos = sampler.eval (gen);
|
||
|
|
||
|
bool const x_perim = pos.x == 0 || pos.x == AREA.w;
|
||
|
bool const y_perim = pos.y == 0 || pos.y == AREA.h;
|
||
|
|
||
|
if (!x_perim && !y_perim) {
|
||
|
tap.fail ("position %! falls outside perimeter: %!", i, pos);
|
||
|
return tap.status ();
|
||
|
}
|
||
|
|
||
|
auto const [val, success] = counts.try_emplace (pos, 0);
|
||
|
val->second++;
|
||
|
}
|
||
|
|
||
|
auto const expected_count = 2 * (AREA[0] + 1) + 2 * (AREA[1] - 1);
|
||
|
tap.expect_eq (
|
||
|
counts.size (),
|
||
|
std::size_t (expected_count),
|
||
|
"all perimeter options are covered"
|
||
|
);
|
||
|
|
||
|
auto const [lo,hi] = std::minmax_element (
|
||
|
std::begin (counts),
|
||
|
std::end (counts),
|
||
|
[] (auto const &a, auto const &b)
|
||
|
{
|
||
|
return a.second < b.second;
|
||
|
});
|
||
|
|
||
|
auto const abs_diff = hi->second - lo->second;
|
||
|
auto const rel_diff = float (abs_diff) / lo->second;
|
||
|
|
||
|
tap.expect_lt (
|
||
|
std::abs (rel_diff), 0.05f,
|
||
|
"occurence counts are evenly spaced: %!:%! vs %!:%!",
|
||
|
hi->first, hi->second,
|
||
|
lo->first, lo->second
|
||
|
);
|
||
|
|
||
|
return tap.status ();
|
||
|
}
|