diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_mod.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_mod.hpp index 38d5597c3..7a6c77ef7 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_mod.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_mod.hpp @@ -78,15 +78,19 @@ namespace nil::crypto3::multiprecision { // Components - constexpr base_type base() const { + private: + constexpr base_type internal_base() const { base_type result; ops().adjust_regular(result, raw_base()); return result; } - constexpr const base_type& mod() const { return ops().mod(); } + public: + constexpr auto base() const { return detail::as_big_uint(internal_base()); } + + constexpr decltype(auto) mod() const { return detail::as_big_uint(ops().mod()); } - explicit constexpr operator base_type() const { return base(); } + explicit constexpr operator auto() const { return base(); } explicit constexpr operator bool() const { return !is_zero(); } @@ -101,12 +105,12 @@ namespace nil::crypto3::multiprecision { \ template, int> = 0> \ friend constexpr bool operator OP_(const big_mod_impl& a, const T& b) noexcept { \ - return a.base() OP_ b; \ + return a.internal_base() OP_ b; \ } \ \ template, int> = 0> \ friend constexpr bool operator OP_(const T& a, const big_mod_impl& b) noexcept { \ - return a OP_ b.base(); \ + return a OP_ b.internal_base(); \ } NIL_CO3_MP_BIG_MOD_COMPARISON_IMPL(==) @@ -118,7 +122,7 @@ namespace nil::crypto3::multiprecision { // In barrett form raw_base is the same as base // In montgomery form raw_base is base multiplied by r, so it is zero iff base // is - return raw_base().is_zero(); + return nil::crypto3::multiprecision::is_zero(raw_base()); } // String conversion @@ -126,12 +130,8 @@ namespace nil::crypto3::multiprecision { constexpr std::string str( std::ios_base::fmtflags flags = std::ios_base::hex | std::ios_base::showbase | std::ios_base::uppercase) const { - if constexpr (std::is_integral_v) { - // TODO(ioxid): support formating flags - return std::to_string(base()); - } else { - return base().str(flags); - } + // TODO(ioxid): optimize when base_type is not big_uint + return base().str(flags); } // Arithmetic operations @@ -227,14 +227,14 @@ namespace nil::crypto3::multiprecision { std::enable_if_t && !std::numeric_limits::is_signed, int> = 0> friend constexpr big_mod_impl pow_unsigned(big_mod_impl b, const T& e) { - b.ops().pow(b.m_raw_base, b.raw_base(), e); + b.ops().pow_unsigned(b.m_raw_base, b.raw_base(), e); return b; } // Hash friend constexpr std::size_t hash_value(const big_mod_impl& value) noexcept { - return hash_value(value.raw_base()); + return boost::hash{}(value.raw_base()); // mod() is ignored because we don't allow comparing numbers with different // moduli anyway } @@ -294,8 +294,8 @@ namespace nil::crypto3::multiprecision { // Simple modular big integer type with compile-time modulus. Modulus should be a // static big_uint constant. Uses barret optimizations. - template - using big_mod = big_mod_ct_impl; + template + using big_mod = big_mod_ct_impl; // Simple modular big integer type with runtime modulus. Uses barret optimizations. template @@ -307,11 +307,11 @@ namespace nil::crypto3::multiprecision { // Modular big integer type with compile-time modulus, which automatically uses // montomery form whenever possible (i.e. for odd moduli). Modulus should be a static // big_uint constant. - template + template using auto_big_mod = std::conditional_t< - modulus == goldilocks_modulus, goldilocks_mod, - std::conditional_t, big_mod>>; + Modulus == goldilocks_modulus, goldilocks_mod, + std::conditional_t, big_mod>>; } // namespace nil::crypto3::multiprecision // std::hash specializations @@ -337,3 +337,11 @@ struct std::hash< a); } }; + +template<> +struct std::hash { + std::size_t operator()( + const nil::crypto3::multiprecision::goldilocks_mod& a) const noexcept { + return boost::hash{}(a); + } +}; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/barrett.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/barrett.hpp index 8cfcb0c9b..4b88209f2 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/barrett.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/barrett.hpp @@ -82,14 +82,10 @@ namespace nil::crypto3::multiprecision::detail { barrett_reduce(result, tmp); } - template< - std::size_t Bits2, std::size_t Bits3, typename T, - // result should fit in the output parameter - std::enable_if_t::Bits >= big_uint_t::Bits && - is_integral_v && !std::numeric_limits::is_signed, - int> = 0> - constexpr void pow(big_uint &result, const big_uint &a, - T exp) const { + template && !std::numeric_limits::is_signed, + int> = 0> + constexpr void pow_unsigned(base_type &result, const base_type &a, T exp) const { // input parameter should be less than modulus BOOST_ASSERT(a < this->mod()); @@ -102,7 +98,7 @@ namespace nil::crypto3::multiprecision::detail { return; } - big_uint<2 * Bits> base(a), res(1u); + big_uint<2 * Bits> base = a, res = 1u; while (true) { bool lsb = bit_test(exp, 0u); @@ -117,7 +113,7 @@ namespace nil::crypto3::multiprecision::detail { base *= base; barrett_reduce(base); } - result = static_cast>(res); + result = static_cast(res); } // Adjust to/from modular form diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/common.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/common.hpp index edf216c21..d9d5fb42e 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/common.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/common.hpp @@ -27,6 +27,8 @@ namespace nil::crypto3::multiprecision::detail { public: using base_type = base_type_; + static_assert(is_integral_v); + constexpr common_modular_ops(const base_type &m) : m_mod(m) {} constexpr bool compare_eq(const common_modular_ops &other) const { @@ -34,7 +36,7 @@ namespace nil::crypto3::multiprecision::detail { } constexpr void negate_inplace(base_type &raw_base) const { - if (!raw_base.is_zero()) { + if (!is_zero(raw_base)) { auto initial_raw_base = raw_base; raw_base = mod(); raw_base -= initial_raw_base; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/goldilocks.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/goldilocks.hpp index e6a9aedd9..2d13e6477 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/goldilocks.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/goldilocks.hpp @@ -83,6 +83,39 @@ Goldilocks::new(t2) result = reduce128(prod); } + template && !std::numeric_limits::is_signed, int> = 0> + constexpr void pow_unsigned(base_type &result, const base_type &a, + T exp) const { + // input parameter should be less than modulus + BOOST_ASSERT(a < this->mod()); + + if (is_zero(exp)) { + result = 1u; + return; + } + if (this->mod() == 1u) { + result = 0u; + return; + } + + base_type base = a, res = 1u; + + while (true) { + bool lsb = bit_test(exp, 0u); + exp >>= 1u; + if (lsb) { + mul(res, base); + if (is_zero(exp)) { + break; + } + } + mul(base, base); + } + result = res; + } + template && !std::numeric_limits::is_signed, int> = 0> diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/montgomery.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/montgomery.hpp index bf3ad315b..b6d5bb43c 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/montgomery.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_mod/modular_ops/montgomery.hpp @@ -25,10 +25,11 @@ #include "nil/crypto3/multiprecision/detail/integer_ops_base.hpp" namespace nil::crypto3::multiprecision::detail { - template - constexpr bool check_montgomery_constraints(const big_uint &m) { + template + constexpr bool modulus_supports_montgomery(const T &m) { + static_assert(is_integral_v && !std::numeric_limits::is_signed); // Check m % 2 == 0 - return m.bit_test(0u); + return bit_test(m, 0u); } // Montgomery modular operations. Uses Barrett reduction internally and inherits @@ -43,7 +44,7 @@ namespace nil::crypto3::multiprecision::detail { static constexpr std::size_t limb_count = big_uint_t::static_limb_count; constexpr montgomery_modular_ops(const big_uint_t &m) : barrett_modular_ops(m) { - if (!check_montgomery_constraints(m)) { + if (!modulus_supports_montgomery(m)) { throw std::invalid_argument("module not usable with montgomery"); } @@ -329,14 +330,10 @@ namespace nil::crypto3::multiprecision::detail { } } - template< - std::size_t Bits2, std::size_t Bits3, typename T, - // result should fit in the output parameter - std::enable_if_t::Bits >= big_uint_t::Bits && - is_integral_v && !std::numeric_limits::is_signed, - int> = 0> - constexpr void pow(big_uint &result, const big_uint &a, - T exp) const { + template && !std::numeric_limits::is_signed, + int> = 0> + constexpr void pow_unsigned(base_type &result, const base_type &a, T exp) const { // input parameter should be less than modulus BOOST_ASSERT(a < this->mod()); @@ -349,7 +346,7 @@ namespace nil::crypto3::multiprecision::detail { return; } - big_uint_t base(a), res = m_one; + big_uint_t base = a, res = m_one; while (true) { bool lsb = bit_test(exp, 0u); diff --git a/crypto3/libs/multiprecision/test/big_mod_basic.cpp b/crypto3/libs/multiprecision/test/big_mod_basic.cpp index 8f74f1436..526f46cc0 100644 --- a/crypto3/libs/multiprecision/test/big_mod_basic.cpp +++ b/crypto3/libs/multiprecision/test/big_mod_basic.cpp @@ -8,17 +8,17 @@ #define BOOST_TEST_MODULE big_mod_basic_test +#include + #include +#include #include -#include - #include "nil/crypto3/multiprecision/big_mod.hpp" #include "nil/crypto3/multiprecision/big_uint.hpp" #include "nil/crypto3/multiprecision/literals.hpp" using namespace nil::crypto3::multiprecision; -using namespace nil::crypto3::multiprecision::literals; NIL_CO3_MP_DEFINE_BIG_UINT_LITERAL(2) NIL_CO3_MP_DEFINE_BIG_UINT_LITERAL(32) @@ -26,11 +26,17 @@ NIL_CO3_MP_DEFINE_BIG_UINT_LITERAL(36) NIL_CO3_MP_DEFINE_BIG_UINT_LITERAL(57) NIL_CO3_MP_DEFINE_BIG_UINT_LITERAL(60) -using namespace nil::crypto3::multiprecision::literals; +constexpr big_uint<64> goldilocks_modulus_big_uint = 0xFFFFFFFF00000001ULL; +static_assert(std::is_same_v, goldilocks_mod>); + +constexpr auto odd_mod = 0x123456789ABCDEF_big_uint57; +constexpr auto even_mod = 0x123456789ABCDEE_big_uint57; + +static_assert(std::is_same_v, montgomery_big_mod>); +static_assert(std::is_same_v, big_mod>); -constexpr auto mod = 0x123456789ABCDEF_big_uint57; -using montgomery_big_mod_t = montgomery_big_mod; -using big_mod_t = big_mod; +using montgomery_big_mod_t = montgomery_big_mod; +using big_mod_t = big_mod; BOOST_AUTO_TEST_SUITE(smoke) @@ -196,25 +202,30 @@ BOOST_AUTO_TEST_CASE(multilimb) { BOOST_AUTO_TEST_CASE(big) { static constexpr auto mod = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001_big_uint224; - big_mod a = 0xC5067EE5D80302E0561545A8467C6D5C98BC4D37672EB301C38CE9A9_big_uint224; + big_mod a = + 0xC5067EE5D80302E0561545A8467C6D5C98BC4D37672EB301C38CE9A9_big_uint224; - big_mod b = 0xE632329C42040E595D127EB6889D22215DBE56F540425C705D6BF83_big_uint224; + big_mod b = + 0xE632329C42040E595D127EB6889D22215DBE56F540425C705D6BF83_big_uint224; - BOOST_CHECK_EQUAL((a * b).base(), - 0x107BC09A9F3443A6F6458495ADD98CBA1FCD15F17D0EAB66302FEFA6_big_uint224); + BOOST_CHECK_EQUAL( + (a * b).base(), + 0x107BC09A9F3443A6F6458495ADD98CBA1FCD15F17D0EAB66302FEFA6_big_uint224); } BOOST_AUTO_TEST_CASE(big_assign) { static constexpr auto mod = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001_big_uint224; - big_mod a = 0xC5067EE5D80302E0561545A8467C6D5C98BC4D37672EB301C38CE9A9_big_uint224; + big_mod a = + 0xC5067EE5D80302E0561545A8467C6D5C98BC4D37672EB301C38CE9A9_big_uint224; - big_mod b = 0xE632329C42040E595D127EB6889D22215DBE56F540425C705D6BF83_big_uint224; + big_mod b = + 0xE632329C42040E595D127EB6889D22215DBE56F540425C705D6BF83_big_uint224; a *= b; - BOOST_CHECK_EQUAL(a.base(), - 0x107BC09A9F3443A6F6458495ADD98CBA1FCD15F17D0EAB66302FEFA6_big_uint224); + BOOST_CHECK_EQUAL( + a.base(), 0x107BC09A9F3443A6F6458495ADD98CBA1FCD15F17D0EAB66302FEFA6_big_uint224); } BOOST_AUTO_TEST_SUITE_END()