Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
to_radix.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <stdexcept>
6#include <vector>
7
11
12namespace bb::avm2::simulation {
13
14std::vector<uint8_t> ToRadix::to_le_radix(const FF& value, uint32_t num_limbs, uint32_t radix)
15{
16 uint256_t value_integer = static_cast<uint256_t>(value);
17 auto limbs = std::vector<uint8_t>();
18 size_t radix_index = static_cast<size_t>(radix);
19 limbs.reserve(std::max(num_limbs, static_cast<uint32_t>(get_p_limbs_per_radix()[radix_index].size())));
20
21 while (value_integer > 0) {
22 limbs.push_back(static_cast<uint8_t>(value_integer % radix));
23 value_integer /= radix;
24 }
25
26 if (num_limbs > limbs.size()) {
27 limbs.insert(limbs.end(), num_limbs - limbs.size(), 0);
28 }
29
30 // The event should never have less limbs than the necessary to perform the decomposition
32 .value = value,
33 .radix = radix,
34 .limbs = limbs,
35 });
36
37 if (num_limbs < limbs.size()) {
38 limbs.erase(limbs.begin() + num_limbs, limbs.end());
39 }
40
41 return limbs;
42}
43
44std::vector<bool> ToRadix::to_le_bits(const FF& value, uint32_t num_limbs)
45{
46 std::vector<uint8_t> limbs = to_le_radix(value, num_limbs, 2);
47 std::vector<bool> bits(limbs.size());
48
49 std::transform(limbs.begin(), limbs.end(), bits.begin(), [](uint8_t val) {
50 return val != 0; // Convert nonzero values to `true`, zero to `false`
51 });
52
53 return bits;
54}
55
57 const FF& value,
58 uint32_t radix,
59 uint32_t num_limbs,
60 bool is_output_bits, // Decides if output is U1 or U8
62{
63 uint32_t execution_clk = execution_id_manager.get_execution_id();
64 uint32_t space_id = memory.get_space_id();
65
66 try {
67 // todo(ilyas): there must be a nicer way to do this in the simulator. See if it's fine to provide
68 // a hierarchy of errors so that we can throw on the first error we encounter
69
70 // Error handling - check that the maximum write address does not exceed the highest memory address
71 // This subtrace writes in the range { dst_addr, dst_addr + 1, ..., dst_addr + num_limbs - 1 }
72 uint64_t max_write_address = static_cast<uint64_t>(dst_addr) + num_limbs - 1;
73 bool dst_out_of_range = gt.gt(max_write_address, AVM_HIGHEST_MEM_ADDRESS);
74
75 // Error handling - check that the radix value is within the valid range
76 // The valid range is [2, 256]. Therefore, the radix is invalid if (2 > radix) or (radix > 256)
77 // We need to perform both checks explicitly since that is what the circuit would do
78 bool radix_is_lt_2 = gt.gt(2, radix);
79 bool radix_is_gt_256 = gt.gt(radix, 256);
80
81 // Error handling - check that if is_output_bits is true, the radix has to be 2
82 bool invalid_bitwise_radix = is_output_bits && (radix != 2);
83 // Error handling - if num_limbs is zero, value needs to be zero
84 bool invalid_num_limbs = (num_limbs == 0) && (value != FF(0));
85
86 if (dst_out_of_range || radix_is_lt_2 || radix_is_gt_256 || invalid_bitwise_radix || invalid_num_limbs) {
87 throw std::runtime_error("Invalid parameters for ToRadix");
88 }
89
90 // If we get to this point, we are error free.
91 std::vector<MemoryValue> be_output_limbs;
92 be_output_limbs.reserve(num_limbs);
93 if (is_output_bits) {
94 std::vector<bool> output_bits = to_le_bits(value, num_limbs);
95 std::ranges::for_each(output_bits.rbegin(), output_bits.rend(), [&](bool bit) {
96 be_output_limbs.push_back(MemoryValue::from<uint1_t>(bit));
97 });
98 } else {
99 std::vector<uint8_t> output_limbs_u8 = to_le_radix(value, num_limbs, radix);
100 std::ranges::for_each(output_limbs_u8.rbegin(), output_limbs_u8.rend(), [&](uint8_t limb) {
101 be_output_limbs.push_back(MemoryValue::from<uint8_t>(limb));
102 });
103 }
104
105 for (uint32_t i = 0; i < num_limbs; i++) {
106 memory.set(dst_addr + i, be_output_limbs[i]);
107 }
108
109 memory_events.emit({
110 .execution_clk = execution_clk,
111 .space_id = space_id,
112 .dst_addr = dst_addr,
113 .value = value,
114 .radix = radix,
115 .is_output_bits = is_output_bits,
116 .limbs = be_output_limbs,
117 });
118
119 } catch (const std::exception& e) {
120 memory_events.emit({
121 .execution_clk = execution_clk,
122 .space_id = space_id,
123 .dst_addr = dst_addr,
124 .value = value,
125 .radix = radix,
126 .is_output_bits = is_output_bits,
127 .limbs = std::vector<MemoryValue>(num_limbs, MemoryValue::from<FF>(0)),
128 });
129 throw ToRadixException("Error during BE conversion, " + std::string(e.what()));
130 }
131}
132
133} // namespace bb::avm2::simulation
#define AVM_HIGHEST_MEM_ADDRESS
virtual uint32_t get_execution_id() const =0
EventEmitterInterface< ToRadixMemoryEvent > & memory_events
Definition to_radix.hpp:50
std::vector< bool > to_le_bits(const FF &value, uint32_t num_limbs) override
Definition to_radix.cpp:44
std::vector< uint8_t > to_le_radix(const FF &value, uint32_t num_limbs, uint32_t radix) override
Definition to_radix.cpp:14
EventEmitterInterface< ToRadixEvent > & events
Definition to_radix.hpp:49
void to_be_radix(MemoryInterface &memory, const FF &value, uint32_t radix, uint32_t num_limbs, bool is_output_bits, MemoryAddress dst_addr) override
Definition to_radix.cpp:56
ExecutionIdManagerInterface & execution_id_manager
Definition to_radix.hpp:47
uint32_t dst_addr
const std::array< std::vector< uint8_t >, 257 > & get_p_limbs_per_radix()
Definition to_radix.cpp:33
uint32_t MemoryAddress
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13