#include "Decimate.h" #include #include #include using namespace udpscope; TEST(MinMaxDecimate, PassesShortInputThroughUnchanged) { const std::vector t{0.0, 1.0, 2.0}; const std::vector v{5.0, 6.0, 7.0}; Series out; MinMaxDecimate(t.data(), v.data(), t.size(), 100, out); EXPECT_EQ(out.t, t); EXPECT_EQ(out.v, v); } // The whole reason for preferring min/max over LTTB: a single-sample spike is // usually the thing the user is looking for, and it must survive decimation. TEST(MinMaxDecimate, PreservesAnIsolatedSpike) { std::vector t(1000), v(1000, 0.0); for (size_t i = 0; i < t.size(); i++) { t[i] = static_cast(i); } v[437] = 42.0; Series out; MinMaxDecimate(t.data(), v.data(), t.size(), 50, out); ASSERT_FALSE(out.v.empty()); EXPECT_EQ(*std::max_element(out.v.begin(), out.v.end()), 42.0); } TEST(MinMaxDecimate, PreservesTheExtremesOfEveryBucket) { std::vector t(100), v(100); for (size_t i = 0; i < t.size(); i++) { t[i] = static_cast(i); v[i] = (i % 10 == 3) ? -9.0 : ((i % 10 == 7) ? 9.0 : 0.0); } Series out; MinMaxDecimate(t.data(), v.data(), t.size(), 20, out); EXPECT_EQ(*std::min_element(out.v.begin(), out.v.end()), -9.0); EXPECT_EQ(*std::max_element(out.v.begin(), out.v.end()), 9.0); } // A ring whose timestamps are not monotonic breaks any later binary search by // time, so the pair emitted per bucket must be ordered by time, not by value. TEST(MinMaxDecimate, EmitsPointsInTimeOrder) { // Two buckets of four. In the first the minimum comes before the maximum, // in the second the order is reversed. An implementation that emitted // (min, max) by value rather than by time passes on bucket 0 and fails on // bucket 1, so this data exercises the swap that a monotonically growing // ramp never triggers. const double st[8] = {0, 1, 2, 3, 4, 5, 6, 7}; const double sv[8] = {-5, 0, 0, 9, 9, 0, 0, -5}; Series pair; MinMaxDecimate(st, sv, 8, 4, pair); ASSERT_EQ(pair.size(), 4u); const double wantT[4] = {0, 3, 4, 7}; const double wantV[4] = {-5, 9, 9, -5}; for (size_t i = 0; i < 4; i++) { EXPECT_EQ(pair.t[i], wantT[i]) << "time at " << i; EXPECT_EQ(pair.v[i], wantV[i]) << "value at " << i; } std::vector t(400), v(400); for (size_t i = 0; i < t.size(); i++) { t[i] = static_cast(i); v[i] = (i % 2 == 0) ? -static_cast(i) : static_cast(i); } Series out; MinMaxDecimate(t.data(), v.data(), t.size(), 40, out); ASSERT_GT(out.t.size(), 1u); for (size_t i = 1; i < out.t.size(); i++) { EXPECT_LE(out.t[i - 1], out.t[i]) << "at index " << i; } } TEST(MinMaxDecimate, HandlesEmptyInput) { Series out; out.t.push_back(1.0); // must be cleared MinMaxDecimate(nullptr, nullptr, 0, 10, out); EXPECT_TRUE(out.t.empty()); EXPECT_TRUE(out.v.empty()); }