diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp new file mode 100644 index 000000000..201834aec --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp @@ -0,0 +1,84 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^4 elements over an mnt4 entity (to be used with constraints) +// with F_p^4 = Fp^2 over Fp^2: +// Fp^4 = Fp^2[x]/(x^2 + u), u = (0,1) +// Fp^2 = Fp[y]/(y^2 + v), v = 17 (0x11), u^2 = v +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class mnt4_fp4_element { + public: + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr mnt4_fp4_element operator*(mnt4_fp4_element const& y) { + + //crypto3::algebra::curves::mnt4<298>::..?..::non_residue; + constexpr auto s = 0x11; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b01]*y[0b10] + x[0b00]*y[0b11] + x[0b10]*y[0b10] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr mnt4_fp4_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a }; + } + friend mnt4_fp4_element operator*(const int a, mnt4_fp4_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a }; + } + constexpr mnt4_fp4_element operator+(mnt4_fp4_element const& y) { + return { x[0] + y.x[0], x[1] + y.x[1], x[2] + y.x[2] }; + } + constexpr mnt4_fp4_element operator-(mnt4_fp4_element const& y) { + return { x[0] - y.x[0], x[1] - y.x[1], x[2] - y.x[2] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp new file mode 100644 index 000000000..8f7ea7fca --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -0,0 +1,493 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{4} raising to a fixed power +// t, which is a parameter of the component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // + // Component for raising to a fixed power t in F_p^4 + // Input: x[4] + // Output: y[4]: y = x^t as elements of F_p^4 + // + + template + class mnt4_fp4_fixed_power; + + template + class mnt4_fp4_fixed_power> + : public plonk_component + { + public: + using component_type = plonk_component; + using var = typename component_type::var; + + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type = crypto3::algebra::fields::fp4; + using integral_type = typename policy_type::extension_policy::integral_type; + using fp4_element = typename policy_type::value_type; + using fp4_constraint = detail::mnt4_fp4_element; + + private: + static std::vector base4(integral_type x) { + if (x > 0) { + std::vector res = {std::uint8_t(x % 4)}; + x /= 4; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % 4)); + x /= 4; + } + return res; + } else { + return {0}; + } + } + + static std::size_t gates_amount_internal(integral_type power) { + std::size_t gates = 1; // at least one for power-4 operations + std::vector exp_plan = base4(power); + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + gates++; // a multiplication gate + } + gates += (std::count(exp_plan.begin(),exp_plan.end(),3) > 0); // a cubing gate + gates += (std::count(exp_plan.begin(),exp_plan.end(),2) > 0); // a squaring gate + + return gates; + } + + public: + using manifest_type = plonk_component_manifest; + + const integral_type power; + + const std::vector exp_plan, exp_precompute; + const std::size_t rows_amount; + + class gate_manifest_type : public component_gate_manifest { + std::array gates_footprint(integral_type power) const { + std::vector exp_plan = base4(power); + return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + const integral_type power; + gate_manifest_type(integral_type power) : power(power) {} + + std::uint32_t gates_amount() const override { + return mnt4_fp4_fixed_power::gates_amount_internal(power); + } + + bool operator<(const component_gate_manifest *other) const override { + integral_type o_power = dynamic_cast(other)->power; + + std::array + gates = gates_footprint(power), + o_gates = gates_footprint(o_power); + return (gates < o_gates); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(policy_type::arity)), // ?? + false + ); + return manifest; + } + + static std::vector get_precomputed_exps(const std::vector exps) + { + std::vector precompute = {exps[0]}; + if ((exps[0] != 3) && (std::count(exps.begin(),exps.end(),3) > 0)) { + precompute.insert(precompute.begin(),3); + } + if ((exps[0] != 2) && (std::count(exps.begin(),exps.end(),2) > 0)) { + precompute.insert(precompute.begin(),2); + } + return precompute; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + std::vector + exp_plan = base4(power), + exp_precompute = get_precomputed_exps(exp_plan); + + std::size_t rows = 0; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + rows += 1 + (exp_precompute[i] > 1); + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + rows += 1 + 2*(exp_plan[i] > 0); + } + return rows; + } + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt4_fp4_fixed_power const& component, std::uint32_t start_row_index) + { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < output.size(); i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() + { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template explicit + mnt4_fp4_fixed_power( + ContainerType witness, + integral_type power) : + component_type(witness, {}, {}, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + template + mnt4_fp4_fixed_power( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + integral_type power) : + component_type(witness, constant, public_input, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + mnt4_fp4_fixed_power( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs, + integral_type power) : + component_type(witnesses, constants, public_inputs, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + }; + + /* */ + + template + typename mnt4_fp4_fixed_power::result_type + generate_assignments( + mnt4_fp4_fixed_power const& component, + assignment> const& assignment, + typename mnt4_fp4_fixed_power::input_type const& instance_input, + const std::uint32_t start_row_index) + { + + using value_type = typename BlueprintFieldType::value_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::array::policy_type::arity> x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using fp4_element = typename mnt4_fp4_fixed_power::fp4_element; + fp4_element X = fp4_element({ {x[0],x[1]}, {x[2],x[3]},}), Y = X; + + std::size_t row = 0; + + auto fill_row = [&component, &assignment, &start_row_index, &row](fp4_element V) { + for(std::size_t i = 0; i < V.arity; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/2].data[i % 2]; + } + row++; + }; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + fill_row(X); // X + if (exp_precompute[i] > 1) { + fill_row(X.pow(exp_precompute[i])); + } + } + + Y = X.pow(exp_plan[0]); + for(std::size_t i = 1; i < exp_plan.size(); i++) { + Y = Y.pow(4); fill_row(Y); // every step includes a power-4 operation + if (exp_plan[i] > 0) { // for every non-zero digit we need a multiplication too + fill_row(X.pow(exp_plan[i])); + Y = Y * X.pow(exp_plan[i]); fill_row(Y); + } + } + + return typename mnt4_fp4_fixed_power::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + mnt4_fp4_fixed_power const& component, + circuit> & bp, + assignment> const& assignment, + typename mnt4_fp4_fixed_power::input_type const& instance_input) + { + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::vector gate_list = {}; // at most 4 gate ids + + + using policy_type = typename mnt4_fp4_fixed_power::policy_type; + typename mnt4_fp4_fixed_power::fp4_constraint X, Y, Z, C; + + // power-4 gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + using constraint_type = typename mnt4_fp4_fixed_power::constraint_type; + + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + // at least one digit besides the first is non-zero + // => we need a multiplication gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),3)) { + // we need a cubing gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),2)) { + // we need a squaring gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + } + return gate_list; + } + + template + void generate_copy_constraints( + mnt4_fp4_fixed_power const& component, + circuit> &bp, + assignment> &assignment, + const typename mnt4_fp4_fixed_power::input_type &instance_input, + const std::size_t start_row_index) + { + using var = typename mnt4_fp4_fixed_power::var; + using policy_type = typename mnt4_fp4_fixed_power::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + // for storing relative ids of rows where x^0, x^1, x^2 and x^3 are stored + std::array row_of_power = {0,1,0,0}; + + for(std::size_t small_power = 3; small_power > 0; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + // this gives a wrong value for small_power = 1, but it is coherent with the next part + row_of_power[small_power] = 2*i + 1; + } + } + } + } + + // copies of initial data + for(std::size_t j = 1; j < 4; j++) { + if (row_of_power[j] > 0) { // => we need a copy of initial data before row_of_power[j] + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + row_of_power[j]-1, false), instance_input.x[i]}); + } + } + } + row_of_power[1] = 0; // from now on we need the real row number for where x^1 is stored + + std::size_t row = 0; + for(std::size_t j = 0; j < exp_precompute.size(); j++) { + row += 1 + (exp_precompute[j] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t j = 1; j < exp_plan.size(); j++) { + row++; // skip the power-4 value + if (exp_plan[j] > 0) { // this row has a copy of some precomputed row + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + row_of_power[exp_plan[j]], false), + var(component.W(i), start_row_index + row, false) + }); + } + row += 2; + } + } + } + + template + typename mnt4_fp4_fixed_power::result_type + generate_circuit( + mnt4_fp4_fixed_power const& component, + circuit>& bp, + assignment>& assignment, + typename mnt4_fp4_fixed_power::input_type const& instance_input, + const std::size_t start_row_index) + { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::size_t row = 0; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + row += 1 + (exp_precompute[i] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); // power-4 gate + row++; + if (exp_plan[i] > 0) { + assignment.enable_selector(selector_index[1], start_row_index + row); // multiplication gate + row += 2; + } + } + // did we even use a multiplication gate? + std::size_t gate_id = 1 + (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1); + + // cubing and squaring gates if we need either of them + for(std::size_t small_power = 3; small_power > 1; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + std::size_t row_of_power; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + row_of_power = 2*i + 1; + } + } + assignment.enable_selector(selector_index[gate_id], start_row_index + row_of_power); + gate_id++; + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename mnt4_fp4_fixed_power::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4484b1081..445149817 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -76,6 +76,7 @@ set(NON_NATIVE_TESTS_FILES "algebra/fields/plonk/non_native/comparison_flag" "algebra/fields/plonk/non_native/equality_flag" "algebra/fields/plonk/non_native/division_remainder" + "algebra/fields/plonk/non_native/mnt4_fp4_fixed_power" "non_native/plonk/bool_scalar_multiplication" "non_native/plonk/add_mul_zkllvm_compatible" "non_native/plonk/scalar_non_native_range" diff --git a/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp new file mode 100644 index 000000000..761ef29fe --- /dev/null +++ b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp @@ -0,0 +1,144 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// exponentiation to a fixed power in Fp4 for MNT4 GT +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt4_fp4_fixed_power_test + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "test_plonk_component.hpp" + +using namespace nil; + +template +void test_mnt4_fp4_fixed_power( + std::vector public_input, + typename CurveType::base_field_type::integral_type power, + typename CurveType::gt_type::value_type expected_res) +{ + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::base_field_type; + + constexpr std::size_t WitnessColumns = 4; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_fp4_fixed_power; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input) + }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "GT (MNT4_FP4) fixed power test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "expected: " << expected_res.data[0].data[0] << "," << expected_res.data[0].data[1] << ",\n"; + std::cout << " : " << expected_res.data[1].data[0] << "," << expected_res.data[1].data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.output[0]).data << "," << var_value(assignment, real_res.output[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.output[2]).data << "," << var_value(assignment, real_res.output[3]).data << "\n\n"; + #endif + assert(expected_res.data[0].data[0] == var_value(assignment, real_res.output[0])); + assert(expected_res.data[0].data[1] == var_value(assignment, real_res.output[1])); + assert(expected_res.data[1].data[0] == var_value(assignment, real_res.output[2])); + assert(expected_res.data[1].data[1] == var_value(assignment, real_res.output[3])); + }; + + component_type component_instance({0, 1, 2, 3},{},{}, power); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + power); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_fp4_fixed_power_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::underlying_field_type::value_type underlying_field_type; + typedef typename group_type::base_field_type::value_type field_value_type; + typedef typename group_type::base_field_type::integral_type integral_type; + + std::vector test_gt_elems = { + group_value_type( + underlying_field_type( + integral_type("52892640404176485498888258250761939645646664521825416611470707830951434005627159209780591"), + integral_type("6901204673282085722087006015175666329010388388376197780557030626874831981593510811368863") + ), + underlying_field_type( + integral_type("52892640404176485498888258250761939645646664521825416611470707830951434005627159209780591"), + integral_type("6901204673282085722087006015175666329010388388376197780557030626874831981593510811368863") + ) + ), + }; + + auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; + + for(std::size_t i = 0; i < test_gt_elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_gt_elems[i]; + group_value_type R = P.cyclotomic_exp(fixed_power); + + test_mnt4_fp4_fixed_power( + std::vector{ P.data[0].data[0], P.data[0].data[1], P.data[1].data[0], P.data[1].data[1] }, + fixed_power, + R); + } +} + +BOOST_AUTO_TEST_SUITE_END()