tasm_lib/arithmetic/u64/
add.rs1use std::collections::HashMap;
2
3use tasm_lib::prelude::BasicSnippet;
4use triton_vm::prelude::*;
5
6use crate::arithmetic;
7use crate::prelude::*;
8use crate::traits::basic_snippet::Reviewer;
9use crate::traits::basic_snippet::SignOffFingerprint;
10
11#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct Add;
31
32impl Add {
33 pub const OVERFLOW_ERROR_ID: i128 = 310;
34}
35
36impl BasicSnippet for Add {
37 fn parameters(&self) -> Vec<(DataType, String)> {
38 ["rhs", "lhs"]
39 .map(|side| (DataType::U64, side.to_string()))
40 .to_vec()
41 }
42
43 fn return_values(&self) -> Vec<(DataType, String)> {
44 vec![(DataType::U64, "sum".to_string())]
45 }
46
47 fn entrypoint(&self) -> String {
48 "tasmlib_arithmetic_u64_add".to_string()
49 }
50
51 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
52 triton_asm!({self.entrypoint()}:
53 {&arithmetic::u64::overflowing_add::OverflowingAdd::addition_code()}
54 push 0
57 eq
58 assert error_id {Self::OVERFLOW_ERROR_ID}
59 return
62 )
63 }
64
65 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
66 let mut sign_offs = HashMap::new();
67 sign_offs.insert(Reviewer("ferdinand"), 0xe275906c3d0c35f3.into());
68 sign_offs
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::arithmetic::u64::overflowing_add::OverflowingAdd;
76 use crate::test_helpers::test_rust_equivalence_given_execution_state;
77 use crate::test_prelude::*;
78
79 impl Closure for Add {
80 type Args = <OverflowingAdd as Closure>::Args;
81
82 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
83 let (left, right) = pop_encodable::<Self::Args>(stack);
84 let sum = left.checked_add(right).expect("overflow occurred");
85 push_encodable(stack, &sum);
86 }
87
88 fn pseudorandom_args(&self, seed: [u8; 32], _: Option<BenchmarkCase>) -> Self::Args {
89 let mut rng = StdRng::from_seed(seed);
90 let left = rng.random();
91 let right = rng.random_range(0..=u64::MAX - left);
92
93 (left, right)
94 }
95
96 fn corner_case_args(&self) -> Vec<Self::Args> {
97 let corner_case_points = OverflowingAdd::corner_case_points();
98
99 corner_case_points
100 .iter()
101 .cartesian_product(&corner_case_points)
102 .filter(|&(&l, &r)| l.checked_add(r).is_some())
103 .map(|(&l, &r)| (l, r))
104 .collect()
105 }
106 }
107
108 #[test]
109 fn unit_test() {
110 ShadowedClosure::new(Add).test();
111 }
112
113 #[proptest]
114 fn proptest(left: u64, #[strategy(0..u64::MAX - #left)] right: u64) {
115 let initial_state = InitVmState::with_stack(Add.set_up_test_stack((left, right)));
116 test_rust_equivalence_given_execution_state(&ShadowedClosure::new(Add), initial_state);
117 }
118
119 #[proptest]
120 fn triton_vm_crashes_on_overflowing_add(left: u64, #[strategy(u64::MAX - #left..)] right: u64) {
121 prop_assume!(left.checked_add(right).is_none());
122
123 test_assertion_failure(
124 &ShadowedClosure::new(Add),
125 InitVmState::with_stack(Add.set_up_test_stack((left, right))),
126 &[Add::OVERFLOW_ERROR_ID],
127 );
128 }
129}
130
131#[cfg(test)]
132mod benches {
133 use super::*;
134 use crate::test_prelude::*;
135
136 #[test]
137 fn benchmark() {
138 ShadowedClosure::new(Add).bench()
139 }
140}