Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sparse_form.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Luke], commit: dd03c4a23ab067274b4964cacb36d1545f73fb14}
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
10#include <array>
11#include <cstddef>
12#include <cstdint>
13#include <vector>
14
15#include "../uint256/uint256.hpp"
16
17namespace bb::numeric {
18
23inline std::vector<uint64_t> slice_input(const uint256_t& input, const uint64_t base, const size_t num_slices)
24{
25 BB_ASSERT(base > 0);
26 uint256_t target = input;
27 std::vector<uint64_t> slices;
28 if (num_slices > 0) {
29 slices.reserve(num_slices);
30 for (size_t i = 0; i < num_slices; ++i) {
31 slices.push_back((target % base).data[0]);
32 target /= base;
33 }
34 } else {
35 while (target > 0) {
36 slices.push_back((target % base).data[0]);
37 target /= base;
38 }
39 }
40 return slices;
41}
42
47inline std::vector<uint64_t> slice_input_using_variable_bases(const uint256_t& input,
48 const std::vector<uint64_t>& bases)
49{
50 uint256_t target = input;
51 std::vector<uint64_t> slices;
52 slices.reserve(bases.size());
53 for (size_t i = 0; i < bases.size(); ++i) {
54 BB_ASSERT(bases[i] > 0);
55 if (target >= bases[i] && i == bases.size() - 1) {
56 throw_or_abort(format("Last key slice greater than ", bases[i]));
57 }
58 slices.push_back((target % bases[i]).data[0]);
59 target /= bases[i];
60 }
61 return slices;
62}
63
67template <uint64_t base, uint64_t num_slices> constexpr std::array<uint256_t, num_slices> get_base_powers()
68{
70 output[0] = 1;
71 for (size_t i = 1; i < num_slices; ++i) {
72 output[i] = output[i - 1] * base;
73 }
74 return output;
75}
76
82template <uint64_t base> constexpr uint256_t map_into_sparse_form(const uint64_t input)
83{
84 uint256_t out = 0UL;
85 auto converted = input;
86
87 constexpr auto base_powers = get_base_powers<base, 32>();
88 for (size_t i = 0; i < 32; ++i) {
89 uint64_t sparse_bit = ((converted >> i) & 1U);
90 if (sparse_bit) {
91 out += base_powers[i];
92 }
93 }
94 return out;
95}
96
102template <uint64_t base> constexpr uint64_t map_from_sparse_form(const uint256_t& input)
103{
104 uint256_t target = input;
105 uint64_t output = 0;
106
107 constexpr auto bases = get_base_powers<base, 32>();
108
109 for (uint64_t i = 0; i < 32; ++i) {
110 const auto& base_power = bases[static_cast<size_t>(31 - i)];
111 uint256_t prev_threshold = 0;
112 for (uint64_t j = 1; j < base + 1; ++j) {
113 const auto threshold = prev_threshold + base_power;
114 if (target < threshold) {
115 bool bit = ((j - 1) & 1);
116 if (bit) {
117 output += (1ULL << (31ULL - i));
118 }
119 if (j > 1) {
120 target -= (prev_threshold);
121 }
122 break;
123 }
124 prev_threshold = threshold;
125 }
126 }
127
128 return output;
129}
130
137template <uint64_t base, size_t num_bits> class sparse_int {
138 public:
139 sparse_int(const uint64_t input = 0)
140 : value(input)
141 {
142 for (size_t i = 0; i < num_bits; ++i) {
143 const uint64_t bit = (input >> i) & 1U;
144 limbs[i] = bit;
145 }
146 }
147 sparse_int(const sparse_int& other) noexcept = default;
148 sparse_int(sparse_int&& other) noexcept = default;
149 sparse_int& operator=(const sparse_int& other) noexcept = default;
150 sparse_int& operator=(sparse_int&& other) noexcept = default;
151 ~sparse_int() noexcept = default;
152
153 // Single-pass carry propagation: correct when all input limbs are < base, which is guaranteed
154 // by the constructor (limbs are 0 or 1) and maintained by this operator (carry produces values < base).
155 sparse_int operator+(const sparse_int& other) const
156 {
157 sparse_int result(*this);
158 for (size_t i = 0; i < num_bits - 1; ++i) {
159 result.limbs[i] += other.limbs[i];
160 if (result.limbs[i] >= base) {
161 result.limbs[i] -= base;
162 ++result.limbs[i + 1];
163 // After carry: result.limbs[i] < base (since both inputs were < base, sum < 2*base,
164 // so subtracting base gives a value < base). The carry of 1 into limbs[i+1] cannot
165 // cascade because limbs[i+1] hasn't been added to other.limbs[i+1] yet.
166 }
167 }
168 result.limbs[num_bits - 1] += other.limbs[num_bits - 1];
169 result.limbs[num_bits - 1] %= base;
170 result.value += other.value;
171 return result;
172 };
173
175 {
176 *this = *this + other;
177 return *this;
178 }
179
180 [[nodiscard]] uint64_t get_value() const { return value; }
181
182 [[nodiscard]] uint64_t get_sparse_value() const
183 {
184 uint64_t result = 0;
185 for (size_t i = num_bits - 1; i < num_bits; --i) {
186 result *= base;
187 result += limbs[i];
188 }
189 return result;
190 }
191
192 const std::array<uint64_t, num_bits>& get_limbs() const { return limbs; }
193
194 private:
195 std::array<uint64_t, num_bits> limbs;
196 uint64_t value;
197};
198
199} // namespace bb::numeric
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
Integer type that stores each bit as a separate digit in the given base. Supports addition with singl...
sparse_int & operator=(sparse_int &&other) noexcept=default
sparse_int & operator=(const sparse_int &other) noexcept=default
std::array< uint64_t, num_bits > limbs
sparse_int(const sparse_int &other) noexcept=default
uint64_t get_sparse_value() const
sparse_int(sparse_int &&other) noexcept=default
const std::array< uint64_t, num_bits > & get_limbs() const
uint64_t get_value() const
~sparse_int() noexcept=default
sparse_int(const uint64_t input=0)
sparse_int operator+=(const sparse_int &other)
std::string format(Args... args)
Definition log.hpp:23
constexpr uint64_t map_from_sparse_form(const uint256_t &input)
Decode a sparse-form uint256_t back to a 32-bit value. Extracts the base-adic digits from most-signif...
constexpr uint256_t map_into_sparse_form(const uint64_t input)
Encode a 32-bit value into sparse form: each binary bit of input becomes a digit in the given base....
std::vector< uint64_t > slice_input(const uint256_t &input, const uint64_t base, const size_t num_slices)
Decompose a uint256_t into digits in the given base (least-significant digit first)....
constexpr std::array< uint256_t, num_slices > get_base_powers()
Compute [1, base, base^2, ..., base^(num_slices-1)] as uint256_t values.
std::vector< uint64_t > slice_input_using_variable_bases(const uint256_t &input, const std::vector< uint64_t > &bases)
Decompose a uint256_t using a different base for each digit position (least-significant first)....
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< Instruction > target
std::byte * data
bb::VectorAffineElementPushSpan< BaseParams > out
void throw_or_abort(std::string const &err)
VectorField result