Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
uint_mutations.hpp
Go to the documentation of this file.
1
8
9#pragma once
10
11#include <algorithm>
12#include <array>
13#include <functional>
14#include <random>
15#include <type_traits>
16#include <vector>
17
21
22template <typename T> struct UintTraits {
23 static constexpr bool has_mask = false;
24 static constexpr T mask() { return T(0); }
25};
26
27template <> struct UintTraits<uint8_t> {
28 static constexpr bool has_mask = true;
29 static constexpr uint8_t mask() { return 0xff; }
30};
31
32template <> struct UintTraits<uint16_t> {
33 static constexpr bool has_mask = true;
34 static constexpr uint16_t mask() { return 0xffff; }
35};
36
37template <> struct UintTraits<uint32_t> {
38 static constexpr bool has_mask = true;
39 static constexpr uint32_t mask() { return 0xffffffffUL; }
40};
41
42template <> struct UintTraits<uint64_t> {
43 static constexpr bool has_mask = true;
44 static constexpr uint64_t mask() { return 0xffffffffffffffffULL; }
45};
46
47template <> struct UintTraits<uint128_t> {
48 static constexpr bool has_mask = false;
49};
50
51// BoundaryValues: curated sets of edge-case values for each uint type
52// These values exercise:
53// - Zero/one edge cases
54// - Power-of-2 midpoints (which trigger different code paths in multi-limb arithmetic)
55// - Maximum value overflow detection
56// - Cross-type boundaries (e.g., U8 max within U16)
57template <typename T> struct BoundaryValues;
58
59template <> struct BoundaryValues<uint8_t> {
60 static constexpr std::array<uint8_t, 8> values = {
61 0, // Zero
62 1, // One
63 2, // Small value
64 127, // 2^7 - 1 (max value with high bit clear)
65 128, // 2^7 (midpoint, high bit set)
66 254, // Max - 1
67 255, // Max (2^8 - 1)
68 0x55, // Alternating bits (01010101)
69 };
70};
71
72template <> struct BoundaryValues<uint16_t> {
73 static constexpr std::array<uint16_t, 10> values = {
74 0, 1, 2,
75 255, // U8 max (cross-type boundary)
76 256, // U8 max + 1
77 32767, // 2^15 - 1 (max with high bit clear)
78 32768, // 2^15 (midpoint, high bit set)
79 65534, // Max - 1
80 65535, // Max (2^16 - 1)
81 0x5555, // Alternating bits
82 };
83};
84
85template <> struct BoundaryValues<uint32_t> {
86 static constexpr std::array<uint32_t, 12> values = {
87 0, 1, 2, 255,
88 256, // U8 boundaries
89 65535,
90 65536, // U16 boundaries
91 0x7FFFFFFF, // 2^31 - 1 (max with high bit clear)
92 0x80000000, // 2^31 (midpoint, high bit set)
93 0xFFFFFFFE, // Max - 1
94 0xFFFFFFFF, // Max (2^32 - 1)
95 0x55555555, // Alternating bits
96 };
97};
98
99template <> struct BoundaryValues<uint64_t> {
100 static constexpr std::array<uint64_t, 14> values = {
101 0,
102 1,
103 2,
104 0xFF,
105 0x100, // U8 boundaries
106 0xFFFF,
107 0x10000, // U16 boundaries
108 0xFFFFFFFF, // U32 max
109 0x100000000, // U32 max + 1 (exercises carry into high word)
110 0x7FFFFFFFFFFFFFFF, // 2^63 - 1 (max with high bit clear)
111 0x8000000000000000, // 2^63 (midpoint, high bit set)
112 0xFFFFFFFFFFFFFFFE, // Max - 1
113 0xFFFFFFFFFFFFFFFF, // Max (2^64 - 1)
114 0x5555555555555555, // Alternating bits
115 };
116};
117
118template <> struct BoundaryValues<uint128_t> {
119 // Critical for multi-limb arithmetic: values at limb boundaries
120 // U128 is stored as two 64-bit limbs, so 2^64 boundary is crucial
121 static inline const std::array<uint128_t, 14> values = {
122 uint128_t(0),
123 uint128_t(1),
124 uint128_t(2),
125 uint128_t(0xFFFFFFFFFFFFFFFFULL), // U64 max (low limb full)
126 uint128_t(0xFFFFFFFFFFFFFFFFULL) + 1, // 2^64 (carry into high limb)
127 uint128_t(1) << 64, // 2^64 (high limb = 1)
128 (uint128_t(0x7FFFFFFFFFFFFFFFULL) << 64) | 0xFFFFFFFFFFFFFFFFULL, // 2^127 - 1
129 uint128_t(1) << 127, // 2^127 (midpoint)
130 (uint128_t(0xFFFFFFFFFFFFFFFFULL) << 64) | 0xFFFFFFFFFFFFFFFEULL, // Max - 1
131 (uint128_t(0xFFFFFFFFFFFFFFFFULL) << 64) | 0xFFFFFFFFFFFFFFFFULL, // Max (2^128 - 1)
132 uint128_t(1) << 96, // 2^96 (3/4 point)
133 (uint128_t(1) << 96) - 1, // 2^96 - 1
134 uint128_t(1) << 63, // 2^63 (quarter point)
135 (uint128_t(0x5555555555555555ULL) << 64) | 0x5555555555555555ULL, // Alternating bits
136 };
137};
138
139template <typename T>
141 std::mt19937_64& rng)
142{
144}
145
147{
148 // Generate two random uint64_t values and combine them
149 uint128_t lo = std::uniform_int_distribution<uint64_t>(0, 0xffffffffffffffffULL)(rng);
150 uint128_t hi = std::uniform_int_distribution<uint64_t>(0, 0xffffffffffffffffULL)(rng);
151 return (hi << 64) + lo;
152}
153
154namespace uint_mutation {
155template <typename T> struct RandomSelection {
156 static void mutate(std::mt19937_64& rng, T& value) { value = generate_random_uint<T>(rng); }
157};
158
159template <typename T> struct IncrementBy1 {
160 static void mutate(T& value)
161 {
162 if constexpr (UintTraits<T>::has_mask) {
163 value = (value + 1) & UintTraits<T>::mask();
164 } else {
165 value = value + 1;
166 }
167 }
168};
169
170template <typename T> struct DecrementBy1 {
171 static void mutate(T& value)
172 {
173 if constexpr (UintTraits<T>::has_mask) {
174 value = (value - 1) & UintTraits<T>::mask();
175 } else {
176 value = value - 1;
177 }
178 }
179};
180
181template <typename T> struct AddRandomValue {
182 static void mutate(T& value, std::mt19937_64& rng)
183 {
184 if constexpr (UintTraits<T>::has_mask) {
185 value = (value + generate_random_uint<T>(rng)) & UintTraits<T>::mask();
186 } else {
187 value = value + generate_random_uint<T>(rng);
188 }
189 }
190};
191
192template <typename T> struct BoundarySelection {
193 static void mutate(std::mt19937_64& rng, T& value)
194 {
195 const auto& bounds = BoundaryValues<T>::values;
196 value = bounds[std::uniform_int_distribution<size_t>(0, bounds.size() - 1)(rng)];
197 }
198};
199} // namespace uint_mutation
200
201// Generic mutation function using WeightedSelectionConfig
202template <typename T, typename ConfigType> void mutate_uint(T& value, std::mt19937_64& rng, const ConfigType& config)
203{
204 UintMutationOptions option = config.select(rng);
205
206 switch (option) {
209 break;
212 break;
215 break;
218 break;
221 break;
222 }
223}
224
226{
227 return generate_random_uint<uint8_t>(rng);
228}
229
231{
232 return generate_random_uint<uint16_t>(rng);
233}
234
236{
237 return generate_random_uint<uint32_t>(rng);
238}
239
241{
242 return generate_random_uint<uint64_t>(rng);
243}
244
UintMutationOptions
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:44
static constexpr uint16_t mask()
static constexpr uint32_t mask()
static constexpr uint64_t mask()
static constexpr uint8_t mask()
static constexpr bool has_mask
static constexpr T mask()
static void mutate(T &value, std::mt19937_64 &rng)
static void mutate(std::mt19937_64 &rng, T &value)
static void mutate(T &value)
static void mutate(T &value)
static void mutate(std::mt19937_64 &rng, T &value)
uint64_t generate_random_uint64(std::mt19937_64 &rng)
uint128_t generate_random_uint128(std::mt19937_64 &rng)
uint32_t generate_random_uint32(std::mt19937_64 &rng)
void mutate_uint(T &value, std::mt19937_64 &rng, const ConfigType &config)
std::enable_if< std::is_integral< T >::value &&std::is_unsigned< T >::value, T >::type generate_random_uint(std::mt19937_64 &rng)
uint16_t generate_random_uint16(std::mt19937_64 &rng)
uint128_t generate_random_uint< uint128_t >(std::mt19937_64 &rng)
uint8_t generate_random_uint8(std::mt19937_64 &rng)