Skip to content

Commit

Permalink
Fix modular constructors and implement hash for signed big integer
Browse files Browse the repository at this point in the history
  • Loading branch information
ioxid committed Nov 13, 2024
1 parent 5255db0 commit 708e3db
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <array>
#include <bit>
#include <boost/functional/hash.hpp>
#include <boost/functional/hash_fwd.hpp>
#include <cctype>
#include <charconv>
#include <climits>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

// IWYU pragma: private; include "nil/crypto3/multiprecision/big_integer/modular/modular_big_integer.hpp"

#include <climits>
#include <cstddef>
#include <ostream>
#include <string>
Expand All @@ -38,20 +39,17 @@ namespace nil::crypto3::multiprecision {
// Constructors

protected:
inline constexpr modular_big_integer_impl(const big_integer_t x,
template<std::size_t Bits2>
inline constexpr modular_big_integer_impl(const big_integer<Bits2>& x,
modular_ops_storage_t&& modular_ops_storage)
: m_modular_ops_storage(std::move(modular_ops_storage)) {
// TODO(ioxid): do we need this?
// NIL_CO3_MP_ASSERT_MSG(Bits == msb(mod()) + 1,
// "modulus precision should match used big_integer");
ops().adjust_modular(m_raw_base, x);
}

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 ops().compare_eq(o.ops()) && m_raw_base == o.m_raw_base;
}

Expand Down Expand Up @@ -101,24 +99,20 @@ namespace nil::crypto3::multiprecision {

using typename base_type::big_integer_t;

constexpr modular_big_integer_ct_impl() : base_type({}, {}) {}

constexpr modular_big_integer_ct_impl(const big_integer_t& b) : base_type(b, {}) {
this->ops().adjust_modular(this->m_raw_base, b);
}
constexpr modular_big_integer_ct_impl() : base_type(big_integer_t(0u), {}) {}

template<std::size_t Bits2>
constexpr explicit modular_big_integer_ct_impl(const big_integer<Bits2>& b)
: base_type(b, {}) {
constexpr modular_big_integer_ct_impl(const big_integer<Bits2>& b) : base_type(b, {}) {
this->ops().adjust_modular(this->m_raw_base, b);
}

// TODO(ioxid): this is buggy, need to remove it asap
// 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<typename SI,
typename std::enable_if_t<std::is_integral_v<SI> && std::is_signed_v<SI>, int> = 0>
constexpr modular_big_integer_ct_impl(SI b) : base_type(0u, {}) {
constexpr modular_big_integer_ct_impl(SI b) : base_type(big_integer_t(0u), {}) {
if (b >= 0) {
this->m_raw_base = static_cast<std::make_unsigned_t<SI>>(b);
} else {
Expand All @@ -133,7 +127,8 @@ namespace nil::crypto3::multiprecision {

template<typename UI, typename std::enable_if_t<
std::is_integral_v<UI> && std::is_unsigned_v<UI>, int> = 0>
constexpr modular_big_integer_ct_impl(UI b) : base_type(b, {}) {}
constexpr modular_big_integer_ct_impl(UI b)
: base_type(big_integer<sizeof(UI) * CHAR_BIT>(b), {}) {}

template<std::size_t Bits2>
inline constexpr modular_big_integer_ct_impl with_replaced_base(
Expand All @@ -153,7 +148,8 @@ namespace nil::crypto3::multiprecision {

template<typename SI,
std::enable_if_t<std::is_integral_v<SI> && std::is_signed_v<SI>, int> = 0>
constexpr modular_big_integer_rt_impl(SI b, const big_integer_t& m) : base_type(0u, m) {
constexpr modular_big_integer_rt_impl(SI b, const big_integer_t& m)
: base_type(big_integer_t(0u), m) {
if (b >= 0) {
this->m_raw_base = b;
} else {
Expand All @@ -166,14 +162,15 @@ namespace nil::crypto3::multiprecision {
this->ops().adjust_modular(this->m_raw_base);
}

template<typename UI, typename std::enable_if_t<
std::is_integral_v<UI> && std::is_unsigned_v<UI>, int> = 0>
constexpr modular_big_integer_rt_impl(UI b, const big_integer_t& m) : base_type(b, m) {}

template<std::size_t Bits2>
constexpr modular_big_integer_rt_impl(const big_integer<Bits2>& b, const big_integer_t& m)
: base_type(b, m) {}

template<typename UI, typename std::enable_if_t<
std::is_integral_v<UI> && std::is_unsigned_v<UI>, int> = 0>
constexpr modular_big_integer_rt_impl(UI b, const big_integer_t& m)
: base_type(big_integer<sizeof(UI) * CHAR_BIT>(b), m) {}

template<std::size_t Bits2>
inline constexpr modular_big_integer_rt_impl with_replaced_base(
const big_integer<Bits2>& b) const {
Expand Down Expand Up @@ -359,7 +356,6 @@ namespace nil::crypto3::multiprecision {
template<std::size_t Bits, typename modular_ops_t>
inline constexpr std::size_t hash_value(
const detail::modular_big_integer_impl<Bits, modular_ops_t>& val) noexcept {
// TODO(ioxid): also hash modulus for runtime type
return hash_value(val.raw_base());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ namespace nil::crypto3::multiprecision::detail {
using big_integer_t = std::decay_t<decltype(Modulus)>;
using modular_ops_t = modular_ops_template<big_integer_t>;

static_assert(big_integer_t::Bits == msb(Modulus) + 1,
"modulus bit width should match used precision");

constexpr modular_ops_storage_ct() {}

static constexpr const modular_ops_t &ops() { return m_modular_ops; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <boost/functional/hash.hpp>
#include <climits>
#include <cmath>
#include <cstring>
Expand All @@ -11,6 +12,9 @@
#include "nil/crypto3/multiprecision/big_integer/detail/assert.hpp"
#include "nil/crypto3/multiprecision/big_integer/detail/config.hpp"

// TODO(ioxid): boost is used for
// boost::hash_combine

namespace nil::crypto3::multiprecision {
template<std::size_t Bits_>
class signed_big_integer {
Expand Down Expand Up @@ -378,7 +382,13 @@ namespace nil::crypto3::multiprecision {
return a;
}

// TODO(ioxid): hash
template<std::size_t Bits>
inline constexpr std::size_t hash_value(const signed_big_integer<Bits>& val) noexcept {
std::size_t result = 0;
boost::hash_combine(result, hash_value(val.abs()));
boost::hash_combine(result, val.negative());
return result;
}

// IO

Expand Down
26 changes: 14 additions & 12 deletions crypto3/libs/multiprecision/test/big_integer_modular.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
using namespace nil::crypto3::multiprecision;
using namespace nil::crypto3::multiprecision::literals;

NIL_CO3_MP_DEFINE_BIG_INTEGER_LITERAL(2)
NIL_CO3_MP_DEFINE_BIG_INTEGER_LITERAL(32)
NIL_CO3_MP_DEFINE_BIG_INTEGER_LITERAL(36)
NIL_CO3_MP_DEFINE_BIG_INTEGER_LITERAL(57)
NIL_CO3_MP_DEFINE_BIG_INTEGER_LITERAL(60)

using namespace nil::crypto3::multiprecision::literals;

constexpr auto mod = 0x123456789ABCDEF_big_integer64;
constexpr auto mod = 0x123456789ABCDEF_big_integer57;
using modular_big_int = montgomery_modular_big_integer<mod>;

BOOST_AUTO_TEST_SUITE(smoke)
Expand All @@ -25,45 +27,45 @@ 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<mod> a = auto_modular_big_integer<mod>(0x5_big_integer64);
static constexpr auto mod = 0x3_big_integer2;
auto_modular_big_integer<mod> a = auto_modular_big_integer<mod>(0x5_big_integer);
BOOST_CHECK_EQUAL(a.str(), "0x2 mod 0x3");
}

BOOST_AUTO_TEST_CASE(construct_modular_rt_trivial_montgomery) {
modular_big_integer_rt<64> a{0x5_big_integer64, 0x3_big_integer64};
modular_big_integer_rt<2> a{0x5_big_integer, 0x3_big_integer};
BOOST_CHECK_EQUAL(a.str(), "0x2 mod 0x3");
}

BOOST_AUTO_TEST_CASE(construct_modular_ct_small_montgomery) {
static constexpr auto mod = 0x79_big_integer64;
auto_modular_big_integer<mod> a = auto_modular_big_integer<mod>(0x1234_big_integer64);
static constexpr auto mod = 0x79_big_integer7;
auto_modular_big_integer<mod> a = auto_modular_big_integer<mod>(0x1234_big_integer);
BOOST_CHECK_EQUAL(a.str(), "0x3E mod 0x79");
}

BOOST_AUTO_TEST_CASE(construct_modular_rt_small_montgomery) {
modular_big_integer_rt<64> a{0x1234_big_integer64, 0x79_big_integer64};
modular_big_integer_rt<7> a{0x1234_big_integer, 0x79_big_integer};
BOOST_CHECK_EQUAL(a.str(), "0x3E mod 0x79");
}

BOOST_AUTO_TEST_CASE(construct_modular_ct_small) {
static constexpr auto mod = 0x78_big_integer64;
auto_modular_big_integer<mod> a = auto_modular_big_integer<mod>(0x1234_big_integer64);
static constexpr auto mod = 0x78_big_integer7;
auto_modular_big_integer<mod> a = auto_modular_big_integer<mod>(0x1234_big_integer);
BOOST_CHECK_EQUAL(a.str(), "0x64 mod 0x78");
}

BOOST_AUTO_TEST_CASE(construct_modular_rt_small) {
modular_big_integer_rt<64> a{0x1234_big_integer64, 0x78_big_integer64};
modular_big_integer_rt<7> a{0x1234_big_integer, 0x78_big_integer};
BOOST_CHECK_EQUAL(a.str(), "0x64 mod 0x78");
}

BOOST_AUTO_TEST_CASE(to_string_trivial) {
BOOST_CHECK_EQUAL((static_cast<modular_big_int>(0x1_big_integer64)).str(),
BOOST_CHECK_EQUAL((static_cast<modular_big_int>(0x1_big_integer)).str(),
"0x1 mod 0x123456789ABCDEF");
}

BOOST_AUTO_TEST_CASE(to_string_small) {
BOOST_CHECK_EQUAL((static_cast<modular_big_int>(0x20_big_integer64)).str(),
BOOST_CHECK_EQUAL((static_cast<modular_big_int>(0x20_big_integer)).str(),
"0x20 mod 0x123456789ABCDEF");
}

Expand Down

0 comments on commit 708e3db

Please sign in to comment.