|
| 1 | +// dgb think() Phase-1 desired-set walk BOUNDS conformance KAT. |
| 2 | +// |
| 3 | +// FENCED, additive (no production code touched this slice). Pins |
| 4 | +// src/impl/dgb/think_p1_walk_bounds.hpp against the p2pool data.py think() |
| 5 | +// Phase-1 oracle for the two pure decisions per unverified head: |
| 6 | +// walk_count = head_height if unrooted (no last) |
| 7 | +// = min(5, max(0, head_height - CHAIN_LENGTH)) otherwise |
| 8 | +// in_pruning_zone = head_height >= 2*CHAIN_LENGTH + 10 (inclusive) |
| 9 | +// |
| 10 | +// Expectations are HAND-DERIVED from the oracle formula, NOT read back from the |
| 11 | +// code under test — a conformance KAT that asks its subject for the answer |
| 12 | +// passes vacuously. Pure arithmetic: links only core (no chain standup). MUST |
| 13 | +// appear in BOTH this dir's CMakeLists.txt AND the build.yml --target allowlist, |
| 14 | +// or it becomes a #143 NOT_BUILT sentinel (compiled-out, silently "passing"). |
| 15 | + |
| 16 | +#include <impl/dgb/think_p1_walk_bounds.hpp> |
| 17 | + |
| 18 | +#include <gtest/gtest.h> |
| 19 | + |
| 20 | +#include <cstdint> |
| 21 | + |
| 22 | +namespace { |
| 23 | + |
| 24 | +// ---- walk_count: unrooted head (has_last == false) → full accumulated height |
| 25 | +TEST(ThinkP1WalkBounds, UnrootedWalksFullHeight) |
| 26 | +{ |
| 27 | + EXPECT_EQ(dgb::think_p1_walk_count(0, false, 10), 0); |
| 28 | + EXPECT_EQ(dgb::think_p1_walk_count(1, false, 10), 1); |
| 29 | + EXPECT_EQ(dgb::think_p1_walk_count(3, false, 10), 3); |
| 30 | + EXPECT_EQ(dgb::think_p1_walk_count(100, false, 10), 100); |
| 31 | + // chain_length must not influence the unrooted branch. |
| 32 | + EXPECT_EQ(dgb::think_p1_walk_count(7, false, 9999), 7); |
| 33 | +} |
| 34 | + |
| 35 | +// ---- walk_count: rooted head (has_last == true) → min(5, max(0, h - CL)) |
| 36 | +TEST(ThinkP1WalkBounds, RootedClampsAtFiveFloorsAtZero) |
| 37 | +{ |
| 38 | + constexpr int32_t CL = 10; |
| 39 | + // below/at CHAIN_LENGTH → floored to 0 |
| 40 | + EXPECT_EQ(dgb::think_p1_walk_count(8, true, CL), 0); // max(0,-2)=0 |
| 41 | + EXPECT_EQ(dgb::think_p1_walk_count(10, true, CL), 0); // max(0, 0)=0 (boundary) |
| 42 | + // between CL and CL+5 → exact difference |
| 43 | + EXPECT_EQ(dgb::think_p1_walk_count(11, true, CL), 1); // min(5,1)=1 |
| 44 | + EXPECT_EQ(dgb::think_p1_walk_count(12, true, CL), 2); // min(5,2)=2 |
| 45 | + EXPECT_EQ(dgb::think_p1_walk_count(15, true, CL), 5); // min(5,5)=5 (boundary) |
| 46 | + // above CL+5 → clamped to 5 |
| 47 | + EXPECT_EQ(dgb::think_p1_walk_count(16, true, CL), 5); // min(5,6)=5 |
| 48 | + EXPECT_EQ(dgb::think_p1_walk_count(20, true, CL), 5); // min(5,10)=5 |
| 49 | + EXPECT_EQ(dgb::think_p1_walk_count(999, true, CL), 5); |
| 50 | +} |
| 51 | + |
| 52 | +// ---- pruning zone: inclusive threshold 2*CL + 10 |
| 53 | +TEST(ThinkP1WalkBounds, PruningZoneInclusiveThreshold) |
| 54 | +{ |
| 55 | + constexpr int32_t CL = 10; // threshold = 2*10 + 10 = 30 |
| 56 | + EXPECT_FALSE(dgb::think_p1_in_pruning_zone(0, CL)); |
| 57 | + EXPECT_FALSE(dgb::think_p1_in_pruning_zone(29, CL)); |
| 58 | + EXPECT_TRUE (dgb::think_p1_in_pruning_zone(30, CL)); // inclusive lower bound |
| 59 | + EXPECT_TRUE (dgb::think_p1_in_pruning_zone(31, CL)); |
| 60 | + EXPECT_TRUE (dgb::think_p1_in_pruning_zone(1000, CL)); |
| 61 | + |
| 62 | + constexpr int32_t CL2 = 24; // threshold = 2*24 + 10 = 58 |
| 63 | + EXPECT_FALSE(dgb::think_p1_in_pruning_zone(57, CL2)); |
| 64 | + EXPECT_TRUE (dgb::think_p1_in_pruning_zone(58, CL2)); |
| 65 | +} |
| 66 | + |
| 67 | +} // namespace |
0 commit comments