Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiprecision refactoring: inverse mod and big_int improvements, checked big_uint operations #214

Merged
merged 46 commits into from
Dec 23, 2024
Merged
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2773159
.clang-format: use ColumnLimit: 90
ioxid Dec 20, 2024
b114204
multiprecision: big_uint: clean up wnaf
ioxid Dec 16, 2024
468661f
marshalling::multiprecision: remove boost includes
ioxid Dec 17, 2024
846f6dd
marshalling::multiprecision: clean up tests
ioxid Dec 17, 2024
f5bb0ac
multiprecision: big_uint: rename limbs_count
ioxid Dec 17, 2024
e253f7b
multiprecision: big_uint: remove carry field, make operators checked …
ioxid Dec 17, 2024
295d46f
multiprecision: make benchmarks optimize correctly
ioxid Dec 17, 2024
0366fdc
multiprecision: big_uint: optimize comparison
ioxid Dec 17, 2024
d3c3945
multiprecision: add inverse benchmarks
ioxid Dec 17, 2024
6e44779
multiprecision: refactor big_int and inverse_extended_euclidean_algor…
ioxid Dec 17, 2024
91a2326
multiprecision: throw on overflowing import
ioxid Dec 18, 2024
998c811
multiprecision: move import and export to class definition
ioxid Dec 18, 2024
0b0be80
multiprecision: simplify intrinsics logic
ioxid Dec 18, 2024
bdad996
multiprecision: remove detail/type_traits and clean up
ioxid Dec 18, 2024
b6acce3
multiprecision: replace custom asserts with boost
ioxid Dec 18, 2024
49a0fdb
multiprecision: clean up formatting
ioxid Dec 18, 2024
c04cebe
multiprecision: move more function inside classes
ioxid Dec 18, 2024
3e1dd57
multiprecision: make internal methods private
ioxid Dec 18, 2024
7595284
multiprecision: disable exception in import which fires in tests
ioxid Dec 18, 2024
5823b00
multiprecision: move boost backend tests into a subdirectory
ioxid Dec 18, 2024
3350ad7
multiprecision: rename tests
ioxid Dec 18, 2024
cf550c1
multiprecision: remove examples
ioxid Dec 18, 2024
5924d12
multiprecision: add tests and fix bugs
ioxid Dec 18, 2024
5e482c5
multiprecision: update and fix copyrights
ioxid Dec 19, 2024
f06444b
multiprecision: add custom and boost tests and fix bugs
ioxid Dec 19, 2024
108a222
multiprecision: make casting to smaller type explicit
ioxid Dec 19, 2024
68338b5
multiprecision: refactor addition with carry
ioxid Dec 19, 2024
4b9cba3
blueprint: use big_uint as zkevm_word_type
ioxid Dec 20, 2024
0c3d1e1
multiprecision: test: generate better test cases
ioxid Dec 20, 2024
2e47cfc
multiprecision: rename benchmarks
ioxid Dec 20, 2024
396cf5f
blueprint: use wrapping operations
ioxid Dec 20, 2024
ab95a39
multiprecision: remove boost_backend implementation, benchmarks, and …
ioxid Dec 20, 2024
a6fdcfa
multiprecison: remove big_mod ordering
ioxid Dec 20, 2024
3cd08a2
multiprecision: rename wrapping operators
ioxid Dec 20, 2024
daad97d
multiprecision: optimize integer bit operations
ioxid Dec 20, 2024
5f8a08b
multiprecision: remove lcm from tests
ioxid Dec 20, 2024
2e168a3
multiprecision: big_mod: rename subtract to sub
ioxid Dec 21, 2024
4606313
multiprecision: reorganize files
ioxid Dec 21, 2024
46d55b1
multiprecision: expor unsigned_utils and use it in the impl of power
ioxid Dec 21, 2024
9f3de69
multiprecision: remove iwyu pragma private
ioxid Dec 21, 2024
e516b7b
placeholder: address some ioxid todos
ioxid Dec 21, 2024
bdb9324
multiprecision: big_uint: reenable exception in import and fix (hopef…
ioxid Dec 21, 2024
3a98db6
multiprecision: make type traits public
ioxid Dec 22, 2024
e4ff7e6
multiprecision: fix big_uint numeric limits: it's not modulo
ioxid Dec 22, 2024
6d95a6e
multiprecision: more tests
ioxid Dec 22, 2024
08bd958
multiprecision: add comments
ioxid Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
multiprecision: optimize integer bit operations
ioxid committed Dec 21, 2024

Verified

This commit was signed with the committer’s verified signature.
renovate-bot Mend Renovate
commit daad97d00723bfa566a159382dd455efd8740f7d
Original file line number Diff line number Diff line change
@@ -1078,9 +1078,8 @@ namespace nil::crypto3::multiprecision {

constexpr bool bit_test(std::size_t index) const {
if (index >= Bits) {
// NB: we assume there are infinite leading zeros
return false;
// TODO(ioxid): this throws in multiexp tests
// throw std::invalid_argument("fixed precision overflow");
}
std::size_t offset = index / limb_bits;
std::size_t shift = index % limb_bits;
@@ -1347,13 +1346,13 @@ namespace nil::crypto3::multiprecision {
// For generic code

template<std::size_t Bits>
constexpr std::size_t msb(const big_uint<Bits>& a) {
return a.msb();
constexpr std::size_t lsb(const big_uint<Bits>& a) {
return a.lsb();
}

template<std::size_t Bits>
constexpr std::size_t lsb(const big_uint<Bits>& a) {
return a.lsb();
constexpr std::size_t msb(const big_uint<Bits>& a) {
return a.msb();
}

template<std::size_t Bits>
Original file line number Diff line number Diff line change
@@ -23,11 +23,13 @@ namespace nil::crypto3::multiprecision {
std::enable_if_t<detail::is_integral_v<std::decay_t<T1>> &&
detail::is_integral_v<std::decay_t<T2>>,
int> = 0>
constexpr std::decay_t<T1> pow(T1 b, T2 e) {
if (is_zero(e)) {
constexpr std::decay_t<T1> pow(T1 b, T2 e_original) {
if (is_zero(e_original)) {
return 1u;
}

auto e = detail::unsigned_or_throw(e_original);

T1 res = 1u;

while (true) {
Original file line number Diff line number Diff line change
@@ -8,48 +8,72 @@

#pragma once

#include <climits>
#include <cstddef>
#include <stdexcept>
#include <type_traits>

#include "nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp"

namespace nil::crypto3::multiprecision {
template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
constexpr std::size_t msb(T a) {
// TODO(ioxid): optimize
return detail::as_big_uint(detail::unsigned_or_throw(a)).msb();
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr std::size_t lsb(T a) {
// TODO(ioxid): optimize
return detail::as_big_uint(detail::unsigned_or_throw(a)).lsb();
if (a == 0) {
throw std::invalid_argument("zero has no lsb");
}
return std::countr_zero(a);
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr std::size_t msb(T a) {
if (a == 0) {
throw std::invalid_argument("zero has no msb");
}
return std::bit_width(a) - 1;
}

template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr bool bit_test(T a, std::size_t index) {
// TODO(ioxid): optimize
return detail::as_big_uint(detail::unsigned_or_throw(a)).bit_test(index);
if (index >= sizeof(T) * CHAR_BIT) {
// NB: we assume there are infinite leading zeros
return false;
}
auto mask = static_cast<T>(1u) << index;
return static_cast<bool>(a & mask);
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr T &bit_set(T &a, std::size_t index) {
// TODO(ioxid): optimize
a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_set(index);
if (index >= sizeof(T) * CHAR_BIT) {
throw std::invalid_argument("fixed precision overflow");
}
auto mask = static_cast<T>(1u) << index;
a |= mask;
return a;
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr T &bit_unset(T &a, std::size_t index) {
// TODO(ioxid): optimize
a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_unset(index);
if (index >= sizeof(T) * CHAR_BIT) {
throw std::invalid_argument("fixed precision overflow");
}
auto mask = static_cast<T>(1u) << index;
a &= ~mask;
return a;
}

template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
template<typename T,
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
constexpr T &bit_flip(T &a, std::size_t index) {
// TODO(ioxid): optimize
a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_flip(index);
if (index >= sizeof(T) * CHAR_BIT) {
throw std::invalid_argument("fixed precision overflow");
}
auto mask = static_cast<T>(1u) << index;
a ^= mask;
return a;
}

1 change: 1 addition & 0 deletions crypto3/libs/multiprecision/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ set(MULTIPRECISION_TESTS_NAMES
"big_uint_arithmetic"
"big_uint_basic"
"big_uint_comparison"
"integer"
"inverse"
"jacobi"
"miller_rabin"
95 changes: 95 additions & 0 deletions crypto3/libs/multiprecision/test/integer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2024 Andrey Nefedov <ioxid@nil.foundation>
//
// 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 integer_test

#include <stdexcept>

#include <boost/test/unit_test.hpp>

#include "nil/crypto3/multiprecision/integer.hpp"

using namespace nil::crypto3::multiprecision;

BOOST_AUTO_TEST_SUITE(bit_operations)

BOOST_AUTO_TEST_CASE(lsb_test) {
BOOST_CHECK_THROW(lsb(0u), std::invalid_argument);
BOOST_CHECK_EQUAL(lsb(0b001u), 0);
BOOST_CHECK_EQUAL(lsb(0b010u), 1);
BOOST_CHECK_EQUAL(lsb(0b011u), 0);
BOOST_CHECK_EQUAL(lsb(0b100u), 2);
BOOST_CHECK_EQUAL(lsb(0b101u), 0);
BOOST_CHECK_EQUAL(lsb(0b110u), 1);
BOOST_CHECK_EQUAL(lsb(0b111u), 0);
}

BOOST_AUTO_TEST_CASE(msb_test) {
BOOST_CHECK_THROW(msb(0u), std::invalid_argument);
BOOST_CHECK_EQUAL(msb(0b001u), 0);
BOOST_CHECK_EQUAL(msb(0b010u), 1);
BOOST_CHECK_EQUAL(msb(0b011u), 1);
BOOST_CHECK_EQUAL(msb(0b100u), 2);
BOOST_CHECK_EQUAL(msb(0b101u), 2);
BOOST_CHECK_EQUAL(msb(0b110u), 2);
BOOST_CHECK_EQUAL(msb(0b111u), 2);
}

BOOST_AUTO_TEST_CASE(bit_test_test) {
BOOST_CHECK_EQUAL(bit_test(0b001u, 0), true);
BOOST_CHECK_EQUAL(bit_test(0b001u, 1), false);
BOOST_CHECK_EQUAL(bit_test(0b001u, 2), false);
BOOST_CHECK_EQUAL(bit_test(0u, 2), false);
BOOST_CHECK_EQUAL(bit_test(0b1111u, 10000), false);
}

BOOST_AUTO_TEST_CASE(bit_set_test) {
unsigned a = 0;
BOOST_CHECK_EQUAL(bit_test(a, 0), false);
BOOST_CHECK_EQUAL(bit_test(bit_set(a, 0), 0), true);
BOOST_CHECK_EQUAL(bit_test(bit_set(a, 0), 0), true);
BOOST_CHECK_EQUAL(bit_test(a, 1), false);
BOOST_CHECK_EQUAL(bit_test(bit_set(a, 1), 1), true);
BOOST_CHECK_EQUAL(bit_test(bit_set(a, 1), 1), true);
BOOST_CHECK_THROW(bit_set(a, 10000), std::invalid_argument);
}

BOOST_AUTO_TEST_CASE(bit_unset_test) {
unsigned a = 0b11;
BOOST_CHECK_EQUAL(bit_test(a, 0), true);
BOOST_CHECK_EQUAL(bit_test(bit_unset(a, 0), 0), false);
BOOST_CHECK_EQUAL(bit_test(bit_unset(a, 0), 0), false);
BOOST_CHECK_EQUAL(bit_test(a, 1), true);
BOOST_CHECK_EQUAL(bit_test(bit_unset(a, 1), 1), false);
BOOST_CHECK_EQUAL(bit_test(bit_unset(a, 1), 1), false);
BOOST_CHECK_THROW(bit_unset(a, 10000), std::invalid_argument);
}

BOOST_AUTO_TEST_CASE(bit_flip_test) {
unsigned a = 0b11;
BOOST_CHECK_EQUAL(bit_test(a, 0), true);
BOOST_CHECK_EQUAL(bit_test(bit_flip(a, 0), 0), false);
BOOST_CHECK_EQUAL(bit_test(bit_flip(a, 0), 0), true);
BOOST_CHECK_EQUAL(bit_test(a, 1), true);
BOOST_CHECK_EQUAL(bit_test(bit_flip(a, 1), 1), false);
BOOST_CHECK_EQUAL(bit_test(bit_flip(a, 1), 1), true);
BOOST_CHECK_THROW(bit_flip(a, 10000), std::invalid_argument);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_CASE(is_zero_test) {
BOOST_CHECK(is_zero(0u));
BOOST_CHECK(is_zero(0));
BOOST_CHECK(!is_zero(1));
BOOST_CHECK(!is_zero(1u));
BOOST_CHECK(!is_zero(-1));
BOOST_CHECK(!is_zero(600));
BOOST_CHECK(!is_zero(600u));
BOOST_CHECK(!is_zero(-600));
}