tasm_lib/arithmetic/u160/
overflowing_add.rs1use triton_vm::prelude::*;
2
3use crate::prelude::*;
4
5#[derive(Debug, Clone)]
22pub struct OverflowingAdd;
23
24impl OverflowingAdd {
25 pub(crate) fn addition_code() -> Vec<LabelledInstruction> {
32 triton_asm!(
33 pick 5
34 add
37 split
38 swap 6
42 add
43 add
44 split
45 swap 6
48 add
49 add
50 split
51 swap 6
54 add
55 add
56 split
57 swap 6
60 add
61 add
62 split
63 place 5
66 )
70 }
71}
72
73impl BasicSnippet for OverflowingAdd {
74 fn parameters(&self) -> Vec<(DataType, String)> {
75 ["lhs", "rhs"]
76 .map(|s| (DataType::U160, s.to_owned()))
77 .to_vec()
78 }
79
80 fn return_values(&self) -> Vec<(DataType, String)> {
81 vec![
82 (DataType::U160, "sum".to_owned()),
83 (DataType::Bool, "overflow".to_owned()),
84 ]
85 }
86
87 fn entrypoint(&self) -> String {
88 "tasmlib_arithmetic_u160_overflowing_add".to_string()
89 }
90
91 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
92 triton_asm! { {self.entrypoint()}: {&Self::addition_code()} return }
93 }
94}
95
96#[cfg(test)]
97pub(crate) mod tests {
98 use num::BigUint;
99 use rand::rngs::StdRng;
100
101 use super::*;
102 use crate::arithmetic::u160::u128_to_u160;
103 use crate::arithmetic::u160::u128_to_u160_shl_32;
104 use crate::test_prelude::*;
105
106 impl OverflowingAdd {
107 fn assert_expected_add_behavior(&self, lhs: [u32; 5], rhs: [u32; 5]) {
108 let initial_stack = self.set_up_test_stack((rhs, lhs));
109
110 let mut expected_stack = initial_stack.clone();
111 self.rust_shadow(&mut expected_stack).unwrap();
112
113 test_rust_equivalence_given_complete_state(
114 &ShadowedClosure::new(Self),
115 &initial_stack,
116 &[],
117 &NonDeterminism::default(),
118 &None,
119 Some(&expected_stack),
120 );
121 }
122
123 pub fn edge_case_points() -> Vec<[u32; 5]> {
124 [0, 0x200000002fffffffffff908f8, 1 << 127, u128::MAX]
125 .into_iter()
126 .flat_map(|p| [p.checked_sub(1), Some(p), p.checked_add(1)])
127 .flatten()
128 .map(u128_to_u160)
129 .chain([[u32::MAX; 5], [0, 0, 0, 0, 1]])
130 .collect()
131 }
132 }
133
134 impl Closure for OverflowingAdd {
135 type Args = ([u32; 5], [u32; 5]);
136
137 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) -> Result<(), RustShadowError> {
138 let left: [u32; 5] = pop_encodable(stack)?;
139 let left: BigUint = BigUint::new(left.to_vec());
140 let right: [u32; 5] = pop_encodable(stack)?;
141 let right: BigUint = BigUint::new(right.to_vec());
142 let sum = left + right;
143 let mut sum = sum.to_u32_digits();
144 let overflow_shape_is_valid =
145 sum.len() <= 5 || (sum.len() == 6 && sum.last().is_some_and(|&x| x == 1));
146 if !overflow_shape_is_valid {
147 return Err(RustShadowError::ArithmeticOverflow);
148 }
149
150 let is_overflow = sum.len() == 6;
151
152 sum.resize(5, 0);
153 let sum: [u32; 5] = sum.to_vec().try_into().unwrap();
154 push_encodable(stack, &sum);
155 push_encodable(stack, &is_overflow);
156 Ok(())
157 }
158
159 fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
160 StdRng::from_seed(seed).random()
161 }
162
163 fn corner_case_args(&self) -> Vec<Self::Args> {
164 let edge_case_points = Self::edge_case_points();
165
166 edge_case_points
167 .iter()
168 .cartesian_product(&edge_case_points)
169 .map(|(&l, &r)| (l, r))
170 .collect()
171 }
172 }
173
174 #[macro_rules_attr::apply(test)]
175 fn rust_shadow() {
176 ShadowedClosure::new(OverflowingAdd).test()
177 }
178
179 #[macro_rules_attr::apply(test)]
180 fn unit_test() {
181 let snippet = OverflowingAdd;
182 snippet.assert_expected_add_behavior(u128_to_u160(1u128 << 67), u128_to_u160(1u128 << 67))
183 }
184
185 #[macro_rules_attr::apply(test)]
186 fn overflow_test() {
187 let test_overflowing_add = |a, b| {
188 OverflowingAdd.assert_expected_add_behavior(a, b);
189 OverflowingAdd.assert_expected_add_behavior(b, a);
190 };
191
192 test_overflowing_add([1, 0, 0, 0, 0], [u32::MAX; 5]);
193 test_overflowing_add(
194 [2, 0, 0, 0, 0],
195 [u32::MAX - 1, u32::MAX, u32::MAX, u32::MAX, u32::MAX],
196 );
197 test_overflowing_add([1 << 31, 0, 0, 0, 0], [1 << 31, 0, 0, 0, 0]);
198 test_overflowing_add([u32::MAX; 5], [u32::MAX; 5]);
199
200 for a in [31, 32, 33, 63, 64, 65, 95, 96, 97].map(|p| 1 << p) {
201 test_overflowing_add([u32::MAX; 5], u128_to_u160(a));
202 }
203
204 for i in 0..128 {
205 let a = 1 << i;
206 let b = u128::MAX - a + 1;
207 debug_assert_eq!((0, true), a.overflowing_add(b), "i = {i}; a = {a}, b = {b}");
208
209 test_overflowing_add(u128_to_u160(a), u128_to_u160_shl_32(b));
210 }
211 }
212}