tasm_lib/arithmetic/u160/
safe_add.rs1use triton_vm::prelude::*;
2
3use crate::arithmetic::u160::overflowing_add::OverflowingAdd;
4use crate::prelude::*;
5
6#[derive(Debug, Clone)]
7pub struct SafeAdd;
8
9impl SafeAdd {
26 pub(crate) const OVERFLOW_ERROR_ID: i128 = 570;
27}
28
29impl BasicSnippet for SafeAdd {
30 fn parameters(&self) -> Vec<(DataType, String)> {
31 vec![
32 (DataType::U160, "lhs".to_owned()),
33 (DataType::U160, "rhs".to_owned()),
34 ]
35 }
36
37 fn return_values(&self) -> Vec<(DataType, String)> {
38 vec![(DataType::U160, "sum".to_owned())]
39 }
40
41 fn entrypoint(&self) -> String {
42 "tasmlib_arithmetic_u160_safe_add".to_string()
43 }
44
45 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
46 let add_code = OverflowingAdd::addition_code();
47
48 triton_asm! {
49 {self.entrypoint()}:
52 {&add_code}
53 push 0
56 eq
57 assert error_id {Self::OVERFLOW_ERROR_ID}
58 return
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use num::BigUint;
69 use rand::rngs::StdRng;
70
71 use super::*;
72 use crate::arithmetic::u160::u128_to_u160_shl_32;
73 use crate::arithmetic::u160::u128_to_u160_shl_32_lower_limb_filled;
74 use crate::test_prelude::*;
75
76 #[test]
77 fn rust_shadow() {
78 ShadowedClosure::new(SafeAdd).test()
79 }
80
81 #[test]
82 fn overflow_test() {
83 for (left, right) in [
84 (1 << 127, 1 << 127),
85 (u128::MAX, u128::MAX),
86 (u128::MAX, 1),
87 (u128::MAX, 1 << 31),
88 (u128::MAX, 1 << 32),
89 (u128::MAX, 1 << 33),
90 (u128::MAX, 1 << 63),
91 (u128::MAX, 1 << 64),
92 (u128::MAX, 1 << 65),
93 (u128::MAX, 1 << 95),
94 (u128::MAX, 1 << 96),
95 (u128::MAX, 1 << 97),
96 (u128::MAX - 1, 2),
97 ]
98 .into_iter()
99 .flat_map(|(left, right)| [(left, right), (right, left)])
100 {
101 let left = u128_to_u160_shl_32_lower_limb_filled(left);
102 let right = u128_to_u160_shl_32(right);
103 test_assertion_failure(
104 &ShadowedClosure::new(SafeAdd),
105 InitVmState::with_stack(SafeAdd.set_up_test_stack((left, right))),
106 &[SafeAdd::OVERFLOW_ERROR_ID],
107 );
108 }
109
110 for i in 0..128 {
111 let left = 1 << i;
112 let right = u128::MAX - left + 1;
113
114 assert_eq!(
115 (0, true),
116 left.overflowing_add(right),
117 "i = {i}. a = {left}, b = {right}"
118 );
119
120 let left = u128_to_u160_shl_32_lower_limb_filled(left);
121 let right = u128_to_u160_shl_32(right);
122
123 test_assertion_failure(
124 &ShadowedClosure::new(SafeAdd),
125 InitVmState::with_stack(SafeAdd.set_up_test_stack((left, right))),
126 &[SafeAdd::OVERFLOW_ERROR_ID],
127 );
128 }
129 }
130
131 impl Closure for SafeAdd {
132 type Args = <OverflowingAdd as Closure>::Args;
133
134 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
135 let left: [u32; 5] = pop_encodable(stack);
136 let left: BigUint = BigUint::new(left.to_vec());
137 let right: [u32; 5] = pop_encodable(stack);
138 let right: BigUint = BigUint::new(right.to_vec());
139 let sum = left + right;
140 let mut sum = sum.to_u32_digits();
141 assert!(sum.len() <= 5, "Overflow");
142
143 sum.resize(5, 0);
144 let sum: [u32; 5] = sum.try_into().unwrap();
145
146 push_encodable(stack, &sum);
147 }
148
149 fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
150 let mut rng = StdRng::from_seed(seed);
151 let lhs: [u32; 5] = rng.random();
152 let lhs_as_biguint: BigUint = BigUint::new(lhs.to_vec());
153
154 let u160_max = BigUint::from_bytes_be(&[0xFF; 20]);
155 let max = &u160_max - &lhs_as_biguint;
156
157 let mut rhs_bytes = [0u8; 20];
159 let rhs = loop {
160 rng.fill(&mut rhs_bytes);
161 let candidate = BigUint::from_bytes_be(&rhs_bytes);
162 if candidate < max {
163 break candidate;
164 }
165 };
166
167 let mut rhs = rhs.to_u32_digits();
168 rhs.resize(5, 0);
169
170 (lhs, rhs.try_into().unwrap())
171 }
172
173 fn corner_case_args(&self) -> Vec<Self::Args> {
174 fn u160_checked_add(l: [u32; 5], r: [u32; 5]) -> Option<[u32; 5]> {
175 let l: BigUint = BigUint::new(l.to_vec());
176 let r: BigUint = BigUint::new(r.to_vec());
177
178 let sum = l + r;
179 let mut sum = sum.to_u32_digits();
180
181 if sum.len() > 5 {
182 None
183 } else {
184 sum.resize(5, 0);
185 Some(sum.try_into().unwrap())
186 }
187 }
188
189 let edge_case_points = OverflowingAdd::edge_case_points();
190
191 edge_case_points
192 .iter()
193 .cartesian_product(&edge_case_points)
194 .filter(|&(&l, &r)| u160_checked_add(l, r).is_some())
195 .map(|(&l, &r)| (l, r))
196 .collect()
197 }
198 }
199}
200
201#[cfg(test)]
202mod benches {
203 use super::*;
204 use crate::test_prelude::*;
205
206 #[test]
207 fn benchmark() {
208 ShadowedClosure::new(SafeAdd).bench()
209 }
210}