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