|
4 | 4 |
|
5 | 5 | #include <gtest/gtest.h> |
6 | 6 | #include <unordered_map> |
| 7 | +#include <unordered_set> |
7 | 8 |
|
8 | 9 | using namespace bb; |
9 | 10 |
|
@@ -121,6 +122,47 @@ TEST_F(UltraCircuitBuilderLookup, DifferentTablesGetUniqueIndices) |
121 | 122 | EXPECT_EQ(builder.get_num_lookup_tables(), 3UL); |
122 | 123 | } |
123 | 124 |
|
| 125 | +// Every basic table reachable through any MultiTable must receive a unique, positive circuit-local |
| 126 | +// table_index. The LogDeriv lookup relation identifies a table solely by table_index (not BasicTableId), |
| 127 | +// so a generator that stores anything other than the builder-assigned index silently collapses distinct tables to one |
| 128 | +// identity. This sweeps the whole table space so any such generator is caught. |
| 129 | +TEST_F(UltraCircuitBuilderLookup, AllMultiTableBasicTablesGetUniquePositiveIndices) |
| 130 | +{ |
| 131 | + Builder builder; |
| 132 | + for (size_t mt = 0; mt < static_cast<size_t>(plookup::MultiTableId::NUM_MULTI_TABLES); ++mt) { |
| 133 | + const auto& multitable = plookup::get_multitable(static_cast<plookup::MultiTableId>(mt)); |
| 134 | + for (const auto id : multitable.basic_table_ids) { |
| 135 | + builder.get_table(id); |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + std::unordered_set<size_t> seen; |
| 140 | + for (const auto& table : builder.get_lookup_tables()) { |
| 141 | + EXPECT_GT(table.table_index, 0UL) << "non-positive index for basic table id " << static_cast<size_t>(table.id); |
| 142 | + EXPECT_TRUE(seen.insert(table.table_index).second) |
| 143 | + << "duplicate table_index " << table.table_index << " for basic table id " << static_cast<size_t>(table.id); |
| 144 | + } |
| 145 | + EXPECT_NO_THROW(builder.finalize_circuit()); |
| 146 | +} |
| 147 | + |
| 148 | +TEST_F(UltraCircuitBuilderLookup, FinalizationRejectsDuplicateTableIndices) |
| 149 | +{ |
| 150 | + Builder builder; |
| 151 | + builder.get_table(plookup::BasicTableId::UINT_XOR_SLICE_6_ROTATE_0); |
| 152 | + builder.get_table(plookup::BasicTableId::UINT_AND_SLICE_6_ROTATE_0); |
| 153 | + builder.get_lookup_tables()[1].table_index = builder.get_lookup_tables()[0].table_index; |
| 154 | + |
| 155 | + EXPECT_THROW_OR_ABORT(builder.finalize_circuit(), "Lookup table indices must be unique within a circuit"); |
| 156 | +} |
| 157 | + |
| 158 | +TEST_F(UltraCircuitBuilderLookup, FinalizationRejectsZeroTableIndex) |
| 159 | +{ |
| 160 | + Builder builder; |
| 161 | + builder.get_table(plookup::BasicTableId::UINT_XOR_SLICE_6_ROTATE_0).table_index = 0; |
| 162 | + |
| 163 | + EXPECT_THROW_OR_ABORT(builder.finalize_circuit(), "Lookup table indices must be positive"); |
| 164 | +} |
| 165 | + |
124 | 166 | // Verifies correct behavior when key_b_index is not provided (2-to-1 lookup without second index) |
125 | 167 | TEST_F(UltraCircuitBuilderLookup, NoKeyBIndex) |
126 | 168 | { |
|
0 commit comments