From 195ec4c863a44ed3e474550252c43d7b5cb4aca0 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Fri, 11 Oct 2024 21:49:29 +0000 Subject: [PATCH 1/9] Multiprecision refactoring WIP --- .../big_integer/big_integer.hpp | 6 + .../big_integer/big_integer_impl.hpp | 264 ++++++++ .../big_integer/big_integer_ops.hpp | 253 +++++++ .../big_integer/detail/config.hpp | 22 + .../multiprecision/big_integer/limits.hpp | 98 +++ .../multiprecision/big_integer/literals.hpp | 155 +++++ .../big_integer/modular/inverse.hpp | 444 +++++++++++++ .../modular/modular_big_integer.hpp | 20 + .../modular/modular_big_integer_impl.hpp | 255 ++++++++ .../modular/modular_big_integer_ops.hpp | 201 ++++++ .../big_integer/modular/modular_functions.hpp | 617 ++++++++++++++++++ .../big_integer/modular/modular_ops.hpp | 482 ++++++++++++++ .../big_integer/modular/modular_params.hpp | 203 ++++++ .../big_integer/modular/modular_policy.hpp | 43 ++ .../multiprecision/big_integer/ops/add.hpp | 174 +++++ .../big_integer/ops/add_unsigned.hpp | 291 +++++++++ .../big_integer/ops/bitwise.hpp | 359 ++++++++++ .../multiprecision/big_integer/ops/divide.hpp | 59 ++ .../big_integer/ops/eval_jacobi.hpp | 78 +++ .../big_integer/ops/import_export.hpp | 205 ++++++ .../big_integer/ops/integer_ops.hpp | 55 ++ .../multiprecision/big_integer/ops/misc.hpp | 268 ++++++++ .../big_integer/ops/multiply.hpp | 58 ++ .../multiprecision/big_integer/storage.hpp | 10 + .../nil/crypto3/multiprecision/literals.hpp | 3 + .../libs/multiprecision/test/CMakeLists.txt | 21 + .../libs/multiprecision/test/big_integer.cpp | 62 ++ .../test/big_integer_comparision.cpp | 81 +++ .../test/big_integer_modular.cpp | 43 ++ 29 files changed, 4830 insertions(+) create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/detail/config.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/literals.hpp create mode 100644 crypto3/libs/multiprecision/test/big_integer.cpp create mode 100644 crypto3/libs/multiprecision/test/big_integer_comparision.cpp create mode 100644 crypto3/libs/multiprecision/test/big_integer_modular.cpp diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer.hpp new file mode 100644 index 0000000000..4be34a65e4 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/limits.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" // IWYU pragma: export diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp new file mode 100644 index 0000000000..3a0ca0a4b9 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -0,0 +1,264 @@ +#pragma once + +// IWYU pragma: private; include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO(ioxid): replace with custom code +#include + +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +namespace nil::crypto3::multiprecision { + template + class big_integer { + public: + constexpr static unsigned Bits = Bits_; + using self_type = big_integer; + + using cpp_int_type = boost::multiprecision::number>; + + using unsigned_types = std::tuple; + using signed_types = std::tuple; + + // Storage + + using limb_type = limb_type; + using double_limb_type = double_limb_type; + using limb_pointer = limb_type*; + using const_limb_pointer = const limb_type*; + + static constexpr unsigned limb_bits = sizeof(limb_type) * CHAR_BIT; + static constexpr limb_type max_limb_value = ~static_cast(0u); + static constexpr unsigned internal_limb_count = + (Bits / limb_bits) + (((Bits % limb_bits) != 0u) ? 1 : 0); + static constexpr limb_type upper_limb_mask = + (Bits % limb_bits) ? (limb_type(1) << (Bits % limb_bits)) - 1 : (~limb_type(0)); + + // + // Helper functions for getting at our internal data, and manipulating storage: + // + inline constexpr unsigned size() const noexcept { + static_assert(internal_limb_count != 0, "No limbs in storage."); + return internal_limb_count; + } + inline constexpr limb_pointer limbs() noexcept { return m_data.data(); } + inline constexpr const_limb_pointer limbs() const noexcept { return m_data.data(); } + inline constexpr bool sign() const noexcept { return false; } + + // Zeros out everything after limb[i], replaces resizing. + inline constexpr void zero_after(std::size_t start_index) { + auto pr = this->limbs(); + for (std::size_t i = start_index; i < this->size(); ++i) { + pr[i] = 0; + } + } + inline constexpr bool has_carry() const noexcept { return m_carry; } + inline constexpr void set_carry(bool carry) noexcept { m_carry = carry; } + + inline constexpr void normalize() noexcept { + limb_pointer p = limbs(); + p[internal_limb_count - 1] &= upper_limb_mask; + } + + inline constexpr void do_swap(big_integer& other) noexcept { + for (unsigned i = 0; i < internal_limb_count; ++i) { + boost::multiprecision::std_constexpr::swap(m_data[i], other.m_data[i]); + } + } + + // Constructor + + inline constexpr big_integer() noexcept {} + + inline explicit constexpr big_integer(const cpp_int_type& other) { + this->from_cpp_int(other); + } + + // TODO(ioxid): forbid signed, implement comparison with signed instead + template /*&& std::is_unsigned_v*/, + bool> = true> + inline constexpr big_integer(UI val) noexcept { + if (val < 0) { + std::cerr << "big_integer: assignment from negative integer" << std::endl; + std::terminate(); + } + // TODO(ioxid): support assignment from uint64_t and uint128_t + do_assign_integral(static_cast(val)); + } + + // Move constructors + + inline constexpr big_integer(big_integer&& other) noexcept { do_assign(other); } + + template + inline constexpr big_integer(big_integer&& other) noexcept { + do_assign(other); + } + + // Copy construction + + inline constexpr big_integer(const big_integer& other) noexcept { do_assign(other); } + template + inline constexpr big_integer(const big_integer& other) noexcept { + do_assign(other); + } + + // Copy assignment + + inline constexpr auto& operator=(const big_integer& other) noexcept { + do_assign(other); + return *this; + } + template + inline constexpr big_integer& operator=(const big_integer& other) noexcept { + do_assign(other); + return *this; + } + + // Move assignment + + inline constexpr auto& operator=(big_integer&& other) noexcept { + do_assign(other); + return *this; + } + template + inline constexpr big_integer& operator=(big_integer&& other) noexcept { + do_assign(other); + return *this; + } + + // Assignment from other types + + // TODO(ioxid): forbid signed, implement comparison with signed instead + template + inline constexpr + typename std::enable_if_t /*&& std::is_unsigned_v*/, + big_integer&> + operator=(UI val) noexcept { + if (val < 0) { + std::cerr << "big_integer: assignment from negative integer" << std::endl; + std::terminate(); + } + do_assign_integral(val); + return *this; + } + + inline constexpr auto& operator=(const char* s) { + cpp_int_type value; + value = s; + this->from_cpp_int(value); + return *this; + } + inline constexpr void swap(big_integer& other) noexcept { this->do_swap(other); } + + ~big_integer() = default; + + inline std::string str(std::streamsize digits = 0, + std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { + // TODO(ioxid): rewrite without cpp_int + cpp_int_type value = to_cpp_int(); + return value.str(digits, f); + } + + // cpp_int conversion + + inline constexpr void from_cpp_int(const cpp_int_type& other) { + // Here we need other.size(), not this->size(), because cpp_int may not use all the + // limbs it has, but we will. + for (unsigned i = 0; i < other.backend().size(); ++i) { + this->limbs()[i] = other.backend().limbs()[i]; + } + // Zero out the rest. + for (unsigned i = other.backend().size(); i < this->size(); ++i) { + this->limbs()[i] = 0; + } + } + + // Converting to cpp_int. We need this for multiplication, division and string + // conversions. Since these operations are rare, there's no reason to implement then for + // big_integer, converting to cpp_int does not result to performance penalty. + inline constexpr cpp_int_type to_cpp_int() const { + cpp_int_type result; + // TODO(ioxid): not constexpr? + // result.backend().resize(this->size(), this->size()); + // for (unsigned i = 0; i < this->size(); ++i) { + // result.backend().limbs()[i] = this->limbs()[i]; + // } + // result.backend().normalize(); + return std::move(result); + } + + // cast to integral types + + template, bool> = true> + explicit inline constexpr operator T() const { + return static_cast(this->limbs()[0]); + } + + private: + inline constexpr void do_assign_integral(limb_type i) noexcept { + // TODO(ioxid): support assignment from uint64_t and uint128_t + *this->limbs() = i; + this->zero_after(1); + this->normalize(); + } + + inline constexpr void do_assign_integral(double_limb_type i) noexcept { + // TODO(ioxid): support assignment from uint128_t + static_assert(sizeof(i) == 2 * sizeof(limb_type), "Failed integer size check"); + auto p = this->limbs(); + *p = static_cast(i); + if (this->size() > 1) { + p[1] = static_cast(i >> limb_bits); + this->zero_after(2); + } + this->normalize(); + } + + template + inline constexpr void do_assign(const big_integer& other) noexcept { + unsigned count = (std::min)(other.size(), this->size()); + for (unsigned i = 0; i < count; ++i) { + this->limbs()[i] = other.limbs()[i]; + } + // Zero out everything after (std::min)(other.size(), this->size()), so if size of + // other was less, we have 0s at the end. + this->zero_after((std::min)(other.size(), this->size())); + this->normalize(); + } + + // m_data[0] contains the lowest bits. + std::array m_data{0}; + + // This is a temporary value which is set when carry has happend during addition. + // If this value is true, reduction by modulus must happen next. + bool m_carry = false; + }; + + // Comparisions + + template + inline constexpr int compare(const big_integer& a, const big_integer& b) noexcept { + auto pa = a.limbs(); + auto pb = b.limbs(); + for (int i = a.size() - 1; i >= 0; --i) { + if (pa[i] != pb[i]) { + return pa[i] > pb[i] ? 1 : -1; + } + } + return 0; + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp new file mode 100644 index 0000000000..6cdf17b8d9 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -0,0 +1,253 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" + +#include "nil/crypto3/multiprecision/big_integer/ops/add.hpp" +#include "nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp" +#include "nil/crypto3/multiprecision/big_integer/ops/divide.hpp" +#include "nil/crypto3/multiprecision/big_integer/ops/import_export.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/ops/misc.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/ops/multiply.hpp" + +namespace nil::crypto3::multiprecision { + namespace detail { + template + static constexpr bool always_false = false; + + template + constexpr bool is_big_integer_v = false; + + template + constexpr bool is_big_integer_v> = true; + + template + constexpr bool is_integral_v = std::is_integral_v || is_big_integer_v; + + template, bool> = true> + constexpr std::size_t get_bits() { + return sizeof(T) * CHAR_BIT; + } + + template, bool> = true> + constexpr std::size_t get_bits() { + return T::Bits; + } + } // namespace detail + +#define CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE \ + template && detail::is_integral_v && \ + (detail::is_big_integer_v || detail::is_big_integer_v), \ + bool> = true, \ + typename result_t = \ + big_integer(), detail::get_bits())>> + +#define CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ + template< \ + typename big_integer_t, typename T, \ + std::enable_if_t && detail::is_integral_v && \ + detail::get_bits() <= big_integer_t::Bits, \ + bool> = true> + +#define CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE \ + template, bool> = true> + + // Comparison + +#define CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(op) \ + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE \ + inline constexpr bool operator op(const T1& a, const T2& b) noexcept { \ + return compare(), detail::get_bits())>(a, b) op 0; \ + } + + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(<) + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(<=) + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(>) + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(>=) + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(==) + CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(!=) + + // TODO(ioxid): implement comparison with signed types, needed for boost::random +#undef CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR + + // Arithmetic operations + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator+(const T1& a, const T2& b) noexcept { + result_t tmp{a}; + eval_add(tmp, b); + return tmp; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator+=(big_integer_t& a, const T& b) noexcept { + eval_add(a, b); + return a; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator++(big_integer_t& a) noexcept { + eval_increment(a); + return a; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator++(big_integer_t& a, int) noexcept { + auto copy = a; + ++a; + return copy; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator+(const big_integer_t& a) noexcept { return a; } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator-(const T1& a, const T2& b) noexcept { + result_t tmp; + eval_subtract(tmp, a, b); + return tmp; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator-=(big_integer_t& a, const T& b) { + eval_subtract(a, b); + return a; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator--(big_integer_t& a) noexcept { + eval_decrement(a); + return a; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator--(big_integer_t& a, int) noexcept { + auto copy = a; + --a; + return copy; + } + + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr big_integer_t operator-(const big_integer_t& a) noexcept { + // TODO(ioxid): implement? + static_assert(detail::always_false, "can't negate unsigned type"); + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator*(const T1& a, const T2& b) noexcept { + result_t result{a}; + eval_multiply(result, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator*=(big_integer_t& a, const T& b) noexcept { + eval_multiply(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator/(const T1& a, const T2& b) noexcept { + result_t result; + eval_divide(result, a, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator/=(big_integer_t& a, const T& b) noexcept { + eval_divide(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator%(const T1& a, const T2& b) noexcept { + result_t result; + eval_modulus(result, a, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator%=(big_integer_t& a, const T& b) { + eval_modulus(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator&(const T1& a, const T2& b) noexcept { + result_t result{a}; + eval_bitwise_and(result, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator&=(big_integer_t& a, const T& b) { + eval_bitwise_and(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator|(const T1& a, const T2& b) noexcept { + result_t result{a}; + eval_bitwise_or(result, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator|=(big_integer_t& a, const T& b) { + eval_bitwise_or(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator^(const T1& a, const T2& b) noexcept { + result_t result{a}; + eval_bitwise_xor(result, b); + return result; + } + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator^=(big_integer_t& a, const T& b) { + eval_bitwise_or(a, b); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator~(const big_integer_t& a) noexcept { + big_integer_t result; + eval_complement(result, a); + return result; + } + + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator<<(const big_integer_t& a, unsigned shift) noexcept { + big_integer_t result{a}; + eval_left_shift(result, shift); + return result; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator<<=(big_integer_t& a, unsigned shift) noexcept { + // TODO(ioxid): check + eval_left_shift(a, shift); + return a; + } + + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator>>(const big_integer_t& a, unsigned shift) noexcept { + big_integer_t result{a}; + eval_right_shift(result, shift); + return result; + } + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator>>=(big_integer_t& a, unsigned shift) noexcept { + // TODO(ioxid): check + eval_right_shift(a, shift); + return a; + } + + // IO + + CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE + std::ostream& operator<<(std::ostream& os, const big_integer_t& value) { + // TODO(ioxid): rewrite without cpp_int + os << value.to_cpp_int() << std::endl; + return os; + } + +#undef CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE +#undef CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE +#undef CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/detail/config.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/detail/config.hpp new file mode 100644 index 0000000000..48f91c5388 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/detail/config.hpp @@ -0,0 +1,22 @@ +#pragma once + +#if !defined(NIL_CO3_MP_FORCEINLINE) + +#if defined(NDEBUG) && !defined(_DEBUG) + +#if defined(_MSC_VER) +#define NIL_CO3_MP_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ > 3 +// Clang also defines __GNUC__ (as 4) +#define NIL_CO3_MP_FORCEINLINE inline __attribute__((__always_inline__)) +#else +#define NIL_CO3_MP_FORCEINLINE inline +#endif + +#else + +#define NIL_CO3_MP_FORCEINLINE inline + +#endif + +#endif diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp new file mode 100644 index 0000000000..083d089a75 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2012 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt + +#pragma once + +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision { + template + class big_integer; +} + +namespace std { + namespace detail { + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4307) +#endif + + template + inline constexpr nil::crypto3::multiprecision::big_integer get_min() { + constexpr const nil::crypto3::multiprecision::big_integer val(0u); + return val; + } + + template + inline constexpr nil::crypto3::multiprecision::big_integer get_max() { + using result_type = nil::crypto3::multiprecision::big_integer; + using ui_type = nil::crypto3::multiprecision::big_integer; + constexpr const result_type val = ~ui_type(0); + return val; + } + + inline constexpr unsigned calc_digits10(unsigned d) { + // + // We need floor(log10(2) * (d-1)), see: + // https://www.exploringbinary.com/number-of-digits-required-for-round-trip-conversions/ + // and references therein. + // + return static_cast( + 0.301029995663981195213738894724493026768189881462108541310 * + static_cast(d - 1u)); + } + } // namespace detail + + template + class numeric_limits> { + using number_type = nil::crypto3::multiprecision::big_integer; + + public: + static constexpr bool is_specialized = true; + // + // Largest and smallest numbers are bounded only by available memory, set + // to zero: + // + static constexpr number_type(min)() { return detail::get_min(); } + static constexpr number_type(max)() { return detail::get_max(); } + static constexpr number_type lowest() { return (min)(); } + static constexpr int digits = number_type::Bits; + static constexpr int digits10 = detail::calc_digits10(digits); + static constexpr int max_digits10 = detail::calc_digits10(digits); + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr int radix = 2; + static constexpr number_type epsilon() { return 0; } + static constexpr number_type round_error() { return 0; } + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr number_type infinity() { return 0; } + static constexpr number_type quiet_NaN() { return 0; } + static constexpr number_type signaling_NaN() { return 0; } + static constexpr number_type denorm_min() { return 0; } + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr bool traps = false; + static constexpr bool tinyness_before = false; + }; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace std diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp new file mode 100644 index 0000000000..d48e5748aa --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2013 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt + +#pragma once + +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision::literals { + namespace detail { + template + constexpr big_integer parse_int_hex() { + static_assert(c1 == '0', "hex literal should start with 0x"); + static_assert(c2 == 'x', "hex literal should start with 0x"); + + constexpr std::size_t hex_digits = sizeof...(STR); + static_assert(Bits >= hex_digits * 4, "not enough bits to store literal"); + static_assert(hex_digits >= 1, "at least one digit expected"); + + big_integer result{0}; + + for (const char c : {STR...}) { + result <<= 4; + result += c - '0'; + } + return result; + } + } // namespace detail + + template + constexpr auto operator"" _big_integer() { + return detail::parse_int_hex(); + } + +#define CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(Bits) \ + template \ + constexpr auto operator"" _big_integer##Bits() { \ + return detail::parse_int_hex(); \ + } + // This is a comprehensive list of all bitlengths we use + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(7) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(13) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(15) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(16) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(17) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(18) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(64) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(92) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(94) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(128) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(130) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(149) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(150) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(151) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(152) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(160) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(161) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(163) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(164) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(177) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(178) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(179) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(180) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(181) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(182) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(183) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(191) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(192) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(205) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(206) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(222) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(223) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(224) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(225) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(226) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(239) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(248) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(249) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(250) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(251) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(252) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(253) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(254) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(255) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(256) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(257) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(263) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(264) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(280) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(281) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(292) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(293) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(294) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(295) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(296) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(297) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(298) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(315) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(316) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(319) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(320) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(330) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(331) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(374) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(375) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(376) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(377) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(378) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(379) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(380) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(381) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(384) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(503) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(504) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(507) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(512) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(515) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(516) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(521) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(546) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(577) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(578) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(585) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(595) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(636) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(706) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(707) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(758) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(753) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(759) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(761) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(859) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(860) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(893) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(894) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(913) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1024) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1490) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1536) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2048) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2790) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(3072) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4096) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4269) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4314) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(6144) + CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8192) + +#undef CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL +} // namespace nil::crypto3::multiprecision::literals diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp new file mode 100644 index 0000000000..0c5ce91606 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp @@ -0,0 +1,444 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2021 Aleksei Moskvin +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision { + using boost::multiprecision::default_ops::eval_add; + using boost::multiprecision::default_ops::eval_bit_set; + using boost::multiprecision::default_ops::eval_bit_test; + using boost::multiprecision::default_ops::eval_is_zero; + using boost::multiprecision::default_ops::eval_modulus; + using boost::multiprecision::default_ops::eval_subtract; + + template + constexpr Backend eval_extended_euclidean_algorithm(const Backend& num1, const Backend& num2, + Backend& bezout_x, Backend& bezout_y) { + Backend x, y, tmp_num1 = num1, tmp_num2 = num2; + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + y = ui_type(1u); + x = ui_type(0u); + + bezout_x = ui_type(1u); + bezout_y = ui_type(0u); + + // Extended Euclidean Algorithm + while (!eval_is_zero(tmp_num2)) { + Backend quotient = tmp_num1; + Backend remainder = tmp_num1; + Backend placeholder; + + eval_divide(quotient, tmp_num2); + eval_modulus(remainder, tmp_num2); + + tmp_num1 = tmp_num2; + tmp_num2 = remainder; + + Backend temp_x = x, temp_y = y; + eval_multiply(placeholder, quotient, x); + eval_subtract(placeholder, bezout_x, placeholder); + x = placeholder; + bezout_x = temp_x; + + eval_multiply(placeholder, quotient, y); + eval_subtract(placeholder, bezout_y, placeholder); + y = placeholder; + bezout_y = temp_y; + } + return tmp_num1; + } + + // a^(-1) mod p + // http://www-math.ucdenver.edu/~wcherowi/courses/m5410/exeucalg.html + template + constexpr void eval_inverse_extended_euclidean_algorithm(Backend& result, const Backend& a, + const Backend& m) { + using Backend_doubled = + typename boost::multiprecision::default_ops::double_precision_type::type; + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + + Backend aa = a, mm = m, x, y, g; + Backend zero = ui_type(0u); + g = eval_extended_euclidean_algorithm(aa, mm, x, y); + if (!eval_eq(g, ui_type(1u))) { + result = zero; + } else { + eval_modulus(x, m); + Backend_doubled tmp(x); + eval_add(tmp, m); + eval_modulus(tmp, m); + result = static_cast(tmp); + } + } + + // Overload the upper code for modular backends. We do not have negative numbers, + // so we will convert to cpp_int_backend to perform the operation and back. + template + constexpr void eval_inverse_extended_euclidean_algorithm(big_integer& result, + const big_integer& a, + const big_integer& m) { + // Careful here, we NEED signed magnitude numbers here. + using signed_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked>; + using unsigned_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::unsigned_magnitude, + boost::multiprecision::unchecked>; + + signed_cpp_int_type a_cpp_int = a.to_cpp_int(); + signed_cpp_int_type m_cpp_int = m.to_cpp_int(); + signed_cpp_int_type result_cpp_int; + eval_inverse_extended_euclidean_algorithm(result_cpp_int, a_cpp_int, m_cpp_int); + + // The result is always unsigned, so no problem here. + unsigned_cpp_int_type result_unsinged_cpp_int; + // Interestingly boost allows as to use operator= but not a constructor to convert + // from signed value to unsigned. + result_unsinged_cpp_int = result_cpp_int; + result.from_cpp_int(result_unsinged_cpp_int); + } + + template + constexpr void eval_inverse_mod_pow2(Backend& result, const Backend& a, const size_t& k) { + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + Backend tmp, zero, one, two; + zero = ui_type(0u); + one = ui_type(1u); + two = ui_type(2u); + + eval_modulus(tmp, a, two); + if (eval_is_zero(tmp) || k == 0) { + result = zero; + return; + } + + if (k == 1) { + result = one; + return; + } + + /* + * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç + * https://eprint.iacr.org/2017/411.pdf sections 5 and 7. + */ + Backend b = one; + Backend r; + for (size_t i = 0; i < k; ++i) { + if (eval_bit_test(b, 0)) { + eval_subtract(b, a); + eval_bit_set(r, i); + } + eval_right_shift(b, 1); + } + result = r; + } + + template + constexpr Backend eval_inverse_mod_odd(const Backend& n, const Backend& mod) { + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + Backend zero, one; + zero = ui_type(0u); + one = ui_type(1u); + // Caller should assure these preconditions: + // BOOST_ASSERT(eval_gt(n, 0) >= 0); + // BOOST_ASSERT(mod >= 0); + // BOOST_ASSERT(n < mod); + // BOOST_ASSERT(mod >= 3 && mod % 2 != 0); + + /* + This uses a modular inversion algorithm designed by Niels Möller + and implemented in Nettle. The same algorithm was later also + adapted to GMP in mpn_sec_invert. + + There is also a description of the algorithm in Appendix 5 of "Fast + Software Polynomial Multiplication on ARM Processors using the NEON Engine" + by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo + Dahab in LNCS 8182 + https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf + + */ + + Backend a = n; + Backend b = mod; + Backend u = one; + Backend v = zero; + + size_t ell = eval_msb(mod); + for (size_t i = 0; i < 2 * ell; ++i) { + size_t odd = eval_bit_test(a, 0); + size_t gteq = boost::multiprecision::default_ops::eval_gt(a, b) || + boost::multiprecision::default_ops::eval_eq(a, b); + if (odd && gteq) { + eval_subtract(a, b); + } else if (odd && !gteq) { + Backend u_tmp = u; + u = v; + v = u_tmp; + Backend tmp = a; + eval_subtract(a, b, a); + b = tmp; + } + eval_right_shift(a, 1); + size_t gteq2 = boost::multiprecision::default_ops::eval_gt(u, v) || + boost::multiprecision::default_ops::eval_eq(u, v); + if (odd && gteq2) { + eval_subtract(u, v); + } else if (odd && !gteq2) { + eval_add(u, mod); + eval_subtract(u, v); + } + + if (eval_bit_test(u, 0)) { + eval_add(u, u, mod); + } + eval_right_shift(u, 1); + } + if (!boost::multiprecision::default_ops::eval_eq( + b, one)) { // if b != 1 then gcd(n,mod) > 1 and inverse does not exist + return zero; + } + return v; + } + + template + constexpr void eval_inverse_mod(Backend& result, const Backend& n, const Backend& mod) { + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + Backend zero = ui_type(0u), one = ui_type(1u), tmp; + + BOOST_ASSERT(eval_gt(mod, ui_type(0u)) && eval_gt(n, ui_type(0u))); + + if (eval_is_zero(n) || (!eval_bit_test(n, 0) && !eval_bit_test(mod, 0))) { + result = zero; + return; + } + + if (eval_bit_test(mod, 0)) { + /* + Fastpath for common case. This leaks if n is greater than mod or + not, but we don't guarantee const time behavior in that case. + */ + eval_modulus(tmp, n, mod); + result = eval_inverse_mod_odd(tmp, mod); + return; + } + + // If n is even and mod is even we already returned 0 + // If n is even and mod is odd we jumped directly to odd-modulus algo + const size_t mod_lz = eval_lsb(mod); + const size_t mod_mz = eval_msb(mod); + + if (mod_lz == mod_mz) { + // In this case we are performing an inversion modulo 2^k + eval_inverse_mod_pow2(result, n, mod_lz); + return; + } + + if (mod_lz == 1) { + /* + Inversion modulo 2*o is an easier special case of CRT + + This is exactly the main CRT flow below but taking advantage of + the fact that any odd number ^-1 modulo 2 is 1. As a result both + inv_2k and c can be taken to be 1, m2k is 2, and h is always + either 0 or 1, and its value depends only on the low bit of inv_o. + + This is worth special casing because we generate RSA primes such + that phi(n) is of this form. However this only works for keys + that we generated in this way; pre-existing keys will typically + fall back to the general algorithm below. + */ + + Backend o = mod; + eval_right_shift(o, 1); + Backend n_redc; + eval_modulus(n_redc, n, o); + const Backend inv_o = eval_inverse_mod_odd(n_redc, o); + + // No modular inverse in this case: + if (eval_is_zero(inv_o)) { + result = zero; + return; + } + + Backend h = inv_o; + + if (!eval_bit_test(inv_o, 0)) { + eval_add(h, o); + } + result = h; + return; + } + + /* + * In this case we are performing an inversion modulo 2^k*o for + * some k >= 2 and some odd (not necessarily prime) integer. + * Compute the inversions modulo 2^k and modulo o, then combine them + * using CRT, which is possible because 2^k and o are relatively prime. + */ + + Backend o = mod; + eval_right_shift(o, mod_lz); + Backend n_redc = n; + eval_modulus(n_redc, o); + const Backend inv_o = eval_inverse_mod_odd(n_redc, o); + Backend inv_2k; + eval_inverse_mod_pow2(inv_2k, n, mod_lz); + + // No modular inverse in this case: + if (eval_is_zero(inv_o) || eval_is_zero(inv_2k)) { + result = zero; + return; + } + + Backend m2k = one; + eval_left_shift(m2k, mod_lz); + // Compute the CRT parameter + Backend c; + eval_inverse_mod_pow2(c, o, mod_lz); + + // Compute h = c*(inv_2k-inv_o) mod 2^k + Backend h; + eval_subtract(h, inv_2k, inv_o); + eval_multiply(h, c); + Backend tmp3 = one; + eval_left_shift(tmp3, mod_lz); + eval_subtract(tmp3, one); + eval_bitwise_and(h, tmp3); + + // Return result inv_o + h * o + eval_multiply(h, o); + eval_add(h, inv_o); + result = h; + } + + // Overload the upper code for modular backends. We do not have negative numbers, + // so we will convert to cpp_int_backend to perform the operation and back. + template + constexpr void eval_inverse_mod(big_integer& result, const big_integer& n, + const big_integer& mod) { + // Careful here, we NEED signed magnitude numbers here. + using signed_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked>; + using unsigned_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::unsigned_magnitude, + boost::multiprecision::unchecked>; + + signed_cpp_int_type n_cpp_int = n.to_cpp_int(); + signed_cpp_int_type mod_cpp_int = mod.to_cpp_int(); + signed_cpp_int_type result_cpp_int; + eval_inverse_mod(result_cpp_int, n_cpp_int, mod_cpp_int); + + // The result is always unsigned, so no problem here. + unsigned_cpp_int_type result_unsinged_cpp_int; + // Interestingly boost allows as to use operator= but not a constructor to convert + // from signed value to unsigned. + result_unsinged_cpp_int = result_cpp_int; + result.from_cpp_int(result_unsinged_cpp_int); + } + + /* + * Compute the inversion number mod p^k. + * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç. + * @see https://eprint.iacr.org/2017/411.pdf sections 5 and 7. + * + * @param a is a non-negative integer + * @param p is a prime number, where gcd(a,p) = 1 + * @param k is a non-negative integer, where a < p^k + * @return x = a^(−1) mod p^k + */ + template + constexpr void eval_monty_inverse(Backend& res, const Backend& a, const Backend& p, + const Backend& k) { + using boost::multiprecision::default_ops::eval_abs; + using boost::multiprecision::default_ops::eval_eq; + using boost::multiprecision::default_ops::eval_gt; + using boost::multiprecision::default_ops::eval_modulus; + using boost::multiprecision::default_ops::eval_subtract; + + using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type; + Backend zero, one, two; + zero = ui_type(0u); + one = ui_type(1u); + two = ui_type(2u); + + /* + * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç + * https://eprint.iacr.org/2017/411.pdf sections 5 and 7. + */ + Backend c, tmp; + + // a^(-1) mod p: + eval_inverse_mod(c, a, p); + + Backend bi = one, bt, i = zero, xi, nextp = one; + res = zero; + + while (!eval_eq(i, k)) { + // xi: + xi = bi; + eval_multiply(xi, c); + eval_modulus(xi, p); + + if (eval_get_sign(xi) < 0) { + tmp = xi; + eval_abs(tmp, tmp); + eval_modulus(tmp, p); + xi = p; + eval_subtract(xi, tmp); + } + + // bi: + tmp = a; + eval_multiply(tmp, xi); + eval_subtract(bi, tmp); + eval_divide(bi, p); + + // res: + tmp = xi; + eval_multiply(tmp, nextp); + eval_multiply(nextp, p); + eval_add(res, tmp); + eval_add(i, one); + } + } + + // Overload the upper code for modular backends. We do not have negative numbers, + // so we will convert to cpp_int_backend to perform the operation and back. + template + constexpr void eval_monty_inverse(big_integer& res, const big_integer& a, + const big_integer& p, const big_integer& k) { + // Careful here, we NEED signed magnitude numbers here. + using signed_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked>; + using unsigned_cpp_int_type = boost::multiprecision::backends::cpp_int_backend< + Bits, Bits, boost::multiprecision::unsigned_magnitude, + boost::multiprecision::unchecked>; + + signed_cpp_int_type a_cpp_int = a.to_cpp_int(); + signed_cpp_int_type p_cpp_int = p.to_cpp_int(); + signed_cpp_int_type k_cpp_int = k.to_cpp_int(); + signed_cpp_int_type result_cpp_int; + eval_monty_inverse(result_cpp_int, a_cpp_int, p_cpp_int, k_cpp_int); + + // The result is always unsigned, so no problem here. + unsigned_cpp_int_type result_unsinged_cpp_int; + // Interestingly boost allows as to use operator= but not a constructor to convert + // from signed value to unsigned. + result_unsinged_cpp_int = result_cpp_int; + res.from_cpp_int(result_unsinged_cpp_int); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp new file mode 100644 index 0000000000..da6b087bea --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" + +namespace nil::crypto3::multiprecision { + template + using modular_big_integer_ct = + modular_big_integer, + modular_params_storage_ct, modulus>>; + + // TODO(ioxid): modulus in constructor + template + using modular_big_integer_rt = + modular_big_integer, modular_params_storage_rt>>; +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp new file mode 100644 index 0000000000..a88290cfa3 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2021 Aleksei Moskvin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +// IWYU pragma: private; include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp" + +#include +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" + +namespace nil::crypto3::multiprecision { + // fixed precision modular big integer which supports compile-time execution + template + class modular_big_integer { + public: + constexpr static auto Bits = big_integer_t::Bits; + using limb_type = typename big_integer_t::limb_type; + using double_limb_type = typename big_integer_t::double_limb_type; + using modular_params_t = modular_params; + using Backend_type = big_integer_t; + + using unsigned_types = typename big_integer_t::unsigned_types; + using signed_types = typename big_integer_t::signed_types; + + protected: + using policy_type = typename modular_params_t::policy_type; + using Backend_padded_limbs = typename policy_type::Backend_padded_limbs; + using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; + modular_params_storage_t modular_params_storage; + + public: + // This version of conversion + constexpr typename big_integer_t::cpp_int_type to_cpp_int() const { + big_integer_t tmp; + modular_params_storage.modular_params().adjust_regular(tmp, this->base_data()); + return tmp.to_cpp_int(); + } + + constexpr auto mod_data() { return modular_params_storage.modular_params(); } + constexpr auto mod_data() const { return modular_params_storage.modular_params(); } + + constexpr big_integer_t& base_data() { return m_base; } + constexpr const big_integer_t& base_data() const { return m_base; } + + constexpr modular_big_integer() {} + + constexpr modular_big_integer(const modular_big_integer& o) : m_base(o.base_data()) { + modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); + } + + constexpr modular_big_integer(modular_big_integer&& o) noexcept + : m_base(std::move(o.base_data())) { + modular_params_storage.set_modular_params( + std::move(o.modular_params_storage.modular_params())); + } + + template && + std::is_unsigned_v> const* = nullptr> + constexpr modular_big_integer(UI b, const big_integer_t& m) : m_base(limb_type(b)) { + modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base); + } + + // A method for converting a signed integer to a modular adaptor. We are not supposed to + // have this, but in the code we already have conversion for an 'int' into modular type. In + // the future we must remove. + template && + std::is_signed_v> const* = nullptr> + constexpr modular_big_integer(SI b) : m_base(limb_type(0u)) { + if (b >= 0) { + m_base = static_cast(b); + } else { + m_base = modular_params_storage.modular_params().get_mod(); + eval_subtract(m_base, static_cast(-b)); + } + + // This method must be called only for compile time modular params. + // modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base); + } + + template && + std::is_unsigned_v> const* = nullptr> + constexpr modular_big_integer(UI b) : m_base(static_cast(b)) { + // This method must be called only for compile time modular params. + // modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base); + } + + template && + std::is_signed_v> const* = nullptr> + constexpr modular_big_integer(SI b, const modular_params_t& m) : m_base(limb_type(0u)) { + if (b >= 0) { + m_base = static_cast(b); + } else { + m_base = modular_params_storage.modular_params().get_mod(); + eval_subtract(m_base, static_cast(-b)); + } + + modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base); + } + + template && + std::is_unsigned_v> const* = nullptr> + constexpr modular_big_integer(UI b, const modular_params_t& m) + : m_base(static_cast(b)) { + modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base); + } + + // TODO + // // We may consider to remove this constructor later, and set Bits2 to Bits only, + // // but we need it for use cases from h2f/h2c, + // // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. + // template + // constexpr modular_big_integer(const number> &b, + // const number &m) { + // modular_params_storage.set_modular_params(m.big_integer_t()); + // modular_params_storage.modular_params().adjust_modular(m_base, b.big_integer_t()); + // } + + // We may consider to remove this constructor later, and set Bits2 to Bits only, + // but we need it for use cases from h2f/h2c, + // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. + template + constexpr modular_big_integer(const big_integer& b, const modular_params_t& m) { + modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base, b); + } + + // We may consider to remove this constructor later, and set Bits2 to Bits only, + // but we need it for use cases from h2f/h2c, + // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. + template + constexpr explicit modular_big_integer(const big_integer& b) { + // This method must be called only for compile time modular params. + // modular_params_storage.set_modular_params(m); + modular_params_storage.modular_params().adjust_modular(m_base, b); + } + + // This function sets default modulus value to zero to make sure it fails if not used with + // compile-time fixed modulus. + modular_big_integer& operator=(const char* s) { + using ui_type = typename std::tuple_element<0, unsigned_types>::type; + ui_type zero = 0u; + + if (s && (*s == '(')) { + std::string part; + const char* p = ++s; + while (*p && (*p != ',') && (*p != ')')) { + ++p; + } + part.assign(s, p); + if (!part.empty()) { + m_base = part.c_str(); + } else { + m_base = zero; + } + s = p; + if (*p && (*p != ')')) { + ++p; + while (*p && (*p != ')')) { + ++p; + } + part.assign(s + 1, p); + } else { + part.erase(); + } + if (!part.empty()) { + modular_params_storage.set_modular_params(part.c_str()); + } else { + modular_params_storage.set_modular_params(zero); + } + } else { + m_base = s; + modular_params_storage.set_modular_params(zero); + } + return *this; + } + + constexpr bool compare_eq(const modular_big_integer& o) const { + return !(modular_params_storage.modular_params()) + .compare(o.modular_params_storage.modular_params()) && + !base_data().compare(o.base_data()); + } + + template + constexpr int compare_eq(const T& val) const { + return !base_data().compare(val); + } + + constexpr modular_big_integer& operator=(const modular_big_integer& o) { + m_base = o.base_data(); + modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); + + return *this; + } + + constexpr modular_big_integer& operator=(modular_big_integer&& o) noexcept { + m_base = o.base_data(); + modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); + + return *this; + } + + ~modular_big_integer() = default; + + // If we want to print a value, we must first convert it back to normal form. + inline std::string str(std::streamsize dig, std::ios_base::fmtflags f) const { + big_integer_t tmp; + modular_params_storage.modular_params().adjust_regular(tmp, m_base); + return tmp.str(dig, f); + } + + inline constexpr void negate() { + if (m_base == m_zero) { + auto initial_m_base = m_base; + m_base = modular_params_storage.modular_params().get_mod(); + eval_subtract(m_base, initial_m_base); + } + } + + protected: + big_integer_t m_base; + static constexpr big_integer_t m_zero = + static_cast::type>(0u); + ; + }; + + template + constexpr void assign_components( + modular_big_integer, modular_params_storage_t>& result, + const big_integer_t1& a, const big_integer_t2& b) { + BOOST_ASSERT_MSG(Bits == eval_msb(b) + 1, + "modulus precision should match used big_integer_t"); + + result.set_modular_params(b); + result.modular_params().adjust_modular(result.base_data(), a); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp new file mode 100644 index 0000000000..4dbeb46187 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -0,0 +1,201 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" + +namespace nil::crypto3::multiprecision { + + // Comparison + +// TODO(ioxid): comparison with big_integer and basic types (including signed) +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(op) \ + template \ + inline constexpr bool operator op( \ + const modular_big_integer& a, \ + const modular_big_integer& b) noexcept { \ + return a.compare_eq(b) op true; \ + } + + CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(==) + CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(!=) +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL + + namespace modular_detail { + template + static constexpr bool always_false = false; + + template + constexpr bool is_modular_big_integer_v = false; + + template + constexpr bool + is_modular_big_integer_v> = true; + + template + constexpr bool is_integral_v = + std::is_integral_v || detail::is_big_integer_v || is_modular_big_integer_v; + } // namespace modular_detail + + namespace detail { + template, bool> = true> + constexpr std::size_t get_bits() { + return T::Bits; + } + } // namespace detail + + // TODO(ioxid): choose result type +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE \ + template< \ + typename T1, typename T2, \ + std::enable_if_t && modular_detail::is_integral_v && \ + (modular_detail::is_modular_big_integer_v || \ + modular_detail::is_modular_big_integer_v), \ + bool> = true, \ + typename result_t = T1> + +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ + template && \ + modular_detail::is_integral_v && \ + detail::get_bits() <= modular_big_integer_t::Bits, \ + bool> = true> + +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE \ + template, \ + bool> = true> + + // Arithmetic operations + + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator+(const T1& a, const T2& b) noexcept { + result_t tmp{a}; + // eval_add(tmp, b); + return tmp; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator+=(modular_big_integer_t& a, const T& b) noexcept { + // eval_add(a, b); + return a; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator++(modular_big_integer_t& a) noexcept { + // eval_increment(a); + return a; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator++(modular_big_integer_t& a, int) noexcept { + auto copy = a; + // eval_increment(a); + return copy; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator+(const modular_big_integer_t& a) noexcept { return a; } + + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator-(const T1& a, const T2& b) noexcept { + result_t tmp; + // eval_subtract(tmp, a, b); + return tmp; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator-=(modular_big_integer_t& a, const T& b) { + // eval_subtract(a, b); + return a; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto& operator--(modular_big_integer_t& a) noexcept { + // eval_decrement(a); + return a; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr auto operator--(modular_big_integer_t& a, int) noexcept { + auto copy = a; + // eval_decrement(a); + return copy; + } + + CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE + inline constexpr modular_big_integer_t operator-(const modular_big_integer_t& a) noexcept { + modular_big_integer_t tmp{a}; + tmp.negate(); + return tmp; + } + + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator*(const T1& a, const T2& b) noexcept { + result_t result{a}; + eval_multiply(result, b); + return result; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator*=(modular_big_integer_t& a, const T& b) noexcept { + // eval_multiply(a, b); + return a; + } + + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE + inline constexpr auto operator/(const T1& a, const T2& b) noexcept { + result_t result; + // eval_divide(result, a, b); + return result; + } + CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE + inline constexpr auto& operator/=(modular_big_integer_t& a, const T& b) noexcept { + // eval_divide(a, b); + return a; + } + + // IO + + template + std::ostream& operator<<( + std::ostream& os, const modular_big_integer, modular_params_t>& value) { + os << value.base_data(); + return os; + } + +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE +} // namespace nil::crypto3::multiprecision + +// TODO(ioxid): should use this optimization? +// // We need to specialize this function, because default boost implementation is "return +// a.compare(b) +// // == 0;", which is waay slower. +// template inline constexpr bool operator==( +// const number, modular_params_t>, +// ExpressionTemplates> &a, +// const number, modular_params_t>, +// ExpressionTemplates> &b) { +// return a.big_integer_t().compare_eq(b.big_integer_t()); +// } +// +// // We need to specialize this function, because default boost implementation is "return +// a.compare(b) +// // == 0;", which is waay slower. +// template inline constexpr bool operator!=( +// const number, modular_params_t>, +// ExpressionTemplates> &a, +// const number, modular_params_t>, +// ExpressionTemplates> &b) { +// return !a.big_integer_t().compare_eq(b.big_integer_t()); +// } +// } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp new file mode 100644 index 0000000000..33c8aa9376 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp @@ -0,0 +1,617 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2021 Aleksei Moskvin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +namespace nil::crypto3::multiprecision { + template + constexpr bool check_montgomery_constraints(const big_integer_t &m) { + // Check m % 2 == 0 + // It's important to have std::size_t on the next line, + // otherwise a function from boost is called, which is not constexpr + // on gcc. + return eval_bit_test(m, std::size_t(0)); + } + + // TODO(ioxid): rewrite it + // + // a little trick to prevent error in constexpr execution of + // eval_right_shift due to non-constexpr nature of right_shift_byte + // + template + constexpr void custom_right_shift(big_integer_t &b, unsigned s) { + if (!s) { + return; + } + + limb_type byte_shift_mask = CHAR_BIT - 1; + if ((s & byte_shift_mask) == 0) { + eval_right_shift(b, s - 1u); + eval_right_shift(b, 1u); + } else { + eval_right_shift(b, s); + } + } + + template + class modular_functions { + public: + constexpr static unsigned Bits = big_integer_t::Bits; + using policy_type = modular_policy; + + protected: + using limb_type = typename policy_type::limb_type; + using double_limb_type = typename policy_type::double_limb_type; + + using Backend_doubled_1 = typename policy_type::Backend_doubled_1; + using Backend_quadruple_1 = typename policy_type::Backend_quadruple_1; + using Backend_padded_limbs = typename policy_type::Backend_padded_limbs; + using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; + using Backend_doubled_padded_limbs = typename policy_type::Backend_doubled_padded_limbs; + + constexpr static auto limbs_count = policy_type::limbs_count; + constexpr static auto limb_bits = policy_type::limb_bits; + + constexpr void initialize_modulus(const big_integer_t &m) { m_mod = m; } + + constexpr void initialize_barrett_params() { + m_barrett_mu = static_cast(0u); + + size_t bit = 2u * (1u + eval_msb(m_mod)); + eval_bit_set(m_barrett_mu, bit); + + // TODO(ioxid): not constexpr + // m_barrett_mu /= m_mod; + } + + constexpr void initialize_montgomery_params() { find_const_variables(); } + + /* + * Compute -input^-1 mod 2^limb_bits. Throws an exception if input + * is even. If input is odd, then input and 2^n are relatively prime + * and an inverse exists. + */ + constexpr limb_type monty_inverse(const limb_type &a) { + limb_type b = 1; + limb_type r = 0; + + for (size_t i = 0; i != limb_bits; ++i) { + const limb_type bi = b % 2; + r >>= 1; + r += bi << (limb_bits - 1); + + b -= a * bi; + b >>= 1; + } + + // Now invert in addition space + r = (~static_cast(0) - r) + 1; + + return r; + } + + constexpr void find_const_variables() { + if (check_montgomery_constraints(m_mod)) { + m_montgomery_p_dash = monty_inverse(m_mod.limbs()[0]); + + Backend_doubled_padded_limbs r; + eval_bit_set(r, 2 * m_mod.size() * limb_bits); + barrett_reduce(r); + + // Here we are intentionally throwing away half of the bits of r, it's + // correct. + m_montgomery_r2 = static_cast(r); + } + + // Compute 2^Bits - Modulus, no matter if modulus is even or odd. + Backend_padded_limbs compliment = static_cast(1u), modulus = m_mod; + eval_left_shift(compliment, Bits); + eval_subtract(compliment, modulus); + m_mod_compliment = compliment; + } + + constexpr void initialize(const big_integer_t &m) { + initialize_modulus(m); + initialize_barrett_params(); + initialize_montgomery_params(); + + m_no_carry_montgomery_mul_allowed = is_applicable_for_no_carry_montgomery_mul(); + } + + public: + constexpr auto &get_mod() { return m_mod; } + constexpr const auto &get_mod_compliment() const { return m_mod_compliment; } + constexpr auto &get_mu() { return m_barrett_mu; } + constexpr auto &get_r2() { return m_montgomery_r2; } + constexpr auto &get_p_dash() { return m_montgomery_p_dash; } + + constexpr const auto &get_mod() const { return m_mod; } + constexpr const auto &get_mu() const { return m_barrett_mu; } + constexpr const auto &get_r2() const { return m_montgomery_r2; } + constexpr auto get_p_dash() const { return m_montgomery_p_dash; } + + constexpr modular_functions() {} + + constexpr modular_functions(const big_integer_t &m) { initialize(m); } + + constexpr modular_functions(const modular_functions &o) + : m_mod(o.get_mod()), + m_mod_compliment(o.get_mod_compliment()), + m_barrett_mu(o.get_mu()), + m_montgomery_r2(o.get_r2()), + m_montgomery_p_dash(o.get_p_dash()), + m_no_carry_montgomery_mul_allowed(is_applicable_for_no_carry_montgomery_mul()) {} + + template + constexpr void barrett_reduce(Backend1 &result) const { + barrett_reduce(result, result); + } + + // + // this overloaded barrett_reduce is intended to work with built-in integral types + // + template + constexpr typename std::enable_if::value && + std::is_unsigned::value>::type + barrett_reduce(Backend1 &result, Backend2 input) const { + using input_backend_type = + typename std::conditional_t Bits), Backend2, big_integer_t>; + + input_backend_type input_adjusted(input); + barrett_reduce(result, input_adjusted); + } + + // + // this overloaded barrett_reduce is intended to work with input Backend2 type of + // less precision than modular big_integer_t to satisfy constraints of core barrett_reduce + // overloading + // + template = true> + constexpr void barrett_reduce(Backend1 &result, const Backend2 &input) const { + big_integer_t input_adjusted(input); + barrett_reduce(result, input_adjusted); + } + + template= big_integer_t::Bits && + /// to prevent problems with trivial cpp_int + Backend2::Bits >= big_integer_t::Bits, + bool> = true> + constexpr void barrett_reduce(Backend1 &result, Backend2 input) const { + // + // to prevent problems with trivial cpp_int + // + Backend2 modulus(m_mod); + + if (eval_msb(input) < 2u * eval_msb(modulus) + 1u) { + Backend_quadruple_1 t1(input); + + eval_multiply(t1, m_barrett_mu); + std::size_t shift_size = 2u * (1u + eval_msb(modulus)); + custom_right_shift(t1, shift_size); + eval_multiply(t1, modulus); + + // We do NOT allow subtracting a larger size number from a smaller one, + // we need to cast to Backend2 here. + eval_subtract(input, static_cast(t1)); + + if (input >= modulus) { + eval_subtract(input, modulus); + } + } else { + // TODO(ioxid): not constexpr + // eval_modulus(input, modulus); + } + result = input; + } + + template= Bits>::type> + constexpr void montgomery_reduce(big_integer &result) const { + Backend_doubled_padded_limbs accum(result); + Backend_doubled_padded_limbs prod; + + for (size_t i = 0; i < m_mod.size(); ++i) { + limb_type limb_accum = accum.limbs()[i]; + double_limb_type mult_res = limb_accum * + /// to prevent overflow error in constexpr + static_cast(m_montgomery_p_dash); + limb_type mult_res_limb = static_cast(mult_res); + + eval_multiply(prod, m_mod, mult_res_limb); + eval_left_shift(prod, i * limb_bits); + eval_add(accum, prod); + } + custom_right_shift(accum, m_mod.size() * limb_bits); + // We cannot use eval_subtract for numbers of difference sizes, so resizing + // m_mod. + Backend_doubled_padded_limbs large_mod = m_mod; + if (accum >= large_mod) { + eval_subtract(accum, large_mod); + } + // Here only the bytes that fit in sizeof result will be copied, and that's + // intentional. + result = accum; + } + + template= Bits2>::type> + constexpr void regular_add(big_integer &result, const big_integer &y) const { + BOOST_ASSERT(eval_lt(result, m_mod) && eval_lt(y, m_mod)); + + eval_add(result, y); + // If we overflow and set the carry, we need to subtract the modulus, which is + // the same as adding 2 ^ Bits - Modulus to the remaining part of the number. + // After this we know for sure that the result < Modulus, do not waste time on + // checking again. + if (result.has_carry()) { + eval_add(result, m_mod_compliment); + result.set_carry(false); + } else if (!eval_lt(result, m_mod)) { + eval_subtract(result, m_mod); + } + } + + template< + typename Backend1, typename Backend2, + /// result should fit in the output parameter + typename = typename boost::enable_if_c= big_integer_t::Bits>::type> + constexpr void regular_mul(Backend1 &result, const Backend2 &y) const { + Backend_doubled_limbs tmp(result); + eval_multiply(tmp, y); + barrett_reduce(result, tmp); + } + + // Delegates Montgomery multiplication to one of corresponding algorithms. + constexpr void montgomery_mul(big_integer_t &result, const big_integer_t &y) const { + if (m_no_carry_montgomery_mul_allowed) { + montgomery_mul_no_carry_impl(result, y); + } else { + montgomery_mul_CIOS_impl(result, y); + } + } + + // Given a value represented in 'double_limb_type', decomposes it into + // two 'limb_type' variables, based on high order bits and low order bits. + // There 'a' receives high order bits of 'X', and 'b' receives the low order bits. + static constexpr void dbl_limb_to_limbs(const double_limb_type &X, limb_type &a, + limb_type &b) { + b = X; + a = X >> limb_bits; + } + + // Tests if the faster implementation of Montgomery multiplication is possible. + // We don't need the template argument Backend1, it's just here to enable + // specialization. + template + constexpr bool is_applicable_for_no_carry_montgomery_mul() const { + // Check that + // 1. The most significant bit of modulus is non-zero, meaning we have at least + // 1 additional bit in the number. I.E. if modulus is 255 bits, then we have 1 + // additional "unused" bit in the number. + // 2. Some other bit in modulus is 0. + // 3. The number has < 12 limbs. + return m_mod.internal_limb_count < 12 && (Bits % sizeof(limb_type) != 0) && + m_mod_compliment != big_integer_t(limb_type(1u)); + } + + // Non-carry implementation of Montgomery multiplication. + // Implemented from pseudo-code at + // "https://hackmd.io/@gnark/modular_multiplication". + template + constexpr void montgomery_mul_no_carry_impl(Backend1 &c, const Backend1 &b) const { + BOOST_ASSERT(c < m_mod && b < m_mod); + BOOST_ASSERT(is_applicable_for_no_carry_montgomery_mul()); + + // Obtain number of limbs + constexpr int N = Backend1::internal_limb_count; + + const Backend1 a(c); // Copy the first argument, as the implemented + // algorithm doesn't work in-place. + + // We cannot write directly to 'c', because b may be equal to c, and by changing + // the value of 'c' we will change 'b' as well. + Backend1 result = limb_type(0u); + + // Prepare temporary variables + limb_type A(0u), C(0u); + double_limb_type tmp(0u); + limb_type dummy(0u); + + auto *a_limbs = a.limbs(); + auto *b_limbs = b.limbs(); + auto *result_limbs = result.limbs(); + auto *m_mod_limbs = m_mod.limbs(); + + for (int i = 0; i < N; ++i) { + // "(A,t[0]) := t[0] + a[0]*b[i]" + tmp = a_limbs[0]; + tmp *= b_limbs[i]; + tmp += result_limbs[0]; + modular_functions::dbl_limb_to_limbs(tmp, A, result_limbs[0]); + + // "m := t[0]*q'[0] mod W" + tmp = result_limbs[0]; + // tmp *= q.limbs()[0]; + tmp *= m_montgomery_p_dash; + // tmp = -tmp; + // Note that m is a shorter integer, and we are taking the last bits of tmp. + limb_type m = tmp; + + // "(C,_) := t[0] + m*q[0]" + tmp = m; + tmp *= m_mod_limbs[0]; + tmp += result_limbs[0]; + modular_functions::dbl_limb_to_limbs(tmp, C, dummy); + + // The lower loop is unrolled. We want to do this for every 3, because + // normally mod_size == 4. + std::size_t j = 1; + +#define MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(X) \ + /* "(A,t[X]) := t[X] + a[X]*b[i] + A" */ \ + tmp = a_limbs[X]; \ + tmp *= b_limbs[i]; \ + tmp += result_limbs[X]; \ + tmp += A; \ + modular_functions::dbl_limb_to_limbs(tmp, A, result_limbs[X]); \ + \ + /* "(C,t[X-1]) := t[X] + m*q[X] + C" */ \ + tmp = m; \ + tmp *= m_mod_limbs[X]; \ + tmp += result_limbs[X]; \ + tmp += C; \ + modular_functions::dbl_limb_to_limbs(tmp, C, result_limbs[X - 1]); + + for (; j + 5 <= N; j += 5) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 3); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 4); + } + + for (; j + 3 <= N; j += 3) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); + } + + for (; j < N; ++j) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + } + + // "t[N-1] = C + A" + result_limbs[N - 1] = C + A; + } + + if (result >= m_mod) { + eval_subtract(result, m_mod); + } + c = result; + } + + // A specialization for non-trivial cpp_int_modular types only. + template + constexpr void montgomery_mul_CIOS_impl(Backend1 &result, const Backend1 &y) const { + BOOST_ASSERT(result < m_mod && y < m_mod); + + big_integer_t A(limb_type(0u)); + const std::size_t mod_size = m_mod.size(); + auto *mod_limbs = m_mod.limbs(); + auto mod_last_limb = static_cast(mod_limbs[0]); + auto y_last_limb = y.limbs()[0]; + auto *y_limbs = y.limbs(); + auto *x_limbs = result.limbs(); + auto *A_limbs = A.limbs(); + limb_type carry = 0; // This is the highest limb of 'A'. + + limb_type x_i = 0; + limb_type A_0 = 0; + limb_type u_i = 0; + + // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right + limb_type k = 0; + limb_type k2 = 0; + + double_limb_type z = 0; + double_limb_type z2 = 0; + + for (std::size_t i = 0; i < mod_size; ++i) { + x_i = x_limbs[i]; + A_0 = A_limbs[0]; + u_i = (A_0 + x_i * y_last_limb) * m_montgomery_p_dash; + + // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right + k = 0; + k2 = 0; + + z = static_cast(y_last_limb) * + static_cast(x_i) + + A_0 + k; + z2 = mod_last_limb * static_cast(u_i) + + static_cast(z) + k2; + k = static_cast(z >> std::numeric_limits::digits); + k2 = static_cast(z2 >> std::numeric_limits::digits); + + std::size_t j = 1; + + // The lower loop is unrolled. We want to do this for every 3, because + // normally mod_size == 4. + double_limb_type t = 0, t2 = 0; + +#define MONTGOMERY_MUL_CIOS_LOOP_BODY(X) \ + t = static_cast(y_limbs[X]) * static_cast(x_i) + \ + A_limbs[X] + k; \ + t2 = static_cast(mod_limbs[X]) * static_cast(u_i) + \ + static_cast(t) + k2; \ + A_limbs[X - 1] = static_cast(t2); \ + k = static_cast(t >> std::numeric_limits::digits); \ + k2 = static_cast(t2 >> std::numeric_limits::digits); + + for (; j + 5 <= mod_size; j += 5) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 3); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 4); + } + + for (; j + 3 <= mod_size; j += 3) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); + } + + for (; j < mod_size; ++j) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + } + + double_limb_type tmp = static_cast(carry) + k + k2; + A_limbs[mod_size - 1] = static_cast(tmp); + carry = static_cast(tmp >> std::numeric_limits::digits); + } + + if (carry) { + // The value of A is actually A + 2 ^ Bits, so remove that 2 ^ Bits. + eval_add(A, m_mod_compliment); + } else if (A >= m_mod) { + eval_subtract(A, m_mod); + } + + result = A; + } + + template< + typename Backend1, typename Backend2, typename Backend3, + /// result should fit in the output parameter + typename = typename boost::enable_if_c= big_integer_t::Bits>::type> + constexpr void regular_exp(Backend1 &result, Backend2 &a, Backend3 exp) const { + BOOST_ASSERT(eval_lt(a, m_mod)); + + if (eval_eq(exp, static_cast(0u))) { + result = static_cast(1u); + return; + } + if (eval_eq(m_mod, static_cast(1u))) { + result = static_cast(0u); + return; + } + + Backend_doubled_limbs base(a), res(static_cast(1u)); + + while (true) { + limb_type lsb = exp.limbs()[0] & 1u; + custom_right_shift(exp, static_cast(1u)); + if (lsb) { + eval_multiply(res, base); + barrett_reduce(res); + if (eval_is_zero(exp)) { + break; + } + } + eval_multiply(base, base); + barrett_reduce(base); + } + result = res; + } + + template< + typename Backend1, typename Backend2, typename Backend3, + /// result should fit in the output parameter + typename = typename boost::enable_if_c= big_integer_t::Bits>::type> + constexpr void montgomery_exp(Backend1 &result, const Backend2 &a, Backend3 exp) const { + /// input parameter should be lesser than modulus + BOOST_ASSERT(eval_lt(a, m_mod)); + + Backend_doubled_limbs tmp(static_cast(1u)); + eval_multiply(tmp, m_montgomery_r2); + montgomery_reduce(tmp); + big_integer_t R_mod_m(tmp); + + big_integer_t base(a); + + if (eval_eq(exp, static_cast(0u))) { + result = static_cast(1u); + // + // TODO: restructure code + // adjust_modular + // + eval_multiply(result, m_montgomery_r2); + montgomery_reduce(result); + return; + } + if (eval_eq(m_mod, static_cast(1u))) { + result = static_cast(0u); + return; + } + + while (true) { + limb_type lsb = exp.limbs()[0] & 1u; + custom_right_shift(exp, static_cast(1u)); + if (lsb) { + montgomery_mul(R_mod_m, base); + if (eval_eq(exp, static_cast(0u))) { + break; + } + } + montgomery_mul(base, base); + } + result = R_mod_m; + } + + constexpr modular_functions &operator=(const modular_functions &o) { + m_mod = o.get_mod(); + m_barrett_mu = o.get_mu(); + m_montgomery_r2 = o.get_r2(); + m_montgomery_p_dash = o.get_p_dash(); + m_mod_compliment = o.get_mod_compliment(); + m_no_carry_montgomery_mul_allowed = is_applicable_for_no_carry_montgomery_mul(); + + return *this; + } + + constexpr modular_functions &operator=(const big_integer_t &m) { + initialize(m); + + return *this; + } + + protected: + big_integer_t m_mod; + // This is 2^Bits - m_mod, precomputed. + big_integer_t m_mod_compliment; + Backend_doubled_1 m_barrett_mu; + big_integer_t m_montgomery_r2; + limb_type m_montgomery_p_dash = 0; + + // If set, no-carry optimization is allowed. Must be initialized by function + // is_applicable_for_no_carry_montgomery_mul() after initialization. + bool m_no_carry_montgomery_mul_allowed = false; + }; +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp new file mode 100644 index 0000000000..6bca814546 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp @@ -0,0 +1,482 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2019-2021 Aleksei Moskvin +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" + +namespace nil::crypto3::multiprecision { + + template + constexpr void eval_add(modular_big_integer, modular_params_t> &result, + const modular_big_integer, modular_params_t> &o) { + BOOST_ASSERT(eval_eq(result.mod_data().get_mod(), o.mod_data().get_mod())); + result.mod_data().mod_add(result.base_data(), o.base_data()); + } + + template + constexpr void eval_add(modular_big_integer, modular_params_t> &result, + const modular_big_integer &o) { + result.mod_data().mod_add(result.base_data(), o.base_data()); + } + + template + constexpr void eval_add(modular_big_integer &result, + const modular_big_integer, modular_params_t> &o) { + o.mod_data().mod_add(result.base_data(), o.base_data()); + } + + template + constexpr void eval_multiply( + modular_big_integer, modular_params_t> &result, + const modular_big_integer, modular_params_t> &o) { + result.mod_data().mod_mul(result.base_data(), o.base_data()); + } + + template + constexpr void eval_multiply(modular_big_integer, modular_params_t> &result, + const modular_big_integer &o) { + result.mod_data().mod_mul(result.base_data(), o.base_data()); + } + + template + constexpr void eval_multiply( + modular_big_integer &result, + const modular_big_integer, modular_params_t> &o) { + o.mod_data().mod_mul(result.base_data(), o.base_data()); + } + + template + constexpr void eval_powm(modular_big_integer, modular_params_t> &result, + const modular_big_integer &b, + const T &e) { + result.set_modular_params(b.mod_data()); + result.mod_data().mod_exp(result.base_data(), b.base_data(), e); + } + + template + constexpr void eval_powm(modular_big_integer, modular_params_t> &result, + const modular_big_integer &b, + const modular_big_integer &e) { + using big_integer_t = big_integer; + + big_integer_t exp; + e.mod_data().adjust_regular(exp, e.base_data()); + eval_powm(result, b, exp); + } + + template + constexpr void eval_inverse_mod( + modular_big_integer, modular_params_t> &result, + const modular_big_integer, modular_params_t> &input) { + using big_integer_t = big_integer; + using big_integer_t_padded_limbs = + typename modular_params::policy_type::Backend_padded_limbs; + + big_integer_t_padded_limbs new_base, res, tmp = input.mod_data().get_mod(); + + input.mod_data().adjust_regular(new_base, input.base_data()); + eval_inverse_mod(res, new_base, tmp); + assign_components(result, res, input.mod_data().get_mod()); + } + + // Used for converting number> to number. + // We cannot change the first argument to a reference... + template + constexpr void eval_convert_to( + big_integer_t *result, const modular_big_integer &val) { + val.mod_data().adjust_regular(*result, val.base_data()); + } + + template + constexpr typename boost::enable_if, bool>::type eval_eq( + const modular_big_integer &a, const T &b) { + return a.compare(b) == 0; + } + + template + constexpr void eval_redc(big_integer_t1 &result, const modular_params &mod) { + mod.reduce(result); + eval_modulus(result, mod.get_mod()); + } + + template + constexpr void eval_add(modular_big_integer &result, + const modular_big_integer &o) { + eval_add(result.base_data(), o.base_data()); + if (!eval_lt(result.base_data(), result.mod_data().get_mod())) { + eval_subtract(result.base_data(), result.mod_data().get_mod()); + } + } + + template + constexpr void eval_subtract(modular_big_integer &result, + const modular_big_integer &o) { + using ui_type = + typename std::tuple_element<0, typename big_integer_t::unsigned_types>::type; + eval_subtract(result.base_data(), o.base_data()); + if (eval_lt(result.base_data(), ui_type(0u))) { + eval_add(result.base_data(), result.mod_data().get_mod()); + } + } + + template + constexpr void eval_subtract( + modular_big_integer, modular_params_t> &result, + const modular_big_integer, modular_params_t> &o) { + if (eval_lt(result.base_data(), o.base_data())) { + auto v = result.mod_data().get_mod(); + eval_subtract(v, o.base_data()); + eval_add(result.base_data(), v); + } else { + eval_subtract(result.base_data(), o.base_data()); + } + } + + template + constexpr void eval_multiply(modular_big_integer &result, + const modular_big_integer &o) { + eval_multiply(result.base_data(), o.base_data()); + eval_redc(result.base_data(), result.mod_data()); + } + + template + constexpr void eval_divide(modular_big_integer &result, + const modular_big_integer &o) { + big_integer_t tmp1, tmp2; + result.mod_data().adjust_regular(tmp1, result.base_data()); + result.mod_data().adjust_regular(tmp2, o.base_data()); + eval_divide(tmp1, tmp2); + result.base_data() = tmp1; + result.mod_data().adjust_modular(result.base_data()); + result.mod_data().adjust_regular(tmp2, result.base_data()); + } + + template + constexpr void eval_modulus(modular_big_integer &result, + const modular_big_integer &o) { + big_integer_t tmp1, tmp2; + result.mod_data().adjust_regular(tmp1, result.base_data()); + result.mod_data().adjust_regular(tmp2, o.base_data()); + eval_modulus(tmp1, tmp2); + result.base_data() = tmp1; + result.mod_data().adjust_modular(result.base_data()); + // result.mod_data().adjust_regular(tmp2, result.base_data()); + } + + // If called with 3 arguments, delegate the call to the upper function. + template + constexpr void eval_modulus(modular_big_integer &result, + const modular_big_integer &u, + const modular_big_integer &v) { + result = std::move(u); + eval_modulus(result, v); + } + + template + constexpr bool eval_is_zero( + const modular_big_integer &val) noexcept { + return eval_is_zero(val.base_data()); + } + + template + constexpr int eval_get_sign( + const modular_big_integer & /*unused*/) { + return 1; + } + + template + constexpr void assign_components(modular_big_integer &result, + const T &a, const V &b) { + result.base_data() = a; + result.mod_data() = b; + result.mod_data().adjust_modular(result.base_data()); + } + + template + constexpr void eval_sqrt(modular_big_integer &result, + const modular_big_integer &val) { + eval_sqrt(result.base_data(), val.base_data()); + } + + template + constexpr void eval_abs(modular_big_integer &result, + const modular_big_integer &val) { + result = val; + } + + inline size_t window_bits(size_t exp_bits) { + constexpr static size_t wsize_count = 6; + constexpr static size_t wsize[wsize_count][2] = {{1434, 7}, {539, 6}, {197, 4}, + {70, 3}, {17, 2}, {0, 0}}; + + size_t window_bits = 1; + + size_t j = wsize_count - 1; + while (wsize[j][0] > exp_bits) { + --j; + } + window_bits += wsize[j][1]; + + return window_bits; + } + + template + inline void find_modular_pow(modular_big_integer &result, + const modular_big_integer &b, + const big_integer_t &exp) { + modular_params mod = b.mod_data(); + size_t m_window_bits; + unsigned long cur_exp_index; + size_t exp_bits = eval_msb(exp); + m_window_bits = window_bits(exp_bits + 1); + + std::vector m_g(1U << m_window_bits); + big_integer_t *p_g = m_g.data(); + big_integer_t x(1, mod); + big_integer_t nibble = exp; + big_integer_t mask; + eval_bit_set(mask, m_window_bits); + eval_decrement(mask); + *p_g = x; + ++p_g; + *p_g = b; + ++p_g; + for (size_t i = 2; i < (1U << m_window_bits); i++) { + eval_multiply(*p_g, m_g[i - 1], b); + ++p_g; + } + size_t exp_nibbles = (exp_bits + 1 + m_window_bits - 1) / m_window_bits; + std::vector exp_index; + + for (size_t i = 0; i < exp_nibbles; ++i) { + big_integer_t tmp = nibble; + eval_bitwise_and(tmp, mask); + eval_convert_to(&cur_exp_index, tmp); + eval_right_shift(nibble, m_window_bits); + exp_index.push_back(cur_exp_index); + } + + eval_multiply(x, m_g[exp_index[exp_nibbles - 1]]); + for (size_t i = exp_nibbles - 1; i > 0; --i) { + for (size_t j = 0; j != m_window_bits; ++j) { + eval_multiply(x, x); + } + + eval_multiply(x, m_g[exp_index[i - 1]]); + } + result = x; + } + + template + constexpr void eval_pow(modular_big_integer &result, + const modular_big_integer &b, + const T &e) { + find_modular_pow(result, b, e); + } + + template + constexpr void eval_pow(modular_big_integer &result, + const modular_big_integer &b, + const modular_big_integer &e) { + big_integer_t exp; + e.mod_data().adjust_regular(exp, e.base_data()); + find_modular_pow(result, b, exp); + } + + template + constexpr void eval_powm(modular_big_integer &result, + const modular_big_integer &b, + const T &e) { + eval_pow(result, b, e); + } + + template + constexpr void eval_powm(modular_big_integer &result, + const modular_big_integer &b, + const modular_big_integer &e) { + eval_pow(result, b, e); + } + + template + inline constexpr void eval_left_shift(modular_big_integer &t, + UI i) noexcept { + big_integer_t tmp; + t.mod_data().adjust_regular(tmp, t.base_data()); + eval_left_shift(tmp, i); + t.base_data() = tmp; + t.mod_data().adjust_modular(t.base_data()); + } + + template + constexpr void eval_right_shift(modular_big_integer &t, UI i) { + big_integer_t tmp; + t.mod_data().adjust_regular(tmp, t.base_data()); + eval_right_shift(tmp, i); + t.base_data() = tmp; + t.mod_data().adjust_modular(t.base_data()); + } + + template + constexpr void eval_left_shift(modular_big_integer &t, + const modular_big_integer &v, + UI i) { + big_integer_t tmp1, tmp2; + t.mod_data().adjust_regular(tmp1, t.base_data()); + t.mod_data().adjust_regular(tmp2, v.base_data()); + eval_left_shift(tmp1, tmp2, static_cast(i)); + t.base_data() = tmp1; + t.mod_data().adjust_modular(t.base_data()); + } + + template + constexpr void eval_right_shift(modular_big_integer &t, + const modular_big_integer &v, + UI i) { + big_integer_t tmp1, tmp2; + t.mod_data().adjust_regular(tmp1, t.base_data()); + t.mod_data().adjust_regular(tmp2, v.base_data()); + eval_right_shift(tmp1, tmp2, static_cast(i)); + t.base_data() = tmp1; + t.mod_data().adjust_modular(t.base_data()); + } + + template + constexpr void eval_bitwise_and(modular_big_integer &result, + const modular_big_integer &v) { + big_integer_t tmp1, tmp2; + result.mod_data().adjust_regular(tmp1, result.base_data()); + v.mod_data().adjust_regular(tmp2, v.base_data()); + eval_bitwise_and(tmp1, tmp2); + result.base_data() = tmp1; + result.mod_data().adjust_modular(result.base_data()); + } + + template + constexpr void eval_bitwise_or(modular_big_integer &result, + const modular_big_integer &v) { + big_integer_t tmp1, tmp2; + result.mod_data().adjust_regular(tmp1, result.base_data()); + v.mod_data().adjust_regular(tmp2, v.base_data()); + eval_bitwise_or(tmp1, tmp2); + result.base_data() = tmp1; + result.mod_data().adjust_modular(result.base_data()); + } + + template + constexpr void eval_bitwise_xor(modular_big_integer &result, + const modular_big_integer &v) { + big_integer_t tmp1, tmp2; + result.mod_data().adjust_regular(tmp1, result.base_data()); + v.mod_data().adjust_regular(tmp2, v.base_data()); + eval_bitwise_xor(tmp1, tmp2); + result.base_data() = tmp1; + result.mod_data().adjust_modular(result.base_data()); + } + + template + constexpr int eval_msb(const modular_big_integer &m) { + big_integer_t tmp; + m.mod_data().adjust_regular(tmp, m.base_data()); + return eval_msb(tmp); + } + + template + constexpr unsigned eval_lsb(const modular_big_integer &m) { + big_integer_t tmp; + m.mod_data().adjust_regular(tmp, m.base_data()); + return eval_lsb(tmp); + } + + template + constexpr bool eval_bit_test(const modular_big_integer &m, + std::size_t index) { + big_integer_t tmp; + m.mod_data().adjust_regular(tmp, m.base_data()); + return eval_bit_test(tmp, index); + } + + template + constexpr void eval_bit_set(modular_big_integer &result, + std::size_t index) { + big_integer_t tmp; + result.mod_data().adjust_regular(tmp, result.base_data()); + eval_bit_set(tmp, index); + result.mod_data().adjust_modular(result.base_data(), tmp); + } + + // We must make sure any call with any integral type ends up here, if we use std::size_t + // here, something this function is not preferred by the compiler and boost's version is + // used, which is worse. + template + constexpr void eval_bit_unset(modular_big_integer &result, + std::size_t index) { + big_integer_t tmp; + result.mod_data().adjust_regular(tmp, result.base_data()); + eval_bit_unset(tmp, index); + result.mod_data().adjust_modular(result.base_data(), tmp); + } + + template + constexpr void eval_bit_flip(modular_big_integer &result, + std::size_t index) { + big_integer_t tmp; + result.mod_data().adjust_regular(tmp, result.base_data()); + eval_bit_flip(tmp, index); + result.mod_data().adjust_modular(result.base_data(), tmp); + } + + template + constexpr modular_big_integer eval_ressol( + const modular_big_integer &input) { + big_integer_t new_base, res; + modular_big_integer res_mod; + + input.mod_data().adjust_regular(new_base, input.base_data()); + res = eval_ressol(new_base, input.mod_data().get_mod()); + assign_components(res_mod, res, input.mod_data().get_mod()); + + return res_mod; + } + + template + constexpr void eval_inverse_mod( + modular_big_integer &result, + const modular_big_integer &input) { + big_integer_t new_base, res; + + input.mod_data().adjust_regular(new_base, input.base_data()); + eval_inverse_mod(res, new_base, input.mod_data().get_mod()); + assign_components(result, res, input.mod_data().get_mod()); + } + + template + inline constexpr std::size_t hash_value( + const modular_big_integer &val) noexcept { + return hash_value(val.base_data()); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp new file mode 100644 index 0000000000..7a82248c15 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2021 Aleksei Moskvin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" + +namespace nil::crypto3::multiprecision { + // TODO(ioxid): merge with modular_functions + // fixed precision modular params type which supports compile-time execution + template + class modular_params { + protected: + using modular_functions_t = modular_functions; + + public: + using policy_type = typename modular_functions_t::policy_type; + + using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; + // using big_integer_t = typename policy_type::big_integer_t; + + constexpr auto &get_modular_functions() { return m_modular_functions; } + constexpr const auto &get_modular_functions() const { return m_modular_functions; } + + constexpr auto &get_is_odd_mod() { return is_odd_mod; } + constexpr const auto &get_is_odd_mod() const { return is_odd_mod; } + + constexpr auto get_mod() const { return m_modular_functions.get_mod(); } + + constexpr modular_params() {} + + constexpr modular_params(const big_integer_t &m) : m_modular_functions(m) { + using boost::multiprecision::default_ops::eval_bit_test; + is_odd_mod = eval_bit_test(m, 0); + } + + constexpr modular_params(const modular_params &o) + : m_modular_functions(o.get_modular_functions()) { + is_odd_mod = o.get_is_odd_mod(); + } + + template + constexpr void reduce(big_integer &result) const { + if (is_odd_mod) { + m_modular_functions.montgomery_reduce(result); + } else { + m_modular_functions.barrett_reduce(result); + } + } + + constexpr void adjust_modular(big_integer_t &result) const { + adjust_modular(result, result); + } + + template + constexpr void adjust_modular(big_integer_t &result, + const big_integer &input) const { + Backend_doubled_limbs tmp; + m_modular_functions.barrett_reduce(tmp, input); + if (is_odd_mod) { + // + // to prevent problems with trivial cpp_int + // + Backend_doubled_limbs r2(m_modular_functions.get_r2()); + + eval_multiply(tmp, r2); + m_modular_functions.montgomery_reduce(tmp); + } + result = tmp; + } + + template= Bits2>::type> + constexpr void adjust_regular(big_integer &result, + const big_integer &input) const { + result = input; + if (is_odd_mod) { + m_modular_functions.montgomery_reduce(result); + } + } + + template + constexpr void mod_exp(Backend1 &result, const T &exp) const { + mod_exp(result, result, exp); + } + + template + constexpr void mod_exp(Backend1 &result, const Backend2 &a, const T &exp) const { + if (is_odd_mod) { + m_modular_functions.montgomery_exp(result, a, exp); + } else { + m_modular_functions.regular_exp(result, a, exp); + } + } + + template + constexpr void mod_mul(Backend1 &result, const Backend1 &y) const { + if (is_odd_mod) { + m_modular_functions.montgomery_mul(result, y); + } else { + m_modular_functions.regular_mul(result, y); + } + } + + template + constexpr void mod_add(Backend1 &result, const Backend2 &y) const { + m_modular_functions.regular_add(result, y); + } + + template + constexpr operator Backend1() { + return get_mod(); + }; + + constexpr bool compare_eq(const modular_params &o) const { + // They are either equal or not: + return get_mod().compare(o.get_mod()) == 0; + } + + constexpr void swap(modular_params &o) noexcept { + m_modular_functions.swap(o.get_modular_functions()); + bool t = is_odd_mod; + is_odd_mod = o.get_is_odd_mod(); + o.get_is_odd_mod() = t; + } + + constexpr modular_params &operator=(const modular_params &o) { + m_modular_functions = o.get_modular_functions(); + is_odd_mod = o.get_is_odd_mod(); + return *this; + } + + constexpr modular_params &operator=(const big_integer_t &m) { + m_modular_functions = m; + is_odd_mod = boost::multiprecision::default_ops::eval_bit_test(m, 0); + return *this; + } + + // TODO: check function correctness + constexpr friend std::ostream &operator<<(std::ostream &o, const modular_params &a) { + o << a.get_mod(); + return o; + } + + protected: + modular_functions_t m_modular_functions; + bool is_odd_mod = false; + }; + + template + class modular_params_storage_ct { + public: + using modular_params_t = modular_params; + + constexpr modular_params_storage_ct() {} + + constexpr modular_params_storage_ct(modular_params_t &input) {} + + constexpr void set_modular_params(const modular_params_t &input) {} + + template + constexpr void set_modular_params(const T &input) {} + + constexpr const modular_params_t &modular_params() const { return m_mod; } + + protected: + constexpr static const modular_params_t m_mod{Modulus}; + }; + + // Must be used only in the tests, we must normally use only modular_params_storage_ct. + template + class modular_params_storage_rt { + public: + using modular_params_t = modular_params; + + constexpr modular_params_storage_rt() {} + + constexpr modular_params_storage_rt(modular_params_t input) : m_mod(input) {} + + constexpr void set_modular_params(const modular_params_t &input) { m_mod = input; } + + constexpr void set_modular_params(const big_integer_t &input) { m_mod = input; } + + constexpr modular_params_t &modular_params() { return m_mod; } + constexpr const modular_params_t &modular_params() const { return m_mod; } + + modular_params_t m_mod; + }; +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp new file mode 100644 index 0000000000..0a0f3e7c10 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp @@ -0,0 +1,43 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision { + // TODO(ioxid): maybe remove + template + struct modular_policy { + using Backend = big_integer; + + using limb_type = limb_type; + using double_limb_type = double_limb_type; + + constexpr static auto limbs_count = Backend::internal_limb_count; + constexpr static auto limb_bits = Backend::limb_bits; + + constexpr static auto BitsCount_doubled = 2u * Bits; + constexpr static auto BitsCount_doubled_1 = BitsCount_doubled + 1; + constexpr static auto BitsCount_quadruple_1 = 2u * BitsCount_doubled + 1; + constexpr static auto BitsCount_padded_limbs = limbs_count * limb_bits + limb_bits; + constexpr static auto BitsCount_doubled_limbs = 2u * limbs_count * limb_bits; + constexpr static auto BitsCount_doubled_padded_limbs = BitsCount_doubled_limbs + limb_bits; + + using Backend_doubled = big_integer; + using Backend_doubled_1 = big_integer; + using Backend_quadruple_1 = big_integer; + using Backend_padded_limbs = big_integer; + using Backend_doubled_limbs = big_integer; + using Backend_doubled_padded_limbs = big_integer; + }; +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp new file mode 100644 index 0000000000..1249b9e3c8 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2012 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Comparison operators for big_integer: +// +#pragma once + +// #include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" +#include "nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +namespace nil::crypto3::multiprecision { + + template + inline constexpr void add_unsigned(big_integer& result, const big_integer& a, + const limb_type& o) noexcept { + // Addition using modular arithmetic. + // Nothing fancy, just let uintmax_t take the strain: + + double_limb_type carry = o; + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer pa = a.limbs(); + unsigned i = 0; + // Addition with carry until we either run out of digits or carry is zero: + for (; carry && (i < result.size()); ++i) { + carry += static_cast(pa[i]); + pr[i] = static_cast(carry); + carry >>= big_integer::limb_bits; + } + // Just copy any remaining digits: + if (&a != &result) { + boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); + } + if (Bits % big_integer::limb_bits == 0) { + result.set_carry(carry); + } else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (pr[result.size() - 1] & ~mask) { + pr[result.size() - 1] &= mask; + result.set_carry(true); + } + } + } + + // + // And again to subtract a single limb: caller is responsible to check that a > b and + // the result is non-negative. + // + template + inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, + const limb_type& b) noexcept { + BOOST_ASSERT(a >= b); + + // Subtract one limb. + // Nothing fancy, just let uintmax_t take the strain: + constexpr double_limb_type borrow = + static_cast(big_integer::max_limb_value) + 1; + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer pa = a.limbs(); + if (*pa >= b) { + *pr = *pa - b; + if (&result != &a) { + boost::multiprecision::std_constexpr::copy(pa + 1, pa + a.size(), pr + 1); + } + } else if (result.size() == 1) { + *pr = b - *pa; + } else { + *pr = static_cast((borrow + *pa) - b); + unsigned i = 1; + while (!pa[i]) { + pr[i] = big_integer::max_limb_value; + ++i; + } + pr[i] = pa[i] - 1; + if (&result != &a) { + ++i; + boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); + } + } + } + + // + // Now the actual functions called by the front end, all of which forward to one of the + // above: + // + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, + const big_integer& o) noexcept { + eval_add(result, result, o); + } + template + inline constexpr void eval_add(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + add_unsigned(result, a, b); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, + const limb_type& o) noexcept { + add_unsigned(result, result, o); + } + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, + const big_integer& a, + const limb_type& o) noexcept { + add_unsigned(result, a, o); + } + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, + const limb_type& o) noexcept { + subtract_unsigned(result, result, o); + } + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, + const big_integer& a, + const limb_type& o) noexcept { + subtract_unsigned(result, a, o); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_increment(big_integer& result) noexcept { + if ((result.limbs()[0] < big_integer::max_limb_value)) { + ++result.limbs()[0]; + } else { + eval_add(result, (limb_type)1); + } + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_decrement(big_integer& result) noexcept { + constexpr const limb_type one = 1; + + if (result.limbs()[0]) { + --result.limbs()[0]; + } else { + eval_subtract(result, one); + } + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, + const big_integer& o) noexcept { + eval_subtract(result, result, o); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, + const big_integer& a, + const big_integer& b) noexcept { + subtract_unsigned(result, a, b); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type eval_subtract( + big_integer& result, const big_integer& o) noexcept { + big_integer o_larger = o; + eval_subtract(result, result, o_larger); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type eval_subtract( + big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + big_integer b_larger = b; + subtract_unsigned(result, a, b_larger); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp new file mode 100644 index 0000000000..aef579034c --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp @@ -0,0 +1,291 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2020 Madhur Chauhan. +// Copyright 2020 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt + +#pragma once + +#include + +// #include // for addcarry_limb + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +namespace nil::crypto3::multiprecision { + template + inline constexpr void add_unsigned_constexpr(big_integer& result, + const big_integer& a, + const big_integer& b) noexcept { + using ::boost::multiprecision::std_constexpr::swap; + // + // This is the generic, C++ only version of addition. + // It's also used for all constexpr branches, hence the name. + // Nothing fancy, just let uintmax_t take the strain: + // + double_limb_type carry = 0; + std::size_t s = a.size(); + if (s == 1) { + double_limb_type r = static_cast(*a.limbs()) + + static_cast(*b.limbs()); + double_limb_type mask = big_integer::upper_limb_mask; + if (r & ~mask) { + result = r & mask; + result.set_carry(true); + } else { + result = r; + } + return; + } + + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + // First where a and b overlap: + for (std::size_t i = 0; i < s; ++i) { + carry += static_cast(*pa) + static_cast(*pb); +#ifdef _C_RUNTIME_CHECKS + *pr = static_cast(carry & ~static_cast(0)); +#else + *pr = static_cast(carry); +#endif + carry >>= big_integer::limb_bits; + ++pr, ++pa, ++pb; + } + if (Bits % big_integer::limb_bits == 0) { + result.set_carry(carry); + } else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (result.limbs()[s - 1] & ~mask) { + result.limbs()[s - 1] &= mask; + result.set_carry(true); + } + } + } + + // + // Core subtraction routine for all non-trivial cpp_int's: + // It is the caller's responsibility to make sure that a >= b. + // + template + inline constexpr void subtract_unsigned_constexpr(big_integer& result, + const big_integer& a, + const big_integer& b) noexcept { + BOOST_ASSERT(a >= b); + + // + // This is the generic, C++ only version of subtraction. + // It's also used for all constexpr branches, hence the name. + // Nothing fancy, just let uintmax_t take the strain: + // + std::size_t s = a.size(); + if (s == 1) { + result = *a.limbs() - *b.limbs(); + return; + } + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + double_limb_type borrow = 0; + // First where a and b overlap: + for (std::size_t i = 0; i < s; ++i) { + borrow = static_cast(pa[i]) - static_cast(pb[i]) - + borrow; + pr[i] = static_cast(borrow); + borrow = (borrow >> big_integer::limb_bits) & 1u; + } + // if a > b, then borrow must be 0 at the end. + BOOST_ASSERT(0 == borrow); + } + +#ifdef CO3_MP_HAS_IMMINTRIN_H + // + // This is the key addition routine where all the argument types are non-trivial + // cpp_int's: + // + // + // This optimization is limited to: GCC, LLVM, ICC (Intel), MSVC for x86_64 and i386. + // If your architecture and compiler supports ADC intrinsic, please file a bug + // + // As of May, 2020 major compilers don't recognize carry chain though adc + // intrinsics are used to hint compilers to use ADC and still compilers don't + // unroll the loop efficiently (except LLVM) so manual unrolling is done. + // + // Also note that these intrinsics were only introduced by Intel as part of the + // ADX processor extensions, even though the addc instruction has been available + // for basically all x86 processors. That means gcc-9, clang-9, msvc-14.2 and up + // are required to support these intrinsics. + // + template + inline constexpr void add_unsigned(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { +#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { + add_unsigned_constexpr(result, a, b); + } else +#endif + { + using std::swap; + + // Nothing fancy, just let uintmax_t take the strain: + unsigned s = a.size(); + if (s == 1) { + double_limb_type v = static_cast(*a.limbs()) + + static_cast(*b.limbs()); + double_limb_type mask = big_integer::upper_limb_mask; + if (v & ~mask) { + v &= mask; + result.set_carry(true); + } + result = v; + return; + } + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + unsigned char carry = 0; +#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) + // + // Special case for 32-bit limbs on 64-bit architecture - we can process + // 2 limbs with each instruction. + // + std::size_t i = 0; + for (; i + 8 <= s; i += 8) { + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 0), + *(unsigned long long*)(pb + i + 0), + (unsigned long long*)(pr + i)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 2), + *(unsigned long long*)(pb + i + 2), + (unsigned long long*)(pr + i + 2)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 4), + *(unsigned long long*)(pb + i + 4), + (unsigned long long*)(pr + i + 4)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 6), + *(unsigned long long*)(pb + i + 6), + (unsigned long long*)(pr + i + 6)); + } +#else + for (; i + 4 <= s; i += 4) { + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 0], pb[i + 0], + pr + i); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 1], pb[i + 1], + pr + i + 1); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 2], pb[i + 2], + pr + i + 2); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 3], pb[i + 3], + pr + i + 3); + } +#endif + for (; i < s; ++i) + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i], pb[i], pr + i); + + if (Bits % big_integer::limb_bits == 0) + result.set_carry(carry); + else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (result.limbs()[s - 1] & ~mask) { + result.limbs()[s - 1] &= mask; + result.set_carry(true); + } + } + } + } + + // It is the caller's responsibility to make sure that a > b. + template + inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + BOOST_ASSERT(!eval_lt(a, b)); + +#ifndef TO3_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { + subtract_unsigned_constexpr(result, a, b); + } else +#endif + { + using std::swap; + + // Nothing fancy, just let uintmax_t take the strain: + std::size_t s = a.size(); + + // + // special cases for small limb counts: + // + if (s == 1) { + result = *a.limbs() - *b.limbs(); + return; + } + // Now that a, b, and result are stable, get pointers to their limbs: + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + std::size_t i = 0; + unsigned char borrow = 0; + // First where a and b overlap: +#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) + // + // Special case for 32-bit limbs on 64-bit architecture - we can process + // 2 limbs with each instruction. + // + for (; i + 8 <= m; i += 8) { + borrow = + _subborrow_u64(borrow, *reinterpret_cast(pa + i), + *reinterpret_cast(pb + i), + reinterpret_cast(pr + i)); + borrow = + _subborrow_u64(borrow, *reinterpret_cast(pa + i + 2), + *reinterpret_cast(pb + i + 2), + reinterpret_cast(pr + i + 2)); + borrow = + _subborrow_u64(borrow, *reinterpret_cast(pa + i + 4), + *reinterpret_cast(pb + i + 4), + reinterpret_cast(pr + i + 4)); + borrow = + _subborrow_u64(borrow, *reinterpret_cast(pa + i + 6), + *reinterpret_cast(pb + i + 6), + reinterpret_cast(pr + i + 6)); + } +#else + for (; i + 4 <= m; i += 4) { + borrow = + boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 1], pb[i + 1], + pr + i + 1); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 2], pb[i + 2], + pr + i + 2); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 3], pb[i + 3], + pr + i + 3); + } +#endif + for (; i < m; ++i) + borrow = + boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); + + BOOST_ASSERT(0 == borrow); + + } // constepxr. + } + +#else + + template + inline constexpr void add_unsigned(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + add_unsigned_constexpr(result, a, b); + } + + template + inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + subtract_unsigned_constexpr(result, a, b); + } + +#endif +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp new file mode 100644 index 0000000000..1a9150ad66 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp @@ -0,0 +1,359 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2012 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Comparison operators for big_integer: +// +#pragma once + +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4319) +#endif + +namespace nil::crypto3::multiprecision { + template + constexpr void bitwise_op(big_integer& result, const big_integer& o, + Op op) noexcept { + // + // Both arguments are unsigned types, very simple case handled as a special case. + // + // First figure out how big the result needs to be and set up some data: + // + unsigned rs = result.size(); + unsigned os = o.size(); + unsigned m(0), x(0); + boost::multiprecision::minmax(rs, os, m, x); + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer po = o.limbs(); + for (unsigned i = rs; i < x; ++i) { + pr[i] = 0; + } + + for (unsigned i = 0; i < os; ++i) { + pr[i] = op(pr[i], po[i]); + } + for (unsigned i = os; i < x; ++i) { + pr[i] = op(pr[i], limb_type(0)); + } + result.normalize(); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_and(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_and()); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_or(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_or()); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_xor(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_xor()); + } + // + // Again for operands which are single limbs: + // + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_and(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] &= l; + result.zero_after(1); + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_or(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] |= l; + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_xor(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] ^= l; + } + + template + NIL_CO3_MP_FORCEINLINE constexpr void eval_complement(big_integer& result, + const big_integer& o) noexcept { + unsigned os = o.size(); + for (unsigned i = 0; i < os; ++i) { + result.limbs()[i] = ~o.limbs()[i]; + } + result.normalize(); + } + + // Left shift will throw away upper Bits. + // This function must be called only when s % 8 == 0, i.e. we shift bytes. + template + inline void left_shift_byte(big_integer& result, double_limb_type s) { + typedef big_integer Int; + + typename Int::limb_pointer pr = result.limbs(); + + std::size_t bytes = static_cast(s / CHAR_BIT); + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + unsigned char* pc = reinterpret_cast(pr); + std::memmove(pc + bytes, pc, result.size() * sizeof(limb_type) - bytes); + std::memset(pc, 0, bytes); + } + } + + // Left shift will throw away upper Bits. + // This function must be called only when s % limb_bits == 0, i.e. we shift limbs, which + // are normally 64 bit. + template + inline constexpr void left_shift_limb(big_integer& result, double_limb_type s) { + typedef big_integer Int; + + limb_type offset = static_cast(s / Int::limb_bits); + BOOST_ASSERT(static_cast(s % Int::limb_bits) == 0); + + typename Int::limb_pointer pr = result.limbs(); + + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + unsigned i = offset; + std::size_t rs = result.size() + offset; + for (; i < result.size(); ++i) { + pr[rs - 1 - i] = pr[result.size() - 1 - i]; + } + for (; i < rs; ++i) { + pr[rs - 1 - i] = 0; + } + } + } + + // Left shift will throw away upper Bits. + template + inline constexpr void left_shift_generic(big_integer& result, double_limb_type s) { + typedef big_integer Int; + + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + limb_type offset = static_cast(s / Int::limb_bits); + limb_type shift = static_cast(s % Int::limb_bits); + + typename Int::limb_pointer pr = result.limbs(); + std::size_t i = 0; + std::size_t rs = result.size(); + // This code only works when shift is non-zero, otherwise we invoke undefined + // behaviour! + BOOST_ASSERT(shift); + for (; rs - i >= 2 + offset; ++i) { + pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; + pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (Int::limb_bits - shift); + } + if (rs - i >= 1 + offset) { + pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; + ++i; + } + for (; i < rs; ++i) { + pr[rs - 1 - i] = 0; + } + } + } + + // Shifting left throws away upper Bits. + template + inline constexpr void eval_left_shift(big_integer& result, double_limb_type s) noexcept { + if (!s) { + return; + } + +#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + + if ((s & limb_shift_mask) == 0) { + left_shift_limb(result, s); + } +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + else if ((s & byte_shift_mask) == 0) +#else + else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + left_shift_byte(result, s); + } +#elif BOOST_ENDIAN_LITTLE_BYTE + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + if ((s & byte_shift_mask) == 0) +#else + constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; + if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { + left_shift_limb(result, s); + } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + left_shift_byte(result, s); + } +#else + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + + if ((s & limb_shift_mask) == 0) { + left_shift_limb(result, s); + } +#endif + else { + left_shift_generic(result, s); + } + result.normalize(); + } + + template + inline void right_shift_byte(big_integer& result, double_limb_type s) { + typedef big_integer Int; + + limb_type offset = static_cast(s / Int::limb_bits); + BOOST_ASSERT((s % CHAR_BIT) == 0); + unsigned ors = result.size(); + unsigned rs = ors; + if (offset >= rs) { + result.zero_after(0); + return; + } + rs -= offset; + typename Int::limb_pointer pr = result.limbs(); + unsigned char* pc = reinterpret_cast(pr); + limb_type shift = static_cast(s / CHAR_BIT); + std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift); + shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT; + if (shift < Int::limb_bits) { + pr[ors - offset - 1] &= (static_cast(1u) << shift) - 1; + if (!pr[ors - offset - 1] && (rs > 1)) { + --rs; + } + } + // Set zeros after 'rs', alternative to resizing to size 'rs'. + result.zero_after(rs); + } + + template + inline constexpr void right_shift_limb(big_integer& result, double_limb_type s) { + typedef big_integer Int; + + limb_type offset = static_cast(s / Int::limb_bits); + BOOST_ASSERT((s % Int::limb_bits) == 0); + unsigned ors = result.size(); + unsigned rs = ors; + if (offset >= rs) { + result.zero_after(0); + return; + } + rs -= offset; + typename Int::limb_pointer pr = result.limbs(); + unsigned i = 0; + for (; i < rs; ++i) { + pr[i] = pr[i + offset]; + } + // Set zeros after 'rs', alternative to resizing to size 'rs'. + result.zero_after(rs); + } + + template + inline constexpr void right_shift_generic(big_integer& result, double_limb_type s) { + typedef big_integer Int; + limb_type offset = static_cast(s / Int::limb_bits); + limb_type shift = static_cast(s % Int::limb_bits); + unsigned ors = result.size(); + unsigned rs = ors; + + if (offset >= rs) { + result = limb_type(0); + return; + } + rs -= offset; + typename Int::limb_pointer pr = result.limbs(); + if ((pr[ors - 1] >> shift) == 0) { + if (--rs == 0) { + result = limb_type(0); + return; + } + } + unsigned i = 0; + + // This code only works for non-zero shift, otherwise we invoke undefined behaviour! + BOOST_ASSERT(shift); + for (; i + offset + 1 < ors; ++i) { + pr[i] = pr[i + offset] >> shift; + pr[i] |= pr[i + offset + 1] << (Int::limb_bits - shift); + } + pr[i] = pr[i + offset] >> shift; + + // We cannot resize any more, so we need to set all the limbs to zero. + result.zero_after(rs); + } + + template + inline constexpr void eval_right_shift(big_integer& result, double_limb_type s) noexcept { + if (!s) { + return; + } + +#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + + if ((s & limb_shift_mask) == 0) right_shift_limb(result, s); +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + else if ((s & byte_shift_mask) == 0) +#else + else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + right_shift_byte(result, s); + } +#elif BOOST_ENDIAN_LITTLE_BYTE + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + if ((s & byte_shift_mask) == 0) +#else + constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; + if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { + right_shift_limb(result, s); + } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + right_shift_byte(result, s); + } +#else + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + + if ((s & limb_shift_mask) == 0) { + right_shift_limb(result, s); + } +#endif + else { + right_shift_generic(result, s); + } + } +} // namespace nil::crypto3::multiprecision + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp new file mode 100644 index 0000000000..6b00ea9ced --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////// +// Copyright (c) 2023 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Contains eval_modulus for big_integer, which uses conversion to cpp_int_backend to +// actually apply the operation. +// + +#pragma once + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" + +namespace nil::crypto3::multiprecision { + + // Functions in this file should be called only for creation of montgomery and Barett + // params, no during "normal" execution, so we do NOT care about the execution speed, + // and will just redirect calls to normal boost::cpp_int. + + template + inline constexpr void eval_modulus(big_integer &result, const big_integer &a, + const big_integer &b) noexcept { + result = a; + // Call the function below. + eval_modulus(result, b); + } + + // Just a call to the upper function, similar to operator*=. + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! + template + inline constexpr void eval_modulus(big_integer &result, + const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int %= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } + + // This function should be called only for creation of montgomery and Barett params and + // calculation of inverse, element, not during "normal" execution. We will use + // conversion to normal boost::cpp_int here and then convert back. + template + inline constexpr void eval_divide(big_integer &result, const big_integer &a, + const big_integer &b) noexcept { + result = a; + // Just make a call to the lower function. + eval_divide(result, b); + } + + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! + // Covers the case where the second operand is not trivial. + template + inline constexpr void eval_divide(big_integer &result, + const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int /= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp new file mode 100644 index 0000000000..a30bbde7e7 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021 Aleksei Moskvin +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" + +namespace nil::crypto3::multiprecision { + template + class big_integer; + + template + constexpr int eval_jacobi(const Backend &a, const Backend &n) { + using boost::multiprecision::default_ops::eval_divide; + using boost::multiprecision::default_ops::eval_get_sign; + using boost::multiprecision::default_ops::eval_gt; + using boost::multiprecision::default_ops::eval_integer_modulus; + using boost::multiprecision::default_ops::eval_is_zero; + using boost::multiprecision::default_ops::eval_lsb; + using boost::multiprecision::default_ops::eval_lt; + using boost::multiprecision::default_ops::eval_modulus; + using boost::multiprecision::default_ops::eval_right_shift; + + // BOOST_THROW_EXCEPTION(std::invalid_argument("jacobi: second argument must be odd + // and > 1")); + BOOST_ASSERT(eval_integer_modulus(n, 2) && eval_gt(n, 1)); + + Backend x = a, y = n; + int J = 1; + + while (eval_gt(y, 1)) { + eval_modulus(x, y); + + Backend yd2 = y; + eval_right_shift(yd2, 1); + + if (eval_gt(x, yd2)) { + Backend tmp(y); + eval_subtract(tmp, x); + x = tmp; + if (eval_integer_modulus(y, 4) == 3) { + J = -J; + } + } + if (eval_is_zero(x)) { + return 0; + } + + size_t shifts = eval_lsb(x); + custom_right_shift(x, shifts); + if (shifts & 1) { + std::size_t y_mod_8 = eval_integer_modulus(y, 8); + if (y_mod_8 == 3 || y_mod_8 == 5) { + J = -J; + } + } + + if (eval_integer_modulus(x, 4) == 3 && eval_integer_modulus(y, 4) == 3) { + J = -J; + } + + // std::swap(x, y); + auto tmp = x; + x = y; + y = tmp; + } + return J; + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp new file mode 100644 index 0000000000..b3cfc85475 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2015 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt + +#pragma once + +#include +#include +#include +#include +#include +#include + +// #include // For 'extract_bits'. +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +namespace nil::crypto3::multiprecision { + + namespace detail { + using limb_type = nil::crypto3::multiprecision::limb_type; + + /* This specialization is used when assigning `chunk_bits` + * of `bits` into `val` at `bit_location` in case where `val` + * is larger than one limb (machine word). + */ + template + void assign_bits(big_integer& val, Unsigned bits, std::size_t bit_location, + std::size_t chunk_bits, const std::integral_constant& tag) { + unsigned limb = bit_location / (sizeof(limb_type) * CHAR_BIT); + unsigned shift = bit_location % (sizeof(limb_type) * CHAR_BIT); + + limb_type mask = chunk_bits >= sizeof(limb_type) * CHAR_BIT + ? ~static_cast(0u) + : (static_cast(1u) << chunk_bits) - 1; + + limb_type value = static_cast(bits & mask) << shift; + if (value) { + // We are ignoring any bits that will not fit into the number. + // We are not throwing, we will use as many bits from the input as we need to. + if (val.size() > limb) { + val.limbs()[limb] |= value; + } + } + + /* If some extra bits need to be assigned to the next limb */ + if (chunk_bits > sizeof(limb_type) * CHAR_BIT - shift) { + shift = sizeof(limb_type) * CHAR_BIT - shift; + chunk_bits -= shift; + bit_location += shift; + auto extra_bits = bits >> shift; + if (extra_bits) { + assign_bits(val, extra_bits, bit_location, chunk_bits, tag); + } + } + } + + /* This specialization is used when assigning `chunk_bits` + * of `bits` into `val` at `bit_location` in case where `val` + * fits into one limb (machine word). + */ + template + void assign_bits(big_integer& val, Unsigned bits, std::size_t bit_location, + std::size_t chunk_bits, + const std::integral_constant& /*unused*/) { + using local_limb_type = typename big_integer::local_limb_type; + // + // Check for possible overflow, this may trigger an exception, or have no effect + // depending on whether this is a checked integer or not: + // + // We are not throwing, we will use as many bits from the input as we need to. + // BOOST_ASSERT(!((bit_location >= sizeof(local_limb_type) * CHAR_BIT) && bits)); + + local_limb_type mask = chunk_bits >= sizeof(local_limb_type) * CHAR_BIT + ? ~static_cast(0u) + : (static_cast(1u) << chunk_bits) - 1; + local_limb_type value = (static_cast(bits) & mask) << bit_location; + *val.limbs() |= value; + + // + // Check for overflow bits: + // + bit_location = sizeof(local_limb_type) * CHAR_BIT - bit_location; + + // We are not throwing, we will use as many bits from the input as we need to. + // BOOST_ASSERT(!((bit_location < sizeof(bits) * CHAR_BIT) && (bits >>= + // bit_location))); + } + + template + big_integer& import_bits_generic(big_integer& val, Iterator i, Iterator j, + std::size_t chunk_size = 0, bool msv_first = true) { + big_integer newval; + + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using size_type = + typename ::boost::multiprecision::detail::make_unsigned::type; + using tag_type = typename big_integer::trivial_tag; + + if (!chunk_size) { + chunk_size = std::numeric_limits::digits; + } + + size_type limbs = std::distance(i, j); + size_type bits = limbs * chunk_size; + + // We are not throwing, we will use as many bits from the input as we need to. + // BOOST_ASSERT(bits <= Bits); + + difference_type bit_location = msv_first ? bits - chunk_size : 0; + difference_type bit_location_change = + msv_first ? -static_cast(chunk_size) : chunk_size; + + while (i != j) { + assign_bits(newval, *i, static_cast(bit_location), chunk_size, + tag_type()); + ++i; + bit_location += bit_location_change; + } + + // This will remove the upper bits using upper_limb_mask. + newval.normalize(); + + val.backend() = std::move(newval); + return val; + } + + template + inline big_integer import_bits_fast(big_integer& val, T* i, T* j, + std::size_t chunk_size = 0) { + std::size_t byte_len = (j - i) * (chunk_size ? chunk_size / CHAR_BIT : sizeof(*i)); + std::size_t limb_len = byte_len / sizeof(limb_type); + if (byte_len % sizeof(limb_type)) { + ++limb_len; + } + + big_integer& result = val.backend(); + BOOST_VERIFY(result.size() > limb_len); + + result.limbs()[result.size() - 1] = 0u; + std::memcpy(result.limbs(), i, (std::min)(byte_len, result.size() * sizeof(limb_type))); + + // This is probably unneeded, but let it stay for now. + result.normalize(); + return val; + } + } // namespace detail + + template + inline big_integer& import_bits(big_integer& val, Iterator i, Iterator j, + std::size_t chunk_size = 0, bool msv_first = true) { + return detail::import_bits_generic(val, i, j, chunk_size, msv_first); + } + + template + inline big_integer& import_bits(big_integer& val, T* i, T* j, + std::size_t chunk_size = 0, bool msv_first = true) { +#if CRYPTO3_MP_ENDIAN_LITTLE_BYTE + if (((chunk_size % CHAR_BIT) == 0) && !msv_first && (sizeof(*i) * CHAR_BIT == chunk_size)) + return detail::import_bits_fast(val, i, j, chunk_size); +#endif + return detail::import_bits_generic(val, i, j, chunk_size, msv_first); + } + + template + OutputIterator export_bits(const big_integer& val, OutputIterator out, + std::size_t chunk_size, bool msv_first = true) { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + using tag_type = typename big_integer::trivial_tag; + if (!val) { + *out = 0; + ++out; + return out; + } + std::size_t bitcount = eval_msb_imp(val.backend()) + 1; + + std::ptrdiff_t bit_location = + msv_first ? static_cast(bitcount - chunk_size) : 0; + const std::ptrdiff_t bit_step = + msv_first ? static_cast(-static_cast(chunk_size)) + : static_cast(chunk_size); + while (bit_location % bit_step) { + ++bit_location; + } + do { + *out = boost::multiprecision::detail::extract_bits(val.backend(), bit_location, + chunk_size, tag_type()); + ++out; + bit_location += bit_step; + } while ((bit_location >= 0) && (bit_location < static_cast(bitcount))); + + return out; +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp new file mode 100644 index 0000000000..b285b95fc7 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp @@ -0,0 +1,55 @@ +//---------------------------------------------------------------------------// +// Copyright 2024 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// We need to include this file after modular adaptor, in order for these functions to 'see' +//---------------------------------------------------------------------------// + +#pragma once + +#include + +namespace nil::crypto3::multiprecision { + // TODO + + // // Only for our modular numbers function powm takes 2 arguments, + // // so we need to add this specialization. + // template + // inline constexpr + // typename std::enable_if::value && is_integral::value, + // number>>::type + // powm(const number< + // boost::multiprecision::backends::modular_adaptor>& + // b, + // const U& p) { + // // We will directly call eval_powm here, that's what a call through a + // // default_ops::powm_func would do if expression tempaltes are off. We don't want to + // // change that structure. + // boost::multiprecision::backends::modular_adaptor result; + // result.set_modular_params(b.backend().mod_data()); + // boost::multiprecision::backends::eval_powm(result, b.backend(), p); + // return result; + // } + + // template + // inline constexpr + // typename std::enable_if<(detail::is_backend::value && + // (is_number::value || is_number_expression::value)), + // number>>::type + // powm(const number< + // boost::multiprecision::backends::modular_adaptor>& + // b, + // const U& p) { + // // We will directly call eval_powm here, that's what a call through a + // // default_ops::powm_func would do if expression tempaltes are off. We don't want to + // // change that structure. + // boost::multiprecision::backends::modular_adaptor result; + // result.set_modular_params(b.backend().mod_data()); + // boost::multiprecision::backends::eval_powm(result, b.backend(), p.backend()); + // return result; + // } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp new file mode 100644 index 0000000000..816bb2e489 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp @@ -0,0 +1,268 @@ +/////////////////////////////////////////////////////////////// +// Copyright 2012-2020 John Maddock. +// Copyright 2020 Madhur Chauhan. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Comparison operators for big_integer: +// +#pragma once + +#include +#include +// #include // lsb etc +#include +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable : 4702) +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning( \ + disable : 4146) // unary minus operator applied to unsigned type, result still unsigned +#endif + +namespace nil::crypto3::multiprecision { + // TODO refactor + template + inline constexpr + typename std::enable_if::value, void>::type + eval_convert_to(R *result, const big_integer &backend) { + if constexpr (boost::multiprecision::backends::numeric_limits_workaround::digits < + big_integer::limb_bits) { + if (boost::multiprecision::detail::is_signed::value && + boost::multiprecision::detail::is_integral::value && + static_cast((std::numeric_limits::max)()) <= backend.limbs()[0]) { + *result = (boost::multiprecision::backends::numeric_limits_workaround::max)(); + return; + } + *result = static_cast(backend.limbs()[0]); + } else + *result = static_cast(backend.limbs()[0]); + + unsigned shift = big_integer::limb_bits; + unsigned i = 1; + if constexpr (boost::multiprecision::backends::numeric_limits_workaround::digits > + big_integer::limb_bits) { + while ( + (i < backend.size()) && + (shift < static_cast( + boost::multiprecision::backends::numeric_limits_workaround::digits - + big_integer::limb_bits))) { + *result += static_cast(backend.limbs()[i]) << shift; + shift += big_integer::limb_bits; + ++i; + } + // + // We have one more limb to extract, but may not need all the Bits, so treat + // this as a special case: + // + if (i < backend.size()) { + const limb_type mask = + boost::multiprecision::backends::numeric_limits_workaround::digits - shift == + big_integer::limb_bits + ? ~static_cast(0) + : (static_cast(1u) + << (boost::multiprecision::backends::numeric_limits_workaround< + R>::digits - + shift)) - + 1; + *result += (static_cast(backend.limbs()[i]) & mask) << shift; + if ((static_cast(backend.limbs()[i]) & static_cast(~mask)) || + (i + 1 < backend.size())) { + // Overflow: + if (boost::multiprecision::detail::is_signed::value) { + *result = + (boost::multiprecision::backends::numeric_limits_workaround::max)(); + } + return; + } + } + } else if (backend.size() > 1) { + // We will check for overflow here. + for (std::size_t i = 1; i < backend.size(); ++i) { + BOOST_ASSERT(backend.limbs()[i] == 0); + } + } + } + + template + NIL_CO3_MP_FORCEINLINE constexpr bool eval_is_zero(const big_integer &val) noexcept { + // std::all_of is not constexpr, so writing manually. + for (std::size_t i = 0; i < val.size(); ++i) { + if (val.limbs()[i] != 0) { + return false; + } + } + return true; + } + + // + // Get the location of the least-significant-bit: + // + template + inline constexpr unsigned eval_lsb(const big_integer &a) { + // + // Find the index of the least significant limb that is non-zero: + // + std::size_t index = 0; + while (!a.limbs()[index] && (index < a.size())) { + ++index; + } + // + // Find the index of the least significant bit within that limb: + // + unsigned result = boost::multiprecision::detail::find_lsb(a.limbs()[index]); + + return result + index * big_integer::limb_bits; + } + + template + inline constexpr unsigned eval_msb(const big_integer &a) { + // + // Find the index of the most significant bit that is non-zero: + // + for (std::size_t i = a.size() - 1; i > 0; --i) { + if (a.limbs()[i] != 0) { + return i * big_integer::limb_bits + + boost::multiprecision::detail::find_msb(a.limbs()[i]); + } + } + if (a.limbs()[0] == 0) { // TODO here should assert/throw + return 1024; // Some big number to indicate that there is no bit 1 + } + return boost::multiprecision::detail::find_msb(a.limbs()[0]); + } + +#ifdef BOOST_GCC +// +// We really shouldn't need to be disabling this warning, but it really does appear to be +// spurious. The warning appears only when in release mode, and asserts are on. +// +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + + template + inline constexpr bool eval_bit_test(const big_integer &val, std::size_t index) noexcept { + unsigned offset = index / big_integer::limb_bits; + unsigned shift = index % big_integer::limb_bits; + limb_type mask = limb_type(1u) << shift; + if (offset >= val.size()) { + return false; + } + return static_cast(val.limbs()[offset] & mask); + } + +#ifdef BOOST_GCC +#pragma GCC diagnostic pop +#endif + + template + inline constexpr void eval_bit_set(big_integer &val, std::size_t index) { + unsigned offset = index / big_integer::limb_bits; + unsigned shift = index % big_integer::limb_bits; + limb_type mask = limb_type(1u) << shift; + if (offset >= val.size()) { + return; // fixed precision overflow + } + val.limbs()[offset] |= mask; + } + + template + inline constexpr void eval_bit_unset(big_integer &val, std::size_t index) noexcept { + unsigned offset = index / big_integer::limb_bits; + unsigned shift = index % big_integer::limb_bits; + limb_type mask = limb_type(1u) << shift; + if (offset >= val.size()) { + return; + } + val.limbs()[offset] &= ~mask; + val.normalize(); + } + + template + inline constexpr void eval_bit_flip(big_integer &val, std::size_t index) { + unsigned offset = index / big_integer::limb_bits; + unsigned shift = index % big_integer::limb_bits; + limb_type mask = limb_type(1u) << shift; + if (offset >= val.size()) { + return; // fixed precision overflow + } + val.limbs()[offset] ^= mask; + val.normalize(); + } + + // Since we don't have signed_type in big_integer, we need to override this + // function. + template + inline constexpr + typename std::enable_if::value, + Integer>::type + eval_integer_modulus(const big_integer &a, Integer mod) { + if constexpr (sizeof(Integer) <= sizeof(limb_type)) { + if (mod <= (std::numeric_limits::max)()) { + const int n = a.size(); + const double_limb_type two_n_mod = + static_cast(1u) + (~static_cast(0u) - mod) % mod; + limb_type res = a.limbs()[n - 1] % mod; + + for (int i = n - 2; i >= 0; --i) { + res = static_cast((res * two_n_mod + a.limbs()[i]) % mod); + } + return res; + } + return boost::multiprecision::default_ops::eval_integer_modulus(a, mod); + } else { + return boost::multiprecision::default_ops::eval_integer_modulus(a, mod); + } + } + + template + NIL_CO3_MP_FORCEINLINE constexpr + typename std::enable_if::value && + boost::multiprecision::detail::is_integral::value, + Integer>::type + eval_integer_modulus(const big_integer &x, Integer val) { + return eval_integer_modulus(x, boost::multiprecision::detail::unsigned_abs(val)); + } + + template + inline constexpr std::size_t hash_value(const big_integer &val) noexcept { + std::size_t result = 0; + for (unsigned i = 0; i < val.size(); ++i) { + boost::hash_combine(result, val.limbs()[i]); + } + return result; + } + +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + +} // namespace nil::crypto3::multiprecision + +// TODO +// namespace detail { + +// // We need to specialize this class, because big_integer does not have +// // signed_types. All we have changed here is Backend::signed_types -> +// // Backend::unsigned_types, this will work for our use cases. +// template +// struct canonical_imp, std::integral_constant> { +// static constexpr int index = +// find_index_of_large_enough_type::unsigned_types, 0, +// bits_of::value>::value; +// using type = typename dereference_tuple::unsigned_types, +// Val>::type; +// }; + +// } // namespace detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp new file mode 100644 index 0000000000..e173b8f464 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////// +// Copyright (c) 2023 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Contains eval_multiply for big_integer, which does nothing but converts it to +// cpp_int_backend and does the multiplication. +// + +#pragma once + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" + +namespace nil::crypto3::multiprecision { + + // Functions in this file should be called only for creation of montgomery and Barett + // params, calculation of inverse element and montgomery_reduce. Since these functions + // are relatively slow and are not called very often, we will not optimize them. We do + // NOT care about the execution speed, and will just redirect calls to normal + // boost::cpp_int. + template + inline constexpr void eval_multiply(big_integer &result, + const big_integer &a, + const big_integer &b) noexcept { + result = a; + // Call the lower function, we don't care about speed here. + eval_multiply(result, b); + } + + // If the second argument is trivial or not, we still assign it to the first the exact + // same way. + template + inline constexpr void eval_multiply(big_integer &result, const big_integer &a, + const limb_type &b) noexcept { + result = a; + // Call the lower function, we don't care about speed here. + eval_multiply(result, b); + } + + template + inline constexpr void eval_multiply(big_integer &result, const limb_type &b) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int *= b; + result.from_cpp_int(result_cpp_int); + } + + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! + // Covers the case where the second operand is not trivial. + // Redirects the call to normal boost::cpp_int. We do not care about speed here. + template + inline constexpr void eval_multiply(big_integer &result, + const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int *= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp new file mode 100644 index 0000000000..b0c50be6bd --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace nil::crypto3::multiprecision { + using limb_type = std::uint32_t; + using double_limb_type = std::uint64_t; + using signed_limb_type = std::int32_t; + using signed_double_limb_type = std::int64_t; +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/literals.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/literals.hpp new file mode 100644 index 0000000000..3dd9f1fc12 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/literals.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include \ No newline at end of file diff --git a/crypto3/libs/multiprecision/test/CMakeLists.txt b/crypto3/libs/multiprecision/test/CMakeLists.txt index e2e050e90c..847f39bba1 100644 --- a/crypto3/libs/multiprecision/test/CMakeLists.txt +++ b/crypto3/libs/multiprecision/test/CMakeLists.txt @@ -8,6 +8,7 @@ #---------------------------------------------------------------------------# add_custom_target(${CURRENT_PROJECT_NAME}_test_suite_modular_cpp_int_tests) +add_custom_target(${CURRENT_PROJECT_NAME}_test_suite_big_integer_tests) cm_test_link_libraries( ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} @@ -48,6 +49,16 @@ macro(define_modular_cpp_int_test name) endmacro(define_modular_cpp_int_test) +macro(define_big_integer_test name) + define_runtime_multiprecision_test(${name}) + + set(test_name "${CURRENT_PROJECT_NAME}_${name}_test") + target_compile_definitions(${test_name} PUBLIC -DTEST_CPP_INT) + # target_link_libraries(${test_name} no_eh_support) + add_dependencies(${CURRENT_PROJECT_NAME}_test_suite_big_integer_tests ${test_name}) + +endmacro(define_big_integer_test) + set(RUNTIME_TESTS_NAMES "inverse" "jacobi" @@ -59,6 +70,12 @@ set(MODULAR_TESTS_NAMES "modular_adaptor_fixed" ) +set(BIG_INTEGER_TESTS_NAMES + "big_integer" + "big_integer_comparision" + "big_integer_modular" +) + foreach(TEST_NAME ${RUNTIME_TESTS_NAMES}) define_runtime_multiprecision_test(${TEST_NAME}) endforeach() @@ -66,3 +83,7 @@ endforeach() foreach(TEST_NAME ${MODULAR_TESTS_NAMES}) define_modular_cpp_int_test(${TEST_NAME}) endforeach() + +foreach(TEST_NAME ${BIG_INTEGER_TESTS_NAMES}) + define_big_integer_test(${TEST_NAME}) +endforeach() diff --git a/crypto3/libs/multiprecision/test/big_integer.cpp b/crypto3/libs/multiprecision/test/big_integer.cpp new file mode 100644 index 0000000000..4f13652ac5 --- /dev/null +++ b/crypto3/libs/multiprecision/test/big_integer.cpp @@ -0,0 +1,62 @@ + +#define BOOST_TEST_MODULE big_integer_test + +#include + +#include + +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/literals.hpp" + +using namespace nil::crypto3::multiprecision::literals; + +BOOST_AUTO_TEST_SUITE(big_integer_smoke_tests) + +BOOST_AUTO_TEST_CASE(construct) { + nil::crypto3::multiprecision::big_integer<315> a = 0x123_big_integer315; +} + +BOOST_AUTO_TEST_CASE(to_string_trivial) { BOOST_CHECK((0x1_big_integer315).str() == "1"); } + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(big_integer_operations_tests) + +BOOST_AUTO_TEST_CASE(ops) { + nil::crypto3::multiprecision::big_integer<315> a = 2u; + + auto b = a + a; + b += a; + ++b; + b++; + + --b; + b--; + b -= a; + b = b - a; + + b = std::move(a); + + b = a * b; + b *= a; + b = a / b; + b /= a; + b = a % b; + b %= a; + + b = a & b; + b &= a; + b = a | b; + b |= a; + b = a ^ b; + b ^= a; + + b = ~a; + + // b = -b; + b = +b; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/multiprecision/test/big_integer_comparision.cpp b/crypto3/libs/multiprecision/test/big_integer_comparision.cpp new file mode 100644 index 0000000000..c04e24737e --- /dev/null +++ b/crypto3/libs/multiprecision/test/big_integer_comparision.cpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE big_integer_comparision_test + +// Suddenly, BOOST_MP_ASSERT is NOT constexpr, and it is used in constexpr functions throughout the +// boost, resulting to compilation errors on all compilers in debug mode. We need to switch +// assertions off inside cpp_int to make this code compile in debug mode. So we use this workaround +// to turn off file 'boost/multiprecision/detail/assert.hpp' which contains definition of +// BOOST_MP_ASSERT and BOOST_MP_ASSERT_MSG. +#ifndef BOOST_MP_DETAIL_ASSERT_HPP +#define BOOST_MP_DETAIL_ASSERT_HPP +#define BOOST_MP_ASSERT(expr) ((void)0) +#define BOOST_MP_ASSERT_MSG(expr, msg) ((void)0) +#endif + +#include +#include +#include +#include +#include + +// We need cpp_int to compare to it. +#include +#include + +using namespace boost::multiprecision; + +using nil::crypto3::multiprecision::big_integer; + +// This test case uses normal boost::cpp_int for comparison to our big_integer +template +void value_comparisons_tests(const big_integer& a, const big_integer& b) { + typedef big_integer Backend1; + typedef big_integer Backend2; + typedef typename Backend1::cpp_int_type cpp_int_number1; + typedef typename Backend2::cpp_int_type cpp_int_number2; + + // Convert from big_integer to cpp_int_backend numbers. + cpp_int_number1 a_cppint = a.to_cpp_int(); + cpp_int_number2 b_cppint = b.to_cpp_int(); + + BOOST_ASSERT_MSG((a > b) == (a_cppint > b_cppint), "g error"); + BOOST_ASSERT_MSG((a >= b) == (a_cppint >= b_cppint), "ge error"); + BOOST_ASSERT_MSG((a == b) == (a_cppint == b_cppint), "e error"); + BOOST_ASSERT_MSG((a < b) == (a_cppint < b_cppint), "l error"); + BOOST_ASSERT_MSG((a <= b) == (a_cppint <= b_cppint), "le error"); + BOOST_ASSERT_MSG((a != b) == (a_cppint != b_cppint), "ne error"); +} + +template +void value_comparisons_tests(const std::size_t N) { + using standard_number1 = big_integer; + using standard_number2 = big_integer; + + int seed = 0; + boost::random::mt19937 gen(seed); + boost::random::uniform_int_distribution d1; + boost::random::uniform_int_distribution d2; + + for (std::size_t i = 0; i < N; ++i) { + standard_number1 a = d1(gen); + standard_number2 b = d2(gen); + value_comparisons_tests(a, b); + } +} + +BOOST_AUTO_TEST_SUITE(static_tests) + +BOOST_AUTO_TEST_CASE(base_test_backend_12_17) { value_comparisons_tests<12, 17>(1000); } + +BOOST_AUTO_TEST_CASE(base_test_backend_260_130) { value_comparisons_tests<260, 130>(1000); } + +BOOST_AUTO_TEST_CASE(base_test_backend_128_256) { value_comparisons_tests<128, 256>(1000); } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/multiprecision/test/big_integer_modular.cpp b/crypto3/libs/multiprecision/test/big_integer_modular.cpp new file mode 100644 index 0000000000..09e373c573 --- /dev/null +++ b/crypto3/libs/multiprecision/test/big_integer_modular.cpp @@ -0,0 +1,43 @@ +#define BOOST_TEST_MODULE big_integer_test + +#include + +#include + +#include + +#include "nil/crypto3/multiprecision/big_integer/literals.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp" + +using namespace nil::crypto3::multiprecision; +using namespace nil::crypto3::multiprecision::literals; + +BOOST_AUTO_TEST_SUITE(big_integer_smoke_tests) + +constexpr auto mod = 0x123_big_integer315; + +BOOST_AUTO_TEST_CASE(operations) { + modular_big_integer_ct a = 2; + + auto b = a + a; + b += a; + ++b; + b++; + + --b; + b--; + b -= a; + b = b - a; + + b = std::move(a); + + b = a * b; + b *= a; + b = a / b; + b /= a; + + b = -b; + b = +b; +} + +BOOST_AUTO_TEST_SUITE_END() From 33d5d69d2507c2502e4da80a05e3948ad6e8e3fc Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Mon, 21 Oct 2024 13:32:16 +0000 Subject: [PATCH 2/9] Multiprecision refactoring: refactor modular --- .../big_integer/{ops => basic_ops}/add.hpp | 68 +- .../{ops => basic_ops}/add_unsigned.hpp | 4 +- .../{ops => basic_ops}/bitwise.hpp | 93 ++- .../big_integer/basic_ops/divide.hpp | 36 ++ .../big_integer/basic_ops/multiply.hpp | 39 ++ .../big_integer/big_integer_impl.hpp | 131 ++-- .../big_integer/big_integer_ops.hpp | 72 +-- .../multiprecision/big_integer/limits.hpp | 77 +-- .../multiprecision/big_integer/literals.hpp | 279 ++++---- .../modular/modular_big_integer.hpp | 16 - .../modular/modular_big_integer_impl.hpp | 378 ++++++----- .../modular/modular_big_integer_ops.hpp | 118 ++-- .../modular/modular_big_integer_ops_impl.hpp | 203 ++++++ .../big_integer/modular/modular_functions.hpp | 316 ++++----- .../big_integer/modular/modular_ops.hpp | 603 +++++------------ .../big_integer/modular/modular_params.hpp | 203 ------ .../big_integer/modular/modular_policy.hpp | 43 -- .../multiprecision/big_integer/ops/divide.hpp | 59 -- .../big_integer/ops/eval_jacobi.hpp | 78 --- .../big_integer/ops/import_export.hpp | 82 +-- .../big_integer/ops/integer_ops.hpp | 55 -- .../multiprecision/big_integer/ops/jacobi.hpp | 66 ++ .../multiprecision/big_integer/ops/misc.hpp | 81 +-- .../big_integer/ops/multiply.hpp | 58 -- .../multiprecision/big_integer/storage.hpp | 22 +- .../libs/multiprecision/test/CMakeLists.txt | 3 +- .../libs/multiprecision/test/big_integer.cpp | 142 +++- .../test/big_integer_modular.cpp | 147 ++++- .../big_integer_modular_comprehensive.cpp | 612 ++++++++++++++++++ 29 files changed, 2175 insertions(+), 1909 deletions(-) rename crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/{ops => basic_ops}/add.hpp (73%) rename crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/{ops => basic_ops}/add_unsigned.hpp (99%) rename crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/{ops => basic_ops}/bitwise.hpp (75%) create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/jacobi.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp create mode 100644 crypto3/libs/multiprecision/test/big_integer_modular_comprehensive.cpp diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp similarity index 73% rename from crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp rename to crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp index 1249b9e3c8..d90e6f8cd3 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp @@ -10,12 +10,12 @@ // #include #include +#include "nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp" #include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" #include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" -#include "nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" -namespace nil::crypto3::multiprecision { +namespace nil::crypto3::multiprecision::detail { template inline constexpr void add_unsigned(big_integer& result, const big_integer& a, @@ -91,84 +91,80 @@ namespace nil::crypto3::multiprecision { // above: // template - NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, - const big_integer& o) noexcept { - eval_add(result, result, o); + NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + add_unsigned(result, a, b); } template - inline constexpr void eval_add(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - add_unsigned(result, a, b); + NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, + const big_integer& o) noexcept { + add(result, result, o); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, - const limb_type& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, + const limb_type& o) noexcept { add_unsigned(result, result, o); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_add(big_integer& result, - const big_integer& a, - const limb_type& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, const big_integer& a, + const limb_type& o) noexcept { add_unsigned(result, a, o); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, - const limb_type& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, + const limb_type& o) noexcept { subtract_unsigned(result, result, o); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, - const big_integer& a, - const limb_type& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, + const big_integer& a, + const limb_type& o) noexcept { subtract_unsigned(result, a, o); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_increment(big_integer& result) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void increment(big_integer& result) noexcept { if ((result.limbs()[0] < big_integer::max_limb_value)) { ++result.limbs()[0]; } else { - eval_add(result, (limb_type)1); + add(result, static_cast(1u)); } } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_decrement(big_integer& result) noexcept { - constexpr const limb_type one = 1; - + NIL_CO3_MP_FORCEINLINE constexpr void decrement(big_integer& result) noexcept { if (result.limbs()[0]) { --result.limbs()[0]; } else { - eval_subtract(result, one); + subtract(result, static_cast(1u)); } } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, - const big_integer& o) noexcept { - eval_subtract(result, result, o); + NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, + const big_integer& a, + const big_integer& b) noexcept { + subtract_unsigned(result, a, b); } - template - NIL_CO3_MP_FORCEINLINE constexpr void eval_subtract(big_integer& result, - const big_integer& a, - const big_integer& b) noexcept { - subtract_unsigned(result, a, b); + NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, + const big_integer& o) noexcept { + subtract(result, result, o); } template - NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type eval_subtract( + NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type subtract( big_integer& result, const big_integer& o) noexcept { big_integer o_larger = o; - eval_subtract(result, result, o_larger); + subtract(result, result, o_larger); } template - NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type eval_subtract( + NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type subtract( big_integer& result, const big_integer& a, const big_integer& b) noexcept { big_integer b_larger = b; subtract_unsigned(result, a, b_larger); } -} // namespace nil::crypto3::multiprecision +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp similarity index 99% rename from crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp rename to crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp index aef579034c..3abef6016d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/add_unsigned.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp @@ -13,7 +13,7 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" -namespace nil::crypto3::multiprecision { +namespace nil::crypto3::multiprecision::detail { template inline constexpr void add_unsigned_constexpr(big_integer& result, const big_integer& a, @@ -288,4 +288,4 @@ namespace nil::crypto3::multiprecision { } #endif -} // namespace nil::crypto3::multiprecision +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp similarity index 75% rename from crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp rename to crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp index 1a9150ad66..a7b10639cd 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp @@ -15,12 +15,7 @@ #include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4319) -#endif - -namespace nil::crypto3::multiprecision { +namespace nil::crypto3::multiprecision::detail { template constexpr void bitwise_op(big_integer& result, const big_integer& o, Op op) noexcept { @@ -49,47 +44,47 @@ namespace nil::crypto3::multiprecision { } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_and(big_integer& result, - const big_integer& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(big_integer& result, + const big_integer& o) noexcept { bitwise_op(result, o, std::bit_and()); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_or(big_integer& result, - const big_integer& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(big_integer& result, + const big_integer& o) noexcept { bitwise_op(result, o, std::bit_or()); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_xor(big_integer& result, - const big_integer& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(big_integer& result, + const big_integer& o) noexcept { bitwise_op(result, o, std::bit_xor()); } // // Again for operands which are single limbs: // template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_and(big_integer& result, - limb_type l) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(big_integer& result, + limb_type l) noexcept { result.limbs()[0] &= l; result.zero_after(1); } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_or(big_integer& result, - limb_type l) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(big_integer& result, + limb_type l) noexcept { result.limbs()[0] |= l; } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_bitwise_xor(big_integer& result, - limb_type l) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(big_integer& result, + limb_type l) noexcept { result.limbs()[0] ^= l; } template - NIL_CO3_MP_FORCEINLINE constexpr void eval_complement(big_integer& result, - const big_integer& o) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr void complement(big_integer& result, + const big_integer& o) noexcept { unsigned os = o.size(); for (unsigned i = 0; i < os; ++i) { result.limbs()[i] = ~o.limbs()[i]; @@ -101,9 +96,9 @@ namespace nil::crypto3::multiprecision { // This function must be called only when s % 8 == 0, i.e. we shift bytes. template inline void left_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer Int; + typedef big_integer big_integer_t; - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); std::size_t bytes = static_cast(s / CHAR_BIT); if (s >= Bits) { @@ -121,12 +116,12 @@ namespace nil::crypto3::multiprecision { // are normally 64 bit. template inline constexpr void left_shift_limb(big_integer& result, double_limb_type s) { - typedef big_integer Int; + using big_integer_t = big_integer; - limb_type offset = static_cast(s / Int::limb_bits); - BOOST_ASSERT(static_cast(s % Int::limb_bits) == 0); + limb_type offset = static_cast(s / big_integer_t::limb_bits); + BOOST_ASSERT(static_cast(s % big_integer_t::limb_bits) == 0); - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); if (s >= Bits) { // Set result to 0. @@ -146,16 +141,16 @@ namespace nil::crypto3::multiprecision { // Left shift will throw away upper Bits. template inline constexpr void left_shift_generic(big_integer& result, double_limb_type s) { - typedef big_integer Int; + using big_integer_t = big_integer; if (s >= Bits) { // Set result to 0. result.zero_after(0); } else { - limb_type offset = static_cast(s / Int::limb_bits); - limb_type shift = static_cast(s % Int::limb_bits); + limb_type offset = static_cast(s / big_integer_t::limb_bits); + limb_type shift = static_cast(s % big_integer_t::limb_bits); - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); std::size_t i = 0; std::size_t rs = result.size(); // This code only works when shift is non-zero, otherwise we invoke undefined @@ -163,7 +158,7 @@ namespace nil::crypto3::multiprecision { BOOST_ASSERT(shift); for (; rs - i >= 2 + offset; ++i) { pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; - pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (Int::limb_bits - shift); + pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (big_integer_t::limb_bits - shift); } if (rs - i >= 1 + offset) { pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; @@ -177,7 +172,7 @@ namespace nil::crypto3::multiprecision { // Shifting left throws away upper Bits. template - inline constexpr void eval_left_shift(big_integer& result, double_limb_type s) noexcept { + inline constexpr void left_shift(big_integer& result, double_limb_type s) noexcept { if (!s) { return; } @@ -226,9 +221,9 @@ namespace nil::crypto3::multiprecision { template inline void right_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer Int; + typedef big_integer big_integer_t; - limb_type offset = static_cast(s / Int::limb_bits); + limb_type offset = static_cast(s / big_integer_t::limb_bits); BOOST_ASSERT((s % CHAR_BIT) == 0); unsigned ors = result.size(); unsigned rs = ors; @@ -237,12 +232,12 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); unsigned char* pc = reinterpret_cast(pr); limb_type shift = static_cast(s / CHAR_BIT); std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift); shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT; - if (shift < Int::limb_bits) { + if (shift < big_integer_t::limb_bits) { pr[ors - offset - 1] &= (static_cast(1u) << shift) - 1; if (!pr[ors - offset - 1] && (rs > 1)) { --rs; @@ -254,10 +249,10 @@ namespace nil::crypto3::multiprecision { template inline constexpr void right_shift_limb(big_integer& result, double_limb_type s) { - typedef big_integer Int; + typedef big_integer big_integer_t; - limb_type offset = static_cast(s / Int::limb_bits); - BOOST_ASSERT((s % Int::limb_bits) == 0); + limb_type offset = static_cast(s / big_integer_t::limb_bits); + BOOST_ASSERT((s % big_integer_t::limb_bits) == 0); unsigned ors = result.size(); unsigned rs = ors; if (offset >= rs) { @@ -265,7 +260,7 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); unsigned i = 0; for (; i < rs; ++i) { pr[i] = pr[i + offset]; @@ -276,9 +271,9 @@ namespace nil::crypto3::multiprecision { template inline constexpr void right_shift_generic(big_integer& result, double_limb_type s) { - typedef big_integer Int; - limb_type offset = static_cast(s / Int::limb_bits); - limb_type shift = static_cast(s % Int::limb_bits); + typedef big_integer big_integer_t; + limb_type offset = static_cast(s / big_integer_t::limb_bits); + limb_type shift = static_cast(s % big_integer_t::limb_bits); unsigned ors = result.size(); unsigned rs = ors; @@ -287,7 +282,7 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename Int::limb_pointer pr = result.limbs(); + typename big_integer_t::limb_pointer pr = result.limbs(); if ((pr[ors - 1] >> shift) == 0) { if (--rs == 0) { result = limb_type(0); @@ -300,7 +295,7 @@ namespace nil::crypto3::multiprecision { BOOST_ASSERT(shift); for (; i + offset + 1 < ors; ++i) { pr[i] = pr[i + offset] >> shift; - pr[i] |= pr[i + offset + 1] << (Int::limb_bits - shift); + pr[i] |= pr[i + offset + 1] << (big_integer_t::limb_bits - shift); } pr[i] = pr[i + offset] >> shift; @@ -309,7 +304,7 @@ namespace nil::crypto3::multiprecision { } template - inline constexpr void eval_right_shift(big_integer& result, double_limb_type s) noexcept { + inline constexpr void right_shift(big_integer& result, double_limb_type s) noexcept { if (!s) { return; } @@ -352,8 +347,4 @@ namespace nil::crypto3::multiprecision { right_shift_generic(result, s); } } -} // namespace nil::crypto3::multiprecision - -#ifdef _MSC_VER -#pragma warning(pop) -#endif +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp new file mode 100644 index 0000000000..1fd314d72a --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////// +// Copyright (c) 2023 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Contains modulus for big_integer, which uses conversion to cpp_int_backend to +// actually apply the operation. +// + +#pragma once + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" + +// Functions in this file should be called only for creation of montgomery and Barett +// params, no during "normal" execution, so we do NOT care about the execution speed, +// and will just redirect calls to normal boost::cpp_int. + +namespace nil::crypto3::multiprecision::detail { + // Just a call to the upper function, similar to operator*=. + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! + template + inline constexpr void modulus(big_integer &result, + const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int %= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } + + template + inline constexpr void divide(big_integer &result, const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int /= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp new file mode 100644 index 0000000000..c71520e98c --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////// +// Copyright (c) 2023 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +// +// Contains multiply for big_integer, which does nothing but converts it to +// cpp_int_backend and does the multiplication. +// + +#pragma once + +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" + +// Functions in this file should be called only for creation of montgomery and Barett +// params, calculation of inverse element and montgomery_reduce. Since these functions +// are relatively slow and are not called very often, we will not optimize them. We do +// NOT care about the execution speed, and will just redirect calls to normal +// boost::cpp_int. + +// Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! + +namespace nil::crypto3::multiprecision::detail { + template + inline constexpr void multiply(big_integer &result, const limb_type &b) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int *= b; + result.from_cpp_int(result_cpp_int); + } + + template + inline constexpr void multiply(big_integer &result, + const big_integer &a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int *= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index 3a0ca0a4b9..8578b9673a 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -9,10 +9,10 @@ #include #include #include +#include #include #include #include -#include // TODO(ioxid): replace with custom code #include @@ -30,22 +30,25 @@ namespace nil::crypto3::multiprecision { Bits, Bits, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked>>; + using limb_type = detail::limb_type; + using double_limb_type = detail::double_limb_type; + using signed_limb_type = detail::signed_limb_type; + using signed_double_limb_type = detail::signed_double_limb_type; + using unsigned_types = std::tuple; using signed_types = std::tuple; // Storage - using limb_type = limb_type; - using double_limb_type = double_limb_type; - using limb_pointer = limb_type*; - using const_limb_pointer = const limb_type*; + using limb_pointer = detail::limb_pointer; + using const_limb_pointer = detail::const_limb_pointer; + static constexpr unsigned limb_bits = detail::limb_bits; + static constexpr unsigned max_limb_value = detail::max_limb_value; - static constexpr unsigned limb_bits = sizeof(limb_type) * CHAR_BIT; - static constexpr limb_type max_limb_value = ~static_cast(0u); static constexpr unsigned internal_limb_count = - (Bits / limb_bits) + (((Bits % limb_bits) != 0u) ? 1 : 0); + (Bits / limb_bits) + (((Bits % limb_bits) != 0u) ? 1u : 0u); static constexpr limb_type upper_limb_mask = - (Bits % limb_bits) ? (limb_type(1) << (Bits % limb_bits)) - 1 : (~limb_type(0)); + (Bits % limb_bits) ? (limb_type(1) << (Bits % limb_bits)) - 1 : (~limb_type(0u)); // // Helper functions for getting at our internal data, and manipulating storage: @@ -88,24 +91,14 @@ namespace nil::crypto3::multiprecision { } // TODO(ioxid): forbid signed, implement comparison with signed instead - template /*&& std::is_unsigned_v*/, - bool> = true> - inline constexpr big_integer(UI val) noexcept { + template /*&& std::is_unsigned_v*/, int> = 0> + inline constexpr big_integer(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; std::terminate(); } - // TODO(ioxid): support assignment from uint64_t and uint128_t - do_assign_integral(static_cast(val)); - } - - // Move constructors - - inline constexpr big_integer(big_integer&& other) noexcept { do_assign(other); } - - template - inline constexpr big_integer(big_integer&& other) noexcept { - do_assign(other); + do_assign_integral(static_cast>(val)); } // Copy construction @@ -128,35 +121,24 @@ namespace nil::crypto3::multiprecision { return *this; } - // Move assignment - - inline constexpr auto& operator=(big_integer&& other) noexcept { - do_assign(other); - return *this; - } - template - inline constexpr big_integer& operator=(big_integer&& other) noexcept { - do_assign(other); - return *this; - } - // Assignment from other types // TODO(ioxid): forbid signed, implement comparison with signed instead - template + template inline constexpr - typename std::enable_if_t /*&& std::is_unsigned_v*/, + typename std::enable_if_t /*&& std::is_unsigned_v*/, big_integer&> - operator=(UI val) noexcept { + operator=(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; std::terminate(); } - do_assign_integral(val); + do_assign_integral(static_cast>(val)); return *this; } inline constexpr auto& operator=(const char* s) { + // TODO(ioxid): rewrite without cpp_int cpp_int_type value; value = s; this->from_cpp_int(value); @@ -175,55 +157,58 @@ namespace nil::crypto3::multiprecision { // cpp_int conversion - inline constexpr void from_cpp_int(const cpp_int_type& other) { - // Here we need other.size(), not this->size(), because cpp_int may not use all the - // limbs it has, but we will. - for (unsigned i = 0; i < other.backend().size(); ++i) { - this->limbs()[i] = other.backend().limbs()[i]; - } - // Zero out the rest. - for (unsigned i = other.backend().size(); i < this->size(); ++i) { - this->limbs()[i] = 0; + inline constexpr void from_cpp_int(cpp_int_type cppint) { + for (limb_type& limb : m_data) { + limb = static_cast(cppint & static_cast(-1)); + cppint >>= limb_bits; } } // Converting to cpp_int. We need this for multiplication, division and string - // conversions. Since these operations are rare, there's no reason to implement then for + // conversions. Since these operations are rare, there's no reason to implement them for // big_integer, converting to cpp_int does not result to performance penalty. inline constexpr cpp_int_type to_cpp_int() const { cpp_int_type result; - // TODO(ioxid): not constexpr? - // result.backend().resize(this->size(), this->size()); - // for (unsigned i = 0; i < this->size(); ++i) { - // result.backend().limbs()[i] = this->limbs()[i]; - // } - // result.backend().normalize(); - return std::move(result); + for (const limb_type limb : m_data | std::views::reverse) { + result <<= limb_bits; + result |= limb; + } + return result; } // cast to integral types - template, bool> = true> + template && std::is_unsigned_v, int> = 0> explicit inline constexpr operator T() const { - return static_cast(this->limbs()[0]); + if constexpr (sizeof(T) <= sizeof(limb_type)) { + return static_cast(this->limbs()[0]); + } else { + constexpr std::size_t n = sizeof(T) / sizeof(limb_type); + T result = 0; + for (std::size_t i = 0; i < n; ++i) { + result <<= limb_bits; + result |= limbs()[n - i - 1]; + } + return result; + } } private: - inline constexpr void do_assign_integral(limb_type i) noexcept { - // TODO(ioxid): support assignment from uint64_t and uint128_t - *this->limbs() = i; - this->zero_after(1); - this->normalize(); - } - - inline constexpr void do_assign_integral(double_limb_type i) noexcept { - // TODO(ioxid): support assignment from uint128_t - static_assert(sizeof(i) == 2 * sizeof(limb_type), "Failed integer size check"); - auto p = this->limbs(); - *p = static_cast(i); - if (this->size() > 1) { - p[1] = static_cast(i >> limb_bits); - this->zero_after(2); + template && std::is_unsigned_v, int> = 0> + inline constexpr void do_assign_integral(T a) noexcept { + if constexpr (sizeof(T) <= sizeof(limb_type)) { + this->limbs()[0] = a; + this->zero_after(1); + } else { + static_assert(sizeof(T) % sizeof(limb_type) == 0); + constexpr std::size_t n = sizeof(T) / sizeof(limb_type); + for (std::size_t i = 0; i < n; ++i) { + limbs()[i] = a & static_cast(static_cast(-1)); + a >>= limb_bits; + } + zero_after(n); } this->normalize(); } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp index 6cdf17b8d9..15cf6d4659 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -8,12 +8,13 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/ops/add.hpp" -#include "nil/crypto3/multiprecision/big_integer/ops/bitwise.hpp" -#include "nil/crypto3/multiprecision/big_integer/ops/divide.hpp" +#include "nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp" +#include "nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp" +#include "nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp" +#include "nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp" + #include "nil/crypto3/multiprecision/big_integer/ops/import_export.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/big_integer/ops/misc.hpp" // IWYU pragma: export -#include "nil/crypto3/multiprecision/big_integer/ops/multiply.hpp" namespace nil::crypto3::multiprecision { namespace detail { @@ -29,12 +30,12 @@ namespace nil::crypto3::multiprecision { template constexpr bool is_integral_v = std::is_integral_v || is_big_integer_v; - template, bool> = true> + template, int> = 0> constexpr std::size_t get_bits() { return sizeof(T) * CHAR_BIT; } - template, bool> = true> + template, int> = 0> constexpr std::size_t get_bits() { return T::Bits; } @@ -44,7 +45,7 @@ namespace nil::crypto3::multiprecision { template && detail::is_integral_v && \ (detail::is_big_integer_v || detail::is_big_integer_v), \ - bool> = true, \ + int> = 0, \ typename result_t = \ big_integer(), detail::get_bits())>> @@ -53,11 +54,11 @@ namespace nil::crypto3::multiprecision { typename big_integer_t, typename T, \ std::enable_if_t && detail::is_integral_v && \ detail::get_bits() <= big_integer_t::Bits, \ - bool> = true> + int> = 0> #define CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE \ template, bool> = true> + std::enable_if_t, int> = 0> // Comparison @@ -82,17 +83,17 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator+(const T1& a, const T2& b) noexcept { result_t tmp{a}; - eval_add(tmp, b); + detail::add(tmp, b); return tmp; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator+=(big_integer_t& a, const T& b) noexcept { - eval_add(a, b); + detail::add(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator++(big_integer_t& a) noexcept { - eval_increment(a); + detail::increment(a); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE @@ -107,17 +108,17 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator-(const T1& a, const T2& b) noexcept { result_t tmp; - eval_subtract(tmp, a, b); + detail::subtract(tmp, a, b); return tmp; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator-=(big_integer_t& a, const T& b) { - eval_subtract(a, b); + detail::subtract(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator--(big_integer_t& a) noexcept { - eval_decrement(a); + detail::decrement(a); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE @@ -136,105 +137,105 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { result_t result{a}; - eval_multiply(result, b); + detail::multiply(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator*=(big_integer_t& a, const T& b) noexcept { - eval_multiply(a, b); + detail::multiply(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator/(const T1& a, const T2& b) noexcept { - result_t result; - eval_divide(result, a, b); + result_t result = a; + detail::divide(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator/=(big_integer_t& a, const T& b) noexcept { - eval_divide(a, b); + detail::divide(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator%(const T1& a, const T2& b) noexcept { - result_t result; - eval_modulus(result, a, b); + result_t result = a; + detail::modulus(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator%=(big_integer_t& a, const T& b) { - eval_modulus(a, b); + detail::modulus(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator&(const T1& a, const T2& b) noexcept { result_t result{a}; - eval_bitwise_and(result, b); + detail::bitwise_and(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator&=(big_integer_t& a, const T& b) { - eval_bitwise_and(a, b); + detail::bitwise_and(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator|(const T1& a, const T2& b) noexcept { result_t result{a}; - eval_bitwise_or(result, b); + detail::bitwise_or(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator|=(big_integer_t& a, const T& b) { - eval_bitwise_or(a, b); + detail::bitwise_or(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator^(const T1& a, const T2& b) noexcept { result_t result{a}; - eval_bitwise_xor(result, b); + detail::bitwise_xor(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator^=(big_integer_t& a, const T& b) { - eval_bitwise_or(a, b); + detail::bitwise_or(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator~(const big_integer_t& a) noexcept { big_integer_t result; - eval_complement(result, a); + detail::complement(result, a); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator<<(const big_integer_t& a, unsigned shift) noexcept { big_integer_t result{a}; - eval_left_shift(result, shift); + detail::left_shift(result, shift); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator<<=(big_integer_t& a, unsigned shift) noexcept { // TODO(ioxid): check - eval_left_shift(a, shift); + detail::left_shift(a, shift); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator>>(const big_integer_t& a, unsigned shift) noexcept { big_integer_t result{a}; - eval_right_shift(result, shift); + detail::right_shift(result, shift); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator>>=(big_integer_t& a, unsigned shift) noexcept { // TODO(ioxid): check - eval_right_shift(a, shift); + detail::right_shift(a, shift); return a; } @@ -242,8 +243,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE std::ostream& operator<<(std::ostream& os, const big_integer_t& value) { - // TODO(ioxid): rewrite without cpp_int - os << value.to_cpp_int() << std::endl; + os << value.str() << std::endl; return os; } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp index 083d089a75..cc54a079b1 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/limits.hpp @@ -8,47 +8,33 @@ #include #include -#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -namespace nil::crypto3::multiprecision { - template - class big_integer; -} - -namespace std { - namespace detail { - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4307) -#endif - - template - inline constexpr nil::crypto3::multiprecision::big_integer get_min() { - constexpr const nil::crypto3::multiprecision::big_integer val(0u); - return val; - } +namespace nil::crypto3::multiprecision::detail { + template + inline constexpr big_integer get_min() { + constexpr big_integer val = 0u; + return val; + } - template - inline constexpr nil::crypto3::multiprecision::big_integer get_max() { - using result_type = nil::crypto3::multiprecision::big_integer; - using ui_type = nil::crypto3::multiprecision::big_integer; - constexpr const result_type val = ~ui_type(0); - return val; - } + template + inline constexpr nil::crypto3::multiprecision::big_integer get_max() { + constexpr auto val = ~big_integer(0u); + return val; + } - inline constexpr unsigned calc_digits10(unsigned d) { - // - // We need floor(log10(2) * (d-1)), see: - // https://www.exploringbinary.com/number-of-digits-required-for-round-trip-conversions/ - // and references therein. - // - return static_cast( - 0.301029995663981195213738894724493026768189881462108541310 * - static_cast(d - 1u)); - } - } // namespace detail + inline constexpr unsigned calc_digits10(unsigned d) { + // + // We need floor(log10(2) * (d-1)), see: + // https://www.exploringbinary.com/number-of-digits-required-for-round-trip-conversions/ + // and references therein. + // + return static_cast(0.301029995663981195213738894724493026768189881462108541310 * + static_cast(d - 1u)); + } +} // namespace nil::crypto3::multiprecision::detail +namespace std { template class numeric_limits> { using number_type = nil::crypto3::multiprecision::big_integer; @@ -59,12 +45,17 @@ namespace std { // Largest and smallest numbers are bounded only by available memory, set // to zero: // - static constexpr number_type(min)() { return detail::get_min(); } - static constexpr number_type(max)() { return detail::get_max(); } + static constexpr number_type(min)() { + return nil::crypto3::multiprecision::detail::get_min(); + } + static constexpr number_type(max)() { + return nil::crypto3::multiprecision::detail::get_max(); + } static constexpr number_type lowest() { return (min)(); } static constexpr int digits = number_type::Bits; - static constexpr int digits10 = detail::calc_digits10(digits); - static constexpr int max_digits10 = detail::calc_digits10(digits); + static constexpr int digits10 = nil::crypto3::multiprecision::detail::calc_digits10(digits); + static constexpr int max_digits10 = + nil::crypto3::multiprecision::detail::calc_digits10(digits); static constexpr bool is_signed = false; static constexpr bool is_integer = true; static constexpr bool is_exact = true; @@ -91,8 +82,4 @@ namespace std { static constexpr bool tinyness_before = false; }; -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } // namespace std diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp index d48e5748aa..aeb8079f0d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/literals.hpp @@ -6,25 +6,58 @@ #pragma once #include +#include #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" namespace nil::crypto3::multiprecision::literals { namespace detail { + constexpr bool is_valid_hex_digit(char c) { + return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); + } + + constexpr int parse_hex_digit(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('a' <= c && c <= 'f') { + return (c - 'a') + 10; + } + return (c - 'A') + 10; + } + template constexpr big_integer parse_int_hex() { static_assert(c1 == '0', "hex literal should start with 0x"); static_assert(c2 == 'x', "hex literal should start with 0x"); - constexpr std::size_t hex_digits = sizeof...(STR); - static_assert(Bits >= hex_digits * 4, "not enough bits to store literal"); - static_assert(hex_digits >= 1, "at least one digit expected"); - big_integer result{0}; + std::size_t bits = 0; for (const char c : {STR...}) { + if (!is_valid_hex_digit(c)) { + throw std::invalid_argument("non hex character in literal"); + } + int digit = parse_hex_digit(c); result <<= 4; - result += c - '0'; + if (bits != 0) { + bits += 4; + } + result += parse_hex_digit(c); + if (digit != 0) { + if (digit >= 8) { + bits = 4; + } else if (digit >= 4) { + bits = 3; + } else if (digit >= 2) { + bits = 2; + } else { + bits = 1; + } + } + } + if (bits > Bits) { + throw "not enough bits to store literal"; } return result; } @@ -32,124 +65,126 @@ namespace nil::crypto3::multiprecision::literals { template constexpr auto operator"" _big_integer() { - return detail::parse_int_hex(); + return detail::parse_int_hex<(sizeof...(STR) - 2) * 4, STR...>(); } +} // namespace nil::crypto3::multiprecision::literals -#define CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(Bits) \ - template \ - constexpr auto operator"" _big_integer##Bits() { \ - return detail::parse_int_hex(); \ +#define CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(Bits) \ + namespace nil::crypto3::multiprecision::literals { \ + template \ + constexpr auto operator"" _big_integer##Bits() { \ + return nil::crypto3::multiprecision::literals::detail::parse_int_hex(); \ + } \ } - // This is a comprehensive list of all bitlengths we use - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(7) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(13) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(15) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(16) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(17) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(18) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(64) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(92) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(94) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(128) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(130) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(149) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(150) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(151) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(152) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(160) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(161) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(163) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(164) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(177) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(178) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(179) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(180) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(181) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(182) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(183) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(191) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(192) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(205) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(206) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(222) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(223) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(224) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(225) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(226) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(239) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(248) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(249) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(250) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(251) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(252) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(253) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(254) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(255) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(256) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(257) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(263) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(264) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(280) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(281) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(292) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(293) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(294) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(295) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(296) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(297) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(298) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(315) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(316) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(319) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(320) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(330) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(331) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(374) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(375) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(376) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(377) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(378) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(379) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(380) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(381) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(384) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(503) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(504) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(507) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(512) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(515) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(516) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(521) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(546) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(577) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(578) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(585) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(595) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(636) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(706) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(707) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(758) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(753) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(759) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(761) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(859) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(860) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(893) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(894) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(913) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1024) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1490) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1536) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2048) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2790) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(3072) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4096) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4269) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4314) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(6144) - CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8192) -#undef CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL -} // namespace nil::crypto3::multiprecision::literals +// This is a comprehensive list of all bitlengths we use in algebra. +// Custom ones can be defined using this macro in every place where they are used. +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(7) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(13) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(15) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(16) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(17) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(18) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(64) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(92) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(94) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(128) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(130) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(149) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(150) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(151) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(152) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(160) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(161) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(163) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(164) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(177) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(178) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(179) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(180) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(181) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(182) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(183) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(191) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(192) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(205) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(206) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(222) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(223) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(224) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(225) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(226) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(239) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(248) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(249) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(250) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(251) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(252) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(253) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(254) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(255) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(256) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(257) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(263) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(264) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(280) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(281) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(292) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(293) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(294) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(295) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(296) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(297) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(298) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(315) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(316) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(319) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(320) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(330) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(331) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(374) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(375) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(376) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(377) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(378) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(379) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(380) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(381) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(384) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(503) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(504) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(507) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(512) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(515) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(516) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(521) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(546) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(577) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(578) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(585) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(595) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(636) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(706) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(707) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(758) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(753) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(759) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(761) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(859) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(860) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(893) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(894) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(913) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1024) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1490) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(1536) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2048) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(2790) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(3072) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4096) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4269) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(4314) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(6144) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(8192) diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp index da6b087bea..05dd23a18e 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp @@ -1,20 +1,4 @@ #pragma once -#include - -#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" #include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp" // IWYU pragma: export -#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" - -namespace nil::crypto3::multiprecision { - template - using modular_big_integer_ct = - modular_big_integer, - modular_params_storage_ct, modulus>>; - - // TODO(ioxid): modulus in constructor - template - using modular_big_integer_rt = - modular_big_integer, modular_params_storage_rt>>; -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp index a88290cfa3..b7ba3b2547 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -19,237 +19,225 @@ #include #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" namespace nil::crypto3::multiprecision { - // fixed precision modular big integer which supports compile-time execution - template - class modular_big_integer { - public: - constexpr static auto Bits = big_integer_t::Bits; - using limb_type = typename big_integer_t::limb_type; - using double_limb_type = typename big_integer_t::double_limb_type; - using modular_params_t = modular_params; - using Backend_type = big_integer_t; - - using unsigned_types = typename big_integer_t::unsigned_types; - using signed_types = typename big_integer_t::signed_types; - - protected: - using policy_type = typename modular_params_t::policy_type; - using Backend_padded_limbs = typename policy_type::Backend_padded_limbs; - using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; - modular_params_storage_t modular_params_storage; - - public: - // This version of conversion - constexpr typename big_integer_t::cpp_int_type to_cpp_int() const { - big_integer_t tmp; - modular_params_storage.modular_params().adjust_regular(tmp, this->base_data()); - return tmp.to_cpp_int(); - } + namespace detail { + // fixed precision modular big integer which supports compile-time execution + template + class modular_big_integer_impl { + public: + using big_integer_t = big_integer_t_; + constexpr static auto Bits = big_integer_t::Bits; + using limb_type = typename big_integer_t::limb_type; + using double_limb_type = typename big_integer_t::double_limb_type; + using modular_ops_t = modular_ops; + + using unsigned_types = typename big_integer_t::unsigned_types; + using signed_types = typename big_integer_t::signed_types; + + private: + using policy_type = typename modular_ops_t::policy_type; + using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; + using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; + + // Constructors + + protected: + inline constexpr modular_big_integer_impl(modular_ops_storage_t&& modular_ops_storage) + : m_modular_ops_storage(std::move(modular_ops_storage)) {} + + public: + // Comparison + + constexpr bool compare_eq(const modular_big_integer_impl& o) const { + // TODO(ioxid): ensure modulus comparison is done in compile time when possible + return modular_ops().compare_eq(o.modular_ops()) && m_base == o.m_base; + } - constexpr auto mod_data() { return modular_params_storage.modular_params(); } - constexpr auto mod_data() const { return modular_params_storage.modular_params(); } + template + constexpr int compare_eq(const T& val) const { + // TODO(ioxid): should compare adjusted? + return m_base == val; + } - constexpr big_integer_t& base_data() { return m_base; } - constexpr const big_integer_t& base_data() const { return m_base; } + // cpp_int conversion - constexpr modular_big_integer() {} + constexpr typename big_integer_t::cpp_int_type to_cpp_int() const { + return modular_ops().adjusted_regular(m_base).to_cpp_int(); + } - constexpr modular_big_integer(const modular_big_integer& o) : m_base(o.base_data()) { - modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); - } + // String conversion - constexpr modular_big_integer(modular_big_integer&& o) noexcept - : m_base(std::move(o.base_data())) { - modular_params_storage.set_modular_params( - std::move(o.modular_params_storage.modular_params())); - } + inline std::string str(std::streamsize digits = 0, + std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { + // TODO(ioxid): add module to output + return modular_ops().adjusted_regular(m_base).str(digits, f); + } - template && - std::is_unsigned_v> const* = nullptr> - constexpr modular_big_integer(UI b, const big_integer_t& m) : m_base(limb_type(b)) { - modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base); - } + // TODO(ioxid): why is it here + // Mathemetical operations - // A method for converting a signed integer to a modular adaptor. We are not supposed to - // have this, but in the code we already have conversion for an 'int' into modular type. In - // the future we must remove. - template && - std::is_signed_v> const* = nullptr> - constexpr modular_big_integer(SI b) : m_base(limb_type(0u)) { - if (b >= 0) { - m_base = static_cast(b); - } else { - m_base = modular_params_storage.modular_params().get_mod(); - eval_subtract(m_base, static_cast(-b)); + inline constexpr void negate() { + if (m_base != m_zero) { + auto initial_m_base = m_base; + m_base = modular_ops().get_mod(); + m_base -= initial_m_base; + } } - // This method must be called only for compile time modular params. - // modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base); - } + // TODO(ioxid): who needs this and why is it an assignment operator + // This function sets default modulus value to zero to make sure it fails if not used + // with compile-time fixed modulus. + modular_big_integer_impl& operator=(const char* s) { + using ui_type = typename std::tuple_element<0, unsigned_types>::type; + ui_type zero = 0u; + + if (s && (*s == '(')) { + std::string part; + const char* p = ++s; + while (*p && (*p != ',') && (*p != ')')) { + ++p; + } + part.assign(s, p); + if (!part.empty()) { + m_base = part.c_str(); + } else { + m_base = zero; + } + s = p; + if (*p && (*p != ')')) { + ++p; + while (*p && (*p != ')')) { + ++p; + } + part.assign(s + 1, p); + } else { + part.erase(); + } + if (!part.empty()) { + m_modular_ops_storage.set_modular_ops(part.c_str()); + } else { + m_modular_ops_storage.set_modular_ops(zero); + } + } else { + m_base = s; + m_modular_ops_storage.set_modular_ops(zero); + } + return *this; + } - template && - std::is_unsigned_v> const* = nullptr> - constexpr modular_big_integer(UI b) : m_base(static_cast(b)) { - // This method must be called only for compile time modular params. - // modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base); - } + constexpr auto& base_data() { return m_base; } + constexpr const auto& base_data() const { return m_base; } - template && - std::is_signed_v> const* = nullptr> - constexpr modular_big_integer(SI b, const modular_params_t& m) : m_base(limb_type(0u)) { - if (b >= 0) { - m_base = static_cast(b); - } else { - m_base = modular_params_storage.modular_params().get_mod(); - eval_subtract(m_base, static_cast(-b)); + constexpr auto& modular_ops() { return m_modular_ops_storage.modular_ops(); } + constexpr const auto& modular_ops() const { + return m_modular_ops_storage.modular_ops(); } - modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base); - } + protected: + modular_ops_storage_t m_modular_ops_storage; - template && - std::is_unsigned_v> const* = nullptr> - constexpr modular_big_integer(UI b, const modular_params_t& m) - : m_base(static_cast(b)) { - modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base); - } + big_integer_t m_base; + static constexpr big_integer_t m_zero = + static_cast::type>(0u); + ; + }; - // TODO - // // We may consider to remove this constructor later, and set Bits2 to Bits only, - // // but we need it for use cases from h2f/h2c, - // // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. - // template - // constexpr modular_big_integer(const number> &b, - // const number &m) { - // modular_params_storage.set_modular_params(m.big_integer_t()); - // modular_params_storage.modular_params().adjust_modular(m_base, b.big_integer_t()); - // } - - // We may consider to remove this constructor later, and set Bits2 to Bits only, - // but we need it for use cases from h2f/h2c, - // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. - template - constexpr modular_big_integer(const big_integer& b, const modular_params_t& m) { - modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base, b); + template + constexpr void assign_components( + modular_big_integer_impl, modular_ops_storage_t>& result, + const big_integer_t1& a, const big_integer_t2& b) { + BOOST_ASSERT_MSG(Bits == msb(b) + 1, + "modulus precision should match used big_integer_t"); + + result.set_modular_ops(b); + result.modular_ops().adjust_modular(result.base_data(), a); } + } // namespace detail + + template + struct modular_big_integer_ct + : public detail::modular_big_integer_impl< + std::decay_t, + detail::modular_ops_storage_ct, modulus>> { + using base_type = detail::modular_big_integer_impl< + std::decay_t, + detail::modular_ops_storage_ct, modulus>>; + + using typename base_type::big_integer_t; + + constexpr modular_big_integer_ct() : base_type({}) {} - // We may consider to remove this constructor later, and set Bits2 to Bits only, - // but we need it for use cases from h2f/h2c, - // where a larger number of 512 or 256 bits is passed to a field of 255 or 254 bits. template - constexpr explicit modular_big_integer(const big_integer& b) { - // This method must be called only for compile time modular params. - // modular_params_storage.set_modular_params(m); - modular_params_storage.modular_params().adjust_modular(m_base, b); + constexpr explicit modular_big_integer_ct(const big_integer& b) : base_type({}) { + this->modular_ops().adjust_modular(this->m_base, b); } - // This function sets default modulus value to zero to make sure it fails if not used with - // compile-time fixed modulus. - modular_big_integer& operator=(const char* s) { - using ui_type = typename std::tuple_element<0, unsigned_types>::type; - ui_type zero = 0u; - - if (s && (*s == '(')) { - std::string part; - const char* p = ++s; - while (*p && (*p != ',') && (*p != ')')) { - ++p; - } - part.assign(s, p); - if (!part.empty()) { - m_base = part.c_str(); - } else { - m_base = zero; - } - s = p; - if (*p && (*p != ')')) { - ++p; - while (*p && (*p != ')')) { - ++p; - } - part.assign(s + 1, p); - } else { - part.erase(); - } - if (!part.empty()) { - modular_params_storage.set_modular_params(part.c_str()); - } else { - modular_params_storage.set_modular_params(zero); - } + // A method for converting a signed integer to a modular adaptor. We are not supposed to + // have this, but in the code we already have conversion for an 'int' into modular type. In + // the future we must remove. + template && std::is_signed_v, int> = 0> + constexpr modular_big_integer_ct(SI b) : base_type({}) { + if (b >= 0) { + this->m_base = static_cast>(b); } else { - m_base = s; - modular_params_storage.set_modular_params(zero); + this->m_base = this->modular_ops().get_mod(); + // TODO(ioxid): should work not just with limb_type + this->m_base -= static_cast(-b); } - return *this; - } - constexpr bool compare_eq(const modular_big_integer& o) const { - return !(modular_params_storage.modular_params()) - .compare(o.modular_params_storage.modular_params()) && - !base_data().compare(o.base_data()); + // This method must be called only for compile time modular params. + // modular_ops_storage.set_modular_ops(m); + this->modular_ops().adjust_modular(this->m_base); } - template - constexpr int compare_eq(const T& val) const { - return !base_data().compare(val); + template && std::is_unsigned_v, int> = 0> + constexpr modular_big_integer_ct(UI b) : base_type({}) { + this->m_base = b; + this->modular_ops().adjust_modular(this->m_base); } + }; - constexpr modular_big_integer& operator=(const modular_big_integer& o) { - m_base = o.base_data(); - modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); + template + struct modular_big_integer_rt + : public detail::modular_big_integer_impl< + big_integer, detail::modular_ops_storage_rt>> { + using base_type = + detail::modular_big_integer_impl, + detail::modular_ops_storage_rt>>; - return *this; - } + using typename base_type::big_integer_t; - constexpr modular_big_integer& operator=(modular_big_integer&& o) noexcept { - m_base = o.base_data(); - modular_params_storage.set_modular_params(o.modular_params_storage.modular_params()); + template && std::is_signed_v, int> = 0> + constexpr modular_big_integer_rt(SI b, const big_integer_t& m): base_type(m) { + if (b >= 0) { + this->m_base = b; + } else { + this->m_base = this->modular_ops().get_mod(); + // TODO(ioxid): should work not just with limb_type + this->m_base -= static_cast(-b); + } - return *this; + this->modular_ops().adjust_modular(this->m_base); } - ~modular_big_integer() = default; - - // If we want to print a value, we must first convert it back to normal form. - inline std::string str(std::streamsize dig, std::ios_base::fmtflags f) const { - big_integer_t tmp; - modular_params_storage.modular_params().adjust_regular(tmp, m_base); - return tmp.str(dig, f); + template && std::is_unsigned_v, int> = 0> + constexpr modular_big_integer_rt(UI b, const big_integer_t& m) : base_type(m) { + this->m_base = b; + this->modular_ops().adjust_modular(this->m_base); } - inline constexpr void negate() { - if (m_base == m_zero) { - auto initial_m_base = m_base; - m_base = modular_params_storage.modular_params().get_mod(); - eval_subtract(m_base, initial_m_base); - } + // TODO(ioxid): move adjust modular to impl constructor + template + constexpr modular_big_integer_rt(const big_integer& b, const big_integer_t& m) + : base_type(m) { + this->modular_ops().adjust_modular(this->m_base, b); } - - protected: - big_integer_t m_base; - static constexpr big_integer_t m_zero = - static_cast::type>(0u); - ; }; - - template - constexpr void assign_components( - modular_big_integer, modular_params_storage_t>& result, - const big_integer_t1& a, const big_integer_t2& b) { - BOOST_ASSERT_MSG(Bits == eval_msb(b) + 1, - "modulus precision should match used big_integer_t"); - - result.set_modular_params(b); - result.modular_params().adjust_modular(result.base_data(), a); - } } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp index 4dbeb46187..04c373db1e 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -14,19 +14,19 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" #include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp" namespace nil::crypto3::multiprecision { // Comparison // TODO(ioxid): comparison with big_integer and basic types (including signed) -#define CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(op) \ - template \ - inline constexpr bool operator op( \ - const modular_big_integer& a, \ - const modular_big_integer& b) noexcept { \ - return a.compare_eq(b) op true; \ +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(op) \ + template \ + inline constexpr bool operator op( \ + const detail::modular_big_integer_impl& a, \ + const detail::modular_big_integer_impl& b) noexcept { \ + return a.compare_eq(b) op true; \ } CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(==) @@ -40,9 +40,11 @@ namespace nil::crypto3::multiprecision { template constexpr bool is_modular_big_integer_v = false; - template - constexpr bool - is_modular_big_integer_v> = true; + template + constexpr bool is_modular_big_integer_v> = true; + + template + constexpr bool is_modular_big_integer_v> = true; template constexpr bool is_integral_v = @@ -50,8 +52,7 @@ namespace nil::crypto3::multiprecision { } // namespace modular_detail namespace detail { - template, bool> = true> + template, int> = 0> constexpr std::size_t get_bits() { return T::Bits; } @@ -64,7 +65,7 @@ namespace nil::crypto3::multiprecision { std::enable_if_t && modular_detail::is_integral_v && \ (modular_detail::is_modular_big_integer_v || \ modular_detail::is_modular_big_integer_v), \ - bool> = true, \ + int> = 0, \ typename result_t = T1> #define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ @@ -72,35 +73,39 @@ namespace nil::crypto3::multiprecision { std::enable_if_t && \ modular_detail::is_integral_v && \ detail::get_bits() <= modular_big_integer_t::Bits, \ - bool> = true> + int> = 0> #define CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE \ template, \ - bool> = true> + int> = 0> // Arithmetic operations CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator+(const T1& a, const T2& b) noexcept { - result_t tmp{a}; - // eval_add(tmp, b); - return tmp; + BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); + result_t result{a}; + a.modular_ops().add(result.base_data(), b.base_data()); + return result; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator+=(modular_big_integer_t& a, const T& b) noexcept { - // eval_add(a, b); + BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); + a.modular_ops().add(a.base_data(), b.base_data()); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator++(modular_big_integer_t& a) noexcept { - // eval_increment(a); + // TODO(ioxid): implement faster + a += static_cast(1u); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator++(modular_big_integer_t& a, int) noexcept { auto copy = a; - // eval_increment(a); + // TODO(ioxid): implement faster + a += static_cast(1u); return copy; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE @@ -108,24 +113,26 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator-(const T1& a, const T2& b) noexcept { - result_t tmp; - // eval_subtract(tmp, a, b); + result_t tmp{a}; + detail::subtract(tmp, b); return tmp; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator-=(modular_big_integer_t& a, const T& b) { - // eval_subtract(a, b); + detail::subtract(a, b); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator--(modular_big_integer_t& a) noexcept { - // eval_decrement(a); + // TODO(ioxid): implement faster + a -= static_cast(1u); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator--(modular_big_integer_t& a, int) noexcept { auto copy = a; - // eval_decrement(a); + // TODO(ioxid): implement faster + a -= static_cast(1u); return copy; } @@ -138,64 +145,55 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { + BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); result_t result{a}; - eval_multiply(result, b); + a.modular_ops().mul(result.base_data(), b.base_data()); return result; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator*=(modular_big_integer_t& a, const T& b) noexcept { - // eval_multiply(a, b); + BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); + a.modular_ops().add(a.base_data(), b.base_data()); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator/(const T1& a, const T2& b) noexcept { result_t result; - // eval_divide(result, a, b); + eval_divide(result, a, b); return result; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator/=(modular_big_integer_t& a, const T& b) noexcept { - // eval_divide(a, b); + eval_divide(a, b); return a; } + template + constexpr bool is_zero( + const detail::modular_big_integer_impl& val) noexcept { + return is_zero(val.base_data()); + } + + // Hash + + template + inline constexpr std::size_t hash_value( + const detail::modular_big_integer_impl& val) noexcept { + return hash_value(val.base_data()); + } + // IO - template + template std::ostream& operator<<( - std::ostream& os, const modular_big_integer, modular_params_t>& value) { - os << value.base_data(); + std::ostream& os, + const detail::modular_big_integer_impl, modular_ops_t>& value) { + os << value.str(); return os; } #undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE #undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE #undef CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE -} // namespace nil::crypto3::multiprecision - -// TODO(ioxid): should use this optimization? -// // We need to specialize this function, because default boost implementation is "return -// a.compare(b) -// // == 0;", which is waay slower. -// template inline constexpr bool operator==( -// const number, modular_params_t>, -// ExpressionTemplates> &a, -// const number, modular_params_t>, -// ExpressionTemplates> &b) { -// return a.big_integer_t().compare_eq(b.big_integer_t()); -// } -// -// // We need to specialize this function, because default boost implementation is "return -// a.compare(b) -// // == 0;", which is waay slower. -// template inline constexpr bool operator!=( -// const number, modular_params_t>, -// ExpressionTemplates> &a, -// const number, modular_params_t>, -// ExpressionTemplates> &b) { -// return !a.big_integer_t().compare_eq(b.big_integer_t()); -// } -// } +} // namespace nil::crypto3::multiprecision \ No newline at end of file diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp new file mode 100644 index 0000000000..1ecb45f6cb --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2019-2021 Aleksei Moskvin +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" + +namespace nil::crypto3::multiprecision::detail { + template + constexpr void subtract(modular_big_integer_impl, modular_ops_t> &result, + const modular_big_integer_impl, modular_ops_t> &o) { + if (result.base_data() < o.base_data()) { + auto v = result.modular_ops().get_mod(); + v -= o.base_data(); + result.base_data() += v; + } else { + result.base_data() -= o.base_data(); + } + } + + // template + // constexpr void eval_powm(modular_big_integer_impl, modular_ops_t> &result, + // const modular_big_integer_impl &b, + // const T &e) { + // result.set_modular_ops(b.modular_ops()); + // result.modular_ops().exp(result.base_data(), b.base_data(), e); + // } + + // template + // constexpr void eval_powm(modular_big_integer_impl, modular_ops_t> &result, + // const modular_big_integer_impl &b, + // const modular_big_integer_impl &e) { + // using big_integer_t = big_integer; + + // big_integer_t exp; + // e.modular_ops().adjust_regular(exp, e.base_data()); + // eval_powm(result, b, exp); + // } + + // template + // constexpr void eval_inverse_mod( + // modular_big_integer_impl, modular_ops_t> &result, + // const modular_big_integer_impl, modular_ops_t> &input) { + // using big_integer_t = big_integer; + // using big_integer_t_padded_limbs = + // typename modular_ops::policy_type::big_integer_padded_limbs; + + // big_integer_t_padded_limbs new_base, res, tmp = input.modular_ops().get_mod(); + + // input.modular_ops().adjust_regular(new_base, input.base_data()); + // eval_inverse_mod(res, new_base, tmp); + // assign_components(result, res, input.modular_ops().get_mod()); + // } + + // template + // constexpr void eval_redc(big_integer_t1 &result, const modular_ops &mod) { + // mod.reduce(result); + // eval_modulus(result, mod.get_mod()); + // } + + // template + // constexpr void eval_multiply(modular_big_integer_impl &result, + // const modular_big_integer_impl &o) + // { + // eval_multiply(result.base_data(), o.base_data()); + // eval_redc(result.base_data(), result.modular_ops()); + // } + + // template + // constexpr void eval_divide(modular_big_integer_impl &result, + // const modular_big_integer_impl &o) { + // big_integer_t tmp1, tmp2; + // result.modular_ops().adjust_regular(tmp1, result.base_data()); + // result.modular_ops().adjust_regular(tmp2, o.base_data()); + // eval_divide(tmp1, tmp2); + // result.base_data() = tmp1; + // result.modular_ops().adjust_modular(result.base_data()); + // result.modular_ops().adjust_regular(tmp2, result.base_data()); + // } + + // template + // constexpr void eval_sqrt(modular_big_integer_impl &result, + // const modular_big_integer_impl &val) { + // eval_sqrt(result.base_data(), val.base_data()); + // } + + // inline size_t window_bits(size_t exp_bits) { + // constexpr static size_t wsize_count = 6; + // constexpr static size_t wsize[wsize_count][2] = {{1434, 7}, {539, 6}, {197, 4}, + // {70, 3}, {17, 2}, {0, 0}}; + + // size_t window_bits = 1; + + // size_t j = wsize_count - 1; + // while (wsize[j][0] > exp_bits) { + // --j; + // } + // window_bits += wsize[j][1]; + + // return window_bits; + // } + + // template + // inline void find_modular_pow(modular_big_integer_impl &result, + // const modular_big_integer_impl &b, + // const big_integer_t &exp) { + // modular_ops mod = b.modular_ops(); + // size_t m_window_bits; + // unsigned long cur_exp_index; + // size_t exp_bits = eval_msb(exp); + // m_window_bits = window_bits(exp_bits + 1); + + // std::vector m_g(1U << m_window_bits); + // big_integer_t *p_g = m_g.data(); + // big_integer_t x(1, mod); + // big_integer_t nibble = exp; + // big_integer_t mask; + // eval_bit_set(mask, m_window_bits); + // eval_decrement(mask); + // *p_g = x; + // ++p_g; + // *p_g = b; + // ++p_g; + // for (size_t i = 2; i < (1U << m_window_bits); i++) { + // eval_multiply(*p_g, m_g[i - 1], b); + // ++p_g; + // } + // size_t exp_nibbles = (exp_bits + 1 + m_window_bits - 1) / m_window_bits; + // std::vector exp_index; + + // for (size_t i = 0; i < exp_nibbles; ++i) { + // big_integer_t tmp = nibble; + // eval_bitwise_and(tmp, mask); + // eval_convert_to(&cur_exp_index, tmp); + // eval_right_shift(nibble, m_window_bits); + // exp_index.push_back(cur_exp_index); + // } + + // eval_multiply(x, m_g[exp_index[exp_nibbles - 1]]); + // for (size_t i = exp_nibbles - 1; i > 0; --i) { + // for (size_t j = 0; j != m_window_bits; ++j) { + // eval_multiply(x, x); + // } + + // eval_multiply(x, m_g[exp_index[i - 1]]); + // } + // result = x; + // } + + // template + // constexpr void eval_pow(modular_big_integer_impl &result, + // const modular_big_integer_impl &b, + // const T &e) { + // find_modular_pow(result, b, e); + // } + + // template + // constexpr void eval_pow(modular_big_integer_impl &result, + // const modular_big_integer_impl &b, + // const modular_big_integer_impl &e) { + // big_integer_t exp; + // e.modular_ops().adjust_regular(exp, e.base_data()); + // find_modular_pow(result, b, exp); + // } + + // template constexpr void eval_powm(modular_big_integer_impl &result, + // const modular_big_integer_impl &b, + // const T &e) { + // eval_pow(result, b, e); + // } + + // template + // constexpr void eval_powm(modular_big_integer_impl &result, + // const modular_big_integer_impl &b, + // const modular_big_integer_impl &e) { + // eval_pow(result, b, e); + // } +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp index 33c8aa9376..55fd216f5c 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp @@ -16,42 +16,39 @@ #include #include -#include #include #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" -namespace nil::crypto3::multiprecision { +namespace nil::crypto3::multiprecision::detail { template constexpr bool check_montgomery_constraints(const big_integer_t &m) { // Check m % 2 == 0 - // It's important to have std::size_t on the next line, - // otherwise a function from boost is called, which is not constexpr - // on gcc. - return eval_bit_test(m, std::size_t(0)); + return bit_test(m, 0u); } - // TODO(ioxid): rewrite it - // - // a little trick to prevent error in constexpr execution of - // eval_right_shift due to non-constexpr nature of right_shift_byte - // - template - constexpr void custom_right_shift(big_integer_t &b, unsigned s) { - if (!s) { - return; - } - - limb_type byte_shift_mask = CHAR_BIT - 1; - if ((s & byte_shift_mask) == 0) { - eval_right_shift(b, s - 1u); - eval_right_shift(b, 1u); - } else { - eval_right_shift(b, s); - } - } + template + struct modular_policy { + using big_integer_t = big_integer; + + constexpr static auto limbs_count = big_integer_t::internal_limb_count; + constexpr static auto limb_bits = big_integer_t::limb_bits; + + constexpr static auto BitsCount_doubled = 2u * Bits; + constexpr static auto BitsCount_doubled_1 = BitsCount_doubled + 1; + constexpr static auto BitsCount_quadruple_1 = 2u * BitsCount_doubled + 1; + constexpr static auto BitsCount_padded_limbs = limbs_count * limb_bits + limb_bits; + constexpr static auto BitsCount_doubled_limbs = 2u * limbs_count * limb_bits; + constexpr static auto BitsCount_doubled_padded_limbs = BitsCount_doubled_limbs + limb_bits; + + using big_integer_doubled = big_integer; + using big_integer_doubled_1 = big_integer; + using big_integer_quadruple_1 = big_integer; + using big_integer_padded_limbs = big_integer; + using big_integer_doubled_limbs = big_integer; + using big_integer_doubled_padded_limbs = big_integer; + }; template class modular_functions { @@ -60,14 +57,11 @@ namespace nil::crypto3::multiprecision { using policy_type = modular_policy; protected: - using limb_type = typename policy_type::limb_type; - using double_limb_type = typename policy_type::double_limb_type; - - using Backend_doubled_1 = typename policy_type::Backend_doubled_1; - using Backend_quadruple_1 = typename policy_type::Backend_quadruple_1; - using Backend_padded_limbs = typename policy_type::Backend_padded_limbs; - using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; - using Backend_doubled_padded_limbs = typename policy_type::Backend_doubled_padded_limbs; + using big_integer_doubled_1 = typename policy_type::big_integer_doubled_1; + using big_integer_quadruple_1 = typename policy_type::big_integer_quadruple_1; + using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; + using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; + using big_integer_doubled_padded_limbs = typename policy_type::big_integer_doubled_padded_limbs; constexpr static auto limbs_count = policy_type::limbs_count; constexpr static auto limb_bits = policy_type::limb_bits; @@ -75,17 +69,17 @@ namespace nil::crypto3::multiprecision { constexpr void initialize_modulus(const big_integer_t &m) { m_mod = m; } constexpr void initialize_barrett_params() { - m_barrett_mu = static_cast(0u); + m_barrett_mu = 0u; - size_t bit = 2u * (1u + eval_msb(m_mod)); - eval_bit_set(m_barrett_mu, bit); + std::size_t bit = 2u * (1u + msb(m_mod)); + bit_set(m_barrett_mu, bit); - // TODO(ioxid): not constexpr - // m_barrett_mu /= m_mod; + m_barrett_mu /= m_mod; } constexpr void initialize_montgomery_params() { find_const_variables(); } + // TODO(ioxid): no exception actually /* * Compute -input^-1 mod 2^limb_bits. Throws an exception if input * is even. If input is odd, then input and 2^n are relatively prime @@ -105,7 +99,7 @@ namespace nil::crypto3::multiprecision { } // Now invert in addition space - r = (~static_cast(0) - r) + 1; + r = (~0u - r) + 1; return r; } @@ -114,8 +108,8 @@ namespace nil::crypto3::multiprecision { if (check_montgomery_constraints(m_mod)) { m_montgomery_p_dash = monty_inverse(m_mod.limbs()[0]); - Backend_doubled_padded_limbs r; - eval_bit_set(r, 2 * m_mod.size() * limb_bits); + big_integer_doubled_padded_limbs r; + bit_set(r, 2 * m_mod.size() * limb_bits); barrett_reduce(r); // Here we are intentionally throwing away half of the bits of r, it's @@ -124,9 +118,9 @@ namespace nil::crypto3::multiprecision { } // Compute 2^Bits - Modulus, no matter if modulus is even or odd. - Backend_padded_limbs compliment = static_cast(1u), modulus = m_mod; - eval_left_shift(compliment, Bits); - eval_subtract(compliment, modulus); + big_integer_padded_limbs compliment = 1u, modulus = m_mod; + compliment <<= Bits; + compliment -= modulus; m_mod_compliment = compliment; } @@ -150,90 +144,80 @@ namespace nil::crypto3::multiprecision { constexpr const auto &get_r2() const { return m_montgomery_r2; } constexpr auto get_p_dash() const { return m_montgomery_p_dash; } - constexpr modular_functions() {} - constexpr modular_functions(const big_integer_t &m) { initialize(m); } - constexpr modular_functions(const modular_functions &o) - : m_mod(o.get_mod()), - m_mod_compliment(o.get_mod_compliment()), - m_barrett_mu(o.get_mu()), - m_montgomery_r2(o.get_r2()), - m_montgomery_p_dash(o.get_p_dash()), - m_no_carry_montgomery_mul_allowed(is_applicable_for_no_carry_montgomery_mul()) {} - - template - constexpr void barrett_reduce(Backend1 &result) const { + template + constexpr void barrett_reduce(big_integer_t1 &result) const { barrett_reduce(result, result); } + // TODO(ioxid): something wrong with parameters here // // this overloaded barrett_reduce is intended to work with built-in integral types // - template - constexpr typename std::enable_if::value && - std::is_unsigned::value>::type - barrett_reduce(Backend1 &result, Backend2 input) const { - using input_backend_type = - typename std::conditional_t Bits), Backend2, big_integer_t>; - - input_backend_type input_adjusted(input); + template + constexpr typename std::enable_if::value && + std::is_unsigned::value>::type + barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { + using input_big_integer_type = + typename std::conditional_t Bits), big_integer_t2, big_integer_t>; + + input_big_integer_type input_adjusted(input); barrett_reduce(result, input_adjusted); } // - // this overloaded barrett_reduce is intended to work with input Backend2 type of + // this overloaded barrett_reduce is intended to work with input big_integer_t2 type of // less precision than modular big_integer_t to satisfy constraints of core barrett_reduce // overloading // - template = true> - constexpr void barrett_reduce(Backend1 &result, const Backend2 &input) const { + template = 0> + constexpr void barrett_reduce(big_integer_t1 &result, const big_integer_t2 &input) const { big_integer_t input_adjusted(input); barrett_reduce(result, input_adjusted); } - template= big_integer_t::Bits && + big_integer_t1::Bits >= big_integer_t::Bits && /// to prevent problems with trivial cpp_int - Backend2::Bits >= big_integer_t::Bits, - bool> = true> - constexpr void barrett_reduce(Backend1 &result, Backend2 input) const { + big_integer_t2::Bits >= big_integer_t::Bits, + int> = 0> + constexpr void barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { // // to prevent problems with trivial cpp_int // - Backend2 modulus(m_mod); + big_integer_t2 modulus(m_mod); - if (eval_msb(input) < 2u * eval_msb(modulus) + 1u) { - Backend_quadruple_1 t1(input); + if (msb(input) < 2u * msb(modulus) + 1u) { + big_integer_quadruple_1 t1(input); - eval_multiply(t1, m_barrett_mu); - std::size_t shift_size = 2u * (1u + eval_msb(modulus)); - custom_right_shift(t1, shift_size); - eval_multiply(t1, modulus); + t1 *= m_barrett_mu; + std::size_t shift_size = 2u * (1u + msb(modulus)); + t1 >>= shift_size; + t1 *= modulus; // We do NOT allow subtracting a larger size number from a smaller one, - // we need to cast to Backend2 here. - eval_subtract(input, static_cast(t1)); + // we need to cast to big_integer_t2 here. + input -= static_cast(t1); if (input >= modulus) { - eval_subtract(input, modulus); + input -= modulus; } } else { - // TODO(ioxid): not constexpr - // eval_modulus(input, modulus); + input %= modulus; } result = input; } template= Bits>::type> + std::enable_if_t= Bits, int> = 0> constexpr void montgomery_reduce(big_integer &result) const { - Backend_doubled_padded_limbs accum(result); - Backend_doubled_padded_limbs prod; + big_integer_doubled_padded_limbs accum(result); + big_integer_doubled_padded_limbs prod; for (size_t i = 0; i < m_mod.size(); ++i) { limb_type limb_accum = accum.limbs()[i]; @@ -242,16 +226,18 @@ namespace nil::crypto3::multiprecision { static_cast(m_montgomery_p_dash); limb_type mult_res_limb = static_cast(mult_res); - eval_multiply(prod, m_mod, mult_res_limb); - eval_left_shift(prod, i * limb_bits); - eval_add(accum, prod); + prod = m_mod; + prod *= mult_res_limb; + prod <<= i * limb_bits; + accum += prod; } - custom_right_shift(accum, m_mod.size() * limb_bits); - // We cannot use eval_subtract for numbers of difference sizes, so resizing + accum >>= m_mod.size() * limb_bits; + // TODO(ioxid): true? + // We cannot use -= for numbers of difference sizes, so resizing // m_mod. - Backend_doubled_padded_limbs large_mod = m_mod; + big_integer_doubled_padded_limbs large_mod = m_mod; if (accum >= large_mod) { - eval_subtract(accum, large_mod); + accum -= large_mod; } // Here only the bytes that fit in sizeof result will be copied, and that's // intentional. @@ -260,30 +246,29 @@ namespace nil::crypto3::multiprecision { template= Bits2>::type> + std::enable_if_t= Bits2, int> = 0> constexpr void regular_add(big_integer &result, const big_integer &y) const { - BOOST_ASSERT(eval_lt(result, m_mod) && eval_lt(y, m_mod)); + BOOST_ASSERT(result < m_mod && y < m_mod); - eval_add(result, y); + result += y; // If we overflow and set the carry, we need to subtract the modulus, which is // the same as adding 2 ^ Bits - Modulus to the remaining part of the number. // After this we know for sure that the result < Modulus, do not waste time on // checking again. if (result.has_carry()) { - eval_add(result, m_mod_compliment); + result += m_mod_compliment; result.set_carry(false); - } else if (!eval_lt(result, m_mod)) { - eval_subtract(result, m_mod); + } else if (result >= m_mod) { + result -= m_mod; } } - template< - typename Backend1, typename Backend2, - /// result should fit in the output parameter - typename = typename boost::enable_if_c= big_integer_t::Bits>::type> - constexpr void regular_mul(Backend1 &result, const Backend2 &y) const { - Backend_doubled_limbs tmp(result); - eval_multiply(tmp, y); + template= big_integer_t::Bits, int> = 0> + constexpr void regular_mul(big_integer_t1 &result, const big_integer_t2 &y) const { + big_integer_doubled_limbs tmp = result; + tmp *= y; barrett_reduce(result, tmp); } @@ -296,19 +281,7 @@ namespace nil::crypto3::multiprecision { } } - // Given a value represented in 'double_limb_type', decomposes it into - // two 'limb_type' variables, based on high order bits and low order bits. - // There 'a' receives high order bits of 'X', and 'b' receives the low order bits. - static constexpr void dbl_limb_to_limbs(const double_limb_type &X, limb_type &a, - limb_type &b) { - b = X; - a = X >> limb_bits; - } - // Tests if the faster implementation of Montgomery multiplication is possible. - // We don't need the template argument Backend1, it's just here to enable - // specialization. - template constexpr bool is_applicable_for_no_carry_montgomery_mul() const { // Check that // 1. The most significant bit of modulus is non-zero, meaning we have at least @@ -323,20 +296,20 @@ namespace nil::crypto3::multiprecision { // Non-carry implementation of Montgomery multiplication. // Implemented from pseudo-code at // "https://hackmd.io/@gnark/modular_multiplication". - template - constexpr void montgomery_mul_no_carry_impl(Backend1 &c, const Backend1 &b) const { + template + constexpr void montgomery_mul_no_carry_impl(big_integer_t1 &c, const big_integer_t1 &b) const { BOOST_ASSERT(c < m_mod && b < m_mod); BOOST_ASSERT(is_applicable_for_no_carry_montgomery_mul()); // Obtain number of limbs - constexpr int N = Backend1::internal_limb_count; + constexpr int N = big_integer_t1::internal_limb_count; - const Backend1 a(c); // Copy the first argument, as the implemented + const big_integer_t1 a(c); // Copy the first argument, as the implemented // algorithm doesn't work in-place. // We cannot write directly to 'c', because b may be equal to c, and by changing // the value of 'c' we will change 'b' as well. - Backend1 result = limb_type(0u); + big_integer_t1 result = limb_type(0u); // Prepare temporary variables limb_type A(0u), C(0u); @@ -353,7 +326,7 @@ namespace nil::crypto3::multiprecision { tmp = a_limbs[0]; tmp *= b_limbs[i]; tmp += result_limbs[0]; - modular_functions::dbl_limb_to_limbs(tmp, A, result_limbs[0]); + dbl_limb_to_limbs(tmp, A, result_limbs[0]); // "m := t[0]*q'[0] mod W" tmp = result_limbs[0]; @@ -367,7 +340,7 @@ namespace nil::crypto3::multiprecision { tmp = m; tmp *= m_mod_limbs[0]; tmp += result_limbs[0]; - modular_functions::dbl_limb_to_limbs(tmp, C, dummy); + dbl_limb_to_limbs(tmp, C, dummy); // The lower loop is unrolled. We want to do this for every 3, because // normally mod_size == 4. @@ -379,14 +352,14 @@ namespace nil::crypto3::multiprecision { tmp *= b_limbs[i]; \ tmp += result_limbs[X]; \ tmp += A; \ - modular_functions::dbl_limb_to_limbs(tmp, A, result_limbs[X]); \ + dbl_limb_to_limbs(tmp, A, result_limbs[X]); \ \ /* "(C,t[X-1]) := t[X] + m*q[X] + C" */ \ tmp = m; \ tmp *= m_mod_limbs[X]; \ tmp += result_limbs[X]; \ tmp += C; \ - modular_functions::dbl_limb_to_limbs(tmp, C, result_limbs[X - 1]); + dbl_limb_to_limbs(tmp, C, result_limbs[(X) - 1]); for (; j + 5 <= N; j += 5) { MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); @@ -411,14 +384,14 @@ namespace nil::crypto3::multiprecision { } if (result >= m_mod) { - eval_subtract(result, m_mod); + result -= m_mod; } c = result; } // A specialization for non-trivial cpp_int_modular types only. - template - constexpr void montgomery_mul_CIOS_impl(Backend1 &result, const Backend1 &y) const { + template + constexpr void montgomery_mul_CIOS_impl(big_integer_t1 &result, const big_integer_t1 &y) const { BOOST_ASSERT(result < m_mod && y < m_mod); big_integer_t A(limb_type(0u)); @@ -470,7 +443,7 @@ namespace nil::crypto3::multiprecision { A_limbs[X] + k; \ t2 = static_cast(mod_limbs[X]) * static_cast(u_i) + \ static_cast(t) + k2; \ - A_limbs[X - 1] = static_cast(t2); \ + A_limbs[(X) - 1] = static_cast(t2); \ k = static_cast(t >> std::numeric_limits::digits); \ k2 = static_cast(t2 >> std::numeric_limits::digits); @@ -499,84 +472,82 @@ namespace nil::crypto3::multiprecision { if (carry) { // The value of A is actually A + 2 ^ Bits, so remove that 2 ^ Bits. - eval_add(A, m_mod_compliment); + A += m_mod_compliment; } else if (A >= m_mod) { - eval_subtract(A, m_mod); + A -= m_mod; } result = A; } - template< - typename Backend1, typename Backend2, typename Backend3, - /// result should fit in the output parameter - typename = typename boost::enable_if_c= big_integer_t::Bits>::type> - constexpr void regular_exp(Backend1 &result, Backend2 &a, Backend3 exp) const { - BOOST_ASSERT(eval_lt(a, m_mod)); + template= big_integer_t::Bits, int> = 0> + constexpr void regular_exp(big_integer_t1 &result, big_integer_t2 &a, big_integer_t3 exp) const { + BOOST_ASSERT(a < m_mod); - if (eval_eq(exp, static_cast(0u))) { - result = static_cast(1u); + if (exp == 0u) { + result = 1u; return; } - if (eval_eq(m_mod, static_cast(1u))) { - result = static_cast(0u); + if (m_mod == 1u) { + result = 0u; return; } - Backend_doubled_limbs base(a), res(static_cast(1u)); + big_integer_doubled_limbs base(a), res(1u); while (true) { limb_type lsb = exp.limbs()[0] & 1u; - custom_right_shift(exp, static_cast(1u)); + exp >>= 1u; if (lsb) { - eval_multiply(res, base); + res *= base; barrett_reduce(res); - if (eval_is_zero(exp)) { + if (is_zero(exp)) { break; } } - eval_multiply(base, base); + base *= base; barrett_reduce(base); } result = res; } - template< - typename Backend1, typename Backend2, typename Backend3, - /// result should fit in the output parameter - typename = typename boost::enable_if_c= big_integer_t::Bits>::type> - constexpr void montgomery_exp(Backend1 &result, const Backend2 &a, Backend3 exp) const { + template= big_integer_t::Bits, int> = 0> + constexpr void montgomery_exp(big_integer_t1 &result, const big_integer_t2 &a, big_integer_t3 exp) const { /// input parameter should be lesser than modulus - BOOST_ASSERT(eval_lt(a, m_mod)); + BOOST_ASSERT(a < m_mod); - Backend_doubled_limbs tmp(static_cast(1u)); - eval_multiply(tmp, m_montgomery_r2); + big_integer_doubled_limbs tmp(1u); + tmp *= m_montgomery_r2; montgomery_reduce(tmp); big_integer_t R_mod_m(tmp); big_integer_t base(a); - if (eval_eq(exp, static_cast(0u))) { - result = static_cast(1u); + if (exp == 0u) { + result = 1u; // // TODO: restructure code // adjust_modular // - eval_multiply(result, m_montgomery_r2); + result *= m_montgomery_r2; montgomery_reduce(result); return; } - if (eval_eq(m_mod, static_cast(1u))) { - result = static_cast(0u); + if (m_mod == 1u) { + result = 0u; return; } while (true) { limb_type lsb = exp.limbs()[0] & 1u; - custom_right_shift(exp, static_cast(1u)); + exp >>= 1u; if (lsb) { montgomery_mul(R_mod_m, base); - if (eval_eq(exp, static_cast(0u))) { + if (exp == 0u) { break; } } @@ -585,17 +556,6 @@ namespace nil::crypto3::multiprecision { result = R_mod_m; } - constexpr modular_functions &operator=(const modular_functions &o) { - m_mod = o.get_mod(); - m_barrett_mu = o.get_mu(); - m_montgomery_r2 = o.get_r2(); - m_montgomery_p_dash = o.get_p_dash(); - m_mod_compliment = o.get_mod_compliment(); - m_no_carry_montgomery_mul_allowed = is_applicable_for_no_carry_montgomery_mul(); - - return *this; - } - constexpr modular_functions &operator=(const big_integer_t &m) { initialize(m); @@ -606,7 +566,7 @@ namespace nil::crypto3::multiprecision { big_integer_t m_mod; // This is 2^Bits - m_mod, precomputed. big_integer_t m_mod_compliment; - Backend_doubled_1 m_barrett_mu; + big_integer_doubled_1 m_barrett_mu; big_integer_t m_montgomery_r2; limb_type m_montgomery_p_dash = 0; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp index 6bca814546..107914b32d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp @@ -1,7 +1,7 @@ //---------------------------------------------------------------------------// // Copyright (c) 2020 Mikhail Komarov -// Copyright (c) 2019-2021 Aleksei Moskvin // Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2021 Aleksei Moskvin // Copyright (c) 2024 Andrey Nefedov // // Distributed under the Boost Software License, Version 1.0 @@ -11,472 +11,163 @@ #pragma once -#include -#include -#include -#include -#include +#include -#include -#include -#include -#include +#include #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp" - -namespace nil::crypto3::multiprecision { - - template - constexpr void eval_add(modular_big_integer, modular_params_t> &result, - const modular_big_integer, modular_params_t> &o) { - BOOST_ASSERT(eval_eq(result.mod_data().get_mod(), o.mod_data().get_mod())); - result.mod_data().mod_add(result.base_data(), o.base_data()); - } - - template - constexpr void eval_add(modular_big_integer, modular_params_t> &result, - const modular_big_integer &o) { - result.mod_data().mod_add(result.base_data(), o.base_data()); - } - - template - constexpr void eval_add(modular_big_integer &result, - const modular_big_integer, modular_params_t> &o) { - o.mod_data().mod_add(result.base_data(), o.base_data()); - } - - template - constexpr void eval_multiply( - modular_big_integer, modular_params_t> &result, - const modular_big_integer, modular_params_t> &o) { - result.mod_data().mod_mul(result.base_data(), o.base_data()); - } - - template - constexpr void eval_multiply(modular_big_integer, modular_params_t> &result, - const modular_big_integer &o) { - result.mod_data().mod_mul(result.base_data(), o.base_data()); - } - - template - constexpr void eval_multiply( - modular_big_integer &result, - const modular_big_integer, modular_params_t> &o) { - o.mod_data().mod_mul(result.base_data(), o.base_data()); - } - - template - constexpr void eval_powm(modular_big_integer, modular_params_t> &result, - const modular_big_integer &b, - const T &e) { - result.set_modular_params(b.mod_data()); - result.mod_data().mod_exp(result.base_data(), b.base_data(), e); - } - - template - constexpr void eval_powm(modular_big_integer, modular_params_t> &result, - const modular_big_integer &b, - const modular_big_integer &e) { - using big_integer_t = big_integer; - - big_integer_t exp; - e.mod_data().adjust_regular(exp, e.base_data()); - eval_powm(result, b, exp); - } - - template - constexpr void eval_inverse_mod( - modular_big_integer, modular_params_t> &result, - const modular_big_integer, modular_params_t> &input) { - using big_integer_t = big_integer; - using big_integer_t_padded_limbs = - typename modular_params::policy_type::Backend_padded_limbs; - - big_integer_t_padded_limbs new_base, res, tmp = input.mod_data().get_mod(); - - input.mod_data().adjust_regular(new_base, input.base_data()); - eval_inverse_mod(res, new_base, tmp); - assign_components(result, res, input.mod_data().get_mod()); - } - - // Used for converting number> to number. - // We cannot change the first argument to a reference... - template - constexpr void eval_convert_to( - big_integer_t *result, const modular_big_integer &val) { - val.mod_data().adjust_regular(*result, val.base_data()); - } - - template - constexpr typename boost::enable_if, bool>::type eval_eq( - const modular_big_integer &a, const T &b) { - return a.compare(b) == 0; - } - - template - constexpr void eval_redc(big_integer_t1 &result, const modular_params &mod) { - mod.reduce(result); - eval_modulus(result, mod.get_mod()); - } - - template - constexpr void eval_add(modular_big_integer &result, - const modular_big_integer &o) { - eval_add(result.base_data(), o.base_data()); - if (!eval_lt(result.base_data(), result.mod_data().get_mod())) { - eval_subtract(result.base_data(), result.mod_data().get_mod()); +#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" + +namespace nil::crypto3::multiprecision::detail { + // TODO(ioxid): merge with modular_functions + // fixed precision modular params type which supports compile-time execution + template + class modular_ops { + private: + using modular_functions_t = modular_functions; + + public: + using policy_type = typename modular_functions_t::policy_type; + + using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; + // using big_integer_t = typename policy_type::big_integer_t; + + constexpr modular_ops(const big_integer_t &m) + : m_modular_functions(m), m_use_montgomery_form(check_montgomery_constraints(m)) {} + + private: + constexpr auto &get_modular_functions() { return m_modular_functions; } + constexpr const auto &get_modular_functions() const { return m_modular_functions; } + + constexpr bool is_montgomery_form() const { return m_use_montgomery_form; } + + public: + constexpr const auto &get_mod() const { return m_modular_functions.get_mod(); } + + template + constexpr void reduce(big_integer &result) const { + if (m_use_montgomery_form) { + m_modular_functions.montgomery_reduce(result); + } else { + m_modular_functions.barrett_reduce(result); + } } - } - - template - constexpr void eval_subtract(modular_big_integer &result, - const modular_big_integer &o) { - using ui_type = - typename std::tuple_element<0, typename big_integer_t::unsigned_types>::type; - eval_subtract(result.base_data(), o.base_data()); - if (eval_lt(result.base_data(), ui_type(0u))) { - eval_add(result.base_data(), result.mod_data().get_mod()); + + constexpr void adjust_modular(big_integer_t &result) const { + adjust_modular(result, result); } - } - - template - constexpr void eval_subtract( - modular_big_integer, modular_params_t> &result, - const modular_big_integer, modular_params_t> &o) { - if (eval_lt(result.base_data(), o.base_data())) { - auto v = result.mod_data().get_mod(); - eval_subtract(v, o.base_data()); - eval_add(result.base_data(), v); - } else { - eval_subtract(result.base_data(), o.base_data()); + + template + constexpr void adjust_modular(big_integer_t &result, + const big_integer &input) const { + big_integer_doubled_limbs tmp; + m_modular_functions.barrett_reduce(tmp, input); + if (m_use_montgomery_form) { + // + // to prevent problems with trivial cpp_int + // + big_integer_doubled_limbs r2(m_modular_functions.get_r2()); + + tmp *= r2; + m_modular_functions.montgomery_reduce(tmp); + } + result = tmp; } - } - - template - constexpr void eval_multiply(modular_big_integer &result, - const modular_big_integer &o) { - eval_multiply(result.base_data(), o.base_data()); - eval_redc(result.base_data(), result.mod_data()); - } - - template - constexpr void eval_divide(modular_big_integer &result, - const modular_big_integer &o) { - big_integer_t tmp1, tmp2; - result.mod_data().adjust_regular(tmp1, result.base_data()); - result.mod_data().adjust_regular(tmp2, o.base_data()); - eval_divide(tmp1, tmp2); - result.base_data() = tmp1; - result.mod_data().adjust_modular(result.base_data()); - result.mod_data().adjust_regular(tmp2, result.base_data()); - } - - template - constexpr void eval_modulus(modular_big_integer &result, - const modular_big_integer &o) { - big_integer_t tmp1, tmp2; - result.mod_data().adjust_regular(tmp1, result.base_data()); - result.mod_data().adjust_regular(tmp2, o.base_data()); - eval_modulus(tmp1, tmp2); - result.base_data() = tmp1; - result.mod_data().adjust_modular(result.base_data()); - // result.mod_data().adjust_regular(tmp2, result.base_data()); - } - - // If called with 3 arguments, delegate the call to the upper function. - template - constexpr void eval_modulus(modular_big_integer &result, - const modular_big_integer &u, - const modular_big_integer &v) { - result = std::move(u); - eval_modulus(result, v); - } - - template - constexpr bool eval_is_zero( - const modular_big_integer &val) noexcept { - return eval_is_zero(val.base_data()); - } - - template - constexpr int eval_get_sign( - const modular_big_integer & /*unused*/) { - return 1; - } - - template - constexpr void assign_components(modular_big_integer &result, - const T &a, const V &b) { - result.base_data() = a; - result.mod_data() = b; - result.mod_data().adjust_modular(result.base_data()); - } - - template - constexpr void eval_sqrt(modular_big_integer &result, - const modular_big_integer &val) { - eval_sqrt(result.base_data(), val.base_data()); - } - - template - constexpr void eval_abs(modular_big_integer &result, - const modular_big_integer &val) { - result = val; - } - - inline size_t window_bits(size_t exp_bits) { - constexpr static size_t wsize_count = 6; - constexpr static size_t wsize[wsize_count][2] = {{1434, 7}, {539, 6}, {197, 4}, - {70, 3}, {17, 2}, {0, 0}}; - - size_t window_bits = 1; - - size_t j = wsize_count - 1; - while (wsize[j][0] > exp_bits) { - --j; + + [[nodiscard]] constexpr big_integer_t adjusted_regular(const big_integer_t &a) const { + big_integer_t result; + adjust_regular(result, a); + return result; } - window_bits += wsize[j][1]; - - return window_bits; - } - - template - inline void find_modular_pow(modular_big_integer &result, - const modular_big_integer &b, - const big_integer_t &exp) { - modular_params mod = b.mod_data(); - size_t m_window_bits; - unsigned long cur_exp_index; - size_t exp_bits = eval_msb(exp); - m_window_bits = window_bits(exp_bits + 1); - - std::vector m_g(1U << m_window_bits); - big_integer_t *p_g = m_g.data(); - big_integer_t x(1, mod); - big_integer_t nibble = exp; - big_integer_t mask; - eval_bit_set(mask, m_window_bits); - eval_decrement(mask); - *p_g = x; - ++p_g; - *p_g = b; - ++p_g; - for (size_t i = 2; i < (1U << m_window_bits); i++) { - eval_multiply(*p_g, m_g[i - 1], b); - ++p_g; + + template= Bits2, int> = 0> + constexpr void adjust_regular(big_integer &result, + const big_integer &input) const { + result = input; + if (m_use_montgomery_form) { + m_modular_functions.montgomery_reduce(result); + } } - size_t exp_nibbles = (exp_bits + 1 + m_window_bits - 1) / m_window_bits; - std::vector exp_index; - - for (size_t i = 0; i < exp_nibbles; ++i) { - big_integer_t tmp = nibble; - eval_bitwise_and(tmp, mask); - eval_convert_to(&cur_exp_index, tmp); - eval_right_shift(nibble, m_window_bits); - exp_index.push_back(cur_exp_index); + + template + constexpr void exp(big_integer_t1 &result, const T &exp) const { + exp(result, result, exp); } - eval_multiply(x, m_g[exp_index[exp_nibbles - 1]]); - for (size_t i = exp_nibbles - 1; i > 0; --i) { - for (size_t j = 0; j != m_window_bits; ++j) { - eval_multiply(x, x); + template + constexpr void exp(big_integer_t1 &result, const big_integer_t2 &a, const T &exp) const { + if (m_use_montgomery_form) { + m_modular_functions.montgomery_exp(result, a, exp); + } else { + m_modular_functions.regular_exp(result, a, exp); } + } - eval_multiply(x, m_g[exp_index[i - 1]]); + template + constexpr void mul(big_integer_t1 &result, const big_integer_t1 &y) const { + if (m_use_montgomery_form) { + m_modular_functions.montgomery_mul(result, y); + } else { + m_modular_functions.regular_mul(result, y); + } } - result = x; - } - - template - constexpr void eval_pow(modular_big_integer &result, - const modular_big_integer &b, - const T &e) { - find_modular_pow(result, b, e); - } - - template - constexpr void eval_pow(modular_big_integer &result, - const modular_big_integer &b, - const modular_big_integer &e) { - big_integer_t exp; - e.mod_data().adjust_regular(exp, e.base_data()); - find_modular_pow(result, b, exp); - } - - template - constexpr void eval_powm(modular_big_integer &result, - const modular_big_integer &b, - const T &e) { - eval_pow(result, b, e); - } - - template - constexpr void eval_powm(modular_big_integer &result, - const modular_big_integer &b, - const modular_big_integer &e) { - eval_pow(result, b, e); - } - - template - inline constexpr void eval_left_shift(modular_big_integer &t, - UI i) noexcept { - big_integer_t tmp; - t.mod_data().adjust_regular(tmp, t.base_data()); - eval_left_shift(tmp, i); - t.base_data() = tmp; - t.mod_data().adjust_modular(t.base_data()); - } - - template - constexpr void eval_right_shift(modular_big_integer &t, UI i) { - big_integer_t tmp; - t.mod_data().adjust_regular(tmp, t.base_data()); - eval_right_shift(tmp, i); - t.base_data() = tmp; - t.mod_data().adjust_modular(t.base_data()); - } - - template - constexpr void eval_left_shift(modular_big_integer &t, - const modular_big_integer &v, - UI i) { - big_integer_t tmp1, tmp2; - t.mod_data().adjust_regular(tmp1, t.base_data()); - t.mod_data().adjust_regular(tmp2, v.base_data()); - eval_left_shift(tmp1, tmp2, static_cast(i)); - t.base_data() = tmp1; - t.mod_data().adjust_modular(t.base_data()); - } - - template - constexpr void eval_right_shift(modular_big_integer &t, - const modular_big_integer &v, - UI i) { - big_integer_t tmp1, tmp2; - t.mod_data().adjust_regular(tmp1, t.base_data()); - t.mod_data().adjust_regular(tmp2, v.base_data()); - eval_right_shift(tmp1, tmp2, static_cast(i)); - t.base_data() = tmp1; - t.mod_data().adjust_modular(t.base_data()); - } - - template - constexpr void eval_bitwise_and(modular_big_integer &result, - const modular_big_integer &v) { - big_integer_t tmp1, tmp2; - result.mod_data().adjust_regular(tmp1, result.base_data()); - v.mod_data().adjust_regular(tmp2, v.base_data()); - eval_bitwise_and(tmp1, tmp2); - result.base_data() = tmp1; - result.mod_data().adjust_modular(result.base_data()); - } - - template - constexpr void eval_bitwise_or(modular_big_integer &result, - const modular_big_integer &v) { - big_integer_t tmp1, tmp2; - result.mod_data().adjust_regular(tmp1, result.base_data()); - v.mod_data().adjust_regular(tmp2, v.base_data()); - eval_bitwise_or(tmp1, tmp2); - result.base_data() = tmp1; - result.mod_data().adjust_modular(result.base_data()); - } - - template - constexpr void eval_bitwise_xor(modular_big_integer &result, - const modular_big_integer &v) { - big_integer_t tmp1, tmp2; - result.mod_data().adjust_regular(tmp1, result.base_data()); - v.mod_data().adjust_regular(tmp2, v.base_data()); - eval_bitwise_xor(tmp1, tmp2); - result.base_data() = tmp1; - result.mod_data().adjust_modular(result.base_data()); - } - - template - constexpr int eval_msb(const modular_big_integer &m) { - big_integer_t tmp; - m.mod_data().adjust_regular(tmp, m.base_data()); - return eval_msb(tmp); - } - - template - constexpr unsigned eval_lsb(const modular_big_integer &m) { - big_integer_t tmp; - m.mod_data().adjust_regular(tmp, m.base_data()); - return eval_lsb(tmp); - } - - template - constexpr bool eval_bit_test(const modular_big_integer &m, - std::size_t index) { - big_integer_t tmp; - m.mod_data().adjust_regular(tmp, m.base_data()); - return eval_bit_test(tmp, index); - } - - template - constexpr void eval_bit_set(modular_big_integer &result, - std::size_t index) { - big_integer_t tmp; - result.mod_data().adjust_regular(tmp, result.base_data()); - eval_bit_set(tmp, index); - result.mod_data().adjust_modular(result.base_data(), tmp); - } - - // We must make sure any call with any integral type ends up here, if we use std::size_t - // here, something this function is not preferred by the compiler and boost's version is - // used, which is worse. - template - constexpr void eval_bit_unset(modular_big_integer &result, - std::size_t index) { - big_integer_t tmp; - result.mod_data().adjust_regular(tmp, result.base_data()); - eval_bit_unset(tmp, index); - result.mod_data().adjust_modular(result.base_data(), tmp); - } - - template - constexpr void eval_bit_flip(modular_big_integer &result, - std::size_t index) { - big_integer_t tmp; - result.mod_data().adjust_regular(tmp, result.base_data()); - eval_bit_flip(tmp, index); - result.mod_data().adjust_modular(result.base_data(), tmp); - } - - template - constexpr modular_big_integer eval_ressol( - const modular_big_integer &input) { - big_integer_t new_base, res; - modular_big_integer res_mod; - - input.mod_data().adjust_regular(new_base, input.base_data()); - res = eval_ressol(new_base, input.mod_data().get_mod()); - assign_components(res_mod, res, input.mod_data().get_mod()); - - return res_mod; - } - - template - constexpr void eval_inverse_mod( - modular_big_integer &result, - const modular_big_integer &input) { - big_integer_t new_base, res; - - input.mod_data().adjust_regular(new_base, input.base_data()); - eval_inverse_mod(res, new_base, input.mod_data().get_mod()); - assign_components(result, res, input.mod_data().get_mod()); - } - - template - inline constexpr std::size_t hash_value( - const modular_big_integer &val) noexcept { - return hash_value(val.base_data()); - } + + template + constexpr void add(big_integer_t1 &result, const big_integer_t2 &y) const { + m_modular_functions.regular_add(result, y); + } + + template + constexpr operator big_integer_t1() { + return get_mod(); + }; + + constexpr bool compare_eq(const modular_ops &o) const { return get_mod() == o.get_mod(); } + + constexpr void swap(modular_ops &o) noexcept { + m_modular_functions.swap(o.get_modular_functions()); + bool t = m_use_montgomery_form; + m_use_montgomery_form = o.m_use_montgomery_form; + o.m_use_montgomery_form = t; + } + + // TODO: check function correctness + constexpr friend std::ostream &operator<<(std::ostream &o, const modular_ops &a) { + o << a.get_mod(); + return o; + } + + private: + modular_functions_t m_modular_functions; + bool m_use_montgomery_form = false; + }; + + template + class modular_ops_storage_ct { + public: + using modular_ops_t = modular_ops; + + constexpr modular_ops_storage_ct() {} + + static constexpr const modular_ops_t &modular_ops() { return m_modular_ops; } + + private: + static constexpr modular_ops_t m_modular_ops{Modulus}; + }; + + // Must be used only in the tests, we must normally use only modular_ops_storage_ct. + template + class modular_ops_storage_rt { + public: + using modular_ops_t = modular_ops; + + constexpr modular_ops_storage_rt(const big_integer_t &input) : m_modular_ops(input) {} + + constexpr const modular_ops_t &modular_ops() const { return m_modular_ops; } + + private: + modular_ops_t m_modular_ops; + }; } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp deleted file mode 100644 index 7a82248c15..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_params.hpp +++ /dev/null @@ -1,203 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) 2020 Mikhail Komarov -// Copyright (c) 2020 Ilias Khairullin -// Copyright (c) 2021 Aleksei Moskvin -// Copyright (c) 2024 Andrey Nefedov -// -// Distributed under the Boost Software License, Version 1.0 -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt -//---------------------------------------------------------------------------// - -#pragma once - -#include - -#include - -#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" - -namespace nil::crypto3::multiprecision { - // TODO(ioxid): merge with modular_functions - // fixed precision modular params type which supports compile-time execution - template - class modular_params { - protected: - using modular_functions_t = modular_functions; - - public: - using policy_type = typename modular_functions_t::policy_type; - - using Backend_doubled_limbs = typename policy_type::Backend_doubled_limbs; - // using big_integer_t = typename policy_type::big_integer_t; - - constexpr auto &get_modular_functions() { return m_modular_functions; } - constexpr const auto &get_modular_functions() const { return m_modular_functions; } - - constexpr auto &get_is_odd_mod() { return is_odd_mod; } - constexpr const auto &get_is_odd_mod() const { return is_odd_mod; } - - constexpr auto get_mod() const { return m_modular_functions.get_mod(); } - - constexpr modular_params() {} - - constexpr modular_params(const big_integer_t &m) : m_modular_functions(m) { - using boost::multiprecision::default_ops::eval_bit_test; - is_odd_mod = eval_bit_test(m, 0); - } - - constexpr modular_params(const modular_params &o) - : m_modular_functions(o.get_modular_functions()) { - is_odd_mod = o.get_is_odd_mod(); - } - - template - constexpr void reduce(big_integer &result) const { - if (is_odd_mod) { - m_modular_functions.montgomery_reduce(result); - } else { - m_modular_functions.barrett_reduce(result); - } - } - - constexpr void adjust_modular(big_integer_t &result) const { - adjust_modular(result, result); - } - - template - constexpr void adjust_modular(big_integer_t &result, - const big_integer &input) const { - Backend_doubled_limbs tmp; - m_modular_functions.barrett_reduce(tmp, input); - if (is_odd_mod) { - // - // to prevent problems with trivial cpp_int - // - Backend_doubled_limbs r2(m_modular_functions.get_r2()); - - eval_multiply(tmp, r2); - m_modular_functions.montgomery_reduce(tmp); - } - result = tmp; - } - - template= Bits2>::type> - constexpr void adjust_regular(big_integer &result, - const big_integer &input) const { - result = input; - if (is_odd_mod) { - m_modular_functions.montgomery_reduce(result); - } - } - - template - constexpr void mod_exp(Backend1 &result, const T &exp) const { - mod_exp(result, result, exp); - } - - template - constexpr void mod_exp(Backend1 &result, const Backend2 &a, const T &exp) const { - if (is_odd_mod) { - m_modular_functions.montgomery_exp(result, a, exp); - } else { - m_modular_functions.regular_exp(result, a, exp); - } - } - - template - constexpr void mod_mul(Backend1 &result, const Backend1 &y) const { - if (is_odd_mod) { - m_modular_functions.montgomery_mul(result, y); - } else { - m_modular_functions.regular_mul(result, y); - } - } - - template - constexpr void mod_add(Backend1 &result, const Backend2 &y) const { - m_modular_functions.regular_add(result, y); - } - - template - constexpr operator Backend1() { - return get_mod(); - }; - - constexpr bool compare_eq(const modular_params &o) const { - // They are either equal or not: - return get_mod().compare(o.get_mod()) == 0; - } - - constexpr void swap(modular_params &o) noexcept { - m_modular_functions.swap(o.get_modular_functions()); - bool t = is_odd_mod; - is_odd_mod = o.get_is_odd_mod(); - o.get_is_odd_mod() = t; - } - - constexpr modular_params &operator=(const modular_params &o) { - m_modular_functions = o.get_modular_functions(); - is_odd_mod = o.get_is_odd_mod(); - return *this; - } - - constexpr modular_params &operator=(const big_integer_t &m) { - m_modular_functions = m; - is_odd_mod = boost::multiprecision::default_ops::eval_bit_test(m, 0); - return *this; - } - - // TODO: check function correctness - constexpr friend std::ostream &operator<<(std::ostream &o, const modular_params &a) { - o << a.get_mod(); - return o; - } - - protected: - modular_functions_t m_modular_functions; - bool is_odd_mod = false; - }; - - template - class modular_params_storage_ct { - public: - using modular_params_t = modular_params; - - constexpr modular_params_storage_ct() {} - - constexpr modular_params_storage_ct(modular_params_t &input) {} - - constexpr void set_modular_params(const modular_params_t &input) {} - - template - constexpr void set_modular_params(const T &input) {} - - constexpr const modular_params_t &modular_params() const { return m_mod; } - - protected: - constexpr static const modular_params_t m_mod{Modulus}; - }; - - // Must be used only in the tests, we must normally use only modular_params_storage_ct. - template - class modular_params_storage_rt { - public: - using modular_params_t = modular_params; - - constexpr modular_params_storage_rt() {} - - constexpr modular_params_storage_rt(modular_params_t input) : m_mod(input) {} - - constexpr void set_modular_params(const modular_params_t &input) { m_mod = input; } - - constexpr void set_modular_params(const big_integer_t &input) { m_mod = input; } - - constexpr modular_params_t &modular_params() { return m_mod; } - constexpr const modular_params_t &modular_params() const { return m_mod; } - - modular_params_t m_mod; - }; -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp deleted file mode 100644 index 0a0f3e7c10..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_policy.hpp +++ /dev/null @@ -1,43 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) 2020 Mikhail Komarov -// Copyright (c) 2020 Ilias Khairullin -// Copyright (c) 2024 Andrey Nefedov -// -// Distributed under the Boost Software License, Version 1.0 -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt -//---------------------------------------------------------------------------// - -#pragma once - -#include - -#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" - -namespace nil::crypto3::multiprecision { - // TODO(ioxid): maybe remove - template - struct modular_policy { - using Backend = big_integer; - - using limb_type = limb_type; - using double_limb_type = double_limb_type; - - constexpr static auto limbs_count = Backend::internal_limb_count; - constexpr static auto limb_bits = Backend::limb_bits; - - constexpr static auto BitsCount_doubled = 2u * Bits; - constexpr static auto BitsCount_doubled_1 = BitsCount_doubled + 1; - constexpr static auto BitsCount_quadruple_1 = 2u * BitsCount_doubled + 1; - constexpr static auto BitsCount_padded_limbs = limbs_count * limb_bits + limb_bits; - constexpr static auto BitsCount_doubled_limbs = 2u * limbs_count * limb_bits; - constexpr static auto BitsCount_doubled_padded_limbs = BitsCount_doubled_limbs + limb_bits; - - using Backend_doubled = big_integer; - using Backend_doubled_1 = big_integer; - using Backend_quadruple_1 = big_integer; - using Backend_padded_limbs = big_integer; - using Backend_doubled_limbs = big_integer; - using Backend_doubled_padded_limbs = big_integer; - }; -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp deleted file mode 100644 index 6b00ea9ced..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/divide.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright (c) 2023 Martun Karapetyan -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Contains eval_modulus for big_integer, which uses conversion to cpp_int_backend to -// actually apply the operation. -// - -#pragma once - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" - -namespace nil::crypto3::multiprecision { - - // Functions in this file should be called only for creation of montgomery and Barett - // params, no during "normal" execution, so we do NOT care about the execution speed, - // and will just redirect calls to normal boost::cpp_int. - - template - inline constexpr void eval_modulus(big_integer &result, const big_integer &a, - const big_integer &b) noexcept { - result = a; - // Call the function below. - eval_modulus(result, b); - } - - // Just a call to the upper function, similar to operator*=. - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! - template - inline constexpr void eval_modulus(big_integer &result, - const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int %= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } - - // This function should be called only for creation of montgomery and Barett params and - // calculation of inverse, element, not during "normal" execution. We will use - // conversion to normal boost::cpp_int here and then convert back. - template - inline constexpr void eval_divide(big_integer &result, const big_integer &a, - const big_integer &b) noexcept { - result = a; - // Just make a call to the lower function. - eval_divide(result, b); - } - - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! - // Covers the case where the second operand is not trivial. - template - inline constexpr void eval_divide(big_integer &result, - const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int /= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp deleted file mode 100644 index a30bbde7e7..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/eval_jacobi.hpp +++ /dev/null @@ -1,78 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) 2018-2020 Mikhail Komarov -// Copyright (c) 2021 Aleksei Moskvin -// -// Distributed under the Boost Software License, Version 1.0 -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt -//---------------------------------------------------------------------------// - -#pragma once - -#include -#include - -#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" - -namespace nil::crypto3::multiprecision { - template - class big_integer; - - template - constexpr int eval_jacobi(const Backend &a, const Backend &n) { - using boost::multiprecision::default_ops::eval_divide; - using boost::multiprecision::default_ops::eval_get_sign; - using boost::multiprecision::default_ops::eval_gt; - using boost::multiprecision::default_ops::eval_integer_modulus; - using boost::multiprecision::default_ops::eval_is_zero; - using boost::multiprecision::default_ops::eval_lsb; - using boost::multiprecision::default_ops::eval_lt; - using boost::multiprecision::default_ops::eval_modulus; - using boost::multiprecision::default_ops::eval_right_shift; - - // BOOST_THROW_EXCEPTION(std::invalid_argument("jacobi: second argument must be odd - // and > 1")); - BOOST_ASSERT(eval_integer_modulus(n, 2) && eval_gt(n, 1)); - - Backend x = a, y = n; - int J = 1; - - while (eval_gt(y, 1)) { - eval_modulus(x, y); - - Backend yd2 = y; - eval_right_shift(yd2, 1); - - if (eval_gt(x, yd2)) { - Backend tmp(y); - eval_subtract(tmp, x); - x = tmp; - if (eval_integer_modulus(y, 4) == 3) { - J = -J; - } - } - if (eval_is_zero(x)) { - return 0; - } - - size_t shifts = eval_lsb(x); - custom_right_shift(x, shifts); - if (shifts & 1) { - std::size_t y_mod_8 = eval_integer_modulus(y, 8); - if (y_mod_8 == 3 || y_mod_8 == 5) { - J = -J; - } - } - - if (eval_integer_modulus(x, 4) == 3 && eval_integer_modulus(y, 4) == 3) { - J = -J; - } - - // std::swap(x, y); - auto tmp = x; - x = y; - y = tmp; - } - return J; - } -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp index b3cfc85475..8ce53c2a67 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/import_export.hpp @@ -7,30 +7,24 @@ #include #include +#include #include #include #include #include -// #include // For 'extract_bits'. -#include -#include - #include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" namespace nil::crypto3::multiprecision { - namespace detail { - using limb_type = nil::crypto3::multiprecision::limb_type; - /* This specialization is used when assigning `chunk_bits` * of `bits` into `val` at `bit_location` in case where `val` * is larger than one limb (machine word). */ template void assign_bits(big_integer& val, Unsigned bits, std::size_t bit_location, - std::size_t chunk_bits, const std::integral_constant& tag) { + std::size_t chunk_bits) { unsigned limb = bit_location / (sizeof(limb_type) * CHAR_BIT); unsigned shift = bit_location % (sizeof(limb_type) * CHAR_BIT); @@ -54,7 +48,7 @@ namespace nil::crypto3::multiprecision { bit_location += shift; auto extra_bits = bits >> shift; if (extra_bits) { - assign_bits(val, extra_bits, bit_location, chunk_bits, tag); + assign_bits(val, extra_bits, bit_location, chunk_bits); } } } @@ -67,40 +61,58 @@ namespace nil::crypto3::multiprecision { void assign_bits(big_integer& val, Unsigned bits, std::size_t bit_location, std::size_t chunk_bits, const std::integral_constant& /*unused*/) { - using local_limb_type = typename big_integer::local_limb_type; + using limb_type = typename big_integer::limb_type; // // Check for possible overflow, this may trigger an exception, or have no effect // depending on whether this is a checked integer or not: // // We are not throwing, we will use as many bits from the input as we need to. - // BOOST_ASSERT(!((bit_location >= sizeof(local_limb_type) * CHAR_BIT) && bits)); + // BOOST_ASSERT(!((bit_location >= sizeof(limb_type) * CHAR_BIT) && bits)); - local_limb_type mask = chunk_bits >= sizeof(local_limb_type) * CHAR_BIT - ? ~static_cast(0u) - : (static_cast(1u) << chunk_bits) - 1; - local_limb_type value = (static_cast(bits) & mask) << bit_location; + limb_type mask = chunk_bits >= sizeof(limb_type) * CHAR_BIT + ? ~static_cast(0u) + : (static_cast(1u) << chunk_bits) - 1; + limb_type value = (static_cast(bits) & mask) << bit_location; *val.limbs() |= value; // // Check for overflow bits: // - bit_location = sizeof(local_limb_type) * CHAR_BIT - bit_location; + bit_location = sizeof(limb_type) * CHAR_BIT - bit_location; // We are not throwing, we will use as many bits from the input as we need to. // BOOST_ASSERT(!((bit_location < sizeof(bits) * CHAR_BIT) && (bits >>= // bit_location))); } + template + std::uintmax_t extract_bits(const big_integer& val, std::size_t location, + std::size_t count) { + std::size_t limb = location / (sizeof(limb_type) * CHAR_BIT); + std::size_t shift = location % (sizeof(limb_type) * CHAR_BIT); + std::uintmax_t result = 0; + std::uintmax_t mask = count == std::numeric_limits::digits + ? ~static_cast(0) + : (static_cast(1u) << count) - 1; + if (count > (sizeof(limb_type) * CHAR_BIT - shift)) { + result = extract_bits(val, location + sizeof(limb_type) * CHAR_BIT - shift, + count - sizeof(limb_type) * CHAR_BIT + shift); + result <<= sizeof(limb_type) * CHAR_BIT - shift; + } + if (limb < val.size()) { + result |= (val.limbs()[limb] >> shift) & mask; + } + return result; + } + template - big_integer& import_bits_generic(big_integer& val, Iterator i, Iterator j, + big_integer& import_bits_generic(big_integer& result, Iterator i, Iterator j, std::size_t chunk_size = 0, bool msv_first = true) { big_integer newval; using value_type = typename std::iterator_traits::value_type; using difference_type = typename std::iterator_traits::difference_type; - using size_type = - typename ::boost::multiprecision::detail::make_unsigned::type; - using tag_type = typename big_integer::trivial_tag; + using size_type = typename std::make_unsigned::type; if (!chunk_size) { chunk_size = std::numeric_limits::digits; @@ -117,8 +129,7 @@ namespace nil::crypto3::multiprecision { msv_first ? -static_cast(chunk_size) : chunk_size; while (i != j) { - assign_bits(newval, *i, static_cast(bit_location), chunk_size, - tag_type()); + assign_bits(newval, *i, static_cast(bit_location), chunk_size); ++i; bit_location += bit_location_change; } @@ -126,12 +137,12 @@ namespace nil::crypto3::multiprecision { // This will remove the upper bits using upper_limb_mask. newval.normalize(); - val.backend() = std::move(newval); - return val; + result = std::move(newval); + return result; } template - inline big_integer import_bits_fast(big_integer& val, T* i, T* j, + inline big_integer import_bits_fast(big_integer& result, T* i, T* j, std::size_t chunk_size = 0) { std::size_t byte_len = (j - i) * (chunk_size ? chunk_size / CHAR_BIT : sizeof(*i)); std::size_t limb_len = byte_len / sizeof(limb_type); @@ -139,7 +150,6 @@ namespace nil::crypto3::multiprecision { ++limb_len; } - big_integer& result = val.backend(); BOOST_VERIFY(result.size() > limb_len); result.limbs()[result.size() - 1] = 0u; @@ -147,7 +157,7 @@ namespace nil::crypto3::multiprecision { // This is probably unneeded, but let it stay for now. result.normalize(); - return val; + return result; } } // namespace detail @@ -170,36 +180,26 @@ namespace nil::crypto3::multiprecision { template OutputIterator export_bits(const big_integer& val, OutputIterator out, std::size_t chunk_size, bool msv_first = true) { -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - using tag_type = typename big_integer::trivial_tag; if (!val) { *out = 0; ++out; return out; } - std::size_t bitcount = eval_msb_imp(val.backend()) + 1; + std::size_t bitcount = msb(val) + 1; std::ptrdiff_t bit_location = msv_first ? static_cast(bitcount - chunk_size) : 0; - const std::ptrdiff_t bit_step = - msv_first ? static_cast(-static_cast(chunk_size)) - : static_cast(chunk_size); + const std::ptrdiff_t bit_step = msv_first ? (-static_cast(chunk_size)) + : static_cast(chunk_size); while (bit_location % bit_step) { ++bit_location; } do { - *out = boost::multiprecision::detail::extract_bits(val.backend(), bit_location, - chunk_size, tag_type()); + *out = detail::extract_bits(val, bit_location, chunk_size); ++out; bit_location += bit_step; } while ((bit_location >= 0) && (bit_location < static_cast(bitcount))); return out; -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif } } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp deleted file mode 100644 index b285b95fc7..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/integer_ops.hpp +++ /dev/null @@ -1,55 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright 2024 Martun Karapetyan -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// We need to include this file after modular adaptor, in order for these functions to 'see' -//---------------------------------------------------------------------------// - -#pragma once - -#include - -namespace nil::crypto3::multiprecision { - // TODO - - // // Only for our modular numbers function powm takes 2 arguments, - // // so we need to add this specialization. - // template - // inline constexpr - // typename std::enable_if::value && is_integral::value, - // number>>::type - // powm(const number< - // boost::multiprecision::backends::modular_adaptor>& - // b, - // const U& p) { - // // We will directly call eval_powm here, that's what a call through a - // // default_ops::powm_func would do if expression tempaltes are off. We don't want to - // // change that structure. - // boost::multiprecision::backends::modular_adaptor result; - // result.set_modular_params(b.backend().mod_data()); - // boost::multiprecision::backends::eval_powm(result, b.backend(), p); - // return result; - // } - - // template - // inline constexpr - // typename std::enable_if<(detail::is_backend::value && - // (is_number::value || is_number_expression::value)), - // number>>::type - // powm(const number< - // boost::multiprecision::backends::modular_adaptor>& - // b, - // const U& p) { - // // We will directly call eval_powm here, that's what a call through a - // // default_ops::powm_func would do if expression tempaltes are off. We don't want to - // // change that structure. - // boost::multiprecision::backends::modular_adaptor result; - // result.set_modular_params(b.backend().mod_data()); - // boost::multiprecision::backends::eval_powm(result, b.backend(), p.backend()); - // return result; - // } -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/jacobi.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/jacobi.hpp new file mode 100644 index 0000000000..9d6ec0c21f --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/jacobi.hpp @@ -0,0 +1,66 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021 Aleksei Moskvin +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision { + template + constexpr int eval_jacobi(const big_integer &a, const big_integer &n) { + using big_integer_t = big_integer; + // BOOST_THROW_EXCEPTION(std::invalid_argument("jacobi: second argument must be odd + // and > 1")); + BOOST_ASSERT(n % 2 && n > 1); + + big_integer_t x = a, y = n; + int J = 1; + + while (y > 1) { + x %= y; + + big_integer_t yd2 = y; + yd2 >>= 1; + + if (eval_gt(x, yd2)) { + big_integer_t tmp(y); + tmp -= x; + x = tmp; + if (y % 4 == 3) { + J = -J; + } + } + if (is_zero(x)) { + return 0; + } + + size_t shifts = lsb(x); + x >>= shifts; + if (shifts & 1) { + std::size_t y_mod_8 = y % 8; + if (y_mod_8 == 3 || y_mod_8 == 5) { + J = -J; + } + } + + if (x % 4 == 3 && y % 4 == 3) { + J = -J; + } + + // std::swap(x, y); + auto tmp = x; + x = y; + y = tmp; + } + return J; + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp index 816bb2e489..4ec3da669f 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp @@ -21,20 +21,13 @@ #include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" -#ifdef BOOST_MSVC -#pragma warning(push) -#pragma warning(disable : 4702) -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning( \ - disable : 4146) // unary minus operator applied to unsigned type, result still unsigned -#endif - namespace nil::crypto3::multiprecision { // TODO refactor template inline constexpr typename std::enable_if::value, void>::type - eval_convert_to(R *result, const big_integer &backend) { + convert_to(R *result, const big_integer &backend) { + using detail::limb_type; if constexpr (boost::multiprecision::backends::numeric_limits_workaround::digits < big_integer::limb_bits) { if (boost::multiprecision::detail::is_signed::value && @@ -44,8 +37,9 @@ namespace nil::crypto3::multiprecision { return; } *result = static_cast(backend.limbs()[0]); - } else + } else { *result = static_cast(backend.limbs()[0]); + } unsigned shift = big_integer::limb_bits; unsigned i = 1; @@ -94,7 +88,7 @@ namespace nil::crypto3::multiprecision { } template - NIL_CO3_MP_FORCEINLINE constexpr bool eval_is_zero(const big_integer &val) noexcept { + NIL_CO3_MP_FORCEINLINE constexpr bool is_zero(const big_integer &val) noexcept { // std::all_of is not constexpr, so writing manually. for (std::size_t i = 0; i < val.size(); ++i) { if (val.limbs()[i] != 0) { @@ -108,7 +102,7 @@ namespace nil::crypto3::multiprecision { // Get the location of the least-significant-bit: // template - inline constexpr unsigned eval_lsb(const big_integer &a) { + inline constexpr unsigned lsb(const big_integer &a) { // // Find the index of the least significant limb that is non-zero: // @@ -125,7 +119,7 @@ namespace nil::crypto3::multiprecision { } template - inline constexpr unsigned eval_msb(const big_integer &a) { + inline constexpr unsigned msb(const big_integer &a) { // // Find the index of the most significant bit that is non-zero: // @@ -141,17 +135,10 @@ namespace nil::crypto3::multiprecision { return boost::multiprecision::detail::find_msb(a.limbs()[0]); } -#ifdef BOOST_GCC -// -// We really shouldn't need to be disabling this warning, but it really does appear to be -// spurious. The warning appears only when in release mode, and asserts are on. -// -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif - template - inline constexpr bool eval_bit_test(const big_integer &val, std::size_t index) noexcept { + inline constexpr bool bit_test(const big_integer &val, std::size_t index) noexcept { + using detail::limb_type; + unsigned offset = index / big_integer::limb_bits; unsigned shift = index % big_integer::limb_bits; limb_type mask = limb_type(1u) << shift; @@ -161,12 +148,10 @@ namespace nil::crypto3::multiprecision { return static_cast(val.limbs()[offset] & mask); } -#ifdef BOOST_GCC -#pragma GCC diagnostic pop -#endif - template - inline constexpr void eval_bit_set(big_integer &val, std::size_t index) { + inline constexpr void bit_set(big_integer &val, std::size_t index) { + using detail::limb_type; + unsigned offset = index / big_integer::limb_bits; unsigned shift = index % big_integer::limb_bits; limb_type mask = limb_type(1u) << shift; @@ -177,7 +162,9 @@ namespace nil::crypto3::multiprecision { } template - inline constexpr void eval_bit_unset(big_integer &val, std::size_t index) noexcept { + inline constexpr void bit_unset(big_integer &val, std::size_t index) noexcept { + using detail::limb_type; + unsigned offset = index / big_integer::limb_bits; unsigned shift = index % big_integer::limb_bits; limb_type mask = limb_type(1u) << shift; @@ -189,7 +176,9 @@ namespace nil::crypto3::multiprecision { } template - inline constexpr void eval_bit_flip(big_integer &val, std::size_t index) { + inline constexpr void bit_flip(big_integer &val, std::size_t index) { + using detail::limb_type; + unsigned offset = index / big_integer::limb_bits; unsigned shift = index % big_integer::limb_bits; limb_type mask = limb_type(1u) << shift; @@ -206,7 +195,10 @@ namespace nil::crypto3::multiprecision { inline constexpr typename std::enable_if::value, Integer>::type - eval_integer_modulus(const big_integer &a, Integer mod) { + integer_modulus(const big_integer &a, Integer mod) { + using detail::limb_type; + using detail::double_limb_type; + if constexpr (sizeof(Integer) <= sizeof(limb_type)) { if (mod <= (std::numeric_limits::max)()) { const int n = a.size(); @@ -230,8 +222,8 @@ namespace nil::crypto3::multiprecision { typename std::enable_if::value && boost::multiprecision::detail::is_integral::value, Integer>::type - eval_integer_modulus(const big_integer &x, Integer val) { - return eval_integer_modulus(x, boost::multiprecision::detail::unsigned_abs(val)); + integer_modulus(const big_integer &x, Integer val) { + return integer_modulus(x, boost::multiprecision::detail::unsigned_abs(val)); } template @@ -242,27 +234,4 @@ namespace nil::crypto3::multiprecision { } return result; } - -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - } // namespace nil::crypto3::multiprecision - -// TODO -// namespace detail { - -// // We need to specialize this class, because big_integer does not have -// // signed_types. All we have changed here is Backend::signed_types -> -// // Backend::unsigned_types, this will work for our use cases. -// template -// struct canonical_imp, std::integral_constant> { -// static constexpr int index = -// find_index_of_large_enough_type::unsigned_types, 0, -// bits_of::value>::value; -// using type = typename dereference_tuple::unsigned_types, -// Val>::type; -// }; - -// } // namespace detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp deleted file mode 100644 index e173b8f464..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/multiply.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright (c) 2023 Martun Karapetyan -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Contains eval_multiply for big_integer, which does nothing but converts it to -// cpp_int_backend and does the multiplication. -// - -#pragma once - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" - -namespace nil::crypto3::multiprecision { - - // Functions in this file should be called only for creation of montgomery and Barett - // params, calculation of inverse element and montgomery_reduce. Since these functions - // are relatively slow and are not called very often, we will not optimize them. We do - // NOT care about the execution speed, and will just redirect calls to normal - // boost::cpp_int. - template - inline constexpr void eval_multiply(big_integer &result, - const big_integer &a, - const big_integer &b) noexcept { - result = a; - // Call the lower function, we don't care about speed here. - eval_multiply(result, b); - } - - // If the second argument is trivial or not, we still assign it to the first the exact - // same way. - template - inline constexpr void eval_multiply(big_integer &result, const big_integer &a, - const limb_type &b) noexcept { - result = a; - // Call the lower function, we don't care about speed here. - eval_multiply(result, b); - } - - template - inline constexpr void eval_multiply(big_integer &result, const limb_type &b) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int *= b; - result.from_cpp_int(result_cpp_int); - } - - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! - // Covers the case where the second operand is not trivial. - // Redirects the call to normal boost::cpp_int. We do not care about speed here. - template - inline constexpr void eval_multiply(big_integer &result, - const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int *= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp index b0c50be6bd..bb079903e2 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/storage.hpp @@ -1,10 +1,28 @@ #pragma once +#include #include -namespace nil::crypto3::multiprecision { +namespace nil::crypto3::multiprecision::detail { using limb_type = std::uint32_t; using double_limb_type = std::uint64_t; using signed_limb_type = std::int32_t; using signed_double_limb_type = std::int64_t; -} // namespace nil::crypto3::multiprecision + + using limb_type = limb_type; + using double_limb_type = double_limb_type; + using limb_pointer = limb_type *; + using const_limb_pointer = const limb_type *; + + static constexpr unsigned limb_bits = sizeof(limb_type) * CHAR_BIT; + static constexpr limb_type max_limb_value = ~static_cast(0u); + + // Given a value represented in 'double_limb_type', decomposes it into + // two 'limb_type' variables, based on high order bits and low order bits. + // There 'a' receives high order bits of 'X', and 'b' receives the low order bits. + static constexpr void dbl_limb_to_limbs(const double_limb_type &X, limb_type &a, limb_type &b) { + b = X; + a = X >> limb_bits; + } + +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/test/CMakeLists.txt b/crypto3/libs/multiprecision/test/CMakeLists.txt index 847f39bba1..0070cc9640 100644 --- a/crypto3/libs/multiprecision/test/CMakeLists.txt +++ b/crypto3/libs/multiprecision/test/CMakeLists.txt @@ -27,7 +27,7 @@ macro(define_runtime_multiprecision_test name) ${Boost_INCLUDE_DIRS} ) - set_target_properties(${test_name} PROPERTIES CXX_STANDARD 17 + set_target_properties(${test_name} PROPERTIES CXX_STANDARD 23 CXX_STANDARD_REQUIRED TRUE) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -74,6 +74,7 @@ set(BIG_INTEGER_TESTS_NAMES "big_integer" "big_integer_comparision" "big_integer_modular" + "big_integer_modular_comprehensive" ) foreach(TEST_NAME ${RUNTIME_TESTS_NAMES}) diff --git a/crypto3/libs/multiprecision/test/big_integer.cpp b/crypto3/libs/multiprecision/test/big_integer.cpp index 4f13652ac5..7f890968ca 100644 --- a/crypto3/libs/multiprecision/test/big_integer.cpp +++ b/crypto3/libs/multiprecision/test/big_integer.cpp @@ -1,62 +1,148 @@ +#include #define BOOST_TEST_MODULE big_integer_test #include #include +#include #include #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" #include "nil/crypto3/multiprecision/big_integer/literals.hpp" +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(60) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(32) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(36) +BOOST_MP_DEFINE_SIZED_CPP_INT_LITERAL(60) + using namespace nil::crypto3::multiprecision::literals; +using namespace boost::multiprecision::literals; -BOOST_AUTO_TEST_SUITE(big_integer_smoke_tests) +BOOST_AUTO_TEST_SUITE(smoke) -BOOST_AUTO_TEST_CASE(construct) { - nil::crypto3::multiprecision::big_integer<315> a = 0x123_big_integer315; +BOOST_AUTO_TEST_CASE(construct_constexpr) { + constexpr nil::crypto3::multiprecision::big_integer<60> a = 0x123_big_integer60; } -BOOST_AUTO_TEST_CASE(to_string_trivial) { BOOST_CHECK((0x1_big_integer315).str() == "1"); } - -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(to_string_trivial) { BOOST_CHECK_EQUAL((0x1_big_integer60).str(), "1"); } -BOOST_AUTO_TEST_SUITE(big_integer_operations_tests) +BOOST_AUTO_TEST_CASE(to_string_small) { BOOST_CHECK_EQUAL((0x20_big_integer60).str(), "32"); } BOOST_AUTO_TEST_CASE(ops) { - nil::crypto3::multiprecision::big_integer<315> a = 2u; + nil::crypto3::multiprecision::big_integer<60> a = 2u, b; - auto b = a + a; - b += a; + auto c1{a}; + auto c2{std::move(a)}; + auto c3{2}; + auto c4{2u}; + b = a; + b = std::move(a); + b = 2; + b = 2u; + +#define TEST_BINARY_OP(op) \ + do { \ + b = 32u; \ + a = 4; \ + b = a op a; \ + /* b = 2 op a; */ \ + /* b = a op 2; */ \ + /* b = 2u op a; */ \ + /* b = a op 2u; */ \ + b = 32u; \ + b op## = a; \ + /* b op## = 2; */ \ + /*b op## = 2u; */ \ + } while (false) + + TEST_BINARY_OP(+); ++b; b++; + b = +b; + TEST_BINARY_OP(-); --b; b--; - b -= a; - b = b - a; - - b = std::move(a); + // b = -b; - b = a * b; - b *= a; - b = a / b; - b /= a; - b = a % b; - b %= a; + TEST_BINARY_OP(%); + TEST_BINARY_OP(/); + TEST_BINARY_OP(*); - b = a & b; - b &= a; - b = a | b; - b |= a; - b = a ^ b; - b ^= a; + TEST_BINARY_OP(&); + TEST_BINARY_OP(|); + TEST_BINARY_OP(^); b = ~a; +} - // b = -b; - b = +b; +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(cpp_int_conversion) + +BOOST_AUTO_TEST_CASE(to_cpp_int) { + BOOST_CHECK_EQUAL((0xFFFFFFFFFFF_big_integer60).to_cpp_int().str(), "17592186044415"); +} + +BOOST_AUTO_TEST_CASE(from_cpp_int) { + nil::crypto3::multiprecision::big_integer<60> result; + result.from_cpp_int(0xFFFFFFFFFFF_cppui60); + BOOST_CHECK_EQUAL(result, 0xFFFFFFFFFFF_big_integer60); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(addition) + +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(0x2_big_integer60 + 0x3_big_integer60, 0x5_big_integer60); +} + +BOOST_AUTO_TEST_CASE(wraps) { + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 + 0x2_big_integer32, 0x1_big_integer32); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(0xAFFFFFFFF_big_integer36 + 0x2_big_integer36, 0xB00000001_big_integer36); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(multiplication) + +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(0x2_big_integer60 * 0x3_big_integer60, 0x6_big_integer60); +} + +BOOST_AUTO_TEST_CASE(wraps) { + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 * 0x2_big_integer32, 0xFFFFFFFE_big_integer32); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(0xAFFFFFFFF_big_integer36 * 0x2_big_integer36, 0x5FFFFFFFE_big_integer36); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(convert) + +BOOST_AUTO_TEST_CASE(to_uint64_t) { + std::uint64_t a = static_cast(0x123456789ABCDEF_big_integer64); + BOOST_CHECK_EQUAL(a, 0x123456789ABCDEF); +} + +BOOST_AUTO_TEST_CASE(from_uint64_t) { + nil::crypto3::multiprecision::big_integer<64> a = + static_cast(0x123456789ABCDEFull); + BOOST_CHECK_EQUAL(a, 0x123456789ABCDEF_big_integer64); +} + +BOOST_AUTO_TEST_CASE(from_int64_t) { + nil::crypto3::multiprecision::big_integer<64> a = + static_cast(0x123456789ABCDEFull); + BOOST_CHECK_EQUAL(a, 0x123456789ABCDEF_big_integer64); } BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/multiprecision/test/big_integer_modular.cpp b/crypto3/libs/multiprecision/test/big_integer_modular.cpp index 09e373c573..1d6e593a89 100644 --- a/crypto3/libs/multiprecision/test/big_integer_modular.cpp +++ b/crypto3/libs/multiprecision/test/big_integer_modular.cpp @@ -12,32 +12,149 @@ using namespace nil::crypto3::multiprecision; using namespace nil::crypto3::multiprecision::literals; -BOOST_AUTO_TEST_SUITE(big_integer_smoke_tests) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(60) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(32) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(36) +BOOST_MP_DEFINE_SIZED_CPP_INT_LITERAL(60) -constexpr auto mod = 0x123_big_integer315; +using namespace nil::crypto3::multiprecision::literals; +using namespace boost::multiprecision::literals; + +constexpr auto mod = 0x123456789ABCDEF_big_integer64; +using modular_big_int = modular_big_integer_ct; + +BOOST_AUTO_TEST_SUITE(smoke) + +BOOST_AUTO_TEST_CASE(construct_constexpr) { + constexpr modular_big_int a = static_cast(0x123_big_integer64); +} + +BOOST_AUTO_TEST_CASE(construct_modular_ct_trivial_montgomery) { + static constexpr auto mod = 0x3_big_integer64; + modular_big_integer_ct a = modular_big_integer_ct(0x5_big_integer64); + BOOST_CHECK_EQUAL(a.str(), "2"); +} + +BOOST_AUTO_TEST_CASE(construct_modular_rt_trivial_montgomery) { + modular_big_integer_rt<64> a{0x5_big_integer64, 0x3_big_integer64}; + BOOST_CHECK_EQUAL(a.str(), "2"); +} + +BOOST_AUTO_TEST_CASE(construct_modular_ct_small_montgomery) { + static constexpr auto mod = 0x79_big_integer64; + modular_big_integer_ct a = modular_big_integer_ct(0x1234_big_integer64); + BOOST_CHECK_EQUAL(a.str(), "62"); +} + +BOOST_AUTO_TEST_CASE(construct_modular_rt_small_montgomery) { + modular_big_integer_rt<64> a{0x1234_big_integer64, 0x79_big_integer64}; + BOOST_CHECK_EQUAL(a.str(), "62"); +} + +BOOST_AUTO_TEST_CASE(construct_modular_ct_small) { + static constexpr auto mod = 0x78_big_integer64; + modular_big_integer_ct a = modular_big_integer_ct(0x1234_big_integer64); + BOOST_CHECK_EQUAL(a.str(), "100"); +} + +BOOST_AUTO_TEST_CASE(construct_modular_rt_small) { + modular_big_integer_rt<64> a{0x1234_big_integer64, 0x78_big_integer64}; + BOOST_CHECK_EQUAL(a.str(), "100"); +} + +BOOST_AUTO_TEST_CASE(to_string_trivial) { + BOOST_CHECK_EQUAL((static_cast(0x1_big_integer64)).str(), "1"); +} + +BOOST_AUTO_TEST_CASE(to_string_small) { + BOOST_CHECK_EQUAL((static_cast(0x20_big_integer64)).str(), "32"); +} + +BOOST_AUTO_TEST_CASE(ops) { + modular_big_int a = 2u, b; + + auto c1{a}; + auto c2{std::move(a)}; + auto c3{2}; + auto c4{2u}; + b = a; + b = std::move(a); + b = 2; + b = 2u; -BOOST_AUTO_TEST_CASE(operations) { - modular_big_integer_ct a = 2; +#define TEST_BINARY_OP(op) \ + do { \ + b = a op a; \ + /* b = 2 op a; */ \ + /* b = a op 2; */ \ + /* b = 2u op a; */ \ + /* b = a op 2u; */ \ + b op## = a; \ + /* b op## = 2; */ \ + /*b op## = 2u; */ \ + } while (false) - auto b = a + a; - b += a; + TEST_BINARY_OP(+); ++b; b++; + b = +b; + TEST_BINARY_OP(-); --b; b--; - b -= a; - b = b - a; + b = -b; +} - b = std::move(a); +BOOST_AUTO_TEST_SUITE_END() - b = a * b; - b *= a; - b = a / b; - b /= a; +BOOST_AUTO_TEST_SUITE(addition) - b = -b; - b = +b; +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(static_cast(0x2_big_integer64) + + static_cast(0x3_big_integer64), + static_cast(0x5_big_integer64)); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(static_cast(0xAFFFFFFFF_big_integer64) + + static_cast(0x2_big_integer36), + static_cast(0xB00000001_big_integer64)); } BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(multiplication) + +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(static_cast(0x2_big_integer64) * + static_cast(0x3_big_integer64), + static_cast(0x6_big_integer64)); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(static_cast(0xAFFFFFFFF_big_integer64) * + static_cast(0x2_big_integer36), + static_cast(0x15FFFFFFFE_big_integer64)); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(convert) + +// BOOST_AUTO_TEST_CASE(to_uint64_t) { +// std::uint64_t a = +// static_cast(static_cast(0x123456789ABCDEF_big_integer64)); +// BOOST_CHECK_EQUAL(a, 0x123456789ABCDEF); +// } + +// BOOST_AUTO_TEST_CASE(from_uint64_t) { +// modular_big_integer_impl a = static_cast(0x123456789ABCDEFull); +// BOOST_CHECK_EQUAL(a, static_cast(0x123456789ABCDEF_big_integer64)); +// } + +// BOOST_AUTO_TEST_CASE(from_int64_t) { +// modular_big_integer_impl a = static_cast(0x123456789ABCDEFull); +// BOOST_CHECK_EQUAL(a, static_cast(0x123456789ABCDEF_big_integer64)); +// } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/multiprecision/test/big_integer_modular_comprehensive.cpp b/crypto3/libs/multiprecision/test/big_integer_modular_comprehensive.cpp new file mode 100644 index 0000000000..51caad8499 --- /dev/null +++ b/crypto3/libs/multiprecision/test/big_integer_modular_comprehensive.cpp @@ -0,0 +1,612 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2024 Martun Karapetyan +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE big_integer_modular_comprehensive_test + +#include + +#include +#include + +// We need cpp_int to compare to it. +#include + +#include +#include +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +using namespace nil::crypto3::multiprecision::literals; + +enum test_set_enum : std::size_t { + mod_e, + a_e, + b_e, + // a_add_b_e, a_sub_b_e, a_mul_b_e, a_div_b_e, a_mod_b_e, a_pow_b_e, + test_set_len +}; + +template +constexpr void pow_test(const big_integer_t& a, const big_integer_t& b, const big_integer_t& m) { + typedef nil::crypto3::multiprecision::modular_big_integer_rt + modular_number; + typedef typename big_integer_t::cpp_int_type standard_number; + + modular_number a_m(a, m); + modular_number b_m(b, m); + + standard_number a_cppint = a.to_cpp_int(); + standard_number b_cppint = b.to_cpp_int(); + standard_number m_cppint = m.to_cpp_int(); + + standard_number a_powm_b = powm(a_cppint, b_cppint, m_cppint); + // pow could be used only with modular_numbers + // modular_number a_m_pow_b_m = pow(a_m, b_m); + // powm could be used with mixed types + // TODO(ioxid): enable these + // modular_number a_m_powm_b_m = powm(a_m, b_m); + // modular_number a_m_powm_b = powm(a_m, b); + // BOOST_ASSERT_MSG(standard_number(a_m_powm_b_m.to_cpp_int()) == a_powm_b, "powm error"); + // BOOST_ASSERT_MSG(standard_number(a_m_powm_b.to_cpp_int()) == a_powm_b, "powm error"); +} + +template +bool base_operations_test(std::array test_set) { + typedef typename big_integer_t::cpp_int_type::backend_type CppIntBackend; + + typedef nil::crypto3::multiprecision::big_integer Backend_doubled; + typedef typename boost::multiprecision::default_ops::double_precision_type::type + CppIntBackend_doubled; + typedef nil::crypto3::multiprecision::modular_big_integer_rt + modular_number; + typedef boost::multiprecision::number standard_number; + typedef boost::multiprecision::number dbl_standard_number; + + // Convert from cpp_int_modular_backend to cpp_int_backend numbers. + standard_number a_cppint = test_set[a_e].to_cpp_int(); + standard_number b_cppint = test_set[b_e].to_cpp_int(); + standard_number e_cppint = test_set[mod_e].to_cpp_int(); + + dbl_standard_number a_add_b_s = + (static_cast(a_cppint) + static_cast(b_cppint)) % + e_cppint; + dbl_standard_number a_sub_b_s = (static_cast(a_cppint) - + static_cast(b_cppint) + e_cppint) % + e_cppint; + dbl_standard_number a_mul_b_s = + (static_cast(a_cppint) * static_cast(b_cppint)) % + e_cppint; + dbl_standard_number a_mod_b_s = + (static_cast(a_cppint) % static_cast(b_cppint)) % + e_cppint; + standard_number a_and_b_s = (a_cppint & b_cppint) % e_cppint; + standard_number a_or_b_s = (a_cppint | b_cppint) % e_cppint; + standard_number a_xor_b_s = (a_cppint ^ b_cppint) % e_cppint; + standard_number a_powm_b_s = powm(a_cppint, b_cppint, e_cppint); + standard_number a_bit_set_s = a_cppint; + bit_set(a_bit_set_s, 1); + a_bit_set_s %= e_cppint; + standard_number a_bit_unset_s = a_cppint; + a_bit_unset_s %= e_cppint; + bit_unset(a_bit_unset_s, 2); + standard_number a_bit_flip_s = a_cppint; + bit_flip(a_bit_flip_s, 3); + a_bit_flip_s %= e_cppint; + + int b_msb_s = msb(b_cppint); + int b_lsb_s = lsb(b_cppint); + + modular_number a(test_set[a_e], test_set[mod_e]); + modular_number b(test_set[b_e], test_set[mod_e]); + + modular_number a_add_b = a + b; + modular_number a_sub_b = a - b; + modular_number a_mul_b = a * b; + // modular_number a_and_b = a & b; + // modular_number a_or_b = a | b; + // modular_number a_xor_b = a ^ b; + // TODO(ioxid): need this one + // modular_number a_powm_b = powm(a, b); + // modular_number a_bit_set = a; + // bit_set(a_bit_set, 1); + // modular_number a_bit_unset = a; + // bit_unset(a_bit_unset, 2); + // modular_number a_bit_flip = a; + // bit_flip(a_bit_flip, 3); + + int b_msb = msb(b_cppint); + int b_lsb = lsb(b_cppint); + + // We cannot use convert_to here, because there's a bug inside boost, convert_to is constexpr, + // but it calls function generic_interconvert which is not. + BOOST_ASSERT_MSG(standard_number(a_add_b.to_cpp_int()) == a_add_b_s, "addition error"); + BOOST_ASSERT_MSG(standard_number(a_sub_b.to_cpp_int()) == a_sub_b_s, "subtraction error"); + BOOST_ASSERT_MSG(standard_number(a_mul_b.to_cpp_int()) == a_mul_b_s, "multiplication error"); + + // BOOST_ASSERT_MSG((a > b) == (a_cppint > b_cppint), "g error"); + // BOOST_ASSERT_MSG((a >= b) == (a_cppint >= b_cppint), "ge error"); + BOOST_ASSERT_MSG((a == b) == (a_cppint == b_cppint), "e error"); + // BOOST_ASSERT_MSG((a < b) == (a_cppint < b_cppint), "l error"); + // BOOST_ASSERT_MSG((a <= b) == (a_cppint <= b_cppint), "le error"); + BOOST_ASSERT_MSG((a != b) == (a_cppint != b_cppint), "ne error"); + + // BOOST_ASSERT_MSG(standard_number(a_and_b.to_cpp_int()) == a_and_b_s, "and error"); + // BOOST_ASSERT_MSG(standard_number(a_or_b.to_cpp_int()) == a_or_b_s, "or error"); + // BOOST_ASSERT_MSG(standard_number(a_xor_b.to_cpp_int()) == a_xor_b_s, "xor error"); + + // BOOST_ASSERT_MSG(standard_number(a_powm_b.to_cpp_int()) == a_powm_b_s, "powm error"); + pow_test(test_set[a_e], test_set[b_e], test_set[mod_e]); + + // BOOST_ASSERT_MSG(standard_number(a_bit_set.to_cpp_int()) == a_bit_set_s, "bit set error"); + // BOOST_ASSERT_MSG(standard_number(a_bit_unset.to_cpp_int()) == a_bit_unset_s, "bit unset + // error"); BOOST_ASSERT_MSG(standard_number(a_bit_flip.to_cpp_int()) == a_bit_flip_s, "bit flip + // error"); + + BOOST_ASSERT_MSG(b_msb_s == b_msb, "msb error"); + BOOST_ASSERT_MSG(b_lsb_s == b_lsb, "lsb error"); + + return true; +} + +template +bool base_operations_test(const std::array, N>& test_data) { + for (const auto& test_set : test_data) { + base_operations_test(test_set); + } + return true; +} + +BOOST_AUTO_TEST_SUITE(static_tests) + +BOOST_AUTO_TEST_CASE(base_ops_prime_mod_backend_130) { + using standard_number = nil::crypto3::multiprecision::big_integer<130>; + using test_set = std::array; + using test_data_t = std::array; + constexpr test_data_t test_data = {{ + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1902d38b6904893e90b9c5b8732d1f37d_big_integer130, + 0x2b9060b88dea177d5213deb6f3794434_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x20de70b3f1426ef30b2b2c85d75e2ff2a_big_integer130, + 0x1a50227dc3bd742a232db8798e16d1fbb_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2395c23e8da249dec864da20301b1b64a_big_integer130, + 0xdf185b46a84f318f34160415cc2cc010_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x43c06149ee7c03529dc8a03d091b4e94_big_integer130, + 0x310216180d322187af2c938af5a1ce59_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x26352a54193fd97c1b30ba3e4f624abf3_big_integer130, + 0x271cc74f6ca6cb859a1c1420922eb29ae_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x55cc328d5cc8b9d3362664a61c49d05_big_integer130, + 0x203c88f7c00196a19ca13f3956d823cf9_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2b7b43b1d5e1d838e06ac851dd57c2921_big_integer130, + 0x1c3e4586b67511bdc48a424ab7934f3e2_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x71bff3e8b1dca8851adc38f3f7949f15_big_integer130, + 0x1aa747f949397fd5dd7c8651e8150552_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1a2ce759cfc6960d663313054f1bb102f_big_integer130, + 0xc7a66c97d85f5662ffeebbb953476196_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x29e2517b6b554a879c74e8c4e0516f177_big_integer130, + 0x1f1218f45001011d29934c8cf15c52970_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x24633cbd7c7bfc5f2bd53ee68d61c35d2_big_integer130, + 0x144cedf22adfea125bb43d0be11ca1d0c_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xfa48dc5dd7b4b1489220e933791b4338_big_integer130, + 0x2b234e335952ad1681afa214a74622526_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x15b8a80d1056036fb5b43afa7acf2fba1_big_integer130, + 0x20100f6e5147f7eae0dd456dcb21a8b57_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x11ad3e87c291c9ee81d50b80086315fff_big_integer130, + 0x21f4e281ab8da64819ab4c2311bb5b18e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1cac9e60c8ba9fe1dd02a72bd7f302986_big_integer130, + 0x1ca1068fbfa3b573d8f1f189dd5747a16_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1ed35770d0d86801f43eeb651aff88be9_big_integer130, + 0x1906ec17bd1c75f7427c8c94bd7d1baa8_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x159635b82f84b811c9dd90d5cb6b178b_big_integer130, + 0x140f2dea986ddcb181072cb9211f57a8d_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x27d6f0f737bacbce899e43dc682b5624d_big_integer130, + 0x1011587e1de922107a18d7b925c77dd54_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x280f4c62adaa0ffd674affb50fe2e1695_big_integer130, + 0x1d094e72b514dcbf1c711fdcf3e53cb8_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x6e5882e4497adbf5bb25efec29dfc7d8_big_integer130, + 0x1a842e4abcdeedbc8abc583f41e46b125_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xdf8e9c5a1aef9fb8a84c47e4c13a8e32_big_integer130, + 0x1a60dcd50617841662e100abea05666b4_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1f80459133a5f8b5049a346d931edca2e_big_integer130, + 0x130fe586a5ec4e0045a6e16c4ebac2486_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1f284be48f9037159abfc1c11bd1e06e6_big_integer130, + 0x31251cfead95629cb37cf61595785efa6_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x63766cb772cbaf3e4dfedffb2af6f181_big_integer130, + 0x311d5240457feecb26f8c0e214a1bca75_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2ce5755bfe84b7cb37503711fe5585523_big_integer130, + 0x1f028047ad558bd3208927b6644ae2ab9_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2bee37fdda5691f95381d391f9194f3f_big_integer130, + 0xcbba87247175168e5d40dfb270b3427a_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x9bff8375de0961c15151ff7bc1c97589_big_integer130, + 0x4ccf8b525bf3db5773680b031b007029_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2b861409455060d7b6a5e1d5f6c652548_big_integer130, + 0x220fdc8a8d41a6ef2b2c0a1ec4569300b_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x239d57e467841c5247327e4eaa8d001f8_big_integer130, + 0x10dfd3fb5b333abdaf5529542ce52b843_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x8c555f62b02b7f2c94987bd4e0c400a4_big_integer130, + 0x326eaaaa17ba3ffef1b2622038e4277a_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2b7199f47c784514517cb65fbc3681820_big_integer130, + 0x53368a9f4b547e43867c3b0fbbb55ba0_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1c867f8a6c7f7ba691baba7c34c8972c0_big_integer130, + 0x18a36d27f551b90f3b70990c02be4040f_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xe08f32c086174483eb5c0fd194284789_big_integer130, + 0x232cee45b9fafa3dd99b916f0da6b5b9f_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2fa93bf0d7dcdd1d2490b228602c11bc4_big_integer130, + 0x313e75ddb32849fc2f920b7dac0784b8e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xfa23f38fe8ee768390a947885b402fbb_big_integer130, + 0x1ed41f3daece99382858b91eb9341352_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x19046693c278bc3362c21e3369d28337b_big_integer130, + 0x213100709424048752d19aaba00d597d2_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1c50381e9fe77faacbb4625d8a73454a9_big_integer130, + 0x4c27748ded9d69446a518953eda5ea0c_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x19038fc207ecfca9a1a474489cd184a6d_big_integer130, + 0xcf46092c1d5ccf5e41dff63f92c079b9_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x248e3a01cf8cd1147bfa3e5ed0b6e4a41_big_integer130, + 0x7388310b8e62700604f76f2d45f98e52_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x282252b232f1c43f5f014529ee6e3134e_big_integer130, + 0xc324f7242a9d93665f3f3d72bf731500_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x136d5cc4b596ecbacbcbea6385708cea8_big_integer130, + 0x2eceb23b47b8beeb5cd704605f0102a27_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xa41d7a6deba861eb210e76fedf048120_big_integer130, + 0x2b80b6fe5fce48e77bc6529a43670fa89_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0xbe044d7bc860898b4b67f2b0e47b2957_big_integer130, + 0x1385abf4521b731a0ef6585e6fcbe1087_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x15431c536a4b40b7a2def9881b3ed3f65_big_integer130, + 0x2c7a86a326513aec20b909a5b06e1d724_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x22dae6c3c21886db3a222b319df2fcc18_big_integer130, + 0x29874fcdec2d26c29d56990f86e49921e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x1966a81455bb9ae791b06c79361cb04a9_big_integer130, + 0x1ffebba8847893384f651589275aaceb1_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x2744dda0f21e9343d45d80cf7717947c4_big_integer130, + 0x5559c5bfee5bdd51d7695f8be84aa7e0_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x217faf49e078d4f40e8bed99a20a4e3f4_big_integer130, + 0x21e2c14e9f39cfdfc87a7eddbfa8de653_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x571a667c902421037c8d855907b904fd_big_integer130, + 0x873f600a7769bf94aab70506e04d3dee_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48bb_big_integer130, + 0x662ba0a5876b6f5f448563d9194ff704_big_integer130, + 0xc88c6b7366ae5740e6860d5f1c906c00_big_integer130}, + }}; + + bool res = base_operations_test(test_data); +} + +BOOST_AUTO_TEST_CASE(base_ops_even_mod_backend_130) { + using standard_number = nil::crypto3::multiprecision::big_integer<130>; + using test_set = std::array; + using test_data_t = std::array; + constexpr test_data_t test_data = {{ + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xf6ce85e6f42595ffc6fcb50fdc3c9160_big_integer130, + 0x2f04006a57e467c0d45f180da37f9a602_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x6d9d6fed00c44c3eaa674b2b86004106_big_integer130, + 0x1f811a14b9a0ea15d873e532bb3364548_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1f72515d6e96f189c58447d4424da9cf5_big_integer130, + 0x1cb8d94653cfbe79c97028957fd6b37d3_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x4c2fe1ff52320c73ec081a03082dfaf7_big_integer130, + 0xb156219a04e9ac54ba60ce2d79d706eb_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xb6222e4281e459f84c0bd5d77ce3493_big_integer130, + 0x32efd252fec869d766989d986cf31fc2_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1043b1bfcefc15dbe214d68e1061d8645_big_integer130, + 0x43dede03370eb136ecbfa7ec396f0d6d_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x514c264f84ecf1c6e27d7c7c78e6b4a2_big_integer130, + 0x2e9bae255ae7a67003d91da36a76615b8_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2ff3bee3f9723332a002b583133320c93_big_integer130, + 0x25283c79b9b1e505f159c2fe560caffc9_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1446284dc17091cd19bf687b8bcfa8829_big_integer130, + 0xc9e35084ad8092316a3ce0b77b61b998_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xc447bb62c4287d9f409ab060dae3ae92_big_integer130, + 0x22efe8c2ac2188ad9e638d88bb4c754b5_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x313265520502bb48dceca31b17afa02f0_big_integer130, + 0x109c4589d316e8ec374c1671ed51ee911_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2d639758db1ae7106138010f0e573079e_big_integer130, + 0x268a07bc08a254c23c9d71e3303352c1b_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x20898a292ba2360df9b8f42651f342554_big_integer130, + 0x13ea3770bbfab80ba966ddc255a419664_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x879758e1d55d9db504d0122b735255ac_big_integer130, + 0x2287b16011ef333a67b1984c1f8d2aa55_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xeff14b264f211d251f3852bd87ebeae2_big_integer130, + 0x1b5ebb5ef4a47e00cc6a801e5b383b69e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xc2723880d0678f28cd177e8792d5ed2d_big_integer130, + 0x2a790884868d6fc5ef87d73d5b80ff46e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x28a83dffca1513a29489e8835d244d6c5_big_integer130, + 0x15d4f2d8e0949e0aa82f178300c2da5d3_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x25ded52e8495fe687f442ab3579d22963_big_integer130, + 0x2754bb6b9fb861566833f59229e28e0b1_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1ec91d30d83ac012534a9b7561f390be4_big_integer130, + 0x308d7237b1cfb01b5ca9fa894ae3c6c48_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xa8b175970e5266b7575376c254c5ad4_big_integer130, + 0x7bd2cb80c703ab2fe711022067a805f1_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x3068f458ab46abf38f3ab53368fefe1b3_big_integer130, + 0x44da306ee50c5462b9910fcba033ec28_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x7347fcf946c50eb24121d2695a74138_big_integer130, + 0x2cc6b5e5400509f854189e73f9e252331_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x13e5c415b09516c96134af94a77018a61_big_integer130, + 0x4b1f1e72fb30cffbef1bbe88d8d6c25_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x157cedbd74cdd229f3b43e8ab59199092_big_integer130, + 0x144db4073011653f19851e3b7231b8eb0_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xadbf28669d70f71efea08f2b7dcd3929_big_integer130, + 0x2352ba5d47b493e545238dc33f8018555_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x3118fbf21bf8820849498b777ae4369c2_big_integer130, + 0x8e3b38851b87235ab9e415c4f9973cb5_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2d87b88301585c45369f392edad58ebbb_big_integer130, + 0x17b9f6b9257dc97b99d2b43da9a557a76_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2836115435665dffa299a2e1dba2bc02_big_integer130, + 0x181aa88e10efd256cebb4563cbfb06436_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x4010011e94719b273a8f657daecb8154_big_integer130, + 0x16aa3735374efa31f48b9f0b63eca9f1e_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xfd82a47f158cf4773e6177c3b9087ce1_big_integer130, + 0x14686b4ba0accbb93c1fb046e69015338_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x662de620b44deb4469155819375bd2ae_big_integer130, + 0xa1a7f7e023df7165ca899d4366bbc7f5_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xd12ea24d1261fc11787313ef5906f58d_big_integer130, + 0x1c2a2731db836bc6156689cd762da9364_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x26d99465f1a39a64ef21a46d454161f24_big_integer130, + 0x27c3ed62253a11d4dc15d370704817498_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2570d84dca037136829b41df4809e3810_big_integer130, + 0x904beba3c2f174a54466d0bed0fb581b_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1183aa1a729def2d3c7138aae27332134_big_integer130, + 0x208bc970b97cf55008c06fc620d025053_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xd8c70b07b915682a3d9f01ea5c51d34b_big_integer130, + 0x6584051efd453ff081e2189928a9a9c4_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xa14b405e334ae992f920fb9373508696_big_integer130, + 0x104aa2d49412388ae364af54a916fb501_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xe5f1875befdf2bc34c0ef450753afb87_big_integer130, + 0x20f84732af830324ed3949c93704a0ce2_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x22dea1e18f9ec2bdb731db37bcab995ad_big_integer130, + 0x1eb896b7179ab4a60a5cfb051c4f6805b_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x322c6c9cd1a662e0a20738db589ae298_big_integer130, + 0x2872647f25d62d395a64406fdcf9ea04b_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xbfde51cd756d321a25d54a3909890edb_big_integer130, + 0xbd2e612e27d726d58a8be3a643373147_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xb63cc77f71d194b5824946e44fe21831_big_integer130, + 0x556942440de628a1b78d70b8ce61cad5_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x28b231976fbb6484e07f1c36b709811c6_big_integer130, + 0x22da443e75ea4b64e4dd5b0e701948b9a_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2b564befbf84fb99e6d3e96a00e28a111_big_integer130, + 0x2680850f06cffc374d3c98dac2a3522ca_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x19738eb221a6b1cc01159dac5232f5f94_big_integer130, + 0x1888b0fd2c91a44559730f4772021e7ae_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x128cbc897d8a21f0924f0824cc0a2a2af_big_integer130, + 0x1667817727b346d00cfe296b83099260a_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x1176a4938cafb93365f979e0648a9b36d_big_integer130, + 0x1d424e90ed8b1d58a4e5f63a983f66b23_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0xe1d5e0b579b9ffabd79f55a3ad58095a_big_integer130, + 0x28facaa806c45f06b33f90a25a256cb57_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x29492c8c9736c8b22abbe8cc151c0bbe5_big_integer130, + 0x1e2e6cf551c64286202dbc4202eb950b4_big_integer130}, + {0x314107b9ef725f87fa08f9fdadd4f48ba_big_integer130, + 0x2d38c6d88e4be77725f5a337fadacb890_big_integer130, + 0x2b711aed3a4d108fd95d3a8c3338bf713_big_integer130}, + }}; + + bool res = base_operations_test(test_data); +} + +// This one tests 64-bit numbers used in Goldilock fields. +BOOST_AUTO_TEST_CASE(base_ops_even_mod_backend_64) { + using standard_number = nil::crypto3::multiprecision::big_integer<64>; + using test_set = std::array; + using test_data_t = std::array; + constexpr test_data_t test_data = {{ + {0xffffffff00000001_big_integer64, 0x1_big_integer64, 0x2_big_integer64}, + {0xffffffff00000001_big_integer64, 0x7fffffff91725e00_big_integer64, + 0x3fffffffe6869400_big_integer64}, + {0xffffffff00000001_big_integer64, 0x7ffaffff91745e00_big_integer64, + 0x1fafff0fe6869400_big_integer64}, + {0xffffffff00000001_big_integer64, 0x7ffaffff91745e00_big_integer64, + 0x1fafff0fe6869400_big_integer64}, + {0xffffffff00000001_big_integer64, 0x1ffaffff91745e00_big_integer64, + 0xffffffff00000000_big_integer64}, + {0xffffffff00000001_big_integer64, 0x00_big_integer64, 0x1_big_integer64}, + }}; + + bool res = base_operations_test(test_data); +} + +// BOOST_AUTO_TEST_CASE(base_ops_even_mod_backend_17) { +// using standard_number = nil::crypto3::multiprecision::big_integer<17>; +// using test_set = std::array; +// using test_data_t = std::array; +// constexpr test_data_t test_data = {{ +// {0x1e240_big_integer17, 0x3a97_big_integer17, 0xc070_big_integer17}, +// {0x1e240_big_integer17, 0x1dea7_big_integer17, 0x1aaab_big_integer17}, +// {0x1e240_big_integer17, 0x1936f_big_integer17, 0xfb0b_big_integer17}, +// {0x1e240_big_integer17, 0x13067_big_integer17, 0x1566c_big_integer17}, +// {0x1e240_big_integer17, 0x1b960_big_integer17, 0x1773f_big_integer17}, +// {0x1e240_big_integer17, 0x101e4_big_integer17, 0x156ca_big_integer17}, +// {0x1e240_big_integer17, 0x167f3_big_integer17, 0x13c52_big_integer17}, +// {0x1e240_big_integer17, 0xc536_big_integer17, 0x14c8e_big_integer17}, +// {0x1e240_big_integer17, 0xed02_big_integer17, 0x1dafc_big_integer17}, +// {0x1e240_big_integer17, 0x126a6_big_integer17, 0x18a8b_big_integer17}, +// {0x1e240_big_integer17, 0x111ac_big_integer17, 0x94c2_big_integer17}, +// {0x1e240_big_integer17, 0x3a03_big_integer17, 0x89d8_big_integer17}, +// {0x1e240_big_integer17, 0x3add_big_integer17, 0x101ae_big_integer17}, +// {0x1e240_big_integer17, 0x8db4_big_integer17, 0x50e2_big_integer17}, +// {0x1e240_big_integer17, 0x1bab_big_integer17, 0x1d5f6_big_integer17}, +// {0x1e240_big_integer17, 0x144dc_big_integer17, 0x172f8_big_integer17}, +// {0x1e240_big_integer17, 0x1cd30_big_integer17, 0x1a5c_big_integer17}, +// {0x1e240_big_integer17, 0x13c3d_big_integer17, 0x4358_big_integer17}, +// {0x1e240_big_integer17, 0x18d68_big_integer17, 0x1299d_big_integer17}, +// {0x1e240_big_integer17, 0x10153_big_integer17, 0x2c8a_big_integer17}, +// }}; + +// bool res = base_operations_test(test_data); +// } + +// BOOST_AUTO_TEST_CASE(base_ops_odd_mod_backend_17) { +// using Backend = cpp_int_modular_backend<17>; +// using standard_number = boost::multiprecision::number; +// using test_set = std::array; +// using test_data_t = std::array; +// constexpr test_data_t test_data = {{ +// {0x1e241_big_integer17, 0x3a97_big_integer17, 0xc070_big_integer17}, +// {0x1e241_big_integer17, 0x1dea7_big_integer17, 0x1aaab_big_integer17}, +// {0x1e241_big_integer17, 0x1936f_big_integer17, 0xfb0b_big_integer17}, +// {0x1e241_big_integer17, 0x13067_big_integer17, 0x1566c_big_integer17}, +// {0x1e241_big_integer17, 0x1b960_big_integer17, 0x1773f_big_integer17}, +// {0x1e241_big_integer17, 0x101e4_big_integer17, 0x156ca_big_integer17}, +// {0x1e241_big_integer17, 0x167f3_big_integer17, 0x13c52_big_integer17}, +// {0x1e241_big_integer17, 0xc536_big_integer17, 0x14c8e_big_integer17}, +// {0x1e241_big_integer17, 0xed02_big_integer17, 0x1dafc_big_integer17}, +// {0x1e241_big_integer17, 0x126a6_big_integer17, 0x18a8b_big_integer17}, +// {0x1e241_big_integer17, 0x111ac_big_integer17, 0x94c2_big_integer17}, +// {0x1e241_big_integer17, 0x3a03_big_integer17, 0x89d8_big_integer17}, +// {0x1e241_big_integer17, 0x3add_big_integer17, 0x101ae_big_integer17}, +// {0x1e241_big_integer17, 0x8db4_big_integer17, 0x50e2_big_integer17}, +// {0x1e241_big_integer17, 0x1bab_big_integer17, 0x1d5f6_big_integer17}, +// {0x1e241_big_integer17, 0x144dc_big_integer17, 0x172f8_big_integer17}, +// {0x1e241_big_integer17, 0x1cd30_big_integer17, 0x1a5c_big_integer17}, +// {0x1e241_big_integer17, 0x13c3d_big_integer17, 0x4358_big_integer17}, +// {0x1e241_big_integer17, 0x18d68_big_integer17, 0x1299d_big_integer17}, +// {0x1e241_big_integer17, 0x10153_big_integer17, 0x2c8a_big_integer17}, +// }}; + +// bool res = base_operations_test(test_data); +// } + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(runtime_tests) + +BOOST_AUTO_TEST_CASE(secp256k1_incorrect_multiplication) { + using standart_number = nil::crypto3::multiprecision::big_integer<256>; + using modular_number = nil::crypto3::multiprecision::modular_big_integer_rt<256>; + + constexpr standart_number modulus = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_big_integer256; + constexpr standart_number x_standard = + 0xb5d724ce6f44c3c587867bbcb417e9eb6fa05e7e2ef029166568f14eb3161387_big_integer256; + constexpr standart_number res_standard = + 0xad6e1fcc680392abfb075838eafa513811112f14c593e0efacb6e9d0d7770b4_big_integer256; + constexpr modular_number x(x_standard, modulus); + constexpr modular_number res(res_standard, modulus); + BOOST_CHECK_EQUAL(x * x, res); +} + +BOOST_AUTO_TEST_CASE(bad_negation) { + using standart_number = nil::crypto3::multiprecision::big_integer<256>; + using modular_number = nil::crypto3::multiprecision::modular_big_integer_rt<256>; + + constexpr standart_number modulus = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_big_integer256; + constexpr modular_number x(0u, modulus); + constexpr modular_number res = -x; + // TODO + // BOOST_CHECK(res == 0u); + BOOST_CHECK(res == x); + BOOST_CHECK(-res == x); +} + +BOOST_AUTO_TEST_CASE(conversion_to_shorter_number) { + using standart_number = nil::crypto3::multiprecision::big_integer<256>; + using short_number = nil::crypto3::multiprecision::big_integer<128>; + constexpr standart_number x = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_big_integer256; + short_number s = x; + // 2nd half of the number must stay. + BOOST_CHECK_EQUAL(s, 0xfffffffffffffffffffffffefffffc2f_big_integer128); +} + +BOOST_AUTO_TEST_SUITE_END() From 35b945b61e9753384f73bd4809d1286121f99c0f Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Thu, 24 Oct 2024 17:35:05 +0000 Subject: [PATCH 3/9] Fix compilation on gcc --- .../modular/modular_big_integer_impl.hpp | 32 +++++++++---------- .../modular/modular_big_integer_ops.hpp | 16 +++++----- .../modular/modular_big_integer_ops_impl.hpp | 32 ++++++------------- .../big_integer/modular/modular_ops.hpp | 4 +-- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp index b7ba3b2547..d4ee0b85c0 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -52,7 +52,7 @@ namespace nil::crypto3::multiprecision { constexpr bool compare_eq(const modular_big_integer_impl& o) const { // TODO(ioxid): ensure modulus comparison is done in compile time when possible - return modular_ops().compare_eq(o.modular_ops()) && m_base == o.m_base; + return ops().compare_eq(o.ops()) && m_base == o.m_base; } template @@ -64,7 +64,7 @@ namespace nil::crypto3::multiprecision { // cpp_int conversion constexpr typename big_integer_t::cpp_int_type to_cpp_int() const { - return modular_ops().adjusted_regular(m_base).to_cpp_int(); + return ops().adjusted_regular(m_base).to_cpp_int(); } // String conversion @@ -72,7 +72,7 @@ namespace nil::crypto3::multiprecision { inline std::string str(std::streamsize digits = 0, std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { // TODO(ioxid): add module to output - return modular_ops().adjusted_regular(m_base).str(digits, f); + return ops().adjusted_regular(m_base).str(digits, f); } // TODO(ioxid): why is it here @@ -81,7 +81,7 @@ namespace nil::crypto3::multiprecision { inline constexpr void negate() { if (m_base != m_zero) { auto initial_m_base = m_base; - m_base = modular_ops().get_mod(); + m_base = ops().get_mod(); m_base -= initial_m_base; } } @@ -130,10 +130,8 @@ namespace nil::crypto3::multiprecision { constexpr auto& base_data() { return m_base; } constexpr const auto& base_data() const { return m_base; } - constexpr auto& modular_ops() { return m_modular_ops_storage.modular_ops(); } - constexpr const auto& modular_ops() const { - return m_modular_ops_storage.modular_ops(); - } + constexpr auto& ops() { return m_modular_ops_storage.ops(); } + constexpr const auto& ops() const { return m_modular_ops_storage.ops(); } protected: modular_ops_storage_t m_modular_ops_storage; @@ -153,7 +151,7 @@ namespace nil::crypto3::multiprecision { "modulus precision should match used big_integer_t"); result.set_modular_ops(b); - result.modular_ops().adjust_modular(result.base_data(), a); + result.ops().adjust_modular(result.base_data(), a); } } // namespace detail @@ -172,7 +170,7 @@ namespace nil::crypto3::multiprecision { template constexpr explicit modular_big_integer_ct(const big_integer& b) : base_type({}) { - this->modular_ops().adjust_modular(this->m_base, b); + this->ops().adjust_modular(this->m_base, b); } // A method for converting a signed integer to a modular adaptor. We are not supposed to @@ -184,21 +182,21 @@ namespace nil::crypto3::multiprecision { if (b >= 0) { this->m_base = static_cast>(b); } else { - this->m_base = this->modular_ops().get_mod(); + this->m_base = this->ops().get_mod(); // TODO(ioxid): should work not just with limb_type this->m_base -= static_cast(-b); } // This method must be called only for compile time modular params. // modular_ops_storage.set_modular_ops(m); - this->modular_ops().adjust_modular(this->m_base); + this->ops().adjust_modular(this->m_base); } template && std::is_unsigned_v, int> = 0> constexpr modular_big_integer_ct(UI b) : base_type({}) { this->m_base = b; - this->modular_ops().adjust_modular(this->m_base); + this->ops().adjust_modular(this->m_base); } }; @@ -218,26 +216,26 @@ namespace nil::crypto3::multiprecision { if (b >= 0) { this->m_base = b; } else { - this->m_base = this->modular_ops().get_mod(); + this->m_base = this->ops().get_mod(); // TODO(ioxid): should work not just with limb_type this->m_base -= static_cast(-b); } - this->modular_ops().adjust_modular(this->m_base); + this->ops().adjust_modular(this->m_base); } template && std::is_unsigned_v, int> = 0> constexpr modular_big_integer_rt(UI b, const big_integer_t& m) : base_type(m) { this->m_base = b; - this->modular_ops().adjust_modular(this->m_base); + this->ops().adjust_modular(this->m_base); } // TODO(ioxid): move adjust modular to impl constructor template constexpr modular_big_integer_rt(const big_integer& b, const big_integer_t& m) : base_type(m) { - this->modular_ops().adjust_modular(this->m_base, b); + this->ops().adjust_modular(this->m_base, b); } }; } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp index 04c373db1e..06dd2d5a34 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -84,15 +84,15 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator+(const T1& a, const T2& b) noexcept { - BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); + BOOST_ASSERT(a.ops().compare_eq(b.ops())); result_t result{a}; - a.modular_ops().add(result.base_data(), b.base_data()); + a.ops().add(result.base_data(), b.base_data()); return result; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator+=(modular_big_integer_t& a, const T& b) noexcept { - BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); - a.modular_ops().add(a.base_data(), b.base_data()); + BOOST_ASSERT(a.ops().compare_eq(b.ops())); + a.ops().add(a.base_data(), b.base_data()); return a; } CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE @@ -145,15 +145,15 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { - BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); + BOOST_ASSERT(a.ops().compare_eq(b.ops())); result_t result{a}; - a.modular_ops().mul(result.base_data(), b.base_data()); + a.ops().mul(result.base_data(), b.base_data()); return result; } CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator*=(modular_big_integer_t& a, const T& b) noexcept { - BOOST_ASSERT(a.modular_ops().compare_eq(b.modular_ops())); - a.modular_ops().add(a.base_data(), b.base_data()); + BOOST_ASSERT(a.ops().compare_eq(b.ops())); + a.ops().add(a.base_data(), b.base_data()); return a; } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp index 1ecb45f6cb..73df0b164d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp @@ -31,7 +31,7 @@ namespace nil::crypto3::multiprecision::detail { constexpr void subtract(modular_big_integer_impl, modular_ops_t> &result, const modular_big_integer_impl, modular_ops_t> &o) { if (result.base_data() < o.base_data()) { - auto v = result.modular_ops().get_mod(); + auto v = result.ops().get_mod(); v -= o.base_data(); result.base_data() += v; } else { @@ -43,8 +43,8 @@ namespace nil::crypto3::multiprecision::detail { // constexpr void eval_powm(modular_big_integer_impl, modular_ops_t> &result, // const modular_big_integer_impl &b, // const T &e) { - // result.set_modular_ops(b.modular_ops()); - // result.modular_ops().exp(result.base_data(), b.base_data(), e); + // result.set_modular_ops(b.ops()); + // result.ops().exp(result.base_data(), b.base_data(), e); // } // template; // big_integer_t exp; - // e.modular_ops().adjust_regular(exp, e.base_data()); + // e.ops().adjust_regular(exp, e.base_data()); // eval_powm(result, b, exp); // } @@ -67,11 +67,11 @@ namespace nil::crypto3::multiprecision::detail { // using big_integer_t_padded_limbs = // typename modular_ops::policy_type::big_integer_padded_limbs; - // big_integer_t_padded_limbs new_base, res, tmp = input.modular_ops().get_mod(); + // big_integer_t_padded_limbs new_base, res, tmp = input.ops().get_mod(); - // input.modular_ops().adjust_regular(new_base, input.base_data()); + // input.ops().adjust_regular(new_base, input.base_data()); // eval_inverse_mod(res, new_base, tmp); - // assign_components(result, res, input.modular_ops().get_mod()); + // assign_components(result, res, input.ops().get_mod()); // } // template @@ -85,19 +85,7 @@ namespace nil::crypto3::multiprecision::detail { // const modular_big_integer_impl &o) // { // eval_multiply(result.base_data(), o.base_data()); - // eval_redc(result.base_data(), result.modular_ops()); - // } - - // template - // constexpr void eval_divide(modular_big_integer_impl &result, - // const modular_big_integer_impl &o) { - // big_integer_t tmp1, tmp2; - // result.modular_ops().adjust_regular(tmp1, result.base_data()); - // result.modular_ops().adjust_regular(tmp2, o.base_data()); - // eval_divide(tmp1, tmp2); - // result.base_data() = tmp1; - // result.modular_ops().adjust_modular(result.base_data()); - // result.modular_ops().adjust_regular(tmp2, result.base_data()); + // eval_redc(result.base_data(), result.ops()); // } // template @@ -126,7 +114,7 @@ namespace nil::crypto3::multiprecision::detail { // inline void find_modular_pow(modular_big_integer_impl &result, // const modular_big_integer_impl &b, // const big_integer_t &exp) { - // modular_ops mod = b.modular_ops(); + // modular_ops mod = b.ops(); // size_t m_window_bits; // unsigned long cur_exp_index; // size_t exp_bits = eval_msb(exp); @@ -181,7 +169,7 @@ namespace nil::crypto3::multiprecision::detail { // const modular_big_integer_impl &b, // const modular_big_integer_impl &e) { // big_integer_t exp; - // e.modular_ops().adjust_regular(exp, e.base_data()); + // e.ops().adjust_regular(exp, e.base_data()); // find_modular_pow(result, b, exp); // } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp index 107914b32d..7a86ba6d9f 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp @@ -151,7 +151,7 @@ namespace nil::crypto3::multiprecision::detail { constexpr modular_ops_storage_ct() {} - static constexpr const modular_ops_t &modular_ops() { return m_modular_ops; } + static constexpr const modular_ops_t &ops() { return m_modular_ops; } private: static constexpr modular_ops_t m_modular_ops{Modulus}; @@ -165,7 +165,7 @@ namespace nil::crypto3::multiprecision::detail { constexpr modular_ops_storage_rt(const big_integer_t &input) : m_modular_ops(input) {} - constexpr const modular_ops_t &modular_ops() const { return m_modular_ops; } + constexpr const modular_ops_t &ops() const { return m_modular_ops; } private: modular_ops_t m_modular_ops; From 0f3232e366fbf83f89241ba2533105dc01af804b Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Fri, 25 Oct 2024 10:54:27 +0000 Subject: [PATCH 4/9] Refactor modular classes --- .../big_integer/big_integer_impl.hpp | 8 +- .../modular/modular_big_integer_impl.hpp | 105 +-- .../modular/modular_big_integer_ops.hpp | 87 ++- .../big_integer/modular/modular_functions.hpp | 577 --------------- .../big_integer/modular/modular_ops.hpp | 666 +++++++++++++++--- .../modular/modular_ops_storage.hpp | 43 ++ .../test/big_integer_modular.cpp | 8 +- 7 files changed, 730 insertions(+), 764 deletions(-) delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops_storage.hpp diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index 8578b9673a..3b1d280b4a 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -124,11 +124,9 @@ namespace nil::crypto3::multiprecision { // Assignment from other types // TODO(ioxid): forbid signed, implement comparison with signed instead - template - inline constexpr - typename std::enable_if_t /*&& std::is_unsigned_v*/, - big_integer&> - operator=(T val) noexcept { + template /*&& std::is_unsigned_v*/, int> = 0> + inline constexpr big_integer& operator=(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; std::terminate(); diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp index d4ee0b85c0..35707179b2 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -20,10 +20,10 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" #include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" +#include "nil/crypto3/multiprecision/big_integer/modular/modular_ops_storage.hpp" namespace nil::crypto3::multiprecision { namespace detail { - // fixed precision modular big integer which supports compile-time execution template class modular_big_integer_impl { public: @@ -31,7 +31,7 @@ namespace nil::crypto3::multiprecision { constexpr static auto Bits = big_integer_t::Bits; using limb_type = typename big_integer_t::limb_type; using double_limb_type = typename big_integer_t::double_limb_type; - using modular_ops_t = modular_ops; + using modular_ops_t = typename modular_ops_storage_t::modular_ops_t; using unsigned_types = typename big_integer_t::unsigned_types; using signed_types = typename big_integer_t::signed_types; @@ -44,8 +44,11 @@ namespace nil::crypto3::multiprecision { // Constructors protected: - inline constexpr modular_big_integer_impl(modular_ops_storage_t&& modular_ops_storage) - : m_modular_ops_storage(std::move(modular_ops_storage)) {} + inline constexpr modular_big_integer_impl(const big_integer_t x, + modular_ops_storage_t&& modular_ops_storage) + : m_modular_ops_storage(std::move(modular_ops_storage)) { + ops().adjust_modular(m_base, x); + } public: // Comparison @@ -55,16 +58,20 @@ namespace nil::crypto3::multiprecision { return ops().compare_eq(o.ops()) && m_base == o.m_base; } - template - constexpr int compare_eq(const T& val) const { - // TODO(ioxid): should compare adjusted? - return m_base == val; - } + // template + // constexpr bool compare_eq(const T& val) const { + // // TODO(ioxid): should compare adjusted? + // return m_base == val; + // } // cpp_int conversion constexpr typename big_integer_t::cpp_int_type to_cpp_int() const { - return ops().adjusted_regular(m_base).to_cpp_int(); + return remove_modulus().to_cpp_int(); + } + + constexpr big_integer_t remove_modulus() const { + return ops().adjusted_regular(m_base); } // String conversion @@ -72,7 +79,7 @@ namespace nil::crypto3::multiprecision { inline std::string str(std::streamsize digits = 0, std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { // TODO(ioxid): add module to output - return ops().adjusted_regular(m_base).str(digits, f); + return remove_modulus().str(digits, f); } // TODO(ioxid): why is it here @@ -155,69 +162,68 @@ namespace nil::crypto3::multiprecision { } } // namespace detail - template - struct modular_big_integer_ct + template typename modular_ops_template> + struct modular_big_integer_ct_impl : public detail::modular_big_integer_impl< std::decay_t, - detail::modular_ops_storage_ct, modulus>> { + detail::modular_ops_storage_ct> { using base_type = detail::modular_big_integer_impl< std::decay_t, - detail::modular_ops_storage_ct, modulus>>; + detail::modular_ops_storage_ct>; using typename base_type::big_integer_t; - constexpr modular_big_integer_ct() : base_type({}) {} + constexpr modular_big_integer_ct_impl() : base_type({}, {}) {} template - constexpr explicit modular_big_integer_ct(const big_integer& b) : base_type({}) { + constexpr explicit modular_big_integer_ct_impl(const big_integer& b) + : base_type(b, {}) { this->ops().adjust_modular(this->m_base, b); } // A method for converting a signed integer to a modular adaptor. We are not supposed to - // have this, but in the code we already have conversion for an 'int' into modular type. In - // the future we must remove. + // have this, but in the code we already have conversion for an 'int' into modular type. + // In the future we must remove. template && std::is_signed_v, int> = 0> - constexpr modular_big_integer_ct(SI b) : base_type({}) { + constexpr modular_big_integer_ct_impl(SI b) : base_type(0u, {}) { if (b >= 0) { this->m_base = static_cast>(b); } else { this->m_base = this->ops().get_mod(); - // TODO(ioxid): should work not just with limb_type + // TODO(ioxid): should work not just with limb_type, and this does not really + // work (m_base may underflow) this->m_base -= static_cast(-b); } - // This method must be called only for compile time modular params. - // modular_ops_storage.set_modular_ops(m); this->ops().adjust_modular(this->m_base); } template && std::is_unsigned_v, int> = 0> - constexpr modular_big_integer_ct(UI b) : base_type({}) { - this->m_base = b; - this->ops().adjust_modular(this->m_base); - } + constexpr modular_big_integer_ct_impl(UI b) : base_type(b, {}) {} }; - template - struct modular_big_integer_rt + template typename modular_ops_template> + struct modular_big_integer_rt_impl : public detail::modular_big_integer_impl< - big_integer, detail::modular_ops_storage_rt>> { - using base_type = - detail::modular_big_integer_impl, - detail::modular_ops_storage_rt>>; + big_integer, + detail::modular_ops_storage_rt, modular_ops_template>> { + using base_type = detail::modular_big_integer_impl< + big_integer, + detail::modular_ops_storage_rt, modular_ops_template>>; using typename base_type::big_integer_t; - template && std::is_signed_v, int> = 0> - constexpr modular_big_integer_rt(SI b, const big_integer_t& m): base_type(m) { + constexpr modular_big_integer_rt_impl(SI b, const big_integer_t& m) : base_type(0u, m) { if (b >= 0) { this->m_base = b; } else { this->m_base = this->ops().get_mod(); - // TODO(ioxid): should work not just with limb_type + // TODO(ioxid): should work not just with limb_type, and this does not really + // work (m_base may underflow) this->m_base -= static_cast(-b); } @@ -226,16 +232,25 @@ namespace nil::crypto3::multiprecision { template && std::is_unsigned_v, int> = 0> - constexpr modular_big_integer_rt(UI b, const big_integer_t& m) : base_type(m) { - this->m_base = b; - this->ops().adjust_modular(this->m_base); - } + constexpr modular_big_integer_rt_impl(UI b, const big_integer_t& m) : base_type(b, m) {} - // TODO(ioxid): move adjust modular to impl constructor template - constexpr modular_big_integer_rt(const big_integer& b, const big_integer_t& m) - : base_type(m) { - this->ops().adjust_modular(this->m_base, b); - } + constexpr modular_big_integer_rt_impl(const big_integer& b, const big_integer_t& m) + : base_type(b, m) {} }; + + template + using montgomery_modular_big_integer = + modular_big_integer_ct_impl; + template + using montgomery_modular_big_integer_rt = + modular_big_integer_rt_impl; + template + using modular_big_integer = modular_big_integer_ct_impl; + template + using modular_big_integer_rt = modular_big_integer_rt_impl; + template + using auto_modular_big_integer = + std::conditional_t, modular_big_integer>; } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp index 06dd2d5a34..014a271bf8 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -17,42 +17,43 @@ #include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp" namespace nil::crypto3::multiprecision { + namespace detail { + template + constexpr bool is_modular_big_integer_v = false; + + template typename modular_ops_storage_t> + constexpr bool + is_modular_big_integer_v> = + true; + + template typename modular_ops_storage_t> + constexpr bool + is_modular_big_integer_v> = + true; + + template + constexpr bool is_modular_integral_v = + std::is_integral_v || detail::is_big_integer_v || is_modular_big_integer_v; + } // namespace detail // Comparison // TODO(ioxid): comparison with big_integer and basic types (including signed) -#define CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(op) \ - template \ - inline constexpr bool operator op( \ - const detail::modular_big_integer_impl& a, \ - const detail::modular_big_integer_impl& b) noexcept { \ - return a.compare_eq(b) op true; \ +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(op) \ + template && \ + detail::is_modular_big_integer_v, \ + int> = 0> \ + inline constexpr bool operator op(const T1& a, const T2& b) noexcept { \ + return a.compare_eq(b) op true; \ } CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(==) CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL(!=) #undef CRYPTO3_MP_MODULAR_BIG_INTEGER_COMPARISON_IMPL - namespace modular_detail { - template - static constexpr bool always_false = false; - - template - constexpr bool is_modular_big_integer_v = false; - - template - constexpr bool is_modular_big_integer_v> = true; - - template - constexpr bool is_modular_big_integer_v> = true; - - template - constexpr bool is_integral_v = - std::is_integral_v || detail::is_big_integer_v || is_modular_big_integer_v; - } // namespace modular_detail - namespace detail { - template, int> = 0> + template, int> = 0> constexpr std::size_t get_bits() { return T::Bits; } @@ -62,23 +63,22 @@ namespace nil::crypto3::multiprecision { #define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE \ template< \ typename T1, typename T2, \ - std::enable_if_t && modular_detail::is_integral_v && \ - (modular_detail::is_modular_big_integer_v || \ - modular_detail::is_modular_big_integer_v), \ + std::enable_if_t && detail::is_modular_integral_v && \ + (detail::is_modular_big_integer_v || \ + detail::is_modular_big_integer_v), \ int> = 0, \ typename result_t = T1> -#define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ - template && \ - modular_detail::is_integral_v && \ - detail::get_bits() <= modular_big_integer_t::Bits, \ +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ + template && \ + detail::is_modular_integral_v && \ + detail::get_bits() <= modular_big_integer_t::Bits, \ int> = 0> -#define CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE \ - template, \ - int> = 0> +#define CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE \ + template, int> = 0> // Arithmetic operations @@ -180,20 +180,19 @@ namespace nil::crypto3::multiprecision { template inline constexpr std::size_t hash_value( const detail::modular_big_integer_impl& val) noexcept { + // TODO(ioxid): also hash modulus for runtime type return hash_value(val.base_data()); } // IO - template - std::ostream& operator<<( - std::ostream& os, - const detail::modular_big_integer_impl, modular_ops_t>& value) { + template, int> = 0> + std::ostream& operator<<(std::ostream& os, const T& value) { os << value.str(); return os; } -#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE -#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE #undef CRYPTO3_MP_MODULAR_BIG_INTEGER_UNARY_TEMPLATE -} // namespace nil::crypto3::multiprecision \ No newline at end of file +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE +#undef CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_TEMPLATE +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp deleted file mode 100644 index 55fd216f5c..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp +++ /dev/null @@ -1,577 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) 2020 Mikhail Komarov -// Copyright (c) 2020 Ilias Khairullin -// Copyright (c) 2021 Aleksei Moskvin -// Copyright (c) 2024 Andrey Nefedov -// -// Distributed under the Boost Software License, Version 1.0 -// See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt -//---------------------------------------------------------------------------// - -#pragma once - -#include -#include -#include -#include - -#include - -#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/storage.hpp" - -namespace nil::crypto3::multiprecision::detail { - template - constexpr bool check_montgomery_constraints(const big_integer_t &m) { - // Check m % 2 == 0 - return bit_test(m, 0u); - } - - template - struct modular_policy { - using big_integer_t = big_integer; - - constexpr static auto limbs_count = big_integer_t::internal_limb_count; - constexpr static auto limb_bits = big_integer_t::limb_bits; - - constexpr static auto BitsCount_doubled = 2u * Bits; - constexpr static auto BitsCount_doubled_1 = BitsCount_doubled + 1; - constexpr static auto BitsCount_quadruple_1 = 2u * BitsCount_doubled + 1; - constexpr static auto BitsCount_padded_limbs = limbs_count * limb_bits + limb_bits; - constexpr static auto BitsCount_doubled_limbs = 2u * limbs_count * limb_bits; - constexpr static auto BitsCount_doubled_padded_limbs = BitsCount_doubled_limbs + limb_bits; - - using big_integer_doubled = big_integer; - using big_integer_doubled_1 = big_integer; - using big_integer_quadruple_1 = big_integer; - using big_integer_padded_limbs = big_integer; - using big_integer_doubled_limbs = big_integer; - using big_integer_doubled_padded_limbs = big_integer; - }; - - template - class modular_functions { - public: - constexpr static unsigned Bits = big_integer_t::Bits; - using policy_type = modular_policy; - - protected: - using big_integer_doubled_1 = typename policy_type::big_integer_doubled_1; - using big_integer_quadruple_1 = typename policy_type::big_integer_quadruple_1; - using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; - using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; - using big_integer_doubled_padded_limbs = typename policy_type::big_integer_doubled_padded_limbs; - - constexpr static auto limbs_count = policy_type::limbs_count; - constexpr static auto limb_bits = policy_type::limb_bits; - - constexpr void initialize_modulus(const big_integer_t &m) { m_mod = m; } - - constexpr void initialize_barrett_params() { - m_barrett_mu = 0u; - - std::size_t bit = 2u * (1u + msb(m_mod)); - bit_set(m_barrett_mu, bit); - - m_barrett_mu /= m_mod; - } - - constexpr void initialize_montgomery_params() { find_const_variables(); } - - // TODO(ioxid): no exception actually - /* - * Compute -input^-1 mod 2^limb_bits. Throws an exception if input - * is even. If input is odd, then input and 2^n are relatively prime - * and an inverse exists. - */ - constexpr limb_type monty_inverse(const limb_type &a) { - limb_type b = 1; - limb_type r = 0; - - for (size_t i = 0; i != limb_bits; ++i) { - const limb_type bi = b % 2; - r >>= 1; - r += bi << (limb_bits - 1); - - b -= a * bi; - b >>= 1; - } - - // Now invert in addition space - r = (~0u - r) + 1; - - return r; - } - - constexpr void find_const_variables() { - if (check_montgomery_constraints(m_mod)) { - m_montgomery_p_dash = monty_inverse(m_mod.limbs()[0]); - - big_integer_doubled_padded_limbs r; - bit_set(r, 2 * m_mod.size() * limb_bits); - barrett_reduce(r); - - // Here we are intentionally throwing away half of the bits of r, it's - // correct. - m_montgomery_r2 = static_cast(r); - } - - // Compute 2^Bits - Modulus, no matter if modulus is even or odd. - big_integer_padded_limbs compliment = 1u, modulus = m_mod; - compliment <<= Bits; - compliment -= modulus; - m_mod_compliment = compliment; - } - - constexpr void initialize(const big_integer_t &m) { - initialize_modulus(m); - initialize_barrett_params(); - initialize_montgomery_params(); - - m_no_carry_montgomery_mul_allowed = is_applicable_for_no_carry_montgomery_mul(); - } - - public: - constexpr auto &get_mod() { return m_mod; } - constexpr const auto &get_mod_compliment() const { return m_mod_compliment; } - constexpr auto &get_mu() { return m_barrett_mu; } - constexpr auto &get_r2() { return m_montgomery_r2; } - constexpr auto &get_p_dash() { return m_montgomery_p_dash; } - - constexpr const auto &get_mod() const { return m_mod; } - constexpr const auto &get_mu() const { return m_barrett_mu; } - constexpr const auto &get_r2() const { return m_montgomery_r2; } - constexpr auto get_p_dash() const { return m_montgomery_p_dash; } - - constexpr modular_functions(const big_integer_t &m) { initialize(m); } - - template - constexpr void barrett_reduce(big_integer_t1 &result) const { - barrett_reduce(result, result); - } - - // TODO(ioxid): something wrong with parameters here - // - // this overloaded barrett_reduce is intended to work with built-in integral types - // - template - constexpr typename std::enable_if::value && - std::is_unsigned::value>::type - barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { - using input_big_integer_type = - typename std::conditional_t Bits), big_integer_t2, big_integer_t>; - - input_big_integer_type input_adjusted(input); - barrett_reduce(result, input_adjusted); - } - - // - // this overloaded barrett_reduce is intended to work with input big_integer_t2 type of - // less precision than modular big_integer_t to satisfy constraints of core barrett_reduce - // overloading - // - template = 0> - constexpr void barrett_reduce(big_integer_t1 &result, const big_integer_t2 &input) const { - big_integer_t input_adjusted(input); - barrett_reduce(result, input_adjusted); - } - - template= big_integer_t::Bits && - /// to prevent problems with trivial cpp_int - big_integer_t2::Bits >= big_integer_t::Bits, - int> = 0> - constexpr void barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { - // - // to prevent problems with trivial cpp_int - // - big_integer_t2 modulus(m_mod); - - if (msb(input) < 2u * msb(modulus) + 1u) { - big_integer_quadruple_1 t1(input); - - t1 *= m_barrett_mu; - std::size_t shift_size = 2u * (1u + msb(modulus)); - t1 >>= shift_size; - t1 *= modulus; - - // We do NOT allow subtracting a larger size number from a smaller one, - // we need to cast to big_integer_t2 here. - input -= static_cast(t1); - - if (input >= modulus) { - input -= modulus; - } - } else { - input %= modulus; - } - result = input; - } - - template= Bits, int> = 0> - constexpr void montgomery_reduce(big_integer &result) const { - big_integer_doubled_padded_limbs accum(result); - big_integer_doubled_padded_limbs prod; - - for (size_t i = 0; i < m_mod.size(); ++i) { - limb_type limb_accum = accum.limbs()[i]; - double_limb_type mult_res = limb_accum * - /// to prevent overflow error in constexpr - static_cast(m_montgomery_p_dash); - limb_type mult_res_limb = static_cast(mult_res); - - prod = m_mod; - prod *= mult_res_limb; - prod <<= i * limb_bits; - accum += prod; - } - accum >>= m_mod.size() * limb_bits; - // TODO(ioxid): true? - // We cannot use -= for numbers of difference sizes, so resizing - // m_mod. - big_integer_doubled_padded_limbs large_mod = m_mod; - if (accum >= large_mod) { - accum -= large_mod; - } - // Here only the bytes that fit in sizeof result will be copied, and that's - // intentional. - result = accum; - } - - template= Bits2, int> = 0> - constexpr void regular_add(big_integer &result, const big_integer &y) const { - BOOST_ASSERT(result < m_mod && y < m_mod); - - result += y; - // If we overflow and set the carry, we need to subtract the modulus, which is - // the same as adding 2 ^ Bits - Modulus to the remaining part of the number. - // After this we know for sure that the result < Modulus, do not waste time on - // checking again. - if (result.has_carry()) { - result += m_mod_compliment; - result.set_carry(false); - } else if (result >= m_mod) { - result -= m_mod; - } - } - - template= big_integer_t::Bits, int> = 0> - constexpr void regular_mul(big_integer_t1 &result, const big_integer_t2 &y) const { - big_integer_doubled_limbs tmp = result; - tmp *= y; - barrett_reduce(result, tmp); - } - - // Delegates Montgomery multiplication to one of corresponding algorithms. - constexpr void montgomery_mul(big_integer_t &result, const big_integer_t &y) const { - if (m_no_carry_montgomery_mul_allowed) { - montgomery_mul_no_carry_impl(result, y); - } else { - montgomery_mul_CIOS_impl(result, y); - } - } - - // Tests if the faster implementation of Montgomery multiplication is possible. - constexpr bool is_applicable_for_no_carry_montgomery_mul() const { - // Check that - // 1. The most significant bit of modulus is non-zero, meaning we have at least - // 1 additional bit in the number. I.E. if modulus is 255 bits, then we have 1 - // additional "unused" bit in the number. - // 2. Some other bit in modulus is 0. - // 3. The number has < 12 limbs. - return m_mod.internal_limb_count < 12 && (Bits % sizeof(limb_type) != 0) && - m_mod_compliment != big_integer_t(limb_type(1u)); - } - - // Non-carry implementation of Montgomery multiplication. - // Implemented from pseudo-code at - // "https://hackmd.io/@gnark/modular_multiplication". - template - constexpr void montgomery_mul_no_carry_impl(big_integer_t1 &c, const big_integer_t1 &b) const { - BOOST_ASSERT(c < m_mod && b < m_mod); - BOOST_ASSERT(is_applicable_for_no_carry_montgomery_mul()); - - // Obtain number of limbs - constexpr int N = big_integer_t1::internal_limb_count; - - const big_integer_t1 a(c); // Copy the first argument, as the implemented - // algorithm doesn't work in-place. - - // We cannot write directly to 'c', because b may be equal to c, and by changing - // the value of 'c' we will change 'b' as well. - big_integer_t1 result = limb_type(0u); - - // Prepare temporary variables - limb_type A(0u), C(0u); - double_limb_type tmp(0u); - limb_type dummy(0u); - - auto *a_limbs = a.limbs(); - auto *b_limbs = b.limbs(); - auto *result_limbs = result.limbs(); - auto *m_mod_limbs = m_mod.limbs(); - - for (int i = 0; i < N; ++i) { - // "(A,t[0]) := t[0] + a[0]*b[i]" - tmp = a_limbs[0]; - tmp *= b_limbs[i]; - tmp += result_limbs[0]; - dbl_limb_to_limbs(tmp, A, result_limbs[0]); - - // "m := t[0]*q'[0] mod W" - tmp = result_limbs[0]; - // tmp *= q.limbs()[0]; - tmp *= m_montgomery_p_dash; - // tmp = -tmp; - // Note that m is a shorter integer, and we are taking the last bits of tmp. - limb_type m = tmp; - - // "(C,_) := t[0] + m*q[0]" - tmp = m; - tmp *= m_mod_limbs[0]; - tmp += result_limbs[0]; - dbl_limb_to_limbs(tmp, C, dummy); - - // The lower loop is unrolled. We want to do this for every 3, because - // normally mod_size == 4. - std::size_t j = 1; - -#define MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(X) \ - /* "(A,t[X]) := t[X] + a[X]*b[i] + A" */ \ - tmp = a_limbs[X]; \ - tmp *= b_limbs[i]; \ - tmp += result_limbs[X]; \ - tmp += A; \ - dbl_limb_to_limbs(tmp, A, result_limbs[X]); \ - \ - /* "(C,t[X-1]) := t[X] + m*q[X] + C" */ \ - tmp = m; \ - tmp *= m_mod_limbs[X]; \ - tmp += result_limbs[X]; \ - tmp += C; \ - dbl_limb_to_limbs(tmp, C, result_limbs[(X) - 1]); - - for (; j + 5 <= N; j += 5) { - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 3); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 4); - } - - for (; j + 3 <= N; j += 3) { - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); - } - - for (; j < N; ++j) { - MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); - } - - // "t[N-1] = C + A" - result_limbs[N - 1] = C + A; - } - - if (result >= m_mod) { - result -= m_mod; - } - c = result; - } - - // A specialization for non-trivial cpp_int_modular types only. - template - constexpr void montgomery_mul_CIOS_impl(big_integer_t1 &result, const big_integer_t1 &y) const { - BOOST_ASSERT(result < m_mod && y < m_mod); - - big_integer_t A(limb_type(0u)); - const std::size_t mod_size = m_mod.size(); - auto *mod_limbs = m_mod.limbs(); - auto mod_last_limb = static_cast(mod_limbs[0]); - auto y_last_limb = y.limbs()[0]; - auto *y_limbs = y.limbs(); - auto *x_limbs = result.limbs(); - auto *A_limbs = A.limbs(); - limb_type carry = 0; // This is the highest limb of 'A'. - - limb_type x_i = 0; - limb_type A_0 = 0; - limb_type u_i = 0; - - // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right - limb_type k = 0; - limb_type k2 = 0; - - double_limb_type z = 0; - double_limb_type z2 = 0; - - for (std::size_t i = 0; i < mod_size; ++i) { - x_i = x_limbs[i]; - A_0 = A_limbs[0]; - u_i = (A_0 + x_i * y_last_limb) * m_montgomery_p_dash; - - // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right - k = 0; - k2 = 0; - - z = static_cast(y_last_limb) * - static_cast(x_i) + - A_0 + k; - z2 = mod_last_limb * static_cast(u_i) + - static_cast(z) + k2; - k = static_cast(z >> std::numeric_limits::digits); - k2 = static_cast(z2 >> std::numeric_limits::digits); - - std::size_t j = 1; - - // The lower loop is unrolled. We want to do this for every 3, because - // normally mod_size == 4. - double_limb_type t = 0, t2 = 0; - -#define MONTGOMERY_MUL_CIOS_LOOP_BODY(X) \ - t = static_cast(y_limbs[X]) * static_cast(x_i) + \ - A_limbs[X] + k; \ - t2 = static_cast(mod_limbs[X]) * static_cast(u_i) + \ - static_cast(t) + k2; \ - A_limbs[(X) - 1] = static_cast(t2); \ - k = static_cast(t >> std::numeric_limits::digits); \ - k2 = static_cast(t2 >> std::numeric_limits::digits); - - for (; j + 5 <= mod_size; j += 5) { - MONTGOMERY_MUL_CIOS_LOOP_BODY(j); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 3); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 4); - } - - for (; j + 3 <= mod_size; j += 3) { - MONTGOMERY_MUL_CIOS_LOOP_BODY(j); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); - MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); - } - - for (; j < mod_size; ++j) { - MONTGOMERY_MUL_CIOS_LOOP_BODY(j); - } - - double_limb_type tmp = static_cast(carry) + k + k2; - A_limbs[mod_size - 1] = static_cast(tmp); - carry = static_cast(tmp >> std::numeric_limits::digits); - } - - if (carry) { - // The value of A is actually A + 2 ^ Bits, so remove that 2 ^ Bits. - A += m_mod_compliment; - } else if (A >= m_mod) { - A -= m_mod; - } - - result = A; - } - - template= big_integer_t::Bits, int> = 0> - constexpr void regular_exp(big_integer_t1 &result, big_integer_t2 &a, big_integer_t3 exp) const { - BOOST_ASSERT(a < m_mod); - - if (exp == 0u) { - result = 1u; - return; - } - if (m_mod == 1u) { - result = 0u; - return; - } - - big_integer_doubled_limbs base(a), res(1u); - - while (true) { - limb_type lsb = exp.limbs()[0] & 1u; - exp >>= 1u; - if (lsb) { - res *= base; - barrett_reduce(res); - if (is_zero(exp)) { - break; - } - } - base *= base; - barrett_reduce(base); - } - result = res; - } - - template= big_integer_t::Bits, int> = 0> - constexpr void montgomery_exp(big_integer_t1 &result, const big_integer_t2 &a, big_integer_t3 exp) const { - /// input parameter should be lesser than modulus - BOOST_ASSERT(a < m_mod); - - big_integer_doubled_limbs tmp(1u); - tmp *= m_montgomery_r2; - montgomery_reduce(tmp); - big_integer_t R_mod_m(tmp); - - big_integer_t base(a); - - if (exp == 0u) { - result = 1u; - // - // TODO: restructure code - // adjust_modular - // - result *= m_montgomery_r2; - montgomery_reduce(result); - return; - } - if (m_mod == 1u) { - result = 0u; - return; - } - - while (true) { - limb_type lsb = exp.limbs()[0] & 1u; - exp >>= 1u; - if (lsb) { - montgomery_mul(R_mod_m, base); - if (exp == 0u) { - break; - } - } - montgomery_mul(base, base); - } - result = R_mod_m; - } - - constexpr modular_functions &operator=(const big_integer_t &m) { - initialize(m); - - return *this; - } - - protected: - big_integer_t m_mod; - // This is 2^Bits - m_mod, precomputed. - big_integer_t m_mod_compliment; - big_integer_doubled_1 m_barrett_mu; - big_integer_t m_montgomery_r2; - limb_type m_montgomery_p_dash = 0; - - // If set, no-carry optimization is allowed. Must be initialized by function - // is_applicable_for_no_carry_montgomery_mul() after initialization. - bool m_no_carry_montgomery_mul_allowed = false; - }; -} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp index 7a86ba6d9f..59d268302d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp @@ -11,48 +11,213 @@ #pragma once -#include - +#include +#include +#include +#include #include +#include + #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_functions.hpp" +#include "nil/crypto3/multiprecision/big_integer/storage.hpp" namespace nil::crypto3::multiprecision::detail { - // TODO(ioxid): merge with modular_functions - // fixed precision modular params type which supports compile-time execution template - class modular_ops { - private: - using modular_functions_t = modular_functions; + constexpr bool check_montgomery_constraints(const big_integer_t &m) { + // Check m % 2 == 0 + return bit_test(m, 0u); + } + + template + struct modular_policy { + using big_integer_t = big_integer; + + constexpr static auto limbs_count = big_integer_t::internal_limb_count; + constexpr static auto limb_bits = big_integer_t::limb_bits; + + constexpr static auto BitsCount_doubled = 2u * Bits; + constexpr static auto BitsCount_doubled_1 = BitsCount_doubled + 1; + constexpr static auto BitsCount_quadruple_1 = 2u * BitsCount_doubled + 1; + constexpr static auto BitsCount_padded_limbs = limbs_count * limb_bits + limb_bits; + constexpr static auto BitsCount_doubled_limbs = 2u * limbs_count * limb_bits; + constexpr static auto BitsCount_doubled_padded_limbs = BitsCount_doubled_limbs + limb_bits; + + using big_integer_doubled = big_integer; + using big_integer_doubled_1 = big_integer; + using big_integer_quadruple_1 = big_integer; + using big_integer_padded_limbs = big_integer; + using big_integer_doubled_limbs = big_integer; + using big_integer_doubled_padded_limbs = big_integer; + }; + template + class barrett_modular_ops { public: - using policy_type = typename modular_functions_t::policy_type; + constexpr static unsigned Bits = big_integer_t::Bits; + using policy_type = modular_policy; + using big_integer_doubled_1 = typename policy_type::big_integer_doubled_1; + using big_integer_quadruple_1 = typename policy_type::big_integer_quadruple_1; + using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; - // using big_integer_t = typename policy_type::big_integer_t; + using big_integer_doubled_padded_limbs = + typename policy_type::big_integer_doubled_padded_limbs; - constexpr modular_ops(const big_integer_t &m) - : m_modular_functions(m), m_use_montgomery_form(check_montgomery_constraints(m)) {} + constexpr static auto limbs_count = policy_type::limbs_count; + constexpr static auto limb_bits = policy_type::limb_bits; - private: - constexpr auto &get_modular_functions() { return m_modular_functions; } - constexpr const auto &get_modular_functions() const { return m_modular_functions; } + constexpr barrett_modular_ops(const big_integer_t &m) : m_mod(m), m_barrett_mu(0u) { + std::size_t bit = 2u * (1u + msb(m_mod)); + bit_set(m_barrett_mu, bit); - constexpr bool is_montgomery_form() const { return m_use_montgomery_form; } + m_barrett_mu /= m_mod; - public: - constexpr const auto &get_mod() const { return m_modular_functions.get_mod(); } + // Compute 2^Bits - Modulus, no matter if modulus is even or odd. + big_integer_padded_limbs compliment = 1u, modulus = m_mod; + compliment <<= Bits; + compliment -= modulus; + m_mod_compliment = compliment; + } + + constexpr auto &get_mod() { return m_mod; } + constexpr const auto &get_mod_compliment() const { return m_mod_compliment; } + constexpr auto &get_mu() { return m_barrett_mu; } + + constexpr const auto &get_mod() const { return m_mod; } + constexpr const auto &get_mu() const { return m_barrett_mu; } + + template + constexpr void barrett_reduce(big_integer_t1 &result) const { + barrett_reduce(result, result); + } + + // TODO(ioxid): something wrong with parameters here + // + // this overloaded barrett_reduce is intended to work with built-in integral types + // + template + constexpr typename std::enable_if::value && + std::is_unsigned::value>::type + barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { + using input_big_integer_type = + typename std::conditional_t Bits), big_integer_t2, + big_integer_t>; + + input_big_integer_type input_adjusted(input); + barrett_reduce(result, input_adjusted); + } + + // + // this overloaded barrett_reduce is intended to work with input big_integer_t2 type of + // less precision than modular big_integer_t to satisfy constraints of core barrett_reduce + // overloading + // + template = 0> + constexpr void barrett_reduce(big_integer_t1 &result, const big_integer_t2 &input) const { + big_integer_t input_adjusted(input); + barrett_reduce(result, input_adjusted); + } - template - constexpr void reduce(big_integer &result) const { - if (m_use_montgomery_form) { - m_modular_functions.montgomery_reduce(result); + template= big_integer_t::Bits && + /// to prevent problems with trivial cpp_int + big_integer_t2::Bits >= big_integer_t::Bits, + int> = 0> + constexpr void barrett_reduce(big_integer_t1 &result, big_integer_t2 input) const { + // TODO(ioxid): what problems? + // + // to prevent problems with trivial cpp_int + // + big_integer_t2 modulus(m_mod); + + if (msb(input) < 2u * msb(modulus) + 1u) { + big_integer_quadruple_1 t1(input); + + t1 *= m_barrett_mu; + std::size_t shift_size = 2u * (1u + msb(modulus)); + t1 >>= shift_size; + t1 *= modulus; + + // We do NOT allow subtracting a larger size number from a smaller one, + // we need to cast to big_integer_t2 here. + input -= static_cast(t1); + + if (input >= modulus) { + input -= modulus; + } } else { - m_modular_functions.barrett_reduce(result); + input %= modulus; + } + result = input; + } + + template= Bits2, int> = 0> + constexpr void add(big_integer &result, const big_integer &y) const { + BOOST_ASSERT(result < m_mod && y < m_mod); + + result += y; + // If we overflow and set the carry, we need to subtract the modulus, which is + // the same as adding 2 ^ Bits - Modulus to the remaining part of the number. + // After this we know for sure that the result < Modulus, do not waste time on + // checking again. + if (result.has_carry()) { + result += m_mod_compliment; + result.set_carry(false); + } else if (result >= m_mod) { + result -= m_mod; } } + template= big_integer_t::Bits, int> = 0> + constexpr void mul(big_integer_t1 &result, const big_integer_t2 &y) const { + big_integer_doubled_limbs tmp = result; + tmp *= y; + barrett_reduce(result, tmp); + } + + template= big_integer_t::Bits, int> = 0> + constexpr void exp(big_integer_t1 &result, big_integer_t2 &a, big_integer_t3 exp) const { + BOOST_ASSERT(a < m_mod); + + if (exp == 0u) { + result = 1u; + return; + } + if (m_mod == 1u) { + result = 0u; + return; + } + + big_integer_doubled_limbs base(a), res(1u); + + while (true) { + limb_type lsb = exp.limbs()[0] & 1u; + exp >>= 1u; + if (lsb) { + res *= base; + barrett_reduce(res); + if (is_zero(exp)) { + break; + } + } + base *= base; + barrett_reduce(base); + } + result = res; + } + + // Adjust to/from modular form + constexpr void adjust_modular(big_integer_t &result) const { adjust_modular(result, result); } @@ -61,16 +226,7 @@ namespace nil::crypto3::multiprecision::detail { constexpr void adjust_modular(big_integer_t &result, const big_integer &input) const { big_integer_doubled_limbs tmp; - m_modular_functions.barrett_reduce(tmp, input); - if (m_use_montgomery_form) { - // - // to prevent problems with trivial cpp_int - // - big_integer_doubled_limbs r2(m_modular_functions.get_r2()); - - tmp *= r2; - m_modular_functions.montgomery_reduce(tmp); - } + barrett_reduce(tmp, input); result = tmp; } @@ -86,88 +242,420 @@ namespace nil::crypto3::multiprecision::detail { constexpr void adjust_regular(big_integer &result, const big_integer &input) const { result = input; - if (m_use_montgomery_form) { - m_modular_functions.montgomery_reduce(result); + } + + constexpr bool compare_eq(const barrett_modular_ops &o) const { + return get_mod() == o.get_mod(); + } + + protected: + big_integer_t m_mod; + // This is 2^Bits - m_mod, precomputed. + big_integer_t m_mod_compliment; + big_integer_doubled_1 m_barrett_mu; + }; + + template + class montgomery_modular_ops : public barrett_modular_ops { + public: + constexpr static unsigned Bits = big_integer_t::Bits; + using policy_type = modular_policy; + + using big_integer_doubled_1 = typename policy_type::big_integer_doubled_1; + using big_integer_quadruple_1 = typename policy_type::big_integer_quadruple_1; + using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; + using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; + using big_integer_doubled_padded_limbs = + typename policy_type::big_integer_doubled_padded_limbs; + + constexpr static auto limbs_count = policy_type::limbs_count; + constexpr static auto limb_bits = policy_type::limb_bits; + + constexpr montgomery_modular_ops(const big_integer_t &m) + : barrett_modular_ops(m) { + if (!check_montgomery_constraints(m)) { + throw std::invalid_argument("module not usable with montgomery"); } + m_montgomery_p_dash = this->monty_inverse(this->m_mod.limbs()[0]); + + big_integer_doubled_padded_limbs r; + bit_set(r, 2 * this->m_mod.size() * limb_bits); + this->barrett_reduce(r); + + // Here we are intentionally throwing away half of the bits of r, it's + // correct. + m_montgomery_r2 = static_cast(r); + + // Compute 2^Bits - Modulus, no matter if modulus is even or odd. + big_integer_padded_limbs compliment = 1u, modulus = this->m_mod; + compliment <<= Bits; + compliment -= modulus; + this->m_mod_compliment = compliment; + + m_no_carry_montgomery_mul_allowed = is_applicable_for_no_carry_montgomery_mul(); } - template - constexpr void exp(big_integer_t1 &result, const T &exp) const { - exp(result, result, exp); + // TODO(ioxid): no exception actually + /* + * Compute -input^-1 mod 2^limb_bits. Throws an exception if input + * is even. If input is odd, then input and 2^n are relatively prime + * and an inverse exists. + */ + constexpr limb_type monty_inverse(const limb_type &a) { + limb_type b = 1; + limb_type r = 0; + + for (size_t i = 0; i != limb_bits; ++i) { + const limb_type bi = b % 2; + r >>= 1; + r += bi << (limb_bits - 1); + + b -= a * bi; + b >>= 1; + } + + // Now invert in addition space + r = (~0u - r) + 1; + + return r; } - template - constexpr void exp(big_integer_t1 &result, const big_integer_t2 &a, const T &exp) const { - if (m_use_montgomery_form) { - m_modular_functions.montgomery_exp(result, a, exp); - } else { - m_modular_functions.regular_exp(result, a, exp); + constexpr auto &get_r2() { return m_montgomery_r2; } + constexpr auto &get_p_dash() { return m_montgomery_p_dash; } + + constexpr const auto &get_r2() const { return m_montgomery_r2; } + constexpr auto get_p_dash() const { return m_montgomery_p_dash; } + + template= Bits, int> = 0> + constexpr void montgomery_reduce(big_integer &result) const { + big_integer_doubled_padded_limbs accum(result); + big_integer_doubled_padded_limbs prod; + + for (size_t i = 0; i < this->m_mod.size(); ++i) { + limb_type limb_accum = accum.limbs()[i]; + double_limb_type mult_res = limb_accum * + /// to prevent overflow error in constexpr + static_cast(m_montgomery_p_dash); + limb_type mult_res_limb = static_cast(mult_res); + + prod = this->m_mod; + prod *= mult_res_limb; + prod <<= i * limb_bits; + accum += prod; + } + accum >>= this->m_mod.size() * limb_bits; + // TODO(ioxid): true? + // We cannot use -= for numbers of difference sizes, so resizing + // m_mod. + big_integer_doubled_padded_limbs large_mod = this->m_mod; + if (accum >= large_mod) { + accum -= large_mod; } + // Here only the bytes that fit in sizeof result will be copied, and that's + // intentional. + result = accum; } - template - constexpr void mul(big_integer_t1 &result, const big_integer_t1 &y) const { - if (m_use_montgomery_form) { - m_modular_functions.montgomery_mul(result, y); + // Delegates Montgomery multiplication to one of corresponding algorithms. + constexpr void mul(big_integer_t &result, const big_integer_t &y) const { + if (m_no_carry_montgomery_mul_allowed) { + montgomery_mul_no_carry_impl(result, y); } else { - m_modular_functions.regular_mul(result, y); + montgomery_mul_CIOS_impl(result, y); } } - template - constexpr void add(big_integer_t1 &result, const big_integer_t2 &y) const { - m_modular_functions.regular_add(result, y); + private: + // Tests if the faster implementation of Montgomery multiplication is possible. + constexpr bool is_applicable_for_no_carry_montgomery_mul() const { + // Check that + // 1. The most significant bit of modulus is non-zero, meaning we have at least + // 1 additional bit in the number. I.E. if modulus is 255 bits, then we have 1 + // additional "unused" bit in the number. + // 2. Some other bit in modulus is 0. + // 3. The number has < 12 limbs. + return this->m_mod.internal_limb_count < 12 && (Bits % sizeof(limb_type) != 0) && + this->m_mod_compliment != big_integer_t(limb_type(1u)); } + public: + // Non-carry implementation of Montgomery multiplication. + // Implemented from pseudo-code at + // "https://hackmd.io/@gnark/modular_multiplication". template - constexpr operator big_integer_t1() { - return get_mod(); - }; - - constexpr bool compare_eq(const modular_ops &o) const { return get_mod() == o.get_mod(); } + constexpr void montgomery_mul_no_carry_impl(big_integer_t1 &c, + const big_integer_t1 &b) const { + BOOST_ASSERT(c < this->m_mod && b < this->m_mod); + BOOST_ASSERT(is_applicable_for_no_carry_montgomery_mul()); + + // Obtain number of limbs + constexpr int N = big_integer_t1::internal_limb_count; + + const big_integer_t1 a(c); // Copy the first argument, as the implemented + // algorithm doesn't work in-place. + + // We cannot write directly to 'c', because b may be equal to c, and by changing + // the value of 'c' we will change 'b' as well. + big_integer_t1 result = limb_type(0u); + + // Prepare temporary variables + limb_type A(0u), C(0u); + double_limb_type tmp(0u); + limb_type dummy(0u); + + auto *a_limbs = a.limbs(); + auto *b_limbs = b.limbs(); + auto *result_limbs = result.limbs(); + auto *m_mod_limbs = this->m_mod.limbs(); + + for (int i = 0; i < N; ++i) { + // "(A,t[0]) := t[0] + a[0]*b[i]" + tmp = a_limbs[0]; + tmp *= b_limbs[i]; + tmp += result_limbs[0]; + dbl_limb_to_limbs(tmp, A, result_limbs[0]); + + // "m := t[0]*q'[0] mod W" + tmp = result_limbs[0]; + // tmp *= q.limbs()[0]; + tmp *= m_montgomery_p_dash; + // tmp = -tmp; + // Note that m is a shorter integer, and we are taking the last bits of tmp. + limb_type m = tmp; + + // "(C,_) := t[0] + m*q[0]" + tmp = m; + tmp *= m_mod_limbs[0]; + tmp += result_limbs[0]; + dbl_limb_to_limbs(tmp, C, dummy); + + // The lower loop is unrolled. We want to do this for every 3, because + // normally mod_size == 4. + std::size_t j = 1; + +#define MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(X) \ + /* "(A,t[X]) := t[X] + a[X]*b[i] + A" */ \ + tmp = a_limbs[X]; \ + tmp *= b_limbs[i]; \ + tmp += result_limbs[X]; \ + tmp += A; \ + dbl_limb_to_limbs(tmp, A, result_limbs[X]); \ + \ + /* "(C,t[X-1]) := t[X] + m*q[X] + C" */ \ + tmp = m; \ + tmp *= m_mod_limbs[X]; \ + tmp += result_limbs[X]; \ + tmp += C; \ + dbl_limb_to_limbs(tmp, C, result_limbs[(X) - 1]); + + for (; j + 5 <= N; j += 5) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 3); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 4); + } + + for (; j + 3 <= N; j += 3) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 1); + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j + 2); + } + + for (; j < N; ++j) { + MONTGOMERY_MUL_NO_CARRY_LOOP_BODY(j); + } + + // "t[N-1] = C + A" + result_limbs[N - 1] = C + A; + } - constexpr void swap(modular_ops &o) noexcept { - m_modular_functions.swap(o.get_modular_functions()); - bool t = m_use_montgomery_form; - m_use_montgomery_form = o.m_use_montgomery_form; - o.m_use_montgomery_form = t; + if (result >= this->m_mod) { + result -= this->m_mod; + } + c = result; } - // TODO: check function correctness - constexpr friend std::ostream &operator<<(std::ostream &o, const modular_ops &a) { - o << a.get_mod(); - return o; + // A specialization for non-trivial cpp_int_modular types only. + template + constexpr void montgomery_mul_CIOS_impl(big_integer_t1 &result, + const big_integer_t1 &y) const { + BOOST_ASSERT(result < this->m_mod && y < this->m_mod); + + big_integer_t A(limb_type(0u)); + const std::size_t mod_size = this->m_mod.size(); + auto *mod_limbs = this->m_mod.limbs(); + auto mod_last_limb = static_cast(mod_limbs[0]); + auto y_last_limb = y.limbs()[0]; + auto *y_limbs = y.limbs(); + auto *x_limbs = result.limbs(); + auto *A_limbs = A.limbs(); + limb_type carry = 0; // This is the highest limb of 'A'. + + limb_type x_i = 0; + limb_type A_0 = 0; + limb_type u_i = 0; + + // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right + limb_type k = 0; + limb_type k2 = 0; + + double_limb_type z = 0; + double_limb_type z2 = 0; + + for (std::size_t i = 0; i < mod_size; ++i) { + x_i = x_limbs[i]; + A_0 = A_limbs[0]; + u_i = (A_0 + x_i * y_last_limb) * m_montgomery_p_dash; + + // A += x[i] * y + u_i * m followed by a 1 limb-shift to the right + k = 0; + k2 = 0; + + z = static_cast(y_last_limb) * + static_cast(x_i) + + A_0 + k; + z2 = mod_last_limb * static_cast(u_i) + + static_cast(z) + k2; + k = static_cast(z >> std::numeric_limits::digits); + k2 = static_cast(z2 >> std::numeric_limits::digits); + + std::size_t j = 1; + + // The lower loop is unrolled. We want to do this for every 3, because + // normally mod_size == 4. + double_limb_type t = 0, t2 = 0; + +#define MONTGOMERY_MUL_CIOS_LOOP_BODY(X) \ + t = static_cast(y_limbs[X]) * static_cast(x_i) + \ + A_limbs[X] + k; \ + t2 = static_cast(mod_limbs[X]) * static_cast(u_i) + \ + static_cast(t) + k2; \ + A_limbs[(X) - 1] = static_cast(t2); \ + k = static_cast(t >> std::numeric_limits::digits); \ + k2 = static_cast(t2 >> std::numeric_limits::digits); + + for (; j + 5 <= mod_size; j += 5) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 3); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 4); + } + + for (; j + 3 <= mod_size; j += 3) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 1); + MONTGOMERY_MUL_CIOS_LOOP_BODY(j + 2); + } + + for (; j < mod_size; ++j) { + MONTGOMERY_MUL_CIOS_LOOP_BODY(j); + } + + double_limb_type tmp = static_cast(carry) + k + k2; + A_limbs[mod_size - 1] = static_cast(tmp); + carry = static_cast(tmp >> std::numeric_limits::digits); + } + + if (carry) { + // The value of A is actually A + 2 ^ Bits, so remove that 2 ^ Bits. + A += this->m_mod_compliment; + } else if (A >= this->m_mod) { + A -= this->m_mod; + } + + result = A; } - private: - modular_functions_t m_modular_functions; - bool m_use_montgomery_form = false; - }; + template= big_integer_t::Bits, int> = 0> + constexpr void exp(big_integer_t1 &result, const big_integer_t2 &a, + big_integer_t3 exp) const { + /// input parameter should be lesser than modulus + BOOST_ASSERT(a < this->m_mod); - template - class modular_ops_storage_ct { - public: - using modular_ops_t = modular_ops; + big_integer_doubled_limbs tmp(1u); + tmp *= m_montgomery_r2; + montgomery_reduce(tmp); + big_integer_t R_mod_m(tmp); - constexpr modular_ops_storage_ct() {} + big_integer_t base(a); - static constexpr const modular_ops_t &ops() { return m_modular_ops; } + if (exp == 0u) { + result = 1u; + // + // TODO: restructure code + // adjust_modular + // + result *= m_montgomery_r2; + montgomery_reduce(result); + return; + } + if (this->m_mod == 1u) { + result = 0u; + return; + } - private: - static constexpr modular_ops_t m_modular_ops{Modulus}; - }; + while (true) { + limb_type lsb = exp.limbs()[0] & 1u; + exp >>= 1u; + if (lsb) { + montgomery_mul(R_mod_m, base); + if (exp == 0u) { + break; + } + } + montgomery_mul(base, base); + } + result = R_mod_m; + } - // Must be used only in the tests, we must normally use only modular_ops_storage_ct. - template - class modular_ops_storage_rt { - public: - using modular_ops_t = modular_ops; + // Adjust to/from modular form - constexpr modular_ops_storage_rt(const big_integer_t &input) : m_modular_ops(input) {} + constexpr void adjust_modular(big_integer_t &result) const { + adjust_modular(result, result); + } - constexpr const modular_ops_t &ops() const { return m_modular_ops; } + template + constexpr void adjust_modular(big_integer_t &result, + const big_integer &input) const { + big_integer_doubled_limbs tmp; + this->barrett_reduce(tmp, input); + // + // to prevent problems with trivial cpp_int + // + big_integer_doubled_limbs r2(get_r2()); + + tmp *= r2; + montgomery_reduce(tmp); + result = tmp; + } - private: - modular_ops_t m_modular_ops; + [[nodiscard]] constexpr big_integer_t adjusted_regular(const big_integer_t &a) const { + big_integer_t result; + adjust_regular(result, a); + return result; + } + + template= Bits2, int> = 0> + constexpr void adjust_regular(big_integer &result, + const big_integer &input) const { + result = input; + montgomery_reduce(result); + } + + protected: + big_integer_t m_montgomery_r2; + limb_type m_montgomery_p_dash = 0; + + // If set, no-carry optimization is allowed. Must be initialized by function + // is_applicable_for_no_carry_montgomery_mul() after initialization. + bool m_no_carry_montgomery_mul_allowed = false; }; -} // namespace nil::crypto3::multiprecision +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops_storage.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops_storage.hpp new file mode 100644 index 0000000000..e918c35873 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_ops_storage.hpp @@ -0,0 +1,43 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2021 Aleksei Moskvin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include +namespace nil::crypto3::multiprecision::detail { + template typename modular_ops_template> + class modular_ops_storage_ct { + public: + using big_integer_t = std::decay_t; + using modular_ops_t = modular_ops_template; + + constexpr modular_ops_storage_ct() {} + + static constexpr const modular_ops_t &ops() { return m_modular_ops; } + + private: + static constexpr modular_ops_t m_modular_ops{Modulus}; + }; + + template typename modular_ops_template> + class modular_ops_storage_rt { + public: + using big_integer_t = big_integer_t_; + using modular_ops_t = modular_ops_template; + + constexpr modular_ops_storage_rt(const big_integer_t &input) : m_modular_ops(input) {} + + constexpr const modular_ops_t &ops() const { return m_modular_ops; } + + private: + modular_ops_t m_modular_ops; + }; +} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/test/big_integer_modular.cpp b/crypto3/libs/multiprecision/test/big_integer_modular.cpp index 1d6e593a89..68cad63e03 100644 --- a/crypto3/libs/multiprecision/test/big_integer_modular.cpp +++ b/crypto3/libs/multiprecision/test/big_integer_modular.cpp @@ -21,7 +21,7 @@ using namespace nil::crypto3::multiprecision::literals; using namespace boost::multiprecision::literals; constexpr auto mod = 0x123456789ABCDEF_big_integer64; -using modular_big_int = modular_big_integer_ct; +using modular_big_int = montgomery_modular_big_integer; BOOST_AUTO_TEST_SUITE(smoke) @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(construct_constexpr) { BOOST_AUTO_TEST_CASE(construct_modular_ct_trivial_montgomery) { static constexpr auto mod = 0x3_big_integer64; - modular_big_integer_ct a = modular_big_integer_ct(0x5_big_integer64); + auto_modular_big_integer a = auto_modular_big_integer(0x5_big_integer64); BOOST_CHECK_EQUAL(a.str(), "2"); } @@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(construct_modular_rt_trivial_montgomery) { BOOST_AUTO_TEST_CASE(construct_modular_ct_small_montgomery) { static constexpr auto mod = 0x79_big_integer64; - modular_big_integer_ct a = modular_big_integer_ct(0x1234_big_integer64); + auto_modular_big_integer a = auto_modular_big_integer(0x1234_big_integer64); BOOST_CHECK_EQUAL(a.str(), "62"); } @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(construct_modular_rt_small_montgomery) { BOOST_AUTO_TEST_CASE(construct_modular_ct_small) { static constexpr auto mod = 0x78_big_integer64; - modular_big_integer_ct a = modular_big_integer_ct(0x1234_big_integer64); + auto_modular_big_integer a = auto_modular_big_integer(0x1234_big_integer64); BOOST_CHECK_EQUAL(a.str(), "100"); } From c512a6b47f178143a92956c5b5c7ff656cfa0b58 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Fri, 25 Oct 2024 23:19:00 +0000 Subject: [PATCH 5/9] Update operations --- .../big_integer/basic_ops/multiply.hpp | 3 +- .../big_integer/big_integer_impl.hpp | 41 ++--- .../big_integer/big_integer_ops.hpp | 53 +++--- .../modular/modular_big_integer_impl.hpp | 6 +- .../modular/modular_big_integer_ops.hpp | 20 +-- .../big_integer/modular/modular_ops.hpp | 1 + .../big_integer/signed_big_integer.hpp | 156 ++++++++++++++++++ .../libs/multiprecision/test/big_integer.cpp | 10 +- .../test/big_integer_comparision.cpp | 12 +- 9 files changed, 222 insertions(+), 80 deletions(-) create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/signed_big_integer.hpp diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp index c71520e98c..d11313a42a 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp @@ -29,11 +29,12 @@ namespace nil::crypto3::multiprecision::detail { result.from_cpp_int(result_cpp_int); } + // TODO(ioxid): lossy template inline constexpr void multiply(big_integer &result, const big_integer &a) noexcept { auto result_cpp_int = result.to_cpp_int(); - result_cpp_int *= a.to_cpp_int(); + result_cpp_int = static_cast(result_cpp_int * a.to_cpp_int()); result.from_cpp_int(result_cpp_int); } } // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index 3b1d280b4a..e8b44c8259 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -76,12 +76,6 @@ namespace nil::crypto3::multiprecision { p[internal_limb_count - 1] &= upper_limb_mask; } - inline constexpr void do_swap(big_integer& other) noexcept { - for (unsigned i = 0; i < internal_limb_count; ++i) { - boost::multiprecision::std_constexpr::swap(m_data[i], other.m_data[i]); - } - } - // Constructor inline constexpr big_integer() noexcept {} @@ -103,7 +97,6 @@ namespace nil::crypto3::multiprecision { // Copy construction - inline constexpr big_integer(const big_integer& other) noexcept { do_assign(other); } template inline constexpr big_integer(const big_integer& other) noexcept { do_assign(other); @@ -111,10 +104,6 @@ namespace nil::crypto3::multiprecision { // Copy assignment - inline constexpr auto& operator=(const big_integer& other) noexcept { - do_assign(other); - return *this; - } template inline constexpr big_integer& operator=(const big_integer& other) noexcept { do_assign(other); @@ -142,9 +131,6 @@ namespace nil::crypto3::multiprecision { this->from_cpp_int(value); return *this; } - inline constexpr void swap(big_integer& other) noexcept { this->do_swap(other); } - - ~big_integer() = default; inline std::string str(std::streamsize digits = 0, std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { @@ -192,6 +178,19 @@ namespace nil::crypto3::multiprecision { } } + // Comparisions + + inline constexpr int compare(const big_integer& b) const noexcept { + auto pa = limbs(); + auto pb = b.limbs(); + for (int i = size() - 1; i >= 0; --i) { + if (pa[i] != pb[i]) { + return pa[i] > pb[i] ? 1 : -1; + } + } + return 0; + } + private: template && std::is_unsigned_v, int> = 0> @@ -230,18 +229,4 @@ namespace nil::crypto3::multiprecision { // If this value is true, reduction by modulus must happen next. bool m_carry = false; }; - - // Comparisions - - template - inline constexpr int compare(const big_integer& a, const big_integer& b) noexcept { - auto pa = a.limbs(); - auto pb = b.limbs(); - for (int i = a.size() - 1; i >= 0; --i) { - if (pa[i] != pb[i]) { - return pa[i] > pb[i] ? 1 : -1; - } - } - return 0; - } } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp index 15cf6d4659..d15a49132f 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -46,15 +46,14 @@ namespace nil::crypto3::multiprecision { std::enable_if_t && detail::is_integral_v && \ (detail::is_big_integer_v || detail::is_big_integer_v), \ int> = 0, \ - typename result_t = \ + typename largest_t = \ big_integer(), detail::get_bits())>> -#define CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ - template< \ - typename big_integer_t, typename T, \ - std::enable_if_t && detail::is_integral_v && \ - detail::get_bits() <= big_integer_t::Bits, \ - int> = 0> + // TODO(ioxid): somehow error on overflow +#define CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ + template && detail::is_integral_v, \ + int> = 0> #define CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE \ template(), detail::get_bits())>(a, b) op 0; \ +#define CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(op) \ + CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE \ + inline constexpr bool operator op(const T1& a, const T2& b) noexcept { \ + largest_t ap = a; \ + largest_t bp = b; \ + return ap.compare(bp) op 0; \ } CRYPTO3_MP_BIG_INTEGER_IMPL_OPERATOR(<) @@ -82,9 +83,9 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator+(const T1& a, const T2& b) noexcept { - result_t tmp{a}; - detail::add(tmp, b); - return tmp; + big_integer result = a; + detail::add(result, b); + return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator+=(big_integer_t& a, const T& b) noexcept { @@ -107,9 +108,9 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator-(const T1& a, const T2& b) noexcept { - result_t tmp; - detail::subtract(tmp, a, b); - return tmp; + largest_t result; + detail::subtract(result, a, b); + return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator-=(big_integer_t& a, const T& b) { @@ -129,14 +130,14 @@ namespace nil::crypto3::multiprecision { } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE - inline constexpr big_integer_t operator-(const big_integer_t& a) noexcept { + inline constexpr big_integer_t operator-(const big_integer_t& /* unused */) noexcept { // TODO(ioxid): implement? static_assert(detail::always_false, "can't negate unsigned type"); } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { - result_t result{a}; + big_integer() + detail::get_bits()> result = a; detail::multiply(result, b); return result; } @@ -148,7 +149,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator/(const T1& a, const T2& b) noexcept { - result_t result = a; + largest_t result = a; detail::divide(result, b); return result; } @@ -160,7 +161,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator%(const T1& a, const T2& b) noexcept { - result_t result = a; + largest_t result = a; detail::modulus(result, b); return result; } @@ -172,7 +173,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator&(const T1& a, const T2& b) noexcept { - result_t result{a}; + largest_t result = a; detail::bitwise_and(result, b); return result; } @@ -184,7 +185,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator|(const T1& a, const T2& b) noexcept { - result_t result{a}; + largest_t result = a; detail::bitwise_or(result, b); return result; } @@ -196,7 +197,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator^(const T1& a, const T2& b) noexcept { - result_t result{a}; + largest_t result = a; detail::bitwise_xor(result, b); return result; } @@ -215,7 +216,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator<<(const big_integer_t& a, unsigned shift) noexcept { - big_integer_t result{a}; + big_integer_t result = a; detail::left_shift(result, shift); return result; } @@ -228,7 +229,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator>>(const big_integer_t& a, unsigned shift) noexcept { - big_integer_t result{a}; + big_integer_t result = a; detail::right_shift(result, shift); return result; } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp index 35707179b2..2a8500bb8f 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -36,11 +36,6 @@ namespace nil::crypto3::multiprecision { using unsigned_types = typename big_integer_t::unsigned_types; using signed_types = typename big_integer_t::signed_types; - private: - using policy_type = typename modular_ops_t::policy_type; - using big_integer_padded_limbs = typename policy_type::big_integer_padded_limbs; - using big_integer_doubled_limbs = typename policy_type::big_integer_doubled_limbs; - // Constructors protected: @@ -149,6 +144,7 @@ namespace nil::crypto3::multiprecision { ; }; + // TODO(ioxid): remove template constexpr void assign_components( diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp index 014a271bf8..28f1b30612 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -67,7 +67,7 @@ namespace nil::crypto3::multiprecision { (detail::is_modular_big_integer_v || \ detail::is_modular_big_integer_v), \ int> = 0, \ - typename result_t = T1> + typename largest_t = T1> #define CRYPTO3_MP_MODULAR_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE \ templatemonty_inverse(this->m_mod.limbs()[0]); big_integer_doubled_padded_limbs r; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/signed_big_integer.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/signed_big_integer.hpp new file mode 100644 index 0000000000..a22473e8d3 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/signed_big_integer.hpp @@ -0,0 +1,156 @@ +#pragma once + +// IWYU pragma: private; include "nil/crypto3/multiprecision/signed_big_integer/signed_big_integer.hpp" + +#include +#include +#include +#include +#include +#include + +// TODO(ioxid): replace with custom code +#include +#include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" + +namespace nil::crypto3::multiprecision { + template + class signed_big_integer { + public: + constexpr static unsigned Bits = Bits_; + using self_type = signed_big_integer; + + using unsigned_type = big_integer; + + using cpp_int_type = boost::multiprecision::number>; + + // Constructor + + inline constexpr signed_big_integer() noexcept {} + + inline constexpr signed_big_integer(const big_integer b) noexcept : m_unsigned(b) {} + + inline explicit constexpr signed_big_integer(const cpp_int_type& other) { + this->from_cpp_int(other); + } + + template, int> = 0> + inline constexpr signed_big_integer(T val) noexcept : m_unsigned(abs(val)) { + // for ADL + using std::abs; + + if (val < 0) { + negate(); + } + } + + // Copy construction + + template + inline constexpr signed_big_integer(const signed_big_integer& other) noexcept { + do_assign(other); + } + + // Copy assignment + + template + inline constexpr signed_big_integer& operator=( + const signed_big_integer& other) noexcept { + do_assign(other); + return *this; + } + + // Assignment from other types + + template, int> = 0> + inline constexpr signed_big_integer& operator=(T val) noexcept { + using std::abs; + m_unsigned = abs(val); + if (val < 0) { + negate(); + } + return *this; + } + + inline std::string str(std::streamsize digits = 0, + std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { + return (negative() ? std::string("-") : std::string("")) + m_unsigned.str(digits, f); + } + + // cpp_int conversion + + inline constexpr void from_cpp_int(cpp_int_type cppint) { + m_unsigned.from_cpp_int(abs(cppint)); + if (cppint.sign() < 0) { + negate(); + } + } + + inline constexpr cpp_int_type to_cpp_int() const { + return multiplied_by_sign(static_cast(m_unsigned.to_cpp_int())); + } + + // cast to integral types + + template, int> = 0> + explicit inline constexpr operator T() const { + return multiplied_by_sign(static_cast(sans_sign())); + } + + inline constexpr bool negative() const { return bit_test(m_unsigned, Bits - 1); } + + inline constexpr int sign() const noexcept { + return negative() ? -1 : (is_zero(m_unsigned) ? 0 : 1); + } + + explicit inline constexpr operator unsigned_type() const { return m_unsigned; } + + inline constexpr big_integer sans_sign() const { + auto copy = m_unsigned; + bit_unset(copy, Bits - 1); + return copy; + } + + inline constexpr void negate() { + if (is_zero(m_unsigned)) { + return; + } + m_unsigned = ~unsigned_type(0u) - (m_unsigned - 1u); + } + + // Comparision + + template + inline constexpr int compare(const signed_big_integer& other) const noexcept { + if (negative() && !other.negative()) { + return -1; + } + if (!negative() && other.negative()) { + return 1; + } + if (negative() && other.negative()) { + return other.m_unsigned.compare(this->m_unsigned); + } + return this->m_unsigned.compare(other.m_unsigned); + } + + private: + template + inline constexpr void multiply_by_sign(T& a) const { + if (negative()) { + a = -a; + } + } + template + inline constexpr T multiplied_by_sign(const T& a) const { + if (negative()) { + return -a; + } + return a; + } + + signed_big_integer m_unsigned; + }; + +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/test/big_integer.cpp b/crypto3/libs/multiprecision/test/big_integer.cpp index 7f890968ca..d307898537 100644 --- a/crypto3/libs/multiprecision/test/big_integer.cpp +++ b/crypto3/libs/multiprecision/test/big_integer.cpp @@ -14,7 +14,9 @@ CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(60) CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(32) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(33) CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(36) +CRYPTO3_MP_DEFINE_BIG_INTEGER_LITERAL(37) BOOST_MP_DEFINE_SIZED_CPP_INT_LITERAL(60) using namespace nil::crypto3::multiprecision::literals; @@ -100,8 +102,8 @@ BOOST_AUTO_TEST_CASE(simple) { BOOST_CHECK_EQUAL(0x2_big_integer60 + 0x3_big_integer60, 0x5_big_integer60); } -BOOST_AUTO_TEST_CASE(wraps) { - BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 + 0x2_big_integer32, 0x1_big_integer32); +BOOST_AUTO_TEST_CASE(does_not_wrap) { + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 + 0x2_big_integer32, 0x100000001_big_integer33); } BOOST_AUTO_TEST_CASE(multilimb) { @@ -117,11 +119,11 @@ BOOST_AUTO_TEST_CASE(simple) { } BOOST_AUTO_TEST_CASE(wraps) { - BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 * 0x2_big_integer32, 0xFFFFFFFE_big_integer32); + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer32 * 0x2_big_integer32, 0x1fffffffe_big_integer33); } BOOST_AUTO_TEST_CASE(multilimb) { - BOOST_CHECK_EQUAL(0xAFFFFFFFF_big_integer36 * 0x2_big_integer36, 0x5FFFFFFFE_big_integer36); + BOOST_CHECK_EQUAL(0xAFFFFFFFF_big_integer36 * 0x2_big_integer36, 0x15FFFFFFFE_big_integer37); } BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/multiprecision/test/big_integer_comparision.cpp b/crypto3/libs/multiprecision/test/big_integer_comparision.cpp index c04e24737e..4a437763bb 100644 --- a/crypto3/libs/multiprecision/test/big_integer_comparision.cpp +++ b/crypto3/libs/multiprecision/test/big_integer_comparision.cpp @@ -45,12 +45,12 @@ void value_comparisons_tests(const big_integer& a, const big_integer b) == (a_cppint > b_cppint), "g error"); - BOOST_ASSERT_MSG((a >= b) == (a_cppint >= b_cppint), "ge error"); - BOOST_ASSERT_MSG((a == b) == (a_cppint == b_cppint), "e error"); - BOOST_ASSERT_MSG((a < b) == (a_cppint < b_cppint), "l error"); - BOOST_ASSERT_MSG((a <= b) == (a_cppint <= b_cppint), "le error"); - BOOST_ASSERT_MSG((a != b) == (a_cppint != b_cppint), "ne error"); + BOOST_CHECK_EQUAL(a > b, a_cppint > b_cppint); + BOOST_CHECK_EQUAL(a >= b, a_cppint >= b_cppint); + BOOST_CHECK_EQUAL(a == b, a_cppint == b_cppint); + BOOST_CHECK_EQUAL(a < b, a_cppint < b_cppint); + BOOST_CHECK_EQUAL(a <= b, a_cppint <= b_cppint); + BOOST_CHECK_EQUAL(a != b, a_cppint != b_cppint); } template From 43c1a16ebd731528903da1f304850c37743d01c6 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Tue, 29 Oct 2024 11:45:02 +0000 Subject: [PATCH 6/9] Move basic operations to big_integer --- .../multiprecision/big_integer/algorithm.hpp | 3 + .../big_integer/basic_ops/add.hpp | 170 ---- .../big_integer/basic_ops/add_unsigned.hpp | 291 ------- .../big_integer/basic_ops/bitwise.hpp | 350 --------- .../big_integer/basic_ops/divide.hpp | 36 - .../big_integer/basic_ops/multiply.hpp | 40 - .../big_integer/big_integer_impl.hpp | 740 +++++++++++++++++- .../big_integer/big_integer_ops.hpp | 51 +- 8 files changed, 760 insertions(+), 921 deletions(-) create mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/algorithm.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp delete mode 100644 crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/algorithm.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/algorithm.hpp new file mode 100644 index 0000000000..143a433feb --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/algorithm.hpp @@ -0,0 +1,3 @@ +#pragma once + +namespace nil::crypto3::multiprecision {} diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp deleted file mode 100644 index d90e6f8cd3..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost -// Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Comparison operators for big_integer: -// -#pragma once - -// #include -#include - -#include "nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp" -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" -#include "nil/crypto3/multiprecision/big_integer/storage.hpp" - -namespace nil::crypto3::multiprecision::detail { - - template - inline constexpr void add_unsigned(big_integer& result, const big_integer& a, - const limb_type& o) noexcept { - // Addition using modular arithmetic. - // Nothing fancy, just let uintmax_t take the strain: - - double_limb_type carry = o; - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer pa = a.limbs(); - unsigned i = 0; - // Addition with carry until we either run out of digits or carry is zero: - for (; carry && (i < result.size()); ++i) { - carry += static_cast(pa[i]); - pr[i] = static_cast(carry); - carry >>= big_integer::limb_bits; - } - // Just copy any remaining digits: - if (&a != &result) { - boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); - } - if (Bits % big_integer::limb_bits == 0) { - result.set_carry(carry); - } else { - limb_type mask = big_integer::upper_limb_mask; - // If we have set any bit above "Bits", then we have a carry. - if (pr[result.size() - 1] & ~mask) { - pr[result.size() - 1] &= mask; - result.set_carry(true); - } - } - } - - // - // And again to subtract a single limb: caller is responsible to check that a > b and - // the result is non-negative. - // - template - inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, - const limb_type& b) noexcept { - BOOST_ASSERT(a >= b); - - // Subtract one limb. - // Nothing fancy, just let uintmax_t take the strain: - constexpr double_limb_type borrow = - static_cast(big_integer::max_limb_value) + 1; - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer pa = a.limbs(); - if (*pa >= b) { - *pr = *pa - b; - if (&result != &a) { - boost::multiprecision::std_constexpr::copy(pa + 1, pa + a.size(), pr + 1); - } - } else if (result.size() == 1) { - *pr = b - *pa; - } else { - *pr = static_cast((borrow + *pa) - b); - unsigned i = 1; - while (!pa[i]) { - pr[i] = big_integer::max_limb_value; - ++i; - } - pr[i] = pa[i] - 1; - if (&result != &a) { - ++i; - boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); - } - } - } - - // - // Now the actual functions called by the front end, all of which forward to one of the - // above: - // - template - NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - add_unsigned(result, a, b); - } - template - NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, - const big_integer& o) noexcept { - add(result, result, o); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, - const limb_type& o) noexcept { - add_unsigned(result, result, o); - } - template - NIL_CO3_MP_FORCEINLINE constexpr void add(big_integer& result, const big_integer& a, - const limb_type& o) noexcept { - add_unsigned(result, a, o); - } - template - NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, - const limb_type& o) noexcept { - subtract_unsigned(result, result, o); - } - template - NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, - const big_integer& a, - const limb_type& o) noexcept { - subtract_unsigned(result, a, o); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void increment(big_integer& result) noexcept { - if ((result.limbs()[0] < big_integer::max_limb_value)) { - ++result.limbs()[0]; - } else { - add(result, static_cast(1u)); - } - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void decrement(big_integer& result) noexcept { - if (result.limbs()[0]) { - --result.limbs()[0]; - } else { - subtract(result, static_cast(1u)); - } - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, - const big_integer& a, - const big_integer& b) noexcept { - subtract_unsigned(result, a, b); - } - template - NIL_CO3_MP_FORCEINLINE constexpr void subtract(big_integer& result, - const big_integer& o) noexcept { - subtract(result, result, o); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type subtract( - big_integer& result, const big_integer& o) noexcept { - big_integer o_larger = o; - subtract(result, result, o_larger); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr typename std::enable_if<(Bits1 >= Bits2)>::type subtract( - big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - big_integer b_larger = b; - subtract_unsigned(result, a, b_larger); - } -} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp deleted file mode 100644 index 3abef6016d..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/add_unsigned.hpp +++ /dev/null @@ -1,291 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright 2020 Madhur Chauhan. -// Copyright 2020 John Maddock. Distributed under the Boost -// Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt - -#pragma once - -#include - -// #include // for addcarry_limb - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/storage.hpp" - -namespace nil::crypto3::multiprecision::detail { - template - inline constexpr void add_unsigned_constexpr(big_integer& result, - const big_integer& a, - const big_integer& b) noexcept { - using ::boost::multiprecision::std_constexpr::swap; - // - // This is the generic, C++ only version of addition. - // It's also used for all constexpr branches, hence the name. - // Nothing fancy, just let uintmax_t take the strain: - // - double_limb_type carry = 0; - std::size_t s = a.size(); - if (s == 1) { - double_limb_type r = static_cast(*a.limbs()) + - static_cast(*b.limbs()); - double_limb_type mask = big_integer::upper_limb_mask; - if (r & ~mask) { - result = r & mask; - result.set_carry(true); - } else { - result = r; - } - return; - } - - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); - - // First where a and b overlap: - for (std::size_t i = 0; i < s; ++i) { - carry += static_cast(*pa) + static_cast(*pb); -#ifdef _C_RUNTIME_CHECKS - *pr = static_cast(carry & ~static_cast(0)); -#else - *pr = static_cast(carry); -#endif - carry >>= big_integer::limb_bits; - ++pr, ++pa, ++pb; - } - if (Bits % big_integer::limb_bits == 0) { - result.set_carry(carry); - } else { - limb_type mask = big_integer::upper_limb_mask; - // If we have set any bit above "Bits", then we have a carry. - if (result.limbs()[s - 1] & ~mask) { - result.limbs()[s - 1] &= mask; - result.set_carry(true); - } - } - } - - // - // Core subtraction routine for all non-trivial cpp_int's: - // It is the caller's responsibility to make sure that a >= b. - // - template - inline constexpr void subtract_unsigned_constexpr(big_integer& result, - const big_integer& a, - const big_integer& b) noexcept { - BOOST_ASSERT(a >= b); - - // - // This is the generic, C++ only version of subtraction. - // It's also used for all constexpr branches, hence the name. - // Nothing fancy, just let uintmax_t take the strain: - // - std::size_t s = a.size(); - if (s == 1) { - result = *a.limbs() - *b.limbs(); - return; - } - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); - - double_limb_type borrow = 0; - // First where a and b overlap: - for (std::size_t i = 0; i < s; ++i) { - borrow = static_cast(pa[i]) - static_cast(pb[i]) - - borrow; - pr[i] = static_cast(borrow); - borrow = (borrow >> big_integer::limb_bits) & 1u; - } - // if a > b, then borrow must be 0 at the end. - BOOST_ASSERT(0 == borrow); - } - -#ifdef CO3_MP_HAS_IMMINTRIN_H - // - // This is the key addition routine where all the argument types are non-trivial - // cpp_int's: - // - // - // This optimization is limited to: GCC, LLVM, ICC (Intel), MSVC for x86_64 and i386. - // If your architecture and compiler supports ADC intrinsic, please file a bug - // - // As of May, 2020 major compilers don't recognize carry chain though adc - // intrinsics are used to hint compilers to use ADC and still compilers don't - // unroll the loop efficiently (except LLVM) so manual unrolling is done. - // - // Also note that these intrinsics were only introduced by Intel as part of the - // ADX processor extensions, even though the addc instruction has been available - // for basically all x86 processors. That means gcc-9, clang-9, msvc-14.2 and up - // are required to support these intrinsics. - // - template - inline constexpr void add_unsigned(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { -#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION - if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { - add_unsigned_constexpr(result, a, b); - } else -#endif - { - using std::swap; - - // Nothing fancy, just let uintmax_t take the strain: - unsigned s = a.size(); - if (s == 1) { - double_limb_type v = static_cast(*a.limbs()) + - static_cast(*b.limbs()); - double_limb_type mask = big_integer::upper_limb_mask; - if (v & ~mask) { - v &= mask; - result.set_carry(true); - } - result = v; - return; - } - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); - - unsigned char carry = 0; -#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) - // - // Special case for 32-bit limbs on 64-bit architecture - we can process - // 2 limbs with each instruction. - // - std::size_t i = 0; - for (; i + 8 <= s; i += 8) { - carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 0), - *(unsigned long long*)(pb + i + 0), - (unsigned long long*)(pr + i)); - carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 2), - *(unsigned long long*)(pb + i + 2), - (unsigned long long*)(pr + i + 2)); - carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 4), - *(unsigned long long*)(pb + i + 4), - (unsigned long long*)(pr + i + 4)); - carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 6), - *(unsigned long long*)(pb + i + 6), - (unsigned long long*)(pr + i + 6)); - } -#else - for (; i + 4 <= s; i += 4) { - carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 0], pb[i + 0], - pr + i); - carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 1], pb[i + 1], - pr + i + 1); - carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 2], pb[i + 2], - pr + i + 2); - carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 3], pb[i + 3], - pr + i + 3); - } -#endif - for (; i < s; ++i) - carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i], pb[i], pr + i); - - if (Bits % big_integer::limb_bits == 0) - result.set_carry(carry); - else { - limb_type mask = big_integer::upper_limb_mask; - // If we have set any bit above "Bits", then we have a carry. - if (result.limbs()[s - 1] & ~mask) { - result.limbs()[s - 1] &= mask; - result.set_carry(true); - } - } - } - } - - // It is the caller's responsibility to make sure that a > b. - template - inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - BOOST_ASSERT(!eval_lt(a, b)); - -#ifndef TO3_MP_NO_CONSTEXPR_DETECTION - if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { - subtract_unsigned_constexpr(result, a, b); - } else -#endif - { - using std::swap; - - // Nothing fancy, just let uintmax_t take the strain: - std::size_t s = a.size(); - - // - // special cases for small limb counts: - // - if (s == 1) { - result = *a.limbs() - *b.limbs(); - return; - } - // Now that a, b, and result are stable, get pointers to their limbs: - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); - - std::size_t i = 0; - unsigned char borrow = 0; - // First where a and b overlap: -#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) - // - // Special case for 32-bit limbs on 64-bit architecture - we can process - // 2 limbs with each instruction. - // - for (; i + 8 <= m; i += 8) { - borrow = - _subborrow_u64(borrow, *reinterpret_cast(pa + i), - *reinterpret_cast(pb + i), - reinterpret_cast(pr + i)); - borrow = - _subborrow_u64(borrow, *reinterpret_cast(pa + i + 2), - *reinterpret_cast(pb + i + 2), - reinterpret_cast(pr + i + 2)); - borrow = - _subborrow_u64(borrow, *reinterpret_cast(pa + i + 4), - *reinterpret_cast(pb + i + 4), - reinterpret_cast(pr + i + 4)); - borrow = - _subborrow_u64(borrow, *reinterpret_cast(pa + i + 6), - *reinterpret_cast(pb + i + 6), - reinterpret_cast(pr + i + 6)); - } -#else - for (; i + 4 <= m; i += 4) { - borrow = - boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); - borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 1], pb[i + 1], - pr + i + 1); - borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 2], pb[i + 2], - pr + i + 2); - borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 3], pb[i + 3], - pr + i + 3); - } -#endif - for (; i < m; ++i) - borrow = - boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); - - BOOST_ASSERT(0 == borrow); - - } // constepxr. - } - -#else - - template - inline constexpr void add_unsigned(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - add_unsigned_constexpr(result, a, b); - } - - template - inline constexpr void subtract_unsigned(big_integer& result, const big_integer& a, - const big_integer& b) noexcept { - subtract_unsigned_constexpr(result, a, b); - } - -#endif -} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp deleted file mode 100644 index a7b10639cd..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp +++ /dev/null @@ -1,350 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost -// Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Comparison operators for big_integer: -// -#pragma once - -#include -#include -#include - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" -#include "nil/crypto3/multiprecision/big_integer/storage.hpp" - -namespace nil::crypto3::multiprecision::detail { - template - constexpr void bitwise_op(big_integer& result, const big_integer& o, - Op op) noexcept { - // - // Both arguments are unsigned types, very simple case handled as a special case. - // - // First figure out how big the result needs to be and set up some data: - // - unsigned rs = result.size(); - unsigned os = o.size(); - unsigned m(0), x(0); - boost::multiprecision::minmax(rs, os, m, x); - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer po = o.limbs(); - for (unsigned i = rs; i < x; ++i) { - pr[i] = 0; - } - - for (unsigned i = 0; i < os; ++i) { - pr[i] = op(pr[i], po[i]); - } - for (unsigned i = os; i < x; ++i) { - pr[i] = op(pr[i], limb_type(0)); - } - result.normalize(); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(big_integer& result, - const big_integer& o) noexcept { - bitwise_op(result, o, std::bit_and()); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(big_integer& result, - const big_integer& o) noexcept { - bitwise_op(result, o, std::bit_or()); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(big_integer& result, - const big_integer& o) noexcept { - bitwise_op(result, o, std::bit_xor()); - } - // - // Again for operands which are single limbs: - // - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(big_integer& result, - limb_type l) noexcept { - result.limbs()[0] &= l; - result.zero_after(1); - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(big_integer& result, - limb_type l) noexcept { - result.limbs()[0] |= l; - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(big_integer& result, - limb_type l) noexcept { - result.limbs()[0] ^= l; - } - - template - NIL_CO3_MP_FORCEINLINE constexpr void complement(big_integer& result, - const big_integer& o) noexcept { - unsigned os = o.size(); - for (unsigned i = 0; i < os; ++i) { - result.limbs()[i] = ~o.limbs()[i]; - } - result.normalize(); - } - - // Left shift will throw away upper Bits. - // This function must be called only when s % 8 == 0, i.e. we shift bytes. - template - inline void left_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - typename big_integer_t::limb_pointer pr = result.limbs(); - - std::size_t bytes = static_cast(s / CHAR_BIT); - if (s >= Bits) { - // Set result to 0. - result.zero_after(0); - } else { - unsigned char* pc = reinterpret_cast(pr); - std::memmove(pc + bytes, pc, result.size() * sizeof(limb_type) - bytes); - std::memset(pc, 0, bytes); - } - } - - // Left shift will throw away upper Bits. - // This function must be called only when s % limb_bits == 0, i.e. we shift limbs, which - // are normally 64 bit. - template - inline constexpr void left_shift_limb(big_integer& result, double_limb_type s) { - using big_integer_t = big_integer; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); - BOOST_ASSERT(static_cast(s % big_integer_t::limb_bits) == 0); - - typename big_integer_t::limb_pointer pr = result.limbs(); - - if (s >= Bits) { - // Set result to 0. - result.zero_after(0); - } else { - unsigned i = offset; - std::size_t rs = result.size() + offset; - for (; i < result.size(); ++i) { - pr[rs - 1 - i] = pr[result.size() - 1 - i]; - } - for (; i < rs; ++i) { - pr[rs - 1 - i] = 0; - } - } - } - - // Left shift will throw away upper Bits. - template - inline constexpr void left_shift_generic(big_integer& result, double_limb_type s) { - using big_integer_t = big_integer; - - if (s >= Bits) { - // Set result to 0. - result.zero_after(0); - } else { - limb_type offset = static_cast(s / big_integer_t::limb_bits); - limb_type shift = static_cast(s % big_integer_t::limb_bits); - - typename big_integer_t::limb_pointer pr = result.limbs(); - std::size_t i = 0; - std::size_t rs = result.size(); - // This code only works when shift is non-zero, otherwise we invoke undefined - // behaviour! - BOOST_ASSERT(shift); - for (; rs - i >= 2 + offset; ++i) { - pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; - pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (big_integer_t::limb_bits - shift); - } - if (rs - i >= 1 + offset) { - pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; - ++i; - } - for (; i < rs; ++i) { - pr[rs - 1 - i] = 0; - } - } - } - - // Shifting left throws away upper Bits. - template - inline constexpr void left_shift(big_integer& result, double_limb_type s) noexcept { - if (!s) { - return; - } - -#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) - constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; - constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; - - if ((s & limb_shift_mask) == 0) { - left_shift_limb(result, s); - } -#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION - else if ((s & byte_shift_mask) == 0) -#else - else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) -#endif - { - left_shift_byte(result, s); - } -#elif BOOST_ENDIAN_LITTLE_BYTE - constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; - -#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION - if ((s & byte_shift_mask) == 0) -#else - constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; - if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { - left_shift_limb(result, s); - } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) -#endif - { - left_shift_byte(result, s); - } -#else - constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; - - if ((s & limb_shift_mask) == 0) { - left_shift_limb(result, s); - } -#endif - else { - left_shift_generic(result, s); - } - result.normalize(); - } - - template - inline void right_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); - BOOST_ASSERT((s % CHAR_BIT) == 0); - unsigned ors = result.size(); - unsigned rs = ors; - if (offset >= rs) { - result.zero_after(0); - return; - } - rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); - unsigned char* pc = reinterpret_cast(pr); - limb_type shift = static_cast(s / CHAR_BIT); - std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift); - shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT; - if (shift < big_integer_t::limb_bits) { - pr[ors - offset - 1] &= (static_cast(1u) << shift) - 1; - if (!pr[ors - offset - 1] && (rs > 1)) { - --rs; - } - } - // Set zeros after 'rs', alternative to resizing to size 'rs'. - result.zero_after(rs); - } - - template - inline constexpr void right_shift_limb(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); - BOOST_ASSERT((s % big_integer_t::limb_bits) == 0); - unsigned ors = result.size(); - unsigned rs = ors; - if (offset >= rs) { - result.zero_after(0); - return; - } - rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); - unsigned i = 0; - for (; i < rs; ++i) { - pr[i] = pr[i + offset]; - } - // Set zeros after 'rs', alternative to resizing to size 'rs'. - result.zero_after(rs); - } - - template - inline constexpr void right_shift_generic(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - limb_type offset = static_cast(s / big_integer_t::limb_bits); - limb_type shift = static_cast(s % big_integer_t::limb_bits); - unsigned ors = result.size(); - unsigned rs = ors; - - if (offset >= rs) { - result = limb_type(0); - return; - } - rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); - if ((pr[ors - 1] >> shift) == 0) { - if (--rs == 0) { - result = limb_type(0); - return; - } - } - unsigned i = 0; - - // This code only works for non-zero shift, otherwise we invoke undefined behaviour! - BOOST_ASSERT(shift); - for (; i + offset + 1 < ors; ++i) { - pr[i] = pr[i + offset] >> shift; - pr[i] |= pr[i + offset + 1] << (big_integer_t::limb_bits - shift); - } - pr[i] = pr[i + offset] >> shift; - - // We cannot resize any more, so we need to set all the limbs to zero. - result.zero_after(rs); - } - - template - inline constexpr void right_shift(big_integer& result, double_limb_type s) noexcept { - if (!s) { - return; - } - -#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) - constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; - constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; - - if ((s & limb_shift_mask) == 0) right_shift_limb(result, s); -#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION - else if ((s & byte_shift_mask) == 0) -#else - else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) -#endif - { - right_shift_byte(result, s); - } -#elif BOOST_ENDIAN_LITTLE_BYTE - constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; - -#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION - if ((s & byte_shift_mask) == 0) -#else - constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; - if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { - right_shift_limb(result, s); - } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) -#endif - { - right_shift_byte(result, s); - } -#else - constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; - - if ((s & limb_shift_mask) == 0) { - right_shift_limb(result, s); - } -#endif - else { - right_shift_generic(result, s); - } - } -} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp deleted file mode 100644 index 1fd314d72a..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright (c) 2023 Martun Karapetyan -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Contains modulus for big_integer, which uses conversion to cpp_int_backend to -// actually apply the operation. -// - -#pragma once - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" - -// Functions in this file should be called only for creation of montgomery and Barett -// params, no during "normal" execution, so we do NOT care about the execution speed, -// and will just redirect calls to normal boost::cpp_int. - -namespace nil::crypto3::multiprecision::detail { - // Just a call to the upper function, similar to operator*=. - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! - template - inline constexpr void modulus(big_integer &result, - const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int %= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } - - template - inline constexpr void divide(big_integer &result, const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int /= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } -} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp deleted file mode 100644 index d11313a42a..0000000000 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/////////////////////////////////////////////////////////////// -// Copyright (c) 2023 Martun Karapetyan -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt -// -// Contains multiply for big_integer, which does nothing but converts it to -// cpp_int_backend and does the multiplication. -// - -#pragma once - -#include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/storage.hpp" - -// Functions in this file should be called only for creation of montgomery and Barett -// params, calculation of inverse element and montgomery_reduce. Since these functions -// are relatively slow and are not called very often, we will not optimize them. We do -// NOT care about the execution speed, and will just redirect calls to normal -// boost::cpp_int. - -// Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! - -namespace nil::crypto3::multiprecision::detail { - template - inline constexpr void multiply(big_integer &result, const limb_type &b) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int *= b; - result.from_cpp_int(result_cpp_int); - } - - // TODO(ioxid): lossy - template - inline constexpr void multiply(big_integer &result, - const big_integer &a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int = static_cast(result_cpp_int * a.to_cpp_int()); - result.from_cpp_int(result_cpp_int); - } -} // namespace nil::crypto3::multiprecision::detail diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index e8b44c8259..ffb5b0f0da 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ // TODO(ioxid): replace with custom code #include +#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" namespace nil::crypto3::multiprecision { @@ -24,7 +26,7 @@ namespace nil::crypto3::multiprecision { class big_integer { public: constexpr static unsigned Bits = Bits_; - using self_type = big_integer; + using self_type = big_integer; using cpp_int_type = boost::multiprecision::number /*&& std::is_unsigned_v*/, int> = 0> + template /*&& std::is_v*/, int> = 0> inline constexpr big_integer(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; @@ -113,8 +114,7 @@ namespace nil::crypto3::multiprecision { // Assignment from other types // TODO(ioxid): forbid signed, implement comparison with signed instead - template /*&& std::is_unsigned_v*/, int> = 0> + template /*&& std::is_v*/, int> = 0> inline constexpr big_integer& operator=(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; @@ -191,7 +191,732 @@ namespace nil::crypto3::multiprecision { return 0; } - private: + // TODO(ioxid): make them private? + public: + // Arithmetic operations + + // Addition/subtraction + + static inline constexpr void add_constexpr(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + using ::boost::multiprecision::std_constexpr::swap; + // + // This is the generic, C++ only version of addition. + // It's also used for all constexpr branches, hence the name. + // Nothing fancy, just let uintmax_t take the strain: + // + double_limb_type carry = 0; + std::size_t s = a.size(); + if (s == 1) { + double_limb_type r = static_cast(*a.limbs()) + + static_cast(*b.limbs()); + double_limb_type mask = big_integer::upper_limb_mask; + if (r & ~mask) { + result = r & mask; + result.set_carry(true); + } else { + result = r; + } + return; + } + + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + // First where a and b overlap: + for (std::size_t i = 0; i < s; ++i) { + carry += static_cast(*pa) + static_cast(*pb); +#ifdef _C_RUNTIME_CHECKS + *pr = static_cast(carry & ~static_cast(0)); +#else + *pr = static_cast(carry); +#endif + carry >>= big_integer::limb_bits; + ++pr, ++pa, ++pb; + } + if (Bits % big_integer::limb_bits == 0) { + result.set_carry(carry); + } else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (result.limbs()[s - 1] & ~mask) { + result.limbs()[s - 1] &= mask; + result.set_carry(true); + } + } + } + + // + // Core subtraction routine for all non-trivial cpp_int's: + // It is the caller's responsibility to make sure that a >= b. + // + static inline constexpr void subtract_constexpr(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + BOOST_ASSERT(a >= b); + + // + // This is the generic, C++ only version of subtraction. + // It's also used for all constexpr branches, hence the name. + // Nothing fancy, just let uintmax_t take the strain: + // + std::size_t s = a.size(); + if (s == 1) { + result = *a.limbs() - *b.limbs(); + return; + } + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + double_limb_type borrow = 0; + // First where a and b overlap: + for (std::size_t i = 0; i < s; ++i) { + borrow = static_cast(pa[i]) - + static_cast(pb[i]) - borrow; + pr[i] = static_cast(borrow); + borrow = (borrow >> big_integer::limb_bits) & 1u; + } + // if a > b, then borrow must be 0 at the end. + BOOST_ASSERT(0 == borrow); + } + +#ifdef CO3_MP_HAS_IMMINTRIN_H + // + // This is the key addition routine where all the argument types are non-trivial + // cpp_int's: + // + // + // This optimization is limited to: GCC, LLVM, ICC (Intel), MSVC for x86_64 and i386. + // If your architecture and compiler supports ADC intrinsic, please file a bug + // + // As of May, 2020 major compilers don't recognize carry chain though adc + // intrinsics are used to hint compilers to use ADC and still compilers don't + // unroll the loop efficiently (except LLVM) so manual unrolling is done. + // + // Also note that these intrinsics were only introduced by Intel as part of the + // ADX processor extensions, even though the addc instruction has been available + // for basically all x86 processors. That means gcc-9, clang-9, msvc-14.2 and up + // are required to support these intrinsics. + // + static inline constexpr void add(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { +#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { + add_constexpr(result, a, b); + } else +#endif + { + using std::swap; + + // Nothing fancy, just let uintmax_t take the strain: + unsigned s = a.size(); + if (s == 1) { + double_limb_type v = static_cast(*a.limbs()) + + static_cast(*b.limbs()); + double_limb_type mask = big_integer::upper_limb_mask; + if (v & ~mask) { + v &= mask; + result.set_carry(true); + } + result = v; + return; + } + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + unsigned char carry = 0; +#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) + // + // Special case for 32-bit limbs on 64-bit architecture - we can process + // 2 limbs with each instruction. + // + std::size_t i = 0; + for (; i + 8 <= s; i += 8) { + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 0), + *(unsigned long long*)(pb + i + 0), + (unsigned long long*)(pr + i)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 2), + *(unsigned long long*)(pb + i + 2), + (unsigned long long*)(pr + i + 2)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 4), + *(unsigned long long*)(pb + i + 4), + (unsigned long long*)(pr + i + 4)); + carry = _addcarry_u64(carry, *(unsigned long long*)(pa + i + 6), + *(unsigned long long*)(pb + i + 6), + (unsigned long long*)(pr + i + 6)); + } +#else + for (; i + 4 <= s; i += 4) { + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 0], + pb[i + 0], pr + i); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 1], + pb[i + 1], pr + i + 1); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 2], + pb[i + 2], pr + i + 2); + carry = ::boost::multiprecision::detail::addcarry_limb(carry, pa[i + 3], + pb[i + 3], pr + i + 3); + } +#endif + for (; i < s; ++i) + carry = + ::boost::multiprecision::detail::addcarry_limb(carry, pa[i], pb[i], pr + i); + + if (Bits % big_integer::limb_bits == 0) + result.set_carry(carry); + else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (result.limbs()[s - 1] & ~mask) { + result.limbs()[s - 1] &= mask; + result.set_carry(true); + } + } + } + } + + // It is the caller's responsibility to make sure that a > b. + static inline constexpr void subtract(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + BOOST_ASSERT(!eval_lt(a, b)); + +#ifndef TO3_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { + subtract_constexpr(result, a, b); + } else +#endif + { + using std::swap; + + // Nothing fancy, just let uintmax_t take the strain: + std::size_t s = a.size(); + + // + // special cases for small limb counts: + // + if (s == 1) { + result = *a.limbs() - *b.limbs(); + return; + } + // Now that a, b, and result are stable, get pointers to their limbs: + typename big_integer::const_limb_pointer pa = a.limbs(); + typename big_integer::const_limb_pointer pb = b.limbs(); + typename big_integer::limb_pointer pr = result.limbs(); + + std::size_t i = 0; + unsigned char borrow = 0; + // First where a and b overlap: +#if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) + // + // Special case for 32-bit limbs on 64-bit architecture - we can process + // 2 limbs with each instruction. + // + for (; i + 8 <= m; i += 8) { + borrow = + _subborrow_u64(borrow, *reinterpret_cast(pa + i), + *reinterpret_cast(pb + i), + reinterpret_cast(pr + i)); + borrow = _subborrow_u64( + borrow, *reinterpret_cast(pa + i + 2), + *reinterpret_cast(pb + i + 2), + reinterpret_cast(pr + i + 2)); + borrow = _subborrow_u64( + borrow, *reinterpret_cast(pa + i + 4), + *reinterpret_cast(pb + i + 4), + reinterpret_cast(pr + i + 4)); + borrow = _subborrow_u64( + borrow, *reinterpret_cast(pa + i + 6), + *reinterpret_cast(pb + i + 6), + reinterpret_cast(pr + i + 6)); + } +#else + for (; i + 4 <= m; i += 4) { + borrow = + boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 1], + pb[i + 1], pr + i + 1); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 2], + pb[i + 2], pr + i + 2); + borrow = boost::multiprecision::detail::subborrow_limb(borrow, pa[i + 3], + pb[i + 3], pr + i + 3); + } +#endif + for (; i < m; ++i) + borrow = + boost::multiprecision::detail::subborrow_limb(borrow, pa[i], pb[i], pr + i); + + BOOST_ASSERT(0 == borrow); + + } // constepxr. + } + +#else + + static inline constexpr void add(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + add_constexpr(result, a, b); + } + + static inline constexpr void subtract(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + subtract_constexpr(result, a, b); + } + +#endif + + static inline constexpr void add(big_integer& result, const big_integer& a, + const limb_type& o) noexcept { + // Addition using modular arithmetic. + // Nothing fancy, just let uintmax_t take the strain: + + double_limb_type carry = o; + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer pa = a.limbs(); + unsigned i = 0; + // Addition with carry until we either run out of digits or carry is zero: + for (; carry && (i < result.size()); ++i) { + carry += static_cast(pa[i]); + pr[i] = static_cast(carry); + carry >>= big_integer::limb_bits; + } + // Just copy any remaining digits: + if (&a != &result) { + boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); + } + if (Bits % big_integer::limb_bits == 0) { + result.set_carry(carry); + } else { + limb_type mask = big_integer::upper_limb_mask; + // If we have set any bit above "Bits", then we have a carry. + if (pr[result.size() - 1] & ~mask) { + pr[result.size() - 1] &= mask; + result.set_carry(true); + } + } + } + + // + // And again to subtract a single limb: caller is responsible to check that a > b and + // the result is non-negative. + // + static inline constexpr void subtract(big_integer& result, const big_integer& a, + const limb_type& b) noexcept { + BOOST_ASSERT(a >= b); + + // Subtract one limb. + // Nothing fancy, just let uintmax_t take the strain: + constexpr double_limb_type borrow = + static_cast(big_integer::max_limb_value) + 1; + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer pa = a.limbs(); + if (*pa >= b) { + *pr = *pa - b; + if (&result != &a) { + boost::multiprecision::std_constexpr::copy(pa + 1, pa + a.size(), pr + 1); + } + } else if (result.size() == 1) { + *pr = b - *pa; + } else { + *pr = static_cast((borrow + *pa) - b); + unsigned i = 1; + while (!pa[i]) { + pr[i] = big_integer::max_limb_value; + ++i; + } + pr[i] = pa[i] - 1; + if (&result != &a) { + ++i; + boost::multiprecision::std_constexpr::copy(pa + i, pa + a.size(), pr + i); + } + } + } + + NIL_CO3_MP_FORCEINLINE constexpr void increment() noexcept { + if (limbs()[0] < big_integer::max_limb_value) { + ++limbs()[0]; + } else { + add(*this, *this, static_cast(1u)); + } + } + + NIL_CO3_MP_FORCEINLINE constexpr void decrement() noexcept { + if (limbs()[0]) { + --limbs()[0]; + } else { + subtract(*this, *this, static_cast(1u)); + } + } + + // Bitwise + + template + static constexpr void bitwise_op(big_integer& result, const big_integer& o, + Op op) noexcept { + // + // Both arguments are unsigned types, very simple case handled as a special case. + // + // First figure out how big the result needs to be and set up some data: + // + unsigned rs = result.size(); + unsigned os = o.size(); + unsigned m(0), x(0); + boost::multiprecision::minmax(rs, os, m, x); + typename big_integer::limb_pointer pr = result.limbs(); + typename big_integer::const_limb_pointer po = o.limbs(); + for (unsigned i = rs; i < x; ++i) { + pr[i] = 0; + } + + for (unsigned i = 0; i < os; ++i) { + pr[i] = op(pr[i], po[i]); + } + for (unsigned i = os; i < x; ++i) { + pr[i] = op(pr[i], limb_type(0)); + } + result.normalize(); + } + + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_and(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_and()); + } + + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_or(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_or()); + } + + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_xor(big_integer& result, + const big_integer& o) noexcept { + bitwise_op(result, o, std::bit_xor()); + } + // + // Again for operands which are single limbs: + // + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_and(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] &= l; + result.zero_after(1); + } + + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_or(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] |= l; + } + + NIL_CO3_MP_FORCEINLINE static constexpr void bitwise_xor(big_integer& result, + limb_type l) noexcept { + result.limbs()[0] ^= l; + } + + NIL_CO3_MP_FORCEINLINE static constexpr void complement(big_integer& result, + const big_integer& o) noexcept { + unsigned os = o.size(); + for (unsigned i = 0; i < os; ++i) { + result.limbs()[i] = ~o.limbs()[i]; + } + result.normalize(); + } + + // Left shift will throw away upper Bits. + // This function must be called only when s % 8 == 0, i.e. we shift bytes. + static inline void left_shift_byte(big_integer& result, double_limb_type s) { + typedef big_integer big_integer_t; + + typename big_integer_t::limb_pointer pr = result.limbs(); + + std::size_t bytes = static_cast(s / CHAR_BIT); + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + unsigned char* pc = reinterpret_cast(pr); + std::memmove(pc + bytes, pc, result.size() * sizeof(limb_type) - bytes); + std::memset(pc, 0, bytes); + } + } + + // Left shift will throw away upper Bits. + // This function must be called only when s % limb_bits == 0, i.e. we shift limbs, which + // are normally 64 bit. + static inline constexpr void left_shift_limb(big_integer& result, double_limb_type s) { + using big_integer_t = big_integer; + + limb_type offset = static_cast(s / big_integer_t::limb_bits); + BOOST_ASSERT(static_cast(s % big_integer_t::limb_bits) == 0); + + typename big_integer_t::limb_pointer pr = result.limbs(); + + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + unsigned i = offset; + std::size_t rs = result.size() + offset; + for (; i < result.size(); ++i) { + pr[rs - 1 - i] = pr[result.size() - 1 - i]; + } + for (; i < rs; ++i) { + pr[rs - 1 - i] = 0; + } + } + } + + // Left shift will throw away upper Bits. + static inline constexpr void left_shift_generic(big_integer& result, double_limb_type s) { + using big_integer_t = big_integer; + + if (s >= Bits) { + // Set result to 0. + result.zero_after(0); + } else { + limb_type offset = static_cast(s / big_integer_t::limb_bits); + limb_type shift = static_cast(s % big_integer_t::limb_bits); + + typename big_integer_t::limb_pointer pr = result.limbs(); + std::size_t i = 0; + std::size_t rs = result.size(); + // This code only works when shift is non-zero, otherwise we invoke undefined + // behaviour! + BOOST_ASSERT(shift); + for (; rs - i >= 2 + offset; ++i) { + pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; + pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (big_integer_t::limb_bits - shift); + } + if (rs - i >= 1 + offset) { + pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; + ++i; + } + for (; i < rs; ++i) { + pr[rs - 1 - i] = 0; + } + } + } + + // Shifting left throws away upper Bits. + static inline constexpr void left_shift(big_integer& result, double_limb_type s) noexcept { + if (!s) { + return; + } + +#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + + if ((s & limb_shift_mask) == 0) { + left_shift_limb(result, s); + } +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + else if ((s & byte_shift_mask) == 0) +#else + else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + left_shift_byte(result, s); + } +#elif BOOST_ENDIAN_LITTLE_BYTE + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + if ((s & byte_shift_mask) == 0) +#else + constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; + if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { + left_shift_limb(result, s); + } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + left_shift_byte(result, s); + } +#else + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + + if ((s & limb_shift_mask) == 0) { + left_shift_limb(result, s); + } +#endif + else { + left_shift_generic(result, s); + } + result.normalize(); + } + + static inline void right_shift_byte(big_integer& result, double_limb_type s) { + typedef big_integer big_integer_t; + + limb_type offset = static_cast(s / big_integer_t::limb_bits); + BOOST_ASSERT((s % CHAR_BIT) == 0); + unsigned ors = result.size(); + unsigned rs = ors; + if (offset >= rs) { + result.zero_after(0); + return; + } + rs -= offset; + typename big_integer_t::limb_pointer pr = result.limbs(); + unsigned char* pc = reinterpret_cast(pr); + limb_type shift = static_cast(s / CHAR_BIT); + std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift); + shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT; + if (shift < big_integer_t::limb_bits) { + pr[ors - offset - 1] &= (static_cast(1u) << shift) - 1; + if (!pr[ors - offset - 1] && (rs > 1)) { + --rs; + } + } + // Set zeros after 'rs', alternative to resizing to size 'rs'. + result.zero_after(rs); + } + + static inline constexpr void right_shift_limb(big_integer& result, double_limb_type s) { + typedef big_integer big_integer_t; + + limb_type offset = static_cast(s / big_integer_t::limb_bits); + BOOST_ASSERT((s % big_integer_t::limb_bits) == 0); + unsigned ors = result.size(); + unsigned rs = ors; + if (offset >= rs) { + result.zero_after(0); + return; + } + rs -= offset; + typename big_integer_t::limb_pointer pr = result.limbs(); + unsigned i = 0; + for (; i < rs; ++i) { + pr[i] = pr[i + offset]; + } + // Set zeros after 'rs', alternative to resizing to size 'rs'. + result.zero_after(rs); + } + + static inline constexpr void right_shift_generic(big_integer& result, double_limb_type s) { + typedef big_integer big_integer_t; + limb_type offset = static_cast(s / big_integer_t::limb_bits); + limb_type shift = static_cast(s % big_integer_t::limb_bits); + unsigned ors = result.size(); + unsigned rs = ors; + + if (offset >= rs) { + result = limb_type(0); + return; + } + rs -= offset; + typename big_integer_t::limb_pointer pr = result.limbs(); + if ((pr[ors - 1] >> shift) == 0) { + if (--rs == 0) { + result = limb_type(0); + return; + } + } + unsigned i = 0; + + // This code only works for non-zero shift, otherwise we invoke undefined behaviour! + BOOST_ASSERT(shift); + for (; i + offset + 1 < ors; ++i) { + pr[i] = pr[i + offset] >> shift; + pr[i] |= pr[i + offset + 1] << (big_integer_t::limb_bits - shift); + } + pr[i] = pr[i + offset] >> shift; + + // We cannot resize any more, so we need to set all the limbs to zero. + result.zero_after(rs); + } + + static inline constexpr void right_shift(big_integer& result, double_limb_type s) noexcept { + if (!s) { + return; + } + +#if BOOST_ENDIAN_LITTLE_BYTE && defined(CRYPTO3_MP_USE_LIMB_SHIFT) + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + + if ((s & limb_shift_mask) == 0) right_shift_limb(result, s); +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + else if ((s & byte_shift_mask) == 0) +#else + else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + right_shift_byte(result, s); + } +#elif BOOST_ENDIAN_LITTLE_BYTE + constexpr const limb_type byte_shift_mask = CHAR_BIT - 1; + +#ifdef BOOST_MP_NO_CONSTEXPR_DETECTION + if ((s & byte_shift_mask) == 0) +#else + constexpr limb_type limb_shift_mask = big_integer::limb_bits - 1; + if (BOOST_MP_IS_CONST_EVALUATED(s) && ((s & limb_shift_mask) == 0)) { + right_shift_limb(result, s); + } else if (((s & byte_shift_mask) == 0) && !BOOST_MP_IS_CONST_EVALUATED(s)) +#endif + { + right_shift_byte(result, s); + } +#else + constexpr const limb_type limb_shift_mask = big_integer::limb_bits - 1; + + if ((s & limb_shift_mask) == 0) { + right_shift_limb(result, s); + } +#endif + else { + right_shift_generic(result, s); + } + } + + // Modulus/divide + + // These should be called only for creation of Montgomery and Barett + // params, no during "normal" execution, so we do NOT care about the execution speed, + // and will just redirect calls to normal boost::cpp_int. + + // Just a call to the upper function, similar to operator*=. + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! + template + static inline constexpr void modulus(big_integer& result, + const big_integer& a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int %= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } + + template + static inline constexpr void divide(big_integer& result, + const big_integer& a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int /= a.to_cpp_int(); + result.from_cpp_int(result_cpp_int); + } + + // Multiplication + + // These should be called only for creation of Montgomery and Barett + // params, calculation of inverse element and montgomery_reduce. Since these functions + // are relatively slow and are not called very often, we will not optimize them. We do + // NOT care about the execution speed, and will just redirect calls to normal + // boost::cpp_int. + + // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! + + static inline constexpr void multiply(big_integer& result, const limb_type& b) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int *= b; + result.from_cpp_int(result_cpp_int); + } + + template + static inline constexpr void multiply(big_integer& result, + const big_integer& a) noexcept { + auto result_cpp_int = result.to_cpp_int(); + result_cpp_int = static_cast(result_cpp_int * a.to_cpp_int()); + result.from_cpp_int(result_cpp_int); + } + + // Assignment + template && std::is_unsigned_v, int> = 0> inline constexpr void do_assign_integral(T a) noexcept { @@ -222,6 +947,9 @@ namespace nil::crypto3::multiprecision { this->normalize(); } + private: + // Data + // m_data[0] contains the lowest bits. std::array m_data{0}; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp index d15a49132f..c8b580d530 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -8,11 +8,6 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/basic_ops/add.hpp" -#include "nil/crypto3/multiprecision/big_integer/basic_ops/bitwise.hpp" -#include "nil/crypto3/multiprecision/big_integer/basic_ops/divide.hpp" -#include "nil/crypto3/multiprecision/big_integer/basic_ops/multiply.hpp" - #include "nil/crypto3/multiprecision/big_integer/ops/import_export.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/big_integer/ops/misc.hpp" // IWYU pragma: export @@ -84,17 +79,17 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator+(const T1& a, const T2& b) noexcept { big_integer result = a; - detail::add(result, b); + decltype(result)::add(result, result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator+=(big_integer_t& a, const T& b) noexcept { - detail::add(a, b); + big_integer_t::add(a, a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator++(big_integer_t& a) noexcept { - detail::increment(a); + a.increment(); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE @@ -109,17 +104,17 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator-(const T1& a, const T2& b) noexcept { largest_t result; - detail::subtract(result, a, b); + T1::subtract(result, a, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator-=(big_integer_t& a, const T& b) { - detail::subtract(a, b); + big_integer_t::subtract(a, a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator--(big_integer_t& a) noexcept { - detail::decrement(a); + a.decrement(); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE @@ -138,105 +133,105 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { big_integer() + detail::get_bits()> result = a; - detail::multiply(result, b); + decltype(result)::multiply(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator*=(big_integer_t& a, const T& b) noexcept { - detail::multiply(a, b); + big_integer_t::multiply(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator/(const T1& a, const T2& b) noexcept { largest_t result = a; - detail::divide(result, b); + largest_t::divide(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator/=(big_integer_t& a, const T& b) noexcept { - detail::divide(a, b); + big_integer_t::divide(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator%(const T1& a, const T2& b) noexcept { largest_t result = a; - detail::modulus(result, b); + largest_t::modulus(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator%=(big_integer_t& a, const T& b) { - detail::modulus(a, b); + big_integer_t::modulus(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator&(const T1& a, const T2& b) noexcept { largest_t result = a; - detail::bitwise_and(result, b); + T1::bitwise_and(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator&=(big_integer_t& a, const T& b) { - detail::bitwise_and(a, b); + big_integer_t::bitwise_and(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator|(const T1& a, const T2& b) noexcept { largest_t result = a; - detail::bitwise_or(result, b); + T1::bitwise_or(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator|=(big_integer_t& a, const T& b) { - detail::bitwise_or(a, b); + big_integer_t::bitwise_or(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator^(const T1& a, const T2& b) noexcept { largest_t result = a; - detail::bitwise_xor(result, b); + T1::bitwise_xor(result, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator^=(big_integer_t& a, const T& b) { - detail::bitwise_or(a, b); + big_integer_t::bitwise_or(a, b); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator~(const big_integer_t& a) noexcept { big_integer_t result; - detail::complement(result, a); + big_integer_t::complement(result, a); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator<<(const big_integer_t& a, unsigned shift) noexcept { big_integer_t result = a; - detail::left_shift(result, shift); + big_integer_t::left_shift(result, shift); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator<<=(big_integer_t& a, unsigned shift) noexcept { // TODO(ioxid): check - detail::left_shift(a, shift); + big_integer_t::left_shift(a, shift); return a; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto operator>>(const big_integer_t& a, unsigned shift) noexcept { big_integer_t result = a; - detail::right_shift(result, shift); + big_integer_t::right_shift(result, shift); return result; } CRYPTO3_MP_BIG_INTEGER_UNARY_TEMPLATE inline constexpr auto& operator>>=(big_integer_t& a, unsigned shift) noexcept { // TODO(ioxid): check - detail::right_shift(a, shift); + big_integer_t::right_shift(a, shift); return a; } From 2d338f916efd4d3e58de4ce35f9b3e2ac04d2958 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Tue, 29 Oct 2024 13:59:37 +0000 Subject: [PATCH 7/9] big_integer: refactor arithmetic operations --- .../big_integer/big_integer_impl.hpp | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index ffb5b0f0da..3ca21b52e9 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -21,6 +21,19 @@ #include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" +// TODO(ioxid): boost is used for +// +// boost::multiprecision::std_constexpr::swap +// boost::multiprecision::std_constexpr::copy +// ^ these can be fixed by using C++20's constexpr functions from std +// v need to copy everything else +// boost::multiprecision::detail::addcarry_limb +// boost::multiprecision::detail::subborrow_limb +// cpp_int.str +// cpp_int.% +// cpp_int./ +// cpp_int.* + namespace nil::crypto3::multiprecision { template class big_integer { @@ -192,7 +205,7 @@ namespace nil::crypto3::multiprecision { } // TODO(ioxid): make them private? - public: + // Arithmetic operations // Addition/subtraction @@ -220,9 +233,9 @@ namespace nil::crypto3::multiprecision { return; } - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); + const_limb_pointer pb = b.limbs(); + limb_pointer pr = result.limbs(); // First where a and b overlap: for (std::size_t i = 0; i < s; ++i) { @@ -265,9 +278,9 @@ namespace nil::crypto3::multiprecision { result = *a.limbs() - *b.limbs(); return; } - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); + const_limb_pointer pb = b.limbs(); + limb_pointer pr = result.limbs(); double_limb_type borrow = 0; // First where a and b overlap: @@ -322,9 +335,9 @@ namespace nil::crypto3::multiprecision { result = v; return; } - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); + const_limb_pointer pb = b.limbs(); + limb_pointer pr = result.limbs(); unsigned char carry = 0; #if defined(BOOST_MSVC) && !defined(BOOST_HAS_INT128) && defined(_M_X64) @@ -381,7 +394,7 @@ namespace nil::crypto3::multiprecision { const big_integer& b) noexcept { BOOST_ASSERT(!eval_lt(a, b)); -#ifndef TO3_MP_NO_CONSTEXPR_DETECTION +#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION if (BOOST_MP_IS_CONST_EVALUATED(a.size())) { subtract_constexpr(result, a, b); } else @@ -400,9 +413,9 @@ namespace nil::crypto3::multiprecision { return; } // Now that a, b, and result are stable, get pointers to their limbs: - typename big_integer::const_limb_pointer pa = a.limbs(); - typename big_integer::const_limb_pointer pb = b.limbs(); - typename big_integer::limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); + const_limb_pointer pb = b.limbs(); + limb_pointer pr = result.limbs(); std::size_t i = 0; unsigned char borrow = 0; @@ -471,8 +484,8 @@ namespace nil::crypto3::multiprecision { // Nothing fancy, just let uintmax_t take the strain: double_limb_type carry = o; - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer pa = a.limbs(); + limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); unsigned i = 0; // Addition with carry until we either run out of digits or carry is zero: for (; carry && (i < result.size()); ++i) { @@ -508,8 +521,8 @@ namespace nil::crypto3::multiprecision { // Nothing fancy, just let uintmax_t take the strain: constexpr double_limb_type borrow = static_cast(big_integer::max_limb_value) + 1; - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer pa = a.limbs(); + limb_pointer pr = result.limbs(); + const_limb_pointer pa = a.limbs(); if (*pa >= b) { *pr = *pa - b; if (&result != &a) { @@ -560,10 +573,9 @@ namespace nil::crypto3::multiprecision { // unsigned rs = result.size(); unsigned os = o.size(); - unsigned m(0), x(0); - boost::multiprecision::minmax(rs, os, m, x); - typename big_integer::limb_pointer pr = result.limbs(); - typename big_integer::const_limb_pointer po = o.limbs(); + auto [m, x] = std::minmax(rs, os); + limb_pointer pr = result.limbs(); + const_limb_pointer po = o.limbs(); for (unsigned i = rs; i < x; ++i) { pr[i] = 0; } @@ -622,9 +634,7 @@ namespace nil::crypto3::multiprecision { // Left shift will throw away upper Bits. // This function must be called only when s % 8 == 0, i.e. we shift bytes. static inline void left_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); std::size_t bytes = static_cast(s / CHAR_BIT); if (s >= Bits) { @@ -641,12 +651,10 @@ namespace nil::crypto3::multiprecision { // This function must be called only when s % limb_bits == 0, i.e. we shift limbs, which // are normally 64 bit. static inline constexpr void left_shift_limb(big_integer& result, double_limb_type s) { - using big_integer_t = big_integer; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); - BOOST_ASSERT(static_cast(s % big_integer_t::limb_bits) == 0); + limb_type offset = static_cast(s / limb_bits); + BOOST_ASSERT(static_cast(s % limb_bits) == 0); - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); if (s >= Bits) { // Set result to 0. @@ -665,16 +673,14 @@ namespace nil::crypto3::multiprecision { // Left shift will throw away upper Bits. static inline constexpr void left_shift_generic(big_integer& result, double_limb_type s) { - using big_integer_t = big_integer; - if (s >= Bits) { // Set result to 0. result.zero_after(0); } else { - limb_type offset = static_cast(s / big_integer_t::limb_bits); - limb_type shift = static_cast(s % big_integer_t::limb_bits); + limb_type offset = static_cast(s / limb_bits); + limb_type shift = static_cast(s % limb_bits); - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); std::size_t i = 0; std::size_t rs = result.size(); // This code only works when shift is non-zero, otherwise we invoke undefined @@ -682,7 +688,7 @@ namespace nil::crypto3::multiprecision { BOOST_ASSERT(shift); for (; rs - i >= 2 + offset; ++i) { pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; - pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (big_integer_t::limb_bits - shift); + pr[rs - 1 - i] |= pr[rs - 2 - i - offset] >> (limb_bits - shift); } if (rs - i >= 1 + offset) { pr[rs - 1 - i] = pr[rs - 1 - i - offset] << shift; @@ -743,9 +749,7 @@ namespace nil::crypto3::multiprecision { } static inline void right_shift_byte(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); + limb_type offset = static_cast(s / limb_bits); BOOST_ASSERT((s % CHAR_BIT) == 0); unsigned ors = result.size(); unsigned rs = ors; @@ -754,12 +758,12 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); unsigned char* pc = reinterpret_cast(pr); limb_type shift = static_cast(s / CHAR_BIT); std::memmove(pc, pc + shift, ors * sizeof(pr[0]) - shift); shift = (sizeof(limb_type) - shift % sizeof(limb_type)) * CHAR_BIT; - if (shift < big_integer_t::limb_bits) { + if (shift < limb_bits) { pr[ors - offset - 1] &= (static_cast(1u) << shift) - 1; if (!pr[ors - offset - 1] && (rs > 1)) { --rs; @@ -770,10 +774,8 @@ namespace nil::crypto3::multiprecision { } static inline constexpr void right_shift_limb(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - - limb_type offset = static_cast(s / big_integer_t::limb_bits); - BOOST_ASSERT((s % big_integer_t::limb_bits) == 0); + limb_type offset = static_cast(s / limb_bits); + BOOST_ASSERT((s % limb_bits) == 0); unsigned ors = result.size(); unsigned rs = ors; if (offset >= rs) { @@ -781,7 +783,7 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); unsigned i = 0; for (; i < rs; ++i) { pr[i] = pr[i + offset]; @@ -791,9 +793,8 @@ namespace nil::crypto3::multiprecision { } static inline constexpr void right_shift_generic(big_integer& result, double_limb_type s) { - typedef big_integer big_integer_t; - limb_type offset = static_cast(s / big_integer_t::limb_bits); - limb_type shift = static_cast(s % big_integer_t::limb_bits); + limb_type offset = static_cast(s / limb_bits); + limb_type shift = static_cast(s % limb_bits); unsigned ors = result.size(); unsigned rs = ors; @@ -802,7 +803,7 @@ namespace nil::crypto3::multiprecision { return; } rs -= offset; - typename big_integer_t::limb_pointer pr = result.limbs(); + limb_pointer pr = result.limbs(); if ((pr[ors - 1] >> shift) == 0) { if (--rs == 0) { result = limb_type(0); @@ -815,7 +816,7 @@ namespace nil::crypto3::multiprecision { BOOST_ASSERT(shift); for (; i + offset + 1 < ors; ++i) { pr[i] = pr[i + offset] >> shift; - pr[i] |= pr[i + offset + 1] << (big_integer_t::limb_bits - shift); + pr[i] |= pr[i + offset + 1] << (limb_bits - shift); } pr[i] = pr[i + offset] >> shift; @@ -915,6 +916,8 @@ namespace nil::crypto3::multiprecision { result.from_cpp_int(result_cpp_int); } + private: + // Assignment templatenormalize(); } - private: // Data // m_data[0] contains the lowest bits. From 1bf602c0c7dfb1b5a53559c33ec0f399ebd14608 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Wed, 30 Oct 2024 15:55:24 +0000 Subject: [PATCH 8/9] Simple multiplication implementation --- .../big_integer/big_integer_impl.hpp | 56 ++++++++++++++----- .../big_integer/big_integer_ops.hpp | 8 ++- .../big_integer/{modular => ops}/inverse.hpp | 12 ++-- .../multiprecision/big_integer/ops/misc.hpp | 11 +++- 4 files changed, 64 insertions(+), 23 deletions(-) rename crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/{modular => ops}/inverse.hpp (97%) diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index 3ca21b52e9..dfd7f9ca96 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -100,7 +101,8 @@ namespace nil::crypto3::multiprecision { } // TODO(ioxid): forbid signed, implement comparison with signed instead - template /*&& std::is_v*/, int> = 0> + template /*&& std::is_unsigned_v*/, int> = 0> inline constexpr big_integer(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; @@ -127,7 +129,8 @@ namespace nil::crypto3::multiprecision { // Assignment from other types // TODO(ioxid): forbid signed, implement comparison with signed instead - template /*&& std::is_v*/, int> = 0> + template /*&& std::is_unsigned_v*/, int> = 0> inline constexpr big_integer& operator=(T val) noexcept { if (val < 0) { std::cerr << "big_integer: assignment from negative integer" << std::endl; @@ -902,18 +905,45 @@ namespace nil::crypto3::multiprecision { // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! - static inline constexpr void multiply(big_integer& result, const limb_type& b) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int *= b; - result.from_cpp_int(result_cpp_int); - } + template + static inline constexpr void multiply(big_integer& result, const big_integer& a, + const big_integer& b) noexcept { + std::size_t as = a.size(); + std::size_t bs = b.size(); + const_limb_pointer pa = a.limbs(); + const_limb_pointer pb = b.limbs(); + limb_pointer pr = result.limbs(); + for (std::size_t i = 0; i < result.size(); ++i) { + pr[i] = 0; + } - template - static inline constexpr void multiply(big_integer& result, - const big_integer& a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int = static_cast(result_cpp_int * a.to_cpp_int()); - result.from_cpp_int(result_cpp_int); + double_limb_type carry = 0; + for (std::size_t i = 0; i < as; ++i) { + BOOST_MP_ASSERT(result.size() > i); + std::size_t inner_limit = (std::min)(result.size() - i, bs); + std::size_t j = 0; + for (; j < inner_limit; ++j) { + BOOST_MP_ASSERT(i + j < result.size()); + carry += + static_cast(pa[i]) * static_cast(pb[j]); + BOOST_MP_ASSERT( + !std::numeric_limits::is_specialized || + ((std::numeric_limits::max)() - carry >= pr[i + j])); + carry += pr[i + j]; + pr[i + j] = static_cast(carry); + carry >>= limb_bits; + BOOST_MP_ASSERT(carry <= max_limb_value); + } + if (carry) { + // TODO(ioxid): throw? + // resize_for_carry(result, i + j + 1); // May throw if checking is enabled + if (i + j < result.size()) { + pr[i + j] = static_cast(carry); + } + } + carry = 0; + } + result.normalize(); } private: diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp index c8b580d530..ef84488374 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -132,13 +132,15 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator*(const T1& a, const T2& b) noexcept { - big_integer() + detail::get_bits()> result = a; - decltype(result)::multiply(result, b); + big_integer() + detail::get_bits()> result; + decltype(result)::multiply(result, a, b); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator*=(big_integer_t& a, const T& b) noexcept { - big_integer_t::multiply(a, b); + big_integer() + detail::get_bits()> result; + decltype(result)::multiply(result, a, static_cast(b)); + a = result; return a; } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/inverse.hpp similarity index 97% rename from crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp rename to crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/inverse.hpp index 0c5ce91606..c8d012073b 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/inverse.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/inverse.hpp @@ -86,7 +86,7 @@ namespace nil::crypto3::multiprecision { } } - // Overload the upper code for modular backends. We do not have negative numbers, + // Overload the code above for modular backends. We do not have negative numbers, // so we will convert to cpp_int_backend to perform the operation and back. template constexpr void eval_inverse_extended_euclidean_algorithm(big_integer& result, @@ -154,17 +154,17 @@ namespace nil::crypto3::multiprecision { zero = ui_type(0u); one = ui_type(1u); // Caller should assure these preconditions: - // BOOST_ASSERT(eval_gt(n, 0) >= 0); - // BOOST_ASSERT(mod >= 0); - // BOOST_ASSERT(n < mod); - // BOOST_ASSERT(mod >= 3 && mod % 2 != 0); + BOOST_ASSERT(eval_gt(n, 0) >= 0); + BOOST_ASSERT(mod >= 0); + BOOST_ASSERT(n < mod); + BOOST_ASSERT(mod >= 3 && mod % 2 != 0); /* This uses a modular inversion algorithm designed by Niels Möller and implemented in Nettle. The same algorithm was later also adapted to GMP in mpn_sec_invert. - There is also a description of the algorithm in Appendix 5 of "Fast + There is also a description of the algorithm in Appendix 5 of "Fast Software Polynomial Multiplication on ARM Processors using the NEON Engine" by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo Dahab in LNCS 8182 diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp index 4ec3da669f..fcebd3df0b 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/ops/misc.hpp @@ -21,8 +21,17 @@ #include "nil/crypto3/multiprecision/big_integer/detail/config.hpp" #include "nil/crypto3/multiprecision/big_integer/storage.hpp" +// TODO(ioxid): refactor +// +// Remove all boost parts, that is: +// boost::multiprecision::backends::numeric_limits_workaround +// boost::multiprecision::detail::is_integral +// boost::multiprecision::detail::is_signed +// boost::multiprecision::detail::find_lsb +// boost::multiprecision::detail::find_msb + namespace nil::crypto3::multiprecision { - // TODO refactor + // TODO(ioxid) refactor template inline constexpr typename std::enable_if::value, void>::type From f092a28cf3174271dce084902c5c2972c82ffba3 Mon Sep 17 00:00:00 2001 From: Andrey Nefedov Date: Wed, 30 Oct 2024 20:30:36 +0000 Subject: [PATCH 9/9] Stub division and custom to string conversions --- .../big_integer/big_integer_impl.hpp | 314 ++++++++++++++++-- .../big_integer/big_integer_ops.hpp | 22 +- ...=> modular_big_integer_additional_ops.hpp} | 12 - .../modular/modular_big_integer_impl.hpp | 5 +- .../modular/modular_big_integer_ops.hpp | 14 +- .../libs/multiprecision/test/big_integer.cpp | 28 +- .../test/big_integer_modular.cpp | 16 +- 7 files changed, 342 insertions(+), 69 deletions(-) rename crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/{modular_big_integer_ops_impl.hpp => modular_big_integer_additional_ops.hpp} (94%) diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp index dfd7f9ca96..0df6f64ad6 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_impl.hpp @@ -4,7 +4,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -12,7 +15,9 @@ #include #include #include +#include #include +#include #include #include @@ -31,9 +36,6 @@ // boost::multiprecision::detail::addcarry_limb // boost::multiprecision::detail::subborrow_limb // cpp_int.str -// cpp_int.% -// cpp_int./ -// cpp_int.* namespace nil::crypto3::multiprecision { template @@ -140,19 +142,26 @@ namespace nil::crypto3::multiprecision { return *this; } - inline constexpr auto& operator=(const char* s) { - // TODO(ioxid): rewrite without cpp_int - cpp_int_type value; - value = s; - this->from_cpp_int(value); - return *this; - } - - inline std::string str(std::streamsize digits = 0, - std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { - // TODO(ioxid): rewrite without cpp_int - cpp_int_type value = to_cpp_int(); - return value.str(digits, f); + inline std::string str() const { + std::string result; + result.reserve(order() * limb_bits / 4 + 2); + result += "0x"; + bool found_first = false; + for (int i = internal_limb_count - 1; i >= 0; --i) { + std::size_t len = (std::bit_width(limbs()[i]) + 3) / 4; + if (found_first) { + len = sizeof(limb_type) * 2; + } + found_first = found_first || len > 0; + if (len > 0) { + std::size_t start_offset = result.size(); + result.resize(result.size() + len); + BOOST_ASSERT(std::to_chars(result.data() + start_offset, + result.data() + result.size(), limbs()[i], 16) + .ec == std::errc{}); + } + } + return result; } // cpp_int conversion @@ -871,28 +880,263 @@ namespace nil::crypto3::multiprecision { } } + inline constexpr std::size_t order() const noexcept { + for (int i = internal_limb_count - 1; i >= 0; --i) { + if (limbs()[i] != 0) { + return i; + } + } + return 0; + } + // Modulus/divide - // These should be called only for creation of Montgomery and Barett - // params, no during "normal" execution, so we do NOT care about the execution speed, - // and will just redirect calls to normal boost::cpp_int. + // This should be called only for creation of Montgomery and Barett + // params, no during "normal" execution, so we do NOT care about the execution speed. - // Just a call to the upper function, similar to operator*=. - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw! - template - static inline constexpr void modulus(big_integer& result, - const big_integer& a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int %= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); - } + template + static inline constexpr void divide(big_integer* result, const big_integer& x, + const big_integer& y, big_integer& r) { + // TODO(ioxid): fix - template - static inline constexpr void divide(big_integer& result, - const big_integer& a) noexcept { - auto result_cpp_int = result.to_cpp_int(); - result_cpp_int /= a.to_cpp_int(); - result.from_cpp_int(result_cpp_int); + if (result) { + result->from_cpp_int(x.to_cpp_int() / y.to_cpp_int()); + } + r.from_cpp_int(x.to_cpp_int() % y.to_cpp_int()); + return; + + /* + Very simple, fairly braindead long division. + Start by setting the remainder equal to x, and the + result equal to 0. Then in each loop we calculate our + "best guess" for how many times y divides into r, + add our guess to the result, and subtract guess*y + from the remainder r. One wrinkle is that the remainder + may go negative, in which case we subtract the current guess + from the result rather than adding. The value of the guess + is determined by dividing the most-significant-limb of the + current remainder by the most-significant-limb of y. + + Note that there are more efficient algorithms than this + available, in particular see Knuth Vol 2. However for small + numbers of limbs this generally outperforms the alternatives + and avoids the normalisation step which would require extra storage. + */ + + if (is_zero(y)) { + throw std::overflow_error("integer division by zero"); + } + + const_limb_pointer px = x.limbs(); + const_limb_pointer py = y.limbs(); + + if (is_zero(x)) { + // x is zero, so is the result: + r = x; + if (result) { + *result = x; + } + return; + } + + r = x; + std::size_t r_order = r.order(); + std::size_t y_order = y.order(); + if (result) { + *result = static_cast(0u); + } + // + // Check if the remainder is already less than the divisor, if so + // we already have the result. Note we try and avoid a full compare + // if we can: + // + if (r_order <= y_order) { + if ((r_order < y_order) || (r < y)) { + return; + } + } + + big_integer t; + bool r_neg = false; + + // + // See if we can short-circuit long division, and use basic arithmetic instead: + // + if (r_order == 0) { + if (result) { + *result = px[0] / py[0]; + } + r = px[0] % py[0]; + return; + } + if (r_order == 1) { + double_limb_type a = (static_cast(px[1]) << limb_bits) | px[0]; + double_limb_type b = + y_order ? (static_cast(py[1]) << limb_bits) | py[0] : py[0]; + if (result) { + *result = a / b; + } + r = a % b; + return; + } + // + // prepare result: + // + // if (result) result->resize(1 + r_order - y_order, 1 + r_order - y_order); + if (result) { + *result = 0; + } + const_limb_pointer prem = r.limbs(); + // This is initialised just to keep the compiler from emitting useless warnings later + // on: + limb_pointer pr = limb_pointer(); + if (result) { + pr = result->limbs(); + for (std::size_t i = 1; i < 1 + r_order - y_order; ++i) { + pr[i] = 0; + } + } + bool first_pass = true; + + do { + // + // Calculate our best guess for how many times y divides into r: + // + limb_type guess = 1; + if ((prem[r_order] <= py[y_order]) && (r_order > 0)) { + double_limb_type a = + (static_cast(prem[r_order]) << limb_bits) | + prem[r_order - 1]; + double_limb_type b = py[y_order]; + double_limb_type v = a / b; + if (v <= max_limb_value) { + guess = static_cast(v); + --r_order; + } + } else if (r_order == 0) { + guess = prem[0] / py[y_order]; + } else { + double_limb_type a = + (static_cast(prem[r_order]) << limb_bits) | + prem[r_order - 1]; + double_limb_type b = + (y_order > 0) ? (static_cast(py[y_order]) << limb_bits) | + py[y_order - 1] + : (static_cast(py[y_order]) << limb_bits); + BOOST_MP_ASSERT(b); + double_limb_type v = a / b; + guess = static_cast(v); + } + BOOST_MP_ASSERT(guess); // If the guess ever gets to zero we go on forever.... + // + // Update result: + // + std::size_t shift = r_order - y_order; + if (result) { + if (r_neg) { + if (pr[shift] > guess) { + pr[shift] -= guess; + } else { + // t.resize(shift + 1, shift + 1); + t.limbs()[shift] = guess; + for (std::size_t i = 0; i < shift; ++i) { + t.limbs()[i] = 0; + } + *result -= t; + } + } else if (max_limb_value - pr[shift] > guess) { + pr[shift] += guess; + } else { + // t.resize(shift + 1, shift + 1); + t.limbs()[shift] = guess; + for (std::size_t i = 0; i < shift; ++i) { + t.limbs()[i] = 0; + } + *result += t; + } + } + // + // Calculate guess * y, we use a fused mutiply-shift O(N) for this + // rather than a full O(N^2) multiply: + // + double_limb_type carry = 0; + // t.resize(y.size() + shift + 1, y.size() + shift); + // bool truncated_t = (t.size() != y.size() + shift + 1); + const bool truncated_t = false; + limb_pointer pt = t.limbs(); + for (std::size_t i = 0; i < shift; ++i) { + pt[i] = 0; + } + for (std::size_t i = 0; i < y.order() + 1; ++i) { + carry += + static_cast(py[i]) * static_cast(guess); + pt[i + shift] = static_cast(carry); + carry >>= limb_bits; + } + if (carry && !truncated_t) { + pt[t.order()] = static_cast(carry); + } else if (!truncated_t) { + // t.resize(t.size() - 1, t.size() - 1); + } + // + // Update r in a way that won't actually produce a negative result + // in case the argument types are unsigned: + // + if (truncated_t && carry) { + // We need to calculate 2^n + t - r + // where n is the number of bits in this type. + // Simplest way is to get 2^n - r by complementing + // r, then add t to it. Note that we can't call eval_complement + // in case this is a signed checked type: + for (std::size_t i = 0; i <= r_order; ++i) { + r.limbs()[i] = ~prem[i]; + } + r.normalize(); + ++r; + r += t; + r_neg = !r_neg; + } else if (r > t) { + r -= t; + } else { + std::swap(r, t); + r -= t; + prem = r.limbs(); + r_neg = !r_neg; + } + // + // First time through we need to strip any leading zero, otherwise + // the termination condition goes belly-up: + // + if (result && first_pass) { + first_pass = false; + // while (pr[result->size() - 1] == 0) + // result->resize(result->size() - 1, result->size() - 1); + } + // + // Update r_order: + // + r_order = r.order(); + if (r_order < y_order) { + break; + } + } + // Termination condition is really just a check that r > y, but with a common + // short-circuit case handled first: + while ((r_order > y_order) || (r >= y)); + + // + // We now just have to normalise the result: + // + if (r_neg && !is_zero(r)) { + // We have one too many in the result: + if (result) { + --*result; + } + r = y - r; + } + + BOOST_MP_ASSERT(r < + y); // remainder must be less than the divisor or our code has failed } // Multiplication @@ -903,7 +1147,7 @@ namespace nil::crypto3::multiprecision { // NOT care about the execution speed, and will just redirect calls to normal // boost::cpp_int. - // Caller is responsible for the result to fit in Bits1 Bits, we will NOT throw!!! + // Caller is responsible for the result to fit in Bits bits, we will NOT throw!!! template static inline constexpr void multiply(big_integer& result, const big_integer& a, diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp index ef84488374..9570d04428 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/big_integer_ops.hpp @@ -103,7 +103,7 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator-(const T1& a, const T2& b) noexcept { - largest_t result; + T1 result; T1::subtract(result, a, b); return result; } @@ -146,25 +146,31 @@ namespace nil::crypto3::multiprecision { CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator/(const T1& a, const T2& b) noexcept { - largest_t result = a; - largest_t::divide(result, b); + largest_t result; + largest_t modulus; + largest_t::divide(&result, a, b, modulus); return result; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator/=(big_integer_t& a, const T& b) noexcept { - big_integer_t::divide(a, b); + big_integer_t result; + big_integer_t modulus; + big_integer_t::divide(&result, a, b, modulus); + a = result; return a; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_TEMPLATE inline constexpr auto operator%(const T1& a, const T2& b) noexcept { - largest_t result = a; - largest_t::modulus(result, b); - return result; + largest_t modulus; + largest_t::divide(nullptr, a, b, modulus); + return modulus; } CRYPTO3_MP_BIG_INTEGER_INTEGRAL_ASSIGNMENT_TEMPLATE inline constexpr auto& operator%=(big_integer_t& a, const T& b) { - big_integer_t::modulus(a, b); + big_integer_t modulus; + big_integer_t::divide(nullptr, a, b, modulus); + a = modulus; return a; } diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_additional_ops.hpp similarity index 94% rename from crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp rename to crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_additional_ops.hpp index 73df0b164d..39b3f1f68d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_additional_ops.hpp @@ -27,18 +27,6 @@ #include "nil/crypto3/multiprecision/big_integer/modular/modular_ops.hpp" namespace nil::crypto3::multiprecision::detail { - template - constexpr void subtract(modular_big_integer_impl, modular_ops_t> &result, - const modular_big_integer_impl, modular_ops_t> &o) { - if (result.base_data() < o.base_data()) { - auto v = result.ops().get_mod(); - v -= o.base_data(); - result.base_data() += v; - } else { - result.base_data() -= o.base_data(); - } - } - // template // constexpr void eval_powm(modular_big_integer_impl, modular_ops_t> &result, // const modular_big_integer_impl &b, diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp index 2a8500bb8f..f1c4755d50 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp @@ -71,10 +71,9 @@ namespace nil::crypto3::multiprecision { // String conversion - inline std::string str(std::streamsize digits = 0, - std::ios_base::fmtflags f = std::ios_base::fmtflags(0)) const { + inline std::string str() const { // TODO(ioxid): add module to output - return remove_modulus().str(digits, f); + return remove_modulus().str(); } // TODO(ioxid): why is it here diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp index 28f1b30612..649805a84d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops.hpp @@ -14,7 +14,6 @@ #include "nil/crypto3/multiprecision/big_integer/big_integer.hpp" #include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_impl.hpp" -#include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer_ops_impl.hpp" namespace nil::crypto3::multiprecision { namespace detail { @@ -57,6 +56,19 @@ namespace nil::crypto3::multiprecision { constexpr std::size_t get_bits() { return T::Bits; } + + template + constexpr void subtract( + modular_big_integer_impl, modular_ops_t>& result, + const modular_big_integer_impl, modular_ops_t>& o) { + if (result.base_data() < o.base_data()) { + auto v = result.ops().get_mod(); + v -= o.base_data(); + result.base_data() += v; + } else { + result.base_data() -= o.base_data(); + } + } } // namespace detail // TODO(ioxid): choose result type diff --git a/crypto3/libs/multiprecision/test/big_integer.cpp b/crypto3/libs/multiprecision/test/big_integer.cpp index d307898537..41bfb34c5f 100644 --- a/crypto3/libs/multiprecision/test/big_integer.cpp +++ b/crypto3/libs/multiprecision/test/big_integer.cpp @@ -28,9 +28,9 @@ BOOST_AUTO_TEST_CASE(construct_constexpr) { constexpr nil::crypto3::multiprecision::big_integer<60> a = 0x123_big_integer60; } -BOOST_AUTO_TEST_CASE(to_string_trivial) { BOOST_CHECK_EQUAL((0x1_big_integer60).str(), "1"); } +BOOST_AUTO_TEST_CASE(to_string_trivial) { BOOST_CHECK_EQUAL((0x1_big_integer60).str(), "0x1"); } -BOOST_AUTO_TEST_CASE(to_string_small) { BOOST_CHECK_EQUAL((0x20_big_integer60).str(), "32"); } +BOOST_AUTO_TEST_CASE(to_string_small) { BOOST_CHECK_EQUAL((0x20_big_integer60).str(), "0x20"); } BOOST_AUTO_TEST_CASE(ops) { nil::crypto3::multiprecision::big_integer<60> a = 2u, b; @@ -128,6 +128,30 @@ BOOST_AUTO_TEST_CASE(multilimb) { BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(division) + +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(0x7_big_integer60 / 0x2_big_integer60, 0x3_big_integer60); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer36 / 0x2_big_integer36, 0x7fffffff_big_integer36); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(modulus) + +BOOST_AUTO_TEST_CASE(simple) { + BOOST_CHECK_EQUAL(0x7_big_integer60 % 0x4_big_integer60, 0x3_big_integer60); +} + +BOOST_AUTO_TEST_CASE(multilimb) { + BOOST_CHECK_EQUAL(0xFFFFFFFF_big_integer36 % 0x7_big_integer36, 0x3_big_integer36); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE(convert) BOOST_AUTO_TEST_CASE(to_uint64_t) { diff --git a/crypto3/libs/multiprecision/test/big_integer_modular.cpp b/crypto3/libs/multiprecision/test/big_integer_modular.cpp index 68cad63e03..20b3a722fd 100644 --- a/crypto3/libs/multiprecision/test/big_integer_modular.cpp +++ b/crypto3/libs/multiprecision/test/big_integer_modular.cpp @@ -32,42 +32,42 @@ BOOST_AUTO_TEST_CASE(construct_constexpr) { BOOST_AUTO_TEST_CASE(construct_modular_ct_trivial_montgomery) { static constexpr auto mod = 0x3_big_integer64; auto_modular_big_integer a = auto_modular_big_integer(0x5_big_integer64); - BOOST_CHECK_EQUAL(a.str(), "2"); + BOOST_CHECK_EQUAL(a.str(), "0x2"); } BOOST_AUTO_TEST_CASE(construct_modular_rt_trivial_montgomery) { modular_big_integer_rt<64> a{0x5_big_integer64, 0x3_big_integer64}; - BOOST_CHECK_EQUAL(a.str(), "2"); + BOOST_CHECK_EQUAL(a.str(), "0x2"); } BOOST_AUTO_TEST_CASE(construct_modular_ct_small_montgomery) { static constexpr auto mod = 0x79_big_integer64; auto_modular_big_integer a = auto_modular_big_integer(0x1234_big_integer64); - BOOST_CHECK_EQUAL(a.str(), "62"); + BOOST_CHECK_EQUAL(a.str(), "0x3e"); } BOOST_AUTO_TEST_CASE(construct_modular_rt_small_montgomery) { modular_big_integer_rt<64> a{0x1234_big_integer64, 0x79_big_integer64}; - BOOST_CHECK_EQUAL(a.str(), "62"); + BOOST_CHECK_EQUAL(a.str(), "0x3e"); } BOOST_AUTO_TEST_CASE(construct_modular_ct_small) { static constexpr auto mod = 0x78_big_integer64; auto_modular_big_integer a = auto_modular_big_integer(0x1234_big_integer64); - BOOST_CHECK_EQUAL(a.str(), "100"); + BOOST_CHECK_EQUAL(a.str(), "0x64"); } BOOST_AUTO_TEST_CASE(construct_modular_rt_small) { modular_big_integer_rt<64> a{0x1234_big_integer64, 0x78_big_integer64}; - BOOST_CHECK_EQUAL(a.str(), "100"); + BOOST_CHECK_EQUAL(a.str(), "0x64"); } BOOST_AUTO_TEST_CASE(to_string_trivial) { - BOOST_CHECK_EQUAL((static_cast(0x1_big_integer64)).str(), "1"); + BOOST_CHECK_EQUAL((static_cast(0x1_big_integer64)).str(), "0x1"); } BOOST_AUTO_TEST_CASE(to_string_small) { - BOOST_CHECK_EQUAL((static_cast(0x20_big_integer64)).str(), "32"); + BOOST_CHECK_EQUAL((static_cast(0x20_big_integer64)).str(), "0x20"); } BOOST_AUTO_TEST_CASE(ops) {