From d9a894db7b0342536e002fe8f12ad8c5834826dd Mon Sep 17 00:00:00 2001 From: Wonyong Kim Date: Sun, 24 Sep 2023 22:20:11 +0900 Subject: [PATCH] feat(math): implement `QuadraticExtensionField` --- .../elliptic_curves/bls/bls12_381/BUILD.bazel | 13 +- .../math/elliptic_curves/bn/bn254/BUILD.bazel | 13 +- .../secp/secp256k1/BUILD.bazel | 2 +- .../short_weierstrass/jacobian_point_impl.h | 2 +- tachyon/math/finite_fields/BUILD.bazel | 11 + .../math/finite_fields/generator/BUILD.bazel | 17 +- .../prime_field_generator/BUILD.bazel | 18 ++ .../build_defs.bzl | 2 +- .../prime_field_generator.cc} | 8 +- .../BUILD.bazel | 16 ++ .../build_defs.bzl | 70 ++++++ .../quadratic_extension_field_generator.cc | 156 ++++++++++++ .../goldilocks_prime/BUILD.bazel | 2 +- tachyon/math/finite_fields/prime_field_base.h | 2 + .../finite_fields/quadratic_extension_field.h | 234 +++++++++++++++++- .../quadratic_extension_field_unittest.cc | 175 +++++++++++++ tachyon/math/finite_fields/test/BUILD.bazel | 14 +- 17 files changed, 722 insertions(+), 33 deletions(-) create mode 100644 tachyon/math/finite_fields/generator/prime_field_generator/BUILD.bazel rename tachyon/math/finite_fields/generator/{ => prime_field_generator}/build_defs.bzl (98%) rename tachyon/math/finite_fields/generator/{generator.cc => prime_field_generator/prime_field_generator.cc} (98%) create mode 100644 tachyon/math/finite_fields/generator/quadratic_extension_field_generator/BUILD.bazel create mode 100644 tachyon/math/finite_fields/generator/quadratic_extension_field_generator/build_defs.bzl create mode 100644 tachyon/math/finite_fields/generator/quadratic_extension_field_generator/quadratic_extension_field_generator.cc create mode 100644 tachyon/math/finite_fields/quadratic_extension_field_unittest.cc diff --git a/tachyon/math/elliptic_curves/bls/bls12_381/BUILD.bazel b/tachyon/math/elliptic_curves/bls/bls12_381/BUILD.bazel index 358c55e6ae..502e08e33b 100644 --- a/tachyon/math/elliptic_curves/bls/bls12_381/BUILD.bazel +++ b/tachyon/math/elliptic_curves/bls/bls12_381/BUILD.bazel @@ -1,5 +1,6 @@ load("//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl", "generate_ec_points") -load("//tachyon/math/finite_fields/generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/quadratic_extension_field_generator:build_defs.bzl", "generate_quadratic_extension_fields") package(default_visibility = ["//visibility:public"]) @@ -71,3 +72,13 @@ generate_ec_points( # Hex: 0x8b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 y = "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569", ) + +generate_quadratic_extension_fields( + name = "fp2", + base_field = "Fq", + base_field_hdr = "tachyon/math/elliptic_curves/bls/bls12_381/fq.h", + class_name = "Fp2", + namespace = "tachyon::math::bls12_381", + non_residue = -1, + deps = [":fq"], +) diff --git a/tachyon/math/elliptic_curves/bn/bn254/BUILD.bazel b/tachyon/math/elliptic_curves/bn/bn254/BUILD.bazel index 6a55572ae7..12affc6dfb 100644 --- a/tachyon/math/elliptic_curves/bn/bn254/BUILD.bazel +++ b/tachyon/math/elliptic_curves/bn/bn254/BUILD.bazel @@ -1,7 +1,8 @@ load("//bazel:tachyon.bzl", "if_polygon_zkevm_backend") load("//bazel:tachyon_cc.bzl", "tachyon_cc_library") load("//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl", "generate_ec_points") -load("//tachyon/math/finite_fields/generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/quadratic_extension_field_generator:build_defs.bzl", "generate_quadratic_extension_fields") package(default_visibility = ["//visibility:public"]) @@ -131,3 +132,13 @@ generate_ec_points( x = "1", y = "2", ) + +generate_quadratic_extension_fields( + name = "fp2", + base_field = "Fq", + base_field_hdr = "tachyon/math/elliptic_curves/bn/bn254/fq.h", + class_name = "Fp2", + namespace = "tachyon::math::bn254", + non_residue = -1, + deps = [":fq"], +) diff --git a/tachyon/math/elliptic_curves/secp/secp256k1/BUILD.bazel b/tachyon/math/elliptic_curves/secp/secp256k1/BUILD.bazel index 79c120a6c9..2fe879b2df 100644 --- a/tachyon/math/elliptic_curves/secp/secp256k1/BUILD.bazel +++ b/tachyon/math/elliptic_curves/secp/secp256k1/BUILD.bazel @@ -1,7 +1,7 @@ load("//bazel:tachyon.bzl", "if_polygon_zkevm_backend") load("//bazel:tachyon_cc.bzl", "tachyon_cc_library") load("//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl", "generate_ec_points") -load("//tachyon/math/finite_fields/generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl", "generate_prime_fields") package(default_visibility = ["//visibility:public"]) diff --git a/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h b/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h index 6c82c28655..f1013ff03f 100644 --- a/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h +++ b/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h @@ -189,7 +189,7 @@ constexpr CLASS& CLASS::DoubleInPlace() { // D = 2 * ((X1 + B)² - A - C) // = 2 * ((X1 + Y1²)² - A - C) // = 2 * 2 * X1 * Y1² - uint64_t ext_deg = BaseField::Config::ExtensionDegree(); + uint64_t ext_deg = BaseField::ExtensionDegree(); BaseField d; if (ext_deg == 1 || ext_deg == 2) { d = x_; diff --git a/tachyon/math/finite_fields/BUILD.bazel b/tachyon/math/finite_fields/BUILD.bazel index ac56d0cb9b..30b2632ef9 100644 --- a/tachyon/math/finite_fields/BUILD.bazel +++ b/tachyon/math/finite_fields/BUILD.bazel @@ -119,12 +119,14 @@ tachyon_cc_test( "modulus_unittest.cc", "prime_field_base_unittest.cc", "prime_field_unittest.cc", + "quadratic_extension_field_unittest.cc", ], deps = [ "//tachyon/base:bits", "//tachyon/math/elliptic_curves/bn/bn254:fq", "//tachyon/math/elliptic_curves/bn/bn254:fr", "//tachyon/math/finite_fields/test:gf7", + "//tachyon/math/finite_fields/test:gf7_quad_ext", ], ) @@ -178,3 +180,12 @@ tachyon_cc_benchmark( "//tachyon/math/finite_fields/goldilocks_prime:goldilocks", ], ) + +tachyon_cc_library( + name = "quadratic_extension_field", + hdrs = ["quadratic_extension_field.h"], + deps = [ + "//tachyon/math/base:field", + "@com_google_absl//absl/strings", + ], +) diff --git a/tachyon/math/finite_fields/generator/BUILD.bazel b/tachyon/math/finite_fields/generator/BUILD.bazel index 3c647e2401..25f4f1fc35 100644 --- a/tachyon/math/finite_fields/generator/BUILD.bazel +++ b/tachyon/math/finite_fields/generator/BUILD.bazel @@ -1,4 +1,4 @@ -load("//bazel:tachyon_cc.bzl", "tachyon_cc_binary", "tachyon_cc_library") +load("//bazel:tachyon_cc.bzl", "tachyon_cc_library") package(default_visibility = ["//visibility:public"]) @@ -12,18 +12,3 @@ tachyon_cc_library( "//tachyon/math/finite_fields:modulus", ], ) - -tachyon_cc_binary( - name = "generator", - srcs = ["generator.cc"], - deps = [ - ":generator_util", - "//tachyon/base/console", - "//tachyon/base/files:file_path_flag", - "//tachyon/base/flag:flag_parser", - "//tachyon/build:cc_writer", - "//tachyon/math/base:bit_iterator", - "//tachyon/math/base/gmp:bit_traits", - "//tachyon/math/finite_fields:prime_field_util", - ], -) diff --git a/tachyon/math/finite_fields/generator/prime_field_generator/BUILD.bazel b/tachyon/math/finite_fields/generator/prime_field_generator/BUILD.bazel new file mode 100644 index 0000000000..e2c7059a88 --- /dev/null +++ b/tachyon/math/finite_fields/generator/prime_field_generator/BUILD.bazel @@ -0,0 +1,18 @@ +load("//bazel:tachyon_cc.bzl", "tachyon_cc_binary") + +package(default_visibility = ["//visibility:public"]) + +tachyon_cc_binary( + name = "prime_field_generator", + srcs = ["prime_field_generator.cc"], + deps = [ + "//tachyon/base/console", + "//tachyon/base/files:file_path_flag", + "//tachyon/base/flag:flag_parser", + "//tachyon/build:cc_writer", + "//tachyon/math/base:bit_iterator", + "//tachyon/math/base/gmp:bit_traits", + "//tachyon/math/finite_fields:prime_field_util", + "//tachyon/math/finite_fields/generator:generator_util", + ], +) diff --git a/tachyon/math/finite_fields/generator/build_defs.bzl b/tachyon/math/finite_fields/generator/prime_field_generator/build_defs.bzl similarity index 98% rename from tachyon/math/finite_fields/generator/build_defs.bzl rename to tachyon/math/finite_fields/generator/prime_field_generator/build_defs.bzl index fef633658b..48489bae76 100644 --- a/tachyon/math/finite_fields/generator/build_defs.bzl +++ b/tachyon/math/finite_fields/generator/prime_field_generator/build_defs.bzl @@ -50,7 +50,7 @@ generate_prime_field = rule( cfg = "target", executable = True, allow_single_file = True, - default = Label("@kroma_network_tachyon//tachyon/math/finite_fields/generator"), + default = Label("@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator"), ), }, ) diff --git a/tachyon/math/finite_fields/generator/generator.cc b/tachyon/math/finite_fields/generator/prime_field_generator/prime_field_generator.cc similarity index 98% rename from tachyon/math/finite_fields/generator/generator.cc rename to tachyon/math/finite_fields/generator/prime_field_generator/prime_field_generator.cc index 7d7cc18a84..7ce7002ee6 100644 --- a/tachyon/math/finite_fields/generator/generator.cc +++ b/tachyon/math/finite_fields/generator/prime_field_generator/prime_field_generator.cc @@ -120,8 +120,6 @@ int GenerationConfig::GenerateConfigHdr() const { "", " constexpr static bool kHasLargeSubgroupRootOfUnity = false;", "", - " constexpr static uint64_t ExtensionDegree() { return 1; }", - "", " static void Init();", "};", "", @@ -322,7 +320,7 @@ int GenerationConfig::GenerateConfigGpuHdr() const { int RealMain(int argc, char** argv) { GenerationConfig config; - config.generator = "//tachyon/math/finite_fields/generator"; + config.generator = "//tachyon/math/finite_fields/prime_field_field_generator"; base::FlagParser parser; parser.AddFlag(&config.out) @@ -331,9 +329,7 @@ int RealMain(int argc, char** argv) { parser.AddFlag(&config.ns_name) .set_long_name("--namespace") .set_required(); - parser.AddFlag(&config.class_name) - .set_long_name("--class") - .set_required(); + parser.AddFlag(&config.class_name).set_long_name("--class"); parser.AddFlag(&config.modulus) .set_long_name("--modulus") .set_required(); diff --git a/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/BUILD.bazel b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/BUILD.bazel new file mode 100644 index 0000000000..8531ae3820 --- /dev/null +++ b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/BUILD.bazel @@ -0,0 +1,16 @@ +load("//bazel:tachyon_cc.bzl", "tachyon_cc_binary") + +package(default_visibility = ["//visibility:public"]) + +tachyon_cc_binary( + name = "quadratic_extension_field_generator", + srcs = ["quadratic_extension_field_generator.cc"], + deps = [ + "//tachyon/base/console", + "//tachyon/base/files:file_path_flag", + "//tachyon/base/flag:flag_parser", + "//tachyon/build:cc_writer", + "//tachyon/math/base:big_int", + "//tachyon/math/base:bit_iterator", + ], +) diff --git a/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/build_defs.bzl b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/build_defs.bzl new file mode 100644 index 0000000000..373c9a87a0 --- /dev/null +++ b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/build_defs.bzl @@ -0,0 +1,70 @@ +load("//bazel:tachyon_cc.bzl", "tachyon_cc_library") + +def _generate_quadratic_extension_field_impl(ctx): + arguments = [ + "--out=%s" % (ctx.outputs.out.path), + "--namespace=%s" % (ctx.attr.namespace), + "--class=%s" % (ctx.attr.class_name), + "--non_residue=%s" % (ctx.attr.non_residue), + "--base_field_hdr=%s" % (ctx.attr.base_field_hdr), + "--base_field=%s" % (ctx.attr.base_field), + ] + + ctx.actions.run( + tools = [ctx.executable._tool], + executable = ctx.executable._tool, + outputs = [ctx.outputs.out], + arguments = arguments, + ) + + return [DefaultInfo(files = depset([ctx.outputs.out]))] + +generate_quadratic_extension_field = rule( + implementation = _generate_quadratic_extension_field_impl, + attrs = { + "out": attr.output(mandatory = True), + "namespace": attr.string(mandatory = True), + "class_name": attr.string(mandatory = True), + "non_residue": attr.int(mandatory = True), + "base_field_hdr": attr.string(mandatory = True), + "base_field": attr.string(mandatory = True), + "_tool": attr.label( + # TODO(chokobole): Change it to "exec" we can build it on macos. + cfg = "target", + executable = True, + allow_single_file = True, + default = Label("@kroma_network_tachyon//tachyon/math/finite_fields/generator/quadratic_extension_field_generator"), + ), + }, +) + +def generate_quadratic_extension_fields( + name, + namespace, + class_name, + non_residue, + base_field_hdr = "", + base_field = "", + deps = [], + **kwargs): + for n in [ + ("{}_gen_hdr".format(name), "{}.h".format(name)), + ]: + generate_quadratic_extension_field( + namespace = namespace, + class_name = class_name, + non_residue = non_residue, + base_field_hdr = base_field_hdr, + base_field = base_field, + name = n[0], + out = n[1], + ) + + tachyon_cc_library( + name = name, + hdrs = [":{}_gen_hdr".format(name)], + deps = deps + [ + "//tachyon/math/finite_fields:quadratic_extension_field", + ], + **kwargs + ) diff --git a/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/quadratic_extension_field_generator.cc b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/quadratic_extension_field_generator.cc new file mode 100644 index 0000000000..ece7e584c1 --- /dev/null +++ b/tachyon/math/finite_fields/generator/quadratic_extension_field_generator/quadratic_extension_field_generator.cc @@ -0,0 +1,156 @@ +#include "absl/strings/str_replace.h" + +#include "tachyon/base/console/iostream.h" +#include "tachyon/base/files/file_path_flag.h" +#include "tachyon/base/flag/flag_parser.h" +#include "tachyon/base/strings/string_util.h" +#include "tachyon/build/cc_writer.h" +#include "tachyon/math/base/big_int.h" +#include "tachyon/math/base/bit_iterator.h" + +namespace tachyon { + +struct GenerationConfig : public build::CcWriter { + std::string ns_name; + std::string class_name; + int non_residue; + std::string base_field_hdr; + std::string base_field; + + int GenerateConfigHdr() const; + int GenerateConfigCpp() const; +}; + +int GenerationConfig::GenerateConfigHdr() const { + // clang-format off + std::vector tpl = { + "#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"", + "#include \"%{base_field_hdr}\"", + "", + "namespace %{namespace} {", + "", + "template ", + "class %{class}Config {", + " public:", + " using BaseField = _BaseField;", + "", + " // NOTE(chokobole): This can't be constexpr because of PrimeFieldGmp support.", + " static BaseField kNonResidue;", + "", + " constexpr static bool kNonResidueIsMinusOne = %{non_residue_is_minus_one};", + "", + " static BaseField MulByNonResidue(const BaseField& v) {", + " BaseField ret = v;", + " return ret%{mul_by_non_residue};", + " }", + "", + " static void Init() {", + "%{init}", + " }", + "};", + "", + "template ", + "BaseField %{class}Config::kNonResidue;", + "", + "using %{class} = QuadraticExtensionField<%{class}Config<%{base_field}>>;", + "#if defined(TACHYON_GMP_BACKEND)", + "using %{class}Gmp = QuadraticExtensionField<%{class}Config<%{base_field}Gmp>>;", + "#endif // defined(TACHYON_GMP_BACKEND)", + "", + "} // namespace %{namespace}", + }; + // clang-format on + std::string tpl_content = absl::StrJoin(tpl, "\n"); + + bool is_negative = non_residue < 0; + uint64_t abs_non_residue = is_negative ? -non_residue : non_residue; + math::BigInt<1> scalar(abs_non_residue); + std::string mul_by_non_residue; + auto it = math::BitIteratorBE>::begin(&scalar, true); + ++it; + auto end = math::BitIteratorBE>::end(&scalar); + { + std::stringstream ss; + while (it != end) { + ss << ".DoubleInPlace()"; + if (*it) { + ss << ".AddInPlace(v)"; + } + ++it; + } + if (is_negative) ss << ".NegInPlace()"; + mul_by_non_residue = ss.str(); + } + + std::string init; + std::vector init_components; + init_components.push_back( + " using BigIntTy = typename BaseField::BigIntTy;"); + if (is_negative) { + init_components.push_back(absl::Substitute( + " kNonResidue = BaseField::FromBigInt(BaseField::Config::kModulus " + "- BigIntTy($0));", + abs_non_residue)); + } else { + init_components.push_back(absl::Substitute( + " kNonResidue = BaseField::FromBigInt(BigIntTy($0));", + abs_non_residue)); + } + init = absl::StrJoin(init_components, "\n"); + + std::string content = absl::StrReplaceAll( + tpl_content, { + {"%{namespace}", ns_name}, + {"%{class}", class_name}, + {"%{base_field_hdr}", base_field_hdr}, + {"%{base_field}", base_field}, + {"%{non_residue_is_minus_one}", + base::BoolToString(non_residue == -1)}, + {"%{mul_by_non_residue}", mul_by_non_residue}, + {"%{init}", init}, + }); + return WriteHdr(content, false); +} + +int RealMain(int argc, char** argv) { + GenerationConfig config; + config.generator = + "//tachyon/math/finite_fields/quadratic_extension_field_generator"; + + base::FlagParser parser; + parser.AddFlag(&config.out) + .set_long_name("--out") + .set_help("path to output"); + parser.AddFlag(&config.ns_name) + .set_long_name("--namespace") + .set_required(); + parser.AddFlag(&config.class_name) + .set_long_name("--class") + .set_required(); + parser.AddFlag(&config.non_residue) + .set_long_name("--non_residue") + .set_required(); + parser.AddFlag(&config.base_field_hdr) + .set_long_name("--base_field_hdr") + .set_required(); + parser.AddFlag(&config.base_field) + .set_long_name("--base_field") + .set_required(); + + std::string error; + if (!parser.Parse(argc, argv, &error)) { + tachyon_cerr << error << std::endl; + return 1; + } + + if (base::EndsWith(config.out.value(), ".h")) { + return config.GenerateConfigHdr(); + } else { + tachyon_cerr << "not supported suffix:" << config.out << std::endl; + return 1; + } +} + +} // namespace tachyon + +int main(int argc, char** argv) { return tachyon::RealMain(argc, argv); } diff --git a/tachyon/math/finite_fields/goldilocks_prime/BUILD.bazel b/tachyon/math/finite_fields/goldilocks_prime/BUILD.bazel index f998d121ea..67a64dd331 100644 --- a/tachyon/math/finite_fields/goldilocks_prime/BUILD.bazel +++ b/tachyon/math/finite_fields/goldilocks_prime/BUILD.bazel @@ -1,6 +1,6 @@ load("//bazel:tachyon.bzl", "if_polygon_zkevm_backend") load("//bazel:tachyon_cc.bzl", "tachyon_cc_library", "tachyon_cc_test") -load("//tachyon/math/finite_fields/generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl", "generate_prime_fields") package(default_visibility = ["//visibility:public"]) diff --git a/tachyon/math/finite_fields/prime_field_base.h b/tachyon/math/finite_fields/prime_field_base.h index d0e6834507..44d8674833 100644 --- a/tachyon/math/finite_fields/prime_field_base.h +++ b/tachyon/math/finite_fields/prime_field_base.h @@ -17,6 +17,8 @@ class PrimeFieldBase : public Field { public: using Config = typename PrimeFieldTraits::Config; + constexpr static uint64_t ExtensionDegree() { return 1; } + // Returns false for either of the following cases: // // When there exists |Config::kLargeSubgroupRootOfUnity|: diff --git a/tachyon/math/finite_fields/quadratic_extension_field.h b/tachyon/math/finite_fields/quadratic_extension_field.h index 13f781cde1..9665f3d63f 100644 --- a/tachyon/math/finite_fields/quadratic_extension_field.h +++ b/tachyon/math/finite_fields/quadratic_extension_field.h @@ -1,12 +1,22 @@ +// Copyright 2022 arkworks contributors +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks +// file. + #ifndef TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_ #define TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_ +#include "absl/strings/substitute.h" + +#include "tachyon/math/base/field.h" + namespace tachyon::math { -template -class QuadraticExtensionField { +template +class QuadraticExtensionField : public Field> { public: - using BaseField = F; + using Config = _Config; + using BaseField = typename Config::BaseField; constexpr QuadraticExtensionField() = default; constexpr QuadraticExtensionField(const BaseField& c0, const BaseField& c1) @@ -14,7 +24,217 @@ class QuadraticExtensionField { constexpr QuadraticExtensionField(BaseField&& c0, BaseField&& c1) : c0_(std::move(c0)), c1_(std::move(c1)) {} - QuadraticExtensionField& ConjugateInPlace() { + constexpr static QuadraticExtensionField Zero() { + return {BaseField::Zero(), BaseField::Zero()}; + } + + constexpr static QuadraticExtensionField One() { + return {BaseField::One(), BaseField::Zero()}; + } + + static QuadraticExtensionField Random() { + return {BaseField::Random(), BaseField::Random()}; + } + + constexpr bool IsZero() const { return c0_.IsZero() && c1_.IsZero(); } + + constexpr bool IsOne() const { return c0_.IsOne() && c1_.IsZero(); } + + constexpr static uint64_t ExtensionDegree() { + return 2 * BaseField::ExtensionDegree(); + } + + constexpr QuadraticExtensionField& ConjugateInPlace() { + c1_.NegInPlace(); + return *this; + } + + std::string ToString() const { + return absl::Substitute("($0, $1)", c0_.ToString(), c1_.ToString()); + } + + constexpr const BaseField& c0() const { return c0_; } + constexpr const BaseField& c1() const { return c1_; } + + constexpr bool operator==(const QuadraticExtensionField& other) const { + return c0_ == other.c0_ && c1_ == other.c1_; + } + + constexpr bool operator!=(const QuadraticExtensionField& other) const { + return c0_ != other.c0_ || c1_ != other.c1_; + } + + constexpr bool operator<(const QuadraticExtensionField& other) const { + if (c1_ == other.c1_) return c0_ < other.c0_; + return c1_ < other.c1_; + } + + constexpr bool operator>(const QuadraticExtensionField& other) const { + if (c1_ == other.c1_) return c0_ > other.c0_; + return c1_ > other.c1_; + } + + constexpr bool operator<=(const QuadraticExtensionField& other) const { + if (c1_ == other.c1_) return c0_ <= other.c0_; + return c1_ <= other.c1_; + } + + constexpr bool operator>=(const QuadraticExtensionField& other) const { + if (c1_ == other.c1_) return c0_ >= other.c0_; + return c1_ >= other.c1_; + } + + // AdditiveSemigroup methods + constexpr QuadraticExtensionField& AddInPlace( + const QuadraticExtensionField& other) { + c0_ += other.c0_; + c1_ += other.c1_; + return *this; + } + + constexpr QuadraticExtensionField& DoubleInPlace() { + c0_.DoubleInPlace(); + c1_.DoubleInPlace(); + return *this; + } + + // AdditiveGroup methods + constexpr QuadraticExtensionField& SubInPlace( + const QuadraticExtensionField& other) { + c0_ -= other.c0_; + c1_ -= other.c1_; + return *this; + } + + constexpr QuadraticExtensionField& NegInPlace() { + c0_.NegInPlace(); + c1_.NegInPlace(); + return *this; + } + + // MultiplicativeSemigroup methods + constexpr QuadraticExtensionField& MulInPlace( + const QuadraticExtensionField& other) { + // clang-format off + // (c0, c1) * (other.c0, other.c1) + // = (c0 + c1 * x) * (other.c0 + other.c1 * x) + // = c0 * other.c0 + (c0 * other.c1 + c1 * other.c0) * x + c1 * other.c1 * x² + // = c0 * other.c0 + c1 * other.c1 * x² + (c0 * other.c1 + c1 * other.c0) * x + // = c0 * other.c0 + c1 * other.c1 * q + (c0 * other.c1 + c1 * other.c0) * x + // = (c0 * other.c0 + c1 * other.c1 * q, c0 * other.c0 + c1 * other.c0) + // Where q is Config::kNonResidue. + // clang-format on + if constexpr (ExtensionDegree() == 2) { + BaseField c0; + { + BaseField lefts[] = {c0_, Config::MulByNonResidue(c1_)}; + BaseField rights[] = {other.c0_, other.c1_}; + c0 = BaseField::SumOfProducts(lefts, rights); + } + BaseField c1; + { + BaseField lefts[] = {c0_, c1_}; + BaseField rights[] = {other.c1_, other.c0_}; + c1 = BaseField::SumOfProducts(lefts, rights); + }; + c0_ = std::move(c0); + c1_ = std::move(c1); + } else { + // Karatsuba multiplication; + // Guide to Pairing-based cryptography, Algorithm 5.16. + // v0 = c0 * other.c0 + BaseField v0 = c0_ * other.c0_; + // v1 = c1 * other.c1 + BaseField v1 = c1_ * other.c1_; + + // c1 = c0 + c1 + c1_ += c0_; + // c1 = (c0 + c1) * (other.c0 + other.c1) + // c1 = c0 * other.c0 + c0 * other.c1 + c1 * other.c0 + c1 * other.c1 + c1_ *= (other.c0_ + other.c1_); + // c1 = c0 * other.c1 + c1 * other.c0 + c1 * other.c1 + c1_ -= v0; + // c1 = c0 * other.c1 + c1 * other.c0 + c1_ -= v1; + // c0 = c0 * other.c0 + q * c1 * other.c1 + c0_ = v0 + Config::MulByNonResidue(v1); + } + return *this; + } + + constexpr QuadraticExtensionField& MulInPlace(const BaseField& element) { + c0_ *= element; + c1_ *= element; + return *this; + } + + constexpr QuadraticExtensionField& SquareInPlace() { + // (c0, c1)² = (c0 + c1 * x)² + // = c0² + 2 * c0 * c1 * x + c1² * x² + // = c0² + c1² * x² + 2 * c0 * c1 * x + // = c0² + c1² * q + 2 * c0 * c1 * x + // = (c0² + c1² * q, 2 * c0 * c1) + // Where q is Config::kNonResidue. + // When q = -1, we can re-use intermediate additions to improve performance. + + // v0 = c0 - c1 + BaseField v0 = c0_; + v0 -= c1_; + // v1 = c0 * c1 + BaseField v1 = c0_ * c1_; + if constexpr (Config::kNonResidueIsMinusOne) { + // When the non-residue is -1, we save 2 intermediate additions, + // and use one fewer intermediate variable + + // v0 = (c0 - c1) * (c0 + c1) + // = c0² - c1² + v0 *= (c0_ + c1_); + + // c0 = c0² - c1² + c0_ = std::move(v0); + // c1 = 2 * c0 * c1 + c1_ = v1.Double(); + } else { + // v2 = c0 - q * c1 + BaseField v2 = c0_ - Config::MulByNonResidue(c1_); + + // v0 = (v0 * v2) + // v0 = (c0 - c1) * (c0 - c1 * q) + // v0 = c0² - c0 * c1 * q - c0 * c1 + c1² * q + // v0 = c0² - (q + 1) * c0 * c1 + c1² * q + // v0 = c0² + c1² * q - (q + 1) * c0 * c1 + v0 *= v2; + + // c0 = v0 + (q + 1) * c0 * c1 + // c0 = c0² + c1² * q - (q + 1) * c0 * c1 + (q + 1) * c0 * c1 + // c0 = c0² + c1² * q + c0_ = std::move(v0); + c0_ += v1; + c0_ += Config::MulByNonResidue(v1); + // c1 = 2 * c0 * c1 + c1_ = v1.Double(); + } + return *this; + } + + // MultiplicativeGroup methods + QuadraticExtensionField& DivInPlace(const QuadraticExtensionField& other) { + return MulInPlace(other.Inverse()); + } + + constexpr QuadraticExtensionField& InverseInPlace() { + // NOTE(chokobole): CHECK(!IsZero()) is not a device code. + if (IsZero()) return *this; + // Guide to Pairing-based Cryptography, Algorithm 5.19. + // v1 = c1² + BaseField v1 = c1_.Square(); + // v0 = c0² - q * v1 + BaseField v0 = c0_.Square(); + v0 -= Config::MulByNonResidue(v1); + + v1 = v0.Inverse(); + c0_ *= v1; + c1_ *= v1; c1_.NegInPlace(); return *this; } @@ -25,6 +245,12 @@ class QuadraticExtensionField { BaseField c1_; }; +template +std::ostream& operator<<(std::ostream& os, + const QuadraticExtensionField& f) { + return os << f.ToString(); +} + } // namespace tachyon::math #endif // TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_ diff --git a/tachyon/math/finite_fields/quadratic_extension_field_unittest.cc b/tachyon/math/finite_fields/quadratic_extension_field_unittest.cc new file mode 100644 index 0000000000..ea7a9cb88e --- /dev/null +++ b/tachyon/math/finite_fields/quadratic_extension_field_unittest.cc @@ -0,0 +1,175 @@ +#include "gtest/gtest.h" + +#include "tachyon/math/finite_fields/test/gf7_quad_ext.h" + +namespace tachyon::math { + +namespace { + +class QuadraticExtensionFieldTest : public testing::Test { + public: + static void SetUpTestSuite() { GF7QuadExt::Config::Init(); } +}; + +} // namespace + +TEST_F(QuadraticExtensionFieldTest, Zero) { + EXPECT_TRUE(GF7QuadExt::Zero().IsZero()); + EXPECT_FALSE(GF7QuadExt::One().IsZero()); +} + +TEST_F(QuadraticExtensionFieldTest, One) { + EXPECT_TRUE(GF7QuadExt::One().IsOne()); + EXPECT_FALSE(GF7QuadExt::Zero().IsOne()); +} + +TEST_F(QuadraticExtensionFieldTest, Random) { + bool success = false; + GF7QuadExt r = GF7QuadExt::Random(); + for (size_t i = 0; i < 100; ++i) { + if (r != GF7QuadExt::Random()) { + success = true; + break; + } + } + EXPECT_TRUE(success); +} + +TEST_F(QuadraticExtensionFieldTest, ConjugateInPlace) { + GF7QuadExt f = GF7QuadExt::Random(); + GF7QuadExt f2 = f; + f2.ConjugateInPlace(); + EXPECT_EQ(f.c0(), f2.c0()); + EXPECT_EQ(f.c1(), -f2.c1()); +} + +TEST_F(QuadraticExtensionFieldTest, EqualityOperator) { + GF7QuadExt f(GF7(3), GF7(4)); + GF7QuadExt f2(GF7(4), GF7(4)); + EXPECT_FALSE(f == f2); + EXPECT_TRUE(f != f2); + + GF7QuadExt f3(GF7(4), GF7(3)); + GF7QuadExt f4(GF7(4), GF7(4)); + EXPECT_FALSE(f == f2); + EXPECT_TRUE(f != f2); + + EXPECT_TRUE(f2 == f4); +} + +TEST_F(QuadraticExtensionFieldTest, ComparisonOperator) { + GF7QuadExt f(GF7(3), GF7(4)); + GF7QuadExt f2(GF7(4), GF7(4)); + EXPECT_TRUE(f < f2); + EXPECT_TRUE(f <= f2); + EXPECT_FALSE(f > f2); + EXPECT_FALSE(f >= f2); + + GF7QuadExt f3(GF7(4), GF7(3)); + GF7QuadExt f4(GF7(4), GF7(4)); + EXPECT_TRUE(f3 < f4); + EXPECT_TRUE(f3 <= f4); + EXPECT_FALSE(f3 > f4); + EXPECT_FALSE(f3 >= f4); +} + +TEST_F(QuadraticExtensionFieldTest, AdditiveOperators) { + struct { + GF7QuadExt a; + GF7QuadExt b; + GF7QuadExt sum; + GF7QuadExt amb; + GF7QuadExt bma; + } tests[] = { + { + {GF7(1), GF7(2)}, + {GF7(3), GF7(5)}, + {GF7(4), GF7(0)}, + {GF7(5), GF7(4)}, + {GF7(2), GF7(3)}, + }, + }; + + for (const auto& test : tests) { + EXPECT_EQ(test.a + test.b, test.sum); + EXPECT_EQ(test.b + test.a, test.sum); + EXPECT_EQ(test.a - test.b, test.amb); + EXPECT_EQ(test.b - test.a, test.bma); + + GF7QuadExt tmp = test.a; + tmp += test.b; + EXPECT_EQ(tmp, test.sum); + tmp -= test.b; + EXPECT_EQ(tmp, test.a); + } +} + +TEST_F(QuadraticExtensionFieldTest, AdditiveGroupOperators) { + GF7QuadExt f(GF7(3), GF7(4)); + GF7QuadExt f_neg(GF7(4), GF7(3)); + EXPECT_EQ(f.Negative(), f_neg); + f.NegInPlace(); + EXPECT_EQ(f, f_neg); + + f = GF7QuadExt(GF7(3), GF7(4)); + GF7QuadExt f_dbl(GF7(6), GF7(1)); + EXPECT_EQ(f.Double(), f_dbl); + f.DoubleInPlace(); + EXPECT_EQ(f, f_dbl); +} + +TEST_F(QuadraticExtensionFieldTest, MultiplicativeOperators) { + struct { + GF7QuadExt a; + GF7QuadExt b; + GF7QuadExt mul; + GF7QuadExt adb; + GF7QuadExt bda; + } tests[] = { + { + {GF7(1), GF7(2)}, + {GF7(3), GF7(5)}, + {GF7(0), GF7(4)}, + {GF7(1), GF7(6)}, + {GF7(4), GF7(4)}, + }, + }; + + for (const auto& test : tests) { + EXPECT_EQ(test.a * test.b, test.mul); + EXPECT_EQ(test.b * test.a, test.mul); + EXPECT_EQ(test.a / test.b, test.adb); + EXPECT_EQ(test.b / test.a, test.bda); + + GF7QuadExt tmp = test.a; + tmp *= test.b; + EXPECT_EQ(tmp, test.mul); + tmp /= test.b; + EXPECT_EQ(tmp, test.a); + } +} + +TEST_F(QuadraticExtensionFieldTest, MultiplicativeOperators2) { + GF7QuadExt f(GF7(3), GF7(4)); + GF7QuadExt f_mul(GF7(6), GF7(1)); + EXPECT_EQ(f * GF7(2), f_mul); + f *= GF7(2); + EXPECT_EQ(f, f_mul); +} + +TEST_F(QuadraticExtensionFieldTest, MultiplicativeGroupOperators) { + GF7QuadExt f = GF7QuadExt::Random(); + GF7QuadExt f_inv = f.Inverse(); + EXPECT_EQ(f * f_inv, GF7QuadExt::One()); + GF7QuadExt f_tmp = f; + f.InverseInPlace(); + EXPECT_EQ(f * f_tmp, GF7QuadExt::One()); + + f = GF7QuadExt(GF7(3), GF7(4)); + GF7QuadExt f_sqr = GF7QuadExt(GF7(0), GF7(3)); + EXPECT_EQ(f.Square(), f_sqr); + f.SquareInPlace(); + EXPECT_EQ(f, f_sqr); +} + +} // namespace tachyon::math diff --git a/tachyon/math/finite_fields/test/BUILD.bazel b/tachyon/math/finite_fields/test/BUILD.bazel index c59e927016..37459f2698 100644 --- a/tachyon/math/finite_fields/test/BUILD.bazel +++ b/tachyon/math/finite_fields/test/BUILD.bazel @@ -1,4 +1,5 @@ -load("//tachyon/math/finite_fields/generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl", "generate_prime_fields") +load("//tachyon/math/finite_fields/generator/quadratic_extension_field_generator:build_defs.bzl", "generate_quadratic_extension_fields") package(default_visibility = ["//visibility:public"]) @@ -10,3 +11,14 @@ generate_prime_fields( namespace = "tachyon::math", subgroup_generator = "3", ) + +generate_quadratic_extension_fields( + name = "gf7_quad_ext", + testonly = True, + base_field = "GF7", + base_field_hdr = "tachyon/math/finite_fields/test/gf7.h", + class_name = "GF7QuadExt", + namespace = "tachyon::math", + non_residue = -1, + deps = [":gf7"], +)