From d7ef89029a9882c4a0a717f6094d6bb86b0e338e Mon Sep 17 00:00:00 2001 From: Jacob Trombetta Date: Fri, 29 Sep 2023 13:13:19 -0400 Subject: [PATCH] feat: the G1 curve should support element conversion ( PROOF-666 ) (#22) * feat: the bls12-381 base field have an operation to move uint8_t elements ( PROOF-666 ) * feat: the g1 curve should be able to move affine elements ( PROOF-666 ) * refactor: the g1 affine element should use uint8_t to represent infinity ( PROOF-666 ) * feat: add converstion between g1 projective and affine elements ( PROOF-666 ) * chore: remove unnecessary namespace prefix ( PROOF-666 ) * feat: add affine to projective element conversion ( PROOF-666 ) * chore: correct test section name ( PROOF-666 ) * refactor: rewrite convert to match conversion_ultilty in curve21 package ( PROOF-666 ) * refactor: move curve_g1 affine identity to element_affine component ( PROOF-666 ) * refactor: move coversion_ultility component to type package ( PROOF-666 ) * remove element_affine cmov function to avoid circular dependencies * refactor: move generic cmov to base/num package ( PROOF-666 ) --- sxt/base/num/BUILD | 10 ++ .../constant/identity.cc => base/num/cmov.cc} | 2 +- .../constant/identity.h => base/num/cmov.h} | 24 +++-- sxt/base/num/cmov.t.cc | 84 ++++++++++++++++ sxt/curve_g1/constant/BUILD | 12 --- sxt/curve_g1/operation/BUILD | 4 +- sxt/curve_g1/operation/add.t.cc | 6 +- sxt/curve_g1/operation/cmov.t.cc | 3 +- sxt/curve_g1/property/BUILD | 2 - sxt/curve_g1/property/curve.t.cc | 3 +- sxt/curve_g1/property/identity.t.cc | 4 +- sxt/curve_g1/type/BUILD | 21 +++- sxt/curve_g1/type/conversion_utility.cc | 17 ++++ sxt/curve_g1/type/conversion_utility.h | 76 ++++++++++++++ sxt/curve_g1/type/conversion_utility.t.cc | 99 +++++++++++++++++++ sxt/curve_g1/type/element_affine.h | 8 +- sxt/field12/operation/cmov.h | 4 +- sxt/field12/operation/cmov.t.cc | 2 +- 18 files changed, 342 insertions(+), 39 deletions(-) rename sxt/{curve_g1/constant/identity.cc => base/num/cmov.cc} (94%) rename sxt/{curve_g1/constant/identity.h => base/num/cmov.h} (69%) create mode 100644 sxt/base/num/cmov.t.cc create mode 100644 sxt/curve_g1/type/conversion_utility.cc create mode 100644 sxt/curve_g1/type/conversion_utility.h create mode 100644 sxt/curve_g1/type/conversion_utility.t.cc diff --git a/sxt/base/num/BUILD b/sxt/base/num/BUILD index 67e309932..e17015d4e 100644 --- a/sxt/base/num/BUILD +++ b/sxt/base/num/BUILD @@ -3,6 +3,16 @@ load( "sxt_cc_component", ) +sxt_cc_component( + name = "cmov", + test_deps = [ + "//sxt/base/test:unit_test", + ], + deps = [ + "//sxt/base/macro:cuda_callable", + ], +) + sxt_cc_component( name = "constexpr_switch", test_deps = [ diff --git a/sxt/curve_g1/constant/identity.cc b/sxt/base/num/cmov.cc similarity index 94% rename from sxt/curve_g1/constant/identity.cc rename to sxt/base/num/cmov.cc index c08b00f96..4f17eb785 100644 --- a/sxt/curve_g1/constant/identity.cc +++ b/sxt/base/num/cmov.cc @@ -14,4 +14,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "sxt/curve_g1/constant/identity.h" +#include "sxt/base/num/cmov.h" diff --git a/sxt/curve_g1/constant/identity.h b/sxt/base/num/cmov.h similarity index 69% rename from sxt/curve_g1/constant/identity.h rename to sxt/base/num/cmov.h index fbd8bc8ec..f63411ada 100644 --- a/sxt/curve_g1/constant/identity.h +++ b/sxt/base/num/cmov.h @@ -16,15 +16,21 @@ */ #pragma once -#include "sxt/curve_g1/type/element_affine.h" -#include "sxt/curve_g1/type/element_p2.h" -#include "sxt/field12/constant/one.h" -#include "sxt/field12/constant/zero.h" -#include "sxt/field12/type/element.h" +#include "sxt/base/macro/cuda_callable.h" -namespace sxt::cg1cn { +namespace sxt::basn { //-------------------------------------------------------------------------------------------------- -// identity_affine_v +// cmov //-------------------------------------------------------------------------------------------------- -static constexpr cg1t::element_affine identity_affine_v{f12cn::zero_v, f12cn::one_v, true}; -} // namespace sxt::cg1cn +/* + Replace (f,g) with (g,g) if b == 1. + Replace (f,g) with (f,g) if b == 0. + * + Preconditions: b in {0,1}. + */ +template +CUDA_CALLABLE inline void cmov(T& f, const T g, unsigned int b) noexcept { + const T mask = static_cast(-static_cast(b)); + f = f ^ (mask & (f ^ g)); +} +} // namespace sxt::basn diff --git a/sxt/base/num/cmov.t.cc b/sxt/base/num/cmov.t.cc new file mode 100644 index 000000000..fd9793839 --- /dev/null +++ b/sxt/base/num/cmov.t.cc @@ -0,0 +1,84 @@ +/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU. + * + * Copyright 2023-present Space and Time Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sxt/base/num/cmov.h" + +#include "sxt/base/test/unit_test.h" + +using namespace sxt; +using namespace sxt::basn; + +TEST_CASE("cmov correctly moves") { + SECTION("uint8_t elements") { + uint8_t h{0}; + uint8_t g{0}; + constexpr uint8_t i{1}; + + cmov(h, i, true); + cmov(g, i, false); + + REQUIRE(h == i); + REQUIRE(g == 0); + } + + SECTION("uint16_t elements") { + uint16_t h{0}; + uint16_t g{0}; + constexpr uint16_t i{1}; + + cmov(h, i, true); + cmov(g, i, false); + + REQUIRE(h == i); + REQUIRE(g == 0); + } + + SECTION("uint32_t elements") { + uint32_t h{0}; + uint32_t g{0}; + constexpr uint32_t i{1}; + + cmov(h, i, true); + cmov(g, i, false); + + REQUIRE(h == i); + REQUIRE(g == 0); + } + + SECTION("uint64_t elements") { + uint64_t h{0}; + uint64_t g{0}; + constexpr uint64_t i{1}; + + cmov(h, i, true); + cmov(g, i, false); + + REQUIRE(h == 1); + REQUIRE(g == 0); + } + + SECTION("signed int elements") { + int h{0}; + int g{0}; + constexpr int i{-1}; + + cmov(h, i, true); + cmov(g, i, false); + + REQUIRE(h == i); + REQUIRE(g == 0); + } +} diff --git a/sxt/curve_g1/constant/BUILD b/sxt/curve_g1/constant/BUILD index 615e9c942..b6155b208 100644 --- a/sxt/curve_g1/constant/BUILD +++ b/sxt/curve_g1/constant/BUILD @@ -37,15 +37,3 @@ sxt_cc_component( "//sxt/field12/type:element", ], ) - -sxt_cc_component( - name = "identity", - with_test = False, - deps = [ - "//sxt/curve_g1/type:element_affine", - "//sxt/curve_g1/type:element_p2", - "//sxt/field12/constant:one", - "//sxt/field12/constant:zero", - "//sxt/field12/type:element", - ], -) diff --git a/sxt/curve_g1/operation/BUILD b/sxt/curve_g1/operation/BUILD index 65c8541d7..9cdaea0ef 100644 --- a/sxt/curve_g1/operation/BUILD +++ b/sxt/curve_g1/operation/BUILD @@ -15,9 +15,9 @@ sxt_cc_component( ":double", "//sxt/base/test:unit_test", "//sxt/curve_g1/constant:generator", - "//sxt/curve_g1/constant:identity", "//sxt/curve_g1/property:curve", "//sxt/curve_g1/property:identity", + "//sxt/curve_g1/type:element_affine", "//sxt/field12/type:element", ], deps = [ @@ -33,6 +33,7 @@ sxt_cc_component( sxt_cc_component( name = "cmov", impl_deps = [ + "//sxt/curve_g1/type:element_affine", "//sxt/curve_g1/type:element_p2", "//sxt/field12/operation:cmov", ], @@ -40,6 +41,7 @@ sxt_cc_component( test_deps = [ "//sxt/base/test:unit_test", "//sxt/curve_g1/constant:generator", + "//sxt/curve_g1/type:element_affine", "//sxt/curve_g1/type:element_p2", ], deps = [ diff --git a/sxt/curve_g1/operation/add.t.cc b/sxt/curve_g1/operation/add.t.cc index d147cefde..0faee8325 100644 --- a/sxt/curve_g1/operation/add.t.cc +++ b/sxt/curve_g1/operation/add.t.cc @@ -18,10 +18,10 @@ #include "sxt/base/test/unit_test.h" #include "sxt/curve_g1/constant/generator.h" -#include "sxt/curve_g1/constant/identity.h" #include "sxt/curve_g1/operation/double.h" #include "sxt/curve_g1/property/curve.h" #include "sxt/curve_g1/property/identity.h" +#include "sxt/curve_g1/type/element_affine.h" #include "sxt/curve_g1/type/element_p2.h" #include "sxt/field12/operation/mul.h" #include "sxt/field12/type/element.h" @@ -97,7 +97,7 @@ TEST_CASE("addition with projective elements") { TEST_CASE("addition with mixed elements") { SECTION("keeps the identity on the curve") { cg1t::element_p2 ret; - add(ret, cg1t::element_p2::identity(), cg1cn::identity_affine_v); + add(ret, cg1t::element_p2::identity(), cg1t::element_affine::identity()); REQUIRE(cg1p::is_identity(ret)); REQUIRE(cg1p::is_on_curve(ret)); @@ -113,7 +113,7 @@ TEST_CASE("addition with mixed elements") { const cg1t::element_p2 projected_generator{x, y, z}; cg1t::element_p2 ret; - add(ret, projected_generator, cg1cn::identity_affine_v); + add(ret, projected_generator, cg1t::element_affine::identity()); REQUIRE(!cg1p::is_identity(ret)); REQUIRE(cg1p::is_on_curve(ret)); diff --git a/sxt/curve_g1/operation/cmov.t.cc b/sxt/curve_g1/operation/cmov.t.cc index 36731e594..63f2a42ae 100644 --- a/sxt/curve_g1/operation/cmov.t.cc +++ b/sxt/curve_g1/operation/cmov.t.cc @@ -18,12 +18,13 @@ #include "sxt/base/test/unit_test.h" #include "sxt/curve_g1/constant/generator.h" +#include "sxt/curve_g1/type/element_affine.h" #include "sxt/curve_g1/type/element_p2.h" using namespace sxt; using namespace sxt::cg1o; -TEST_CASE("cmov returns expected perspective coordinates") { +TEST_CASE("cmov returns the expected projective coordinates") { cg1t::element_p2 expect_generator{cg1cn::generator_p2_v}; cg1t::element_p2 expect_identity{cg1t::element_p2::identity()}; diff --git a/sxt/curve_g1/property/BUILD b/sxt/curve_g1/property/BUILD index a02629731..feee90e0b 100644 --- a/sxt/curve_g1/property/BUILD +++ b/sxt/curve_g1/property/BUILD @@ -20,7 +20,6 @@ sxt_cc_component( "//sxt/base/test:unit_test", "//sxt/curve_g1/constant:b", "//sxt/curve_g1/constant:generator", - "//sxt/curve_g1/constant:identity", "//sxt/curve_g1/type:element_affine", "//sxt/curve_g1/type:element_p2", "//sxt/field12/constant:one", @@ -34,7 +33,6 @@ sxt_cc_component( test_deps = [ "//sxt/base/test:unit_test", "//sxt/curve_g1/constant:generator", - "//sxt/curve_g1/constant:identity", ], deps = [ "//sxt/base/macro:cuda_callable", diff --git a/sxt/curve_g1/property/curve.t.cc b/sxt/curve_g1/property/curve.t.cc index 1e49d5963..37557e27a 100644 --- a/sxt/curve_g1/property/curve.t.cc +++ b/sxt/curve_g1/property/curve.t.cc @@ -19,7 +19,6 @@ #include "sxt/base/test/unit_test.h" #include "sxt/curve_g1/constant/b.h" #include "sxt/curve_g1/constant/generator.h" -#include "sxt/curve_g1/constant/identity.h" #include "sxt/curve_g1/type/element_affine.h" #include "sxt/curve_g1/type/element_p2.h" #include "sxt/field12/constant/one.h" @@ -35,7 +34,7 @@ TEST_CASE("an affine element") { } SECTION("equal to the identity is on the curve") { - REQUIRE(is_on_curve(cg1cn::identity_affine_v)); + REQUIRE(is_on_curve(cg1t::element_affine::identity())); } SECTION("equal to (1,1) is not on the curve") { diff --git a/sxt/curve_g1/property/identity.t.cc b/sxt/curve_g1/property/identity.t.cc index 770cf7f1a..5f38635a9 100644 --- a/sxt/curve_g1/property/identity.t.cc +++ b/sxt/curve_g1/property/identity.t.cc @@ -18,7 +18,7 @@ #include "sxt/base/test/unit_test.h" #include "sxt/curve_g1/constant/generator.h" -#include "sxt/curve_g1/constant/identity.h" +#include "sxt/curve_g1/type/element_affine.h" #include "sxt/curve_g1/type/element_p2.h" using namespace sxt; @@ -26,7 +26,7 @@ using namespace sxt::cg1p; TEST_CASE("the identity can be identified") { SECTION("as an affine element") { - REQUIRE(is_identity(cg1cn::identity_affine_v)); + REQUIRE(is_identity(cg1t::element_affine::identity())); REQUIRE(!is_identity(cg1cn::generator_affine_v)); } diff --git a/sxt/curve_g1/type/BUILD b/sxt/curve_g1/type/BUILD index 936da14e0..0250fcd2d 100644 --- a/sxt/curve_g1/type/BUILD +++ b/sxt/curve_g1/type/BUILD @@ -3,14 +3,31 @@ load( "sxt_cc_component", ) +sxt_cc_component( + name = "conversion_utility", + test_deps = [ + "//sxt/base/test:unit_test", + ], + deps = [ + ":element_affine", + ":element_p2", + "//sxt/base/macro:cuda_callable", + "//sxt/base/num:cmov", + "//sxt/field12/operation:cmov", + "//sxt/field12/operation:invert", + "//sxt/field12/operation:mul", + "//sxt/field12/type:element", + ], +) + sxt_cc_component( name = "element_affine", test_deps = [ "//sxt/base/test:unit_test", - "//sxt/field12/constant:one", - "//sxt/field12/constant:zero", ], deps = [ + "//sxt/field12/constant:one", + "//sxt/field12/constant:zero", "//sxt/field12/type:element", ], ) diff --git a/sxt/curve_g1/type/conversion_utility.cc b/sxt/curve_g1/type/conversion_utility.cc new file mode 100644 index 000000000..748db921a --- /dev/null +++ b/sxt/curve_g1/type/conversion_utility.cc @@ -0,0 +1,17 @@ +/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU. + * + * Copyright 2023-present Space and Time Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sxt/curve_g1/type/conversion_utility.h" diff --git a/sxt/curve_g1/type/conversion_utility.h b/sxt/curve_g1/type/conversion_utility.h new file mode 100644 index 000000000..fab5d1b8f --- /dev/null +++ b/sxt/curve_g1/type/conversion_utility.h @@ -0,0 +1,76 @@ +/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU. + * + * Copyright 2023-present Space and Time Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Adopted from zcash/librustzcash + * + * Copyright (c) 2017 + * Zcash Company + * + * See third_party/license/zcash.LICENSE + */ +#pragma once + +#include "sxt/base/macro/cuda_callable.h" +#include "sxt/base/num/cmov.h" +#include "sxt/curve_g1/type/element_affine.h" +#include "sxt/curve_g1/type/element_p2.h" +#include "sxt/field12/operation/cmov.h" +#include "sxt/field12/operation/invert.h" +#include "sxt/field12/operation/mul.h" +#include "sxt/field12/type/element.h" + +namespace sxt::cg1t { +//-------------------------------------------------------------------------------------------------- +// to_element_affine +//-------------------------------------------------------------------------------------------------- +/* + Converts projective to affine element. + */ +CUDA_CALLABLE +inline void to_element_affine(element_affine& a, const element_p2& p) noexcept { + f12t::element z_inv; + const bool is_zero{f12o::invert(z_inv, p.Z)}; + f12o::cmov(z_inv, f12cn::zero_v, is_zero); + + f12t::element x; + f12t::element y; + f12o::mul(x, p.X, z_inv); + f12o::mul(y, p.Y, z_inv); + + a.X = x; + a.Y = y; + a.infinity = false; + + f12o::cmov(a.X, element_affine::identity().X, is_zero); + f12o::cmov(a.Y, element_affine::identity().Y, is_zero); + basn::cmov(a.infinity, element_affine::identity().infinity, is_zero); +} + +//-------------------------------------------------------------------------------------------------- +// to_element_p2 +//-------------------------------------------------------------------------------------------------- +/* + Converts affine to projective element. + */ +CUDA_CALLABLE +inline void to_element_p2(element_p2& p, const element_affine& a) noexcept { + p.X = a.X; + p.Y = a.Y; + p.Z = f12cn::one_v; + f12o::cmov(p.Z, f12cn::zero_v, a.infinity); +} +} // namespace sxt::cg1t diff --git a/sxt/curve_g1/type/conversion_utility.t.cc b/sxt/curve_g1/type/conversion_utility.t.cc new file mode 100644 index 000000000..337929556 --- /dev/null +++ b/sxt/curve_g1/type/conversion_utility.t.cc @@ -0,0 +1,99 @@ +/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU. + * + * Copyright 2023-present Space and Time Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Adopted from zcash/librustzcash + * + * Copyright (c) 2017 + * Zcash Company + * + * See third_party/license/zcash.LICENSE + */ +#include "sxt/curve_g1/type/conversion_utility.h" + +#include "sxt/base/test/unit_test.h" +#include "sxt/curve_g1/type/element_affine.h" +#include "sxt/curve_g1/type/element_p2.h" +#include "sxt/field12/operation/mul.h" +#include "sxt/field12/type/element.h" + +using namespace sxt; +using namespace sxt::cg1t; + +constexpr f12t::element generator_x{0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75}; + +constexpr f12t::element generator_y{0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, + 0x51ac582950405194, 0x0e1c8c3fad0059c0, 0x0bbc3efc5008a26a}; + +constexpr element_p2 generator_projective{generator_x, generator_y, f12cn::one_v}; +constexpr element_p2 identity_projective{element_p2::identity()}; + +constexpr element_affine generator_affine{generator_x, generator_y, false}; +constexpr element_affine identity_affine{element_affine::identity()}; + +TEST_CASE("conversion from projective to affine elements") { + SECTION("does not change the generator") { + element_affine result_affine; + + to_element_affine(result_affine, generator_projective); + + REQUIRE(result_affine == generator_affine); + } + + SECTION("does not change the identity") { + element_affine result_affine; + + to_element_affine(result_affine, identity_projective); + + REQUIRE(result_affine == identity_affine); + } + + SECTION("does not change a projected generator coordinate") { + constexpr f12t::element z{0xba7afa1f9a6fe250, 0xfa0f5b595eafe731, 0x3bdc477694c306e7, + 0x2149be4b3949fa24, 0x64aa6e0649b2078c, 0x12b108ac33643c3e}; + + f12t::element gpx_z; + f12t::element gpy_z; + f12o::mul(gpx_z, generator_projective.X, z); + f12o::mul(gpy_z, generator_projective.Y, z); + element_p2 projective_pt{gpx_z, gpy_z, z}; + + element_affine affine_pt; + + to_element_affine(affine_pt, projective_pt); + + REQUIRE(affine_pt == generator_affine); + } +} + +TEST_CASE("conversion from affine to projective elements") { + SECTION("does not change the generator") { + element_p2 result_projective; + + to_element_p2(result_projective, generator_affine); + + REQUIRE(result_projective == generator_projective); + } + + SECTION("does not change the identity") { + element_p2 result_projective; + + to_element_p2(result_projective, identity_affine); + + REQUIRE(result_projective == identity_projective); + } +} diff --git a/sxt/curve_g1/type/element_affine.h b/sxt/curve_g1/type/element_affine.h index f788cc694..cf2fd1d87 100644 --- a/sxt/curve_g1/type/element_affine.h +++ b/sxt/curve_g1/type/element_affine.h @@ -25,6 +25,8 @@ */ #pragma once +#include "sxt/field12/constant/one.h" +#include "sxt/field12/constant/zero.h" #include "sxt/field12/type/element.h" namespace sxt::cg1t { @@ -40,7 +42,11 @@ namespace sxt::cg1t { struct element_affine { f12t::element X; f12t::element Y; - bool infinity; + uint8_t infinity; + + static constexpr element_affine identity() noexcept { + return element_affine{f12cn::zero_v, f12cn::one_v, true}; + } bool operator==(const element_affine& rhs) const noexcept = default; }; diff --git a/sxt/field12/operation/cmov.h b/sxt/field12/operation/cmov.h index 3cbbdad61..50e0357dc 100644 --- a/sxt/field12/operation/cmov.h +++ b/sxt/field12/operation/cmov.h @@ -27,8 +27,8 @@ namespace sxt::f12o { // cmov //-------------------------------------------------------------------------------------------------- /* - Replace (f,g) with (g,g) if b == 1; - replace (f,g) with (f,g) if b == 0. + Replace (f,g) with (g,g) if b == 1. + Replace (f,g) with (f,g) if b == 0. * Preconditions: b in {0,1}. */ diff --git a/sxt/field12/operation/cmov.t.cc b/sxt/field12/operation/cmov.t.cc index 2849eb6e4..b7abed480 100644 --- a/sxt/field12/operation/cmov.t.cc +++ b/sxt/field12/operation/cmov.t.cc @@ -24,7 +24,7 @@ using namespace sxt; using namespace sxt::f12o; -TEST_CASE("cmov moves elements correctly") { +TEST_CASE("cmov correctly moves field elements") { f12t::element h{f12cn::zero_v}; f12t::element g{f12cn::zero_v};