diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/gen_perm_sort_relation.hpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/gen_perm_sort_relation.hpp index 03c413d9c9f0..ac25d184d231 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/gen_perm_sort_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/gen_perm_sort_relation.hpp @@ -93,6 +93,137 @@ template class GenPermSortRelationImpl { }; }; +template class GoblinTranslatorGenPermSortRelationImpl { + public: + using FF = FF_; + + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree((lagrange_last-1) * D(D - 1)(D - 2)(D - 3)) = 5 + + static constexpr std::array SUBRELATION_LENGTHS{ + 6, // ordered_range_constraints_0 step in {0,1,2,3} subrelation + 6, // ordered_range_constraints_1 step in {0,1,2,3} subrelation + 6, // ordered_range_constraints_2 step in {0,1,2,3} subrelation + 6, // ordered_range_constraints_3 step in {0,1,2,3} subrelation + 6, // ordered_range_constraints_4 step in {0,1,2,3} subrelation + 3, // ordered_range_constraints_0 ends with defined maximum value subrelation + 3, // ordered_range_constraints_1 ends with defined maximum value subrelation + 3, // ordered_range_constraints_2 ends with defined maximum value subrelation + 3, // ordered_range_constraints_3 ends with defined maximum value subrelation + 3 // ordered_range_constraints_4 ends with defined maximum value subrelation + + }; + + /** + * @brief Expression for the generalized permutation sort relation + * + * @details The relation enforces 2 constraints on each of the ordered_range_constraints wires: + * 1) 2 sequential values are non-descending and have a difference of at most 3, except for the value at last index + * 2) The value at last index is 2¹⁴ - 1 + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + template + inline static void accumulate(ContainerOverSubrelations& accumulators, + const AllEntities& in, + const RelationParameters&, + const FF& scaling_factor) + { + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + auto ordered_range_constraints_0 = View(in.ordered_range_constraints_0); + auto ordered_range_constraints_1 = View(in.ordered_range_constraints_1); + auto ordered_range_constraints_2 = View(in.ordered_range_constraints_2); + auto ordered_range_constraints_3 = View(in.ordered_range_constraints_3); + auto ordered_range_constraints_4 = View(in.ordered_range_constraints_4); + auto ordered_range_constraints_0_shift = View(in.ordered_range_constraints_0_shift); + auto ordered_range_constraints_1_shift = View(in.ordered_range_constraints_1_shift); + auto ordered_range_constraints_2_shift = View(in.ordered_range_constraints_2_shift); + auto ordered_range_constraints_3_shift = View(in.ordered_range_constraints_3_shift); + auto ordered_range_constraints_4_shift = View(in.ordered_range_constraints_4_shift); + auto lagrange_last = View(in.lagrange_last); + + static const FF minus_one = FF(-1); + static const FF minus_two = FF(-2); + static const FF minus_three = FF(-3); + static const size_t micro_limb_bits = 14; + static const auto maximum_sort_value = -FF((1 << micro_limb_bits) - 1); + // Compute wire differences + auto delta_1 = ordered_range_constraints_0_shift - ordered_range_constraints_0; + auto delta_2 = ordered_range_constraints_1_shift - ordered_range_constraints_1; + auto delta_3 = ordered_range_constraints_2_shift - ordered_range_constraints_2; + auto delta_4 = ordered_range_constraints_3_shift - ordered_range_constraints_3; + auto delta_5 = ordered_range_constraints_4_shift - ordered_range_constraints_4; + + // Contribution (1) (contributions 1-5 ensure that the sequential values have a difference of {0,1,2,3}) + auto tmp_1 = delta_1; + tmp_1 *= (delta_1 + minus_one); + tmp_1 *= (delta_1 + minus_two); + tmp_1 *= (delta_1 + minus_three); + tmp_1 *= (lagrange_last + minus_one); + tmp_1 *= scaling_factor; + std::get<0>(accumulators) += tmp_1; + + // Contribution (2) + auto tmp_2 = delta_2; + tmp_2 *= (delta_2 + minus_one); + tmp_2 *= (delta_2 + minus_two); + tmp_2 *= (delta_2 + minus_three); + tmp_2 *= (lagrange_last + minus_one); + tmp_2 *= scaling_factor; + + std::get<1>(accumulators) += tmp_2; + + // Contribution (3) + auto tmp_3 = delta_3; + tmp_3 *= (delta_3 + minus_one); + tmp_3 *= (delta_3 + minus_two); + tmp_3 *= (delta_3 + minus_three); + tmp_3 *= (lagrange_last + minus_one); + tmp_3 *= scaling_factor; + std::get<2>(accumulators) += tmp_3; + + // Contribution (4) + auto tmp_4 = delta_4; + tmp_4 *= (delta_4 + minus_one); + tmp_4 *= (delta_4 + minus_two); + tmp_4 *= (delta_4 + minus_three); + tmp_4 *= (lagrange_last + minus_one); + tmp_4 *= scaling_factor; + std::get<3>(accumulators) += tmp_4; + + // Contribution (5) + auto tmp_5 = delta_5; + tmp_5 *= (delta_5 + minus_one); + tmp_5 *= (delta_5 + minus_two); + tmp_5 *= (delta_5 + minus_three); + tmp_5 *= (lagrange_last + minus_one); + tmp_5 *= scaling_factor; + std::get<4>(accumulators) += tmp_5; + + // Contribution (6) (Contributions 6-10 ensure that the last value is the designated maximum value. We don't + // need to constrain the first value to be 0, because the shift mechanic does this for us) + std::get<5>(accumulators) += + lagrange_last * (ordered_range_constraints_0 + maximum_sort_value) * scaling_factor; + // Contribution (7) + std::get<6>(accumulators) += + lagrange_last * (ordered_range_constraints_1 + maximum_sort_value) * scaling_factor; + // Contribution (8) + std::get<7>(accumulators) += + lagrange_last * (ordered_range_constraints_2 + maximum_sort_value) * scaling_factor; + // Contribution (9) + std::get<8>(accumulators) += + lagrange_last * (ordered_range_constraints_3 + maximum_sort_value) * scaling_factor; + // Contribution (10) + std::get<9>(accumulators) += + lagrange_last * (ordered_range_constraints_4 + maximum_sort_value) * scaling_factor; + }; +}; template using GenPermSortRelation = Relation>; +template +using GoblinTranslatorGenPermSortRelation = Relation>; } // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp index 00727527b279..b9bb54f8f6f0 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp @@ -12,6 +12,7 @@ * */ #include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/proof_system/relations/gen_perm_sort_relation.hpp" #include "barretenberg/proof_system/relations/permutation_relation.hpp" #include @@ -73,50 +74,50 @@ struct InputElements { FF& p_y_high_limbs_range_constraint_3 = std::get<29>(this->_data); FF& p_y_high_limbs_range_constraint_4 = std::get<30>(this->_data); FF& p_y_high_limbs_range_constraint_tail = std::get<31>(this->_data); - FF& z_lo_limbs = std::get<32>(this->_data); - FF& z_lo_limbs_range_constraint_0 = std::get<33>(this->_data); - FF& z_lo_limbs_range_constraint_1 = std::get<34>(this->_data); - FF& z_lo_limbs_range_constraint_2 = std::get<35>(this->_data); - FF& z_lo_limbs_range_constraint_3 = std::get<36>(this->_data); - FF& z_lo_limbs_range_constraint_4 = std::get<37>(this->_data); - FF& z_lo_limbs_range_constraint_tail = std::get<38>(this->_data); - FF& z_hi_limbs = std::get<39>(this->_data); - FF& z_hi_limbs_range_constraint_0 = std::get<40>(this->_data); - FF& z_hi_limbs_range_constraint_1 = std::get<41>(this->_data); - FF& z_hi_limbs_range_constraint_2 = std::get<42>(this->_data); - FF& z_hi_limbs_range_constraint_3 = std::get<43>(this->_data); - FF& z_hi_limbs_range_constraint_4 = std::get<44>(this->_data); - FF& z_hi_limbs_range_constraint_tail = std::get<45>(this->_data); + FF& z_low_limbs = std::get<32>(this->_data); + FF& z_low_limbs_range_constraint_0 = std::get<33>(this->_data); + FF& z_low_limbs_range_constraint_1 = std::get<34>(this->_data); + FF& z_low_limbs_range_constraint_2 = std::get<35>(this->_data); + FF& z_low_limbs_range_constraint_3 = std::get<36>(this->_data); + FF& z_low_limbs_range_constraint_4 = std::get<37>(this->_data); + FF& z_low_limbs_range_constraint_tail = std::get<38>(this->_data); + FF& z_high_limbs = std::get<39>(this->_data); + FF& z_high_limbs_range_constraint_0 = std::get<40>(this->_data); + FF& z_high_limbs_range_constraint_1 = std::get<41>(this->_data); + FF& z_high_limbs_range_constraint_2 = std::get<42>(this->_data); + FF& z_high_limbs_range_constraint_3 = std::get<43>(this->_data); + FF& z_high_limbs_range_constraint_4 = std::get<44>(this->_data); + FF& z_high_limbs_range_constraint_tail = std::get<45>(this->_data); FF& accumulators_binary_limbs_0 = std::get<46>(this->_data); FF& accumulators_binary_limbs_1 = std::get<47>(this->_data); FF& accumulators_binary_limbs_2 = std::get<48>(this->_data); FF& accumulators_binary_limbs_3 = std::get<49>(this->_data); - FF& accumulator_lo_limbs_range_constraint_0 = std::get<50>(this->_data); - FF& accumulator_lo_limbs_range_constraint_1 = std::get<51>(this->_data); - FF& accumulator_lo_limbs_range_constraint_2 = std::get<52>(this->_data); - FF& accumulator_lo_limbs_range_constraint_3 = std::get<53>(this->_data); - FF& accumulator_lo_limbs_range_constraint_4 = std::get<54>(this->_data); - FF& accumulator_lo_limbs_range_constraint_tail = std::get<55>(this->_data); - FF& accumulator_hi_limbs_range_constraint_0 = std::get<56>(this->_data); - FF& accumulator_hi_limbs_range_constraint_1 = std::get<57>(this->_data); - FF& accumulator_hi_limbs_range_constraint_2 = std::get<58>(this->_data); - FF& accumulator_hi_limbs_range_constraint_3 = std::get<59>(this->_data); - FF& accumulator_hi_limbs_range_constraint_4 = std::get<60>(this->_data); - FF& accumulator_hi_limbs_range_constraint_tail = std::get<61>(this->_data); - FF& quotient_lo_binary_limbs = std::get<62>(this->_data); - FF& quotient_hi_binary_limbs = std::get<63>(this->_data); - FF& quotient_lo_limbs_range_constraint_0 = std::get<64>(this->_data); - FF& quotient_lo_limbs_range_constraint_1 = std::get<65>(this->_data); - FF& quotient_lo_limbs_range_constraint_2 = std::get<66>(this->_data); - FF& quotient_lo_limbs_range_constraint_3 = std::get<67>(this->_data); - FF& quotient_lo_limbs_range_constraint_4 = std::get<68>(this->_data); - FF& quotient_lo_limbs_range_constraint_tail = std::get<69>(this->_data); - FF& quotient_hi_limbs_range_constraint_0 = std::get<70>(this->_data); - FF& quotient_hi_limbs_range_constraint_1 = std::get<71>(this->_data); - FF& quotient_hi_limbs_range_constraint_2 = std::get<72>(this->_data); - FF& quotient_hi_limbs_range_constraint_3 = std::get<73>(this->_data); - FF& quotient_hi_limbs_range_constraint_4 = std::get<74>(this->_data); - FF& quotient_hi_limbs_range_constraint_tail = std::get<75>(this->_data); + FF& accumulator_low_limbs_range_constraint_0 = std::get<50>(this->_data); + FF& accumulator_low_limbs_range_constraint_1 = std::get<51>(this->_data); + FF& accumulator_low_limbs_range_constraint_2 = std::get<52>(this->_data); + FF& accumulator_low_limbs_range_constraint_3 = std::get<53>(this->_data); + FF& accumulator_low_limbs_range_constraint_4 = std::get<54>(this->_data); + FF& accumulator_low_limbs_range_constraint_tail = std::get<55>(this->_data); + FF& accumulator_high_limbs_range_constraint_0 = std::get<56>(this->_data); + FF& accumulator_high_limbs_range_constraint_1 = std::get<57>(this->_data); + FF& accumulator_high_limbs_range_constraint_2 = std::get<58>(this->_data); + FF& accumulator_high_limbs_range_constraint_3 = std::get<59>(this->_data); + FF& accumulator_high_limbs_range_constraint_4 = std::get<60>(this->_data); + FF& accumulator_high_limbs_range_constraint_tail = std::get<61>(this->_data); + FF& quotient_low_binary_limbs = std::get<62>(this->_data); + FF& quotient_high_binary_limbs = std::get<63>(this->_data); + FF& quotient_low_limbs_range_constraint_0 = std::get<64>(this->_data); + FF& quotient_low_limbs_range_constraint_1 = std::get<65>(this->_data); + FF& quotient_low_limbs_range_constraint_2 = std::get<66>(this->_data); + FF& quotient_low_limbs_range_constraint_3 = std::get<67>(this->_data); + FF& quotient_low_limbs_range_constraint_4 = std::get<68>(this->_data); + FF& quotient_low_limbs_range_constraint_tail = std::get<69>(this->_data); + FF& quotient_high_limbs_range_constraint_0 = std::get<70>(this->_data); + FF& quotient_high_limbs_range_constraint_1 = std::get<71>(this->_data); + FF& quotient_high_limbs_range_constraint_2 = std::get<72>(this->_data); + FF& quotient_high_limbs_range_constraint_3 = std::get<73>(this->_data); + FF& quotient_high_limbs_range_constraint_4 = std::get<74>(this->_data); + FF& quotient_high_limbs_range_constraint_tail = std::get<75>(this->_data); FF& relation_wide_limbs = std::get<76>(this->_data); FF& relation_wide_limbs_range_constraint_0 = std::get<77>(this->_data); FF& relation_wide_limbs_range_constraint_1 = std::get<78>(this->_data); @@ -163,50 +164,50 @@ struct InputElements { FF& p_y_high_limbs_range_constraint_3_shift = std::get<119>(this->_data); FF& p_y_high_limbs_range_constraint_4_shift = std::get<120>(this->_data); FF& p_y_high_limbs_range_constraint_tail_shift = std::get<121>(this->_data); - FF& z_lo_limbs_shift = std::get<122>(this->_data); - FF& z_lo_limbs_range_constraint_0_shift = std::get<123>(this->_data); - FF& z_lo_limbs_range_constraint_1_shift = std::get<124>(this->_data); - FF& z_lo_limbs_range_constraint_2_shift = std::get<125>(this->_data); - FF& z_lo_limbs_range_constraint_3_shift = std::get<126>(this->_data); - FF& z_lo_limbs_range_constraint_4_shift = std::get<127>(this->_data); - FF& z_lo_limbs_range_constraint_tail_shift = std::get<128>(this->_data); - FF& z_hi_limbs_shift = std::get<129>(this->_data); - FF& z_hi_limbs_range_constraint_0_shift = std::get<130>(this->_data); - FF& z_hi_limbs_range_constraint_1_shift = std::get<131>(this->_data); - FF& z_hi_limbs_range_constraint_2_shift = std::get<132>(this->_data); - FF& z_hi_limbs_range_constraint_3_shift = std::get<133>(this->_data); - FF& z_hi_limbs_range_constraint_4_shift = std::get<134>(this->_data); - FF& z_hi_limbs_range_constraint_tail_shift = std::get<135>(this->_data); + FF& z_low_limbs_shift = std::get<122>(this->_data); + FF& z_low_limbs_range_constraint_0_shift = std::get<123>(this->_data); + FF& z_low_limbs_range_constraint_1_shift = std::get<124>(this->_data); + FF& z_low_limbs_range_constraint_2_shift = std::get<125>(this->_data); + FF& z_low_limbs_range_constraint_3_shift = std::get<126>(this->_data); + FF& z_low_limbs_range_constraint_4_shift = std::get<127>(this->_data); + FF& z_low_limbs_range_constraint_tail_shift = std::get<128>(this->_data); + FF& z_high_limbs_shift = std::get<129>(this->_data); + FF& z_high_limbs_range_constraint_0_shift = std::get<130>(this->_data); + FF& z_high_limbs_range_constraint_1_shift = std::get<131>(this->_data); + FF& z_high_limbs_range_constraint_2_shift = std::get<132>(this->_data); + FF& z_high_limbs_range_constraint_3_shift = std::get<133>(this->_data); + FF& z_high_limbs_range_constraint_4_shift = std::get<134>(this->_data); + FF& z_high_limbs_range_constraint_tail_shift = std::get<135>(this->_data); FF& accumulators_binary_limbs_0_shift = std::get<136>(this->_data); FF& accumulators_binary_limbs_1_shift = std::get<137>(this->_data); FF& accumulators_binary_limbs_2_shift = std::get<138>(this->_data); FF& accumulators_binary_limbs_3_shift = std::get<139>(this->_data); - FF& accumulator_lo_limbs_range_constraint_0_shift = std::get<140>(this->_data); - FF& accumulator_lo_limbs_range_constraint_1_shift = std::get<141>(this->_data); - FF& accumulator_lo_limbs_range_constraint_2_shift = std::get<142>(this->_data); - FF& accumulator_lo_limbs_range_constraint_3_shift = std::get<143>(this->_data); - FF& accumulator_lo_limbs_range_constraint_4_shift = std::get<144>(this->_data); - FF& accumulator_lo_limbs_range_constraint_tail_shift = std::get<145>(this->_data); - FF& accumulator_hi_limbs_range_constraint_0_shift = std::get<146>(this->_data); - FF& accumulator_hi_limbs_range_constraint_1_shift = std::get<147>(this->_data); - FF& accumulator_hi_limbs_range_constraint_2_shift = std::get<148>(this->_data); - FF& accumulator_hi_limbs_range_constraint_3_shift = std::get<149>(this->_data); - FF& accumulator_hi_limbs_range_constraint_4_shift = std::get<150>(this->_data); - FF& accumulator_hi_limbs_range_constraint_tail_shift = std::get<151>(this->_data); - FF& quotient_lo_binary_limbs_shift = std::get<152>(this->_data); - FF& quotient_hi_binary_limbs_shift = std::get<153>(this->_data); - FF& quotient_lo_limbs_range_constraint_0_shift = std::get<154>(this->_data); - FF& quotient_lo_limbs_range_constraint_1_shift = std::get<155>(this->_data); - FF& quotient_lo_limbs_range_constraint_2_shift = std::get<156>(this->_data); - FF& quotient_lo_limbs_range_constraint_3_shift = std::get<157>(this->_data); - FF& quotient_lo_limbs_range_constraint_4_shift = std::get<158>(this->_data); - FF& quotient_lo_limbs_range_constraint_tail_shift = std::get<159>(this->_data); - FF& quotient_hi_limbs_range_constraint_0_shift = std::get<160>(this->_data); - FF& quotient_hi_limbs_range_constraint_1_shift = std::get<161>(this->_data); - FF& quotient_hi_limbs_range_constraint_2_shift = std::get<162>(this->_data); - FF& quotient_hi_limbs_range_constraint_3_shift = std::get<163>(this->_data); - FF& quotient_hi_limbs_range_constraint_4_shift = std::get<164>(this->_data); - FF& quotient_hi_limbs_range_constraint_tail_shift = std::get<165>(this->_data); + FF& accumulator_low_limbs_range_constraint_0_shift = std::get<140>(this->_data); + FF& accumulator_low_limbs_range_constraint_1_shift = std::get<141>(this->_data); + FF& accumulator_low_limbs_range_constraint_2_shift = std::get<142>(this->_data); + FF& accumulator_low_limbs_range_constraint_3_shift = std::get<143>(this->_data); + FF& accumulator_low_limbs_range_constraint_4_shift = std::get<144>(this->_data); + FF& accumulator_low_limbs_range_constraint_tail_shift = std::get<145>(this->_data); + FF& accumulator_high_limbs_range_constraint_0_shift = std::get<146>(this->_data); + FF& accumulator_high_limbs_range_constraint_1_shift = std::get<147>(this->_data); + FF& accumulator_high_limbs_range_constraint_2_shift = std::get<148>(this->_data); + FF& accumulator_high_limbs_range_constraint_3_shift = std::get<149>(this->_data); + FF& accumulator_high_limbs_range_constraint_4_shift = std::get<150>(this->_data); + FF& accumulator_high_limbs_range_constraint_tail_shift = std::get<151>(this->_data); + FF& quotient_low_binary_limbs_shift = std::get<152>(this->_data); + FF& quotient_high_binary_limbs_shift = std::get<153>(this->_data); + FF& quotient_low_limbs_range_constraint_0_shift = std::get<154>(this->_data); + FF& quotient_low_limbs_range_constraint_1_shift = std::get<155>(this->_data); + FF& quotient_low_limbs_range_constraint_2_shift = std::get<156>(this->_data); + FF& quotient_low_limbs_range_constraint_3_shift = std::get<157>(this->_data); + FF& quotient_low_limbs_range_constraint_4_shift = std::get<158>(this->_data); + FF& quotient_low_limbs_range_constraint_tail_shift = std::get<159>(this->_data); + FF& quotient_high_limbs_range_constraint_0_shift = std::get<160>(this->_data); + FF& quotient_high_limbs_range_constraint_1_shift = std::get<161>(this->_data); + FF& quotient_high_limbs_range_constraint_2_shift = std::get<162>(this->_data); + FF& quotient_high_limbs_range_constraint_3_shift = std::get<163>(this->_data); + FF& quotient_high_limbs_range_constraint_4_shift = std::get<164>(this->_data); + FF& quotient_high_limbs_range_constraint_tail_shift = std::get<165>(this->_data); FF& relation_wide_limbs_shift = std::get<166>(this->_data); FF& relation_wide_limbs_range_constraint_0_shift = std::get<167>(this->_data); FF& relation_wide_limbs_range_constraint_1_shift = std::get<168>(this->_data); @@ -289,4 +290,68 @@ TEST_F(GoblinTranslatorRelationConsistency, PermutationRelation) run_test(/*random_inputs=*/true); }; +TEST_F(GoblinTranslatorRelationConsistency, GenPermSortRelation) +{ + const auto run_test = [](bool random_inputs) { + using Relation = GoblinTranslatorGenPermSortRelation; + using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + + const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); + + const auto& ordered_range_constraints_0 = input_elements.ordered_range_constraints_0; + const auto& ordered_range_constraints_1 = input_elements.ordered_range_constraints_1; + const auto& ordered_range_constraints_2 = input_elements.ordered_range_constraints_2; + const auto& ordered_range_constraints_3 = input_elements.ordered_range_constraints_3; + const auto& ordered_range_constraints_4 = input_elements.ordered_range_constraints_4; + const auto& ordered_range_constraints_0_shift = input_elements.ordered_range_constraints_0_shift; + const auto& ordered_range_constraints_1_shift = input_elements.ordered_range_constraints_1_shift; + const auto& ordered_range_constraints_2_shift = input_elements.ordered_range_constraints_2_shift; + const auto& ordered_range_constraints_3_shift = input_elements.ordered_range_constraints_3_shift; + const auto& ordered_range_constraints_4_shift = input_elements.ordered_range_constraints_4_shift; + const auto& lagrange_last = input_elements.lagrange_last; + + RelationValues expected_values; + + const auto parameters = RelationParameters::get_random(); + + const size_t MICRO_LIMB_BITS = 14; + const auto minus_one = FF(-1); + const auto minus_two = FF(-2); + const auto minus_three = FF(-3); + const auto maximum_value = -FF((1 << MICRO_LIMB_BITS) - 1); + + // First compute individual deltas + const auto delta_1 = ordered_range_constraints_0_shift - ordered_range_constraints_0; + const auto delta_2 = ordered_range_constraints_1_shift - ordered_range_constraints_1; + const auto delta_3 = ordered_range_constraints_2_shift - ordered_range_constraints_2; + const auto delta_4 = ordered_range_constraints_3_shift - ordered_range_constraints_3; + const auto delta_5 = ordered_range_constraints_4_shift - ordered_range_constraints_4; + + const auto not_last = lagrange_last + minus_one; + + // Check the delta is {0,1,2,3} + auto delta_in_range = [not_last, minus_one, minus_two, minus_three](auto delta) { + return not_last * delta * (delta + minus_one) * (delta + minus_two) * (delta + minus_three); + }; + + // Check delta correctness + expected_values[0] = delta_in_range(delta_1); + expected_values[1] = delta_in_range(delta_2); + expected_values[2] = delta_in_range(delta_3); + expected_values[3] = delta_in_range(delta_4); + expected_values[4] = delta_in_range(delta_5); + // Check that the last value is maximum allowed + expected_values[5] = lagrange_last * (ordered_range_constraints_0 + maximum_value); + expected_values[6] = lagrange_last * (ordered_range_constraints_1 + maximum_value); + expected_values[7] = lagrange_last * (ordered_range_constraints_2 + maximum_value); + expected_values[8] = lagrange_last * (ordered_range_constraints_3 + maximum_value); + expected_values[9] = lagrange_last * (ordered_range_constraints_4 + maximum_value); + // We don't check that the first value is zero, because the shift mechanism already ensures it + + validate_relation_execution(expected_values, input_elements, parameters); + }; + run_test(/*random_inputs=*/false); + run_test(/*random_inputs=*/true); +}; + } // namespace proof_system::ultra_relation_consistency_tests