tasm_lib/arithmetic/u64/
incr.rs

1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::prelude::*;
6use crate::traits::basic_snippet::Reviewer;
7use crate::traits::basic_snippet::SignOffFingerprint;
8
9/// Increment a `u64` by 1.
10///
11/// Crashes the VM if the input is [`u64::MAX`].
12///
13/// ### Behavior
14///
15/// ```text
16/// BEFORE: _ v
17/// AFTER:  _ (v+1)
18/// ```
19///
20/// ### Preconditions
21///
22/// - all input arguments are properly [`BFieldCodec`] encoded
23///
24/// ### Postconditions
25///
26/// - the output is properly [`BFieldCodec`] encoded
27#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
28pub struct Incr;
29
30impl Incr {
31    pub const OVERFLOW_ERROR_ID: i128 = 440;
32}
33
34impl BasicSnippet for Incr {
35    fn inputs(&self) -> Vec<(DataType, String)> {
36        vec![(DataType::U64, "value".to_string())]
37    }
38
39    fn outputs(&self) -> Vec<(DataType, String)> {
40        vec![(DataType::U64, "value + 1".to_string())]
41    }
42
43    fn entrypoint(&self) -> String {
44        "tasmlib_arithmetic_u64_incr".to_string()
45    }
46
47    fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
48        let entrypoint = self.entrypoint();
49        let carry = format!("{entrypoint}_carry");
50        triton_asm!(
51            // BEFORE: _ [value: u64]
52            // AFTER:  _ [value + 1: u64]
53            {entrypoint}:
54                addi 1
55                dup 0
56                push {1_u64 << 32}
57                eq
58                skiz
59                    call {carry}
60                return
61
62            {carry}:
63                pop 1
64                addi 1
65                dup 0
66                push {1_u64 << 32}
67                eq
68                push 0
69                eq
70                assert error_id {Self::OVERFLOW_ERROR_ID}
71                push 0
72                return
73        )
74    }
75
76    fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
77        let mut sign_offs = HashMap::new();
78        sign_offs.insert(Reviewer("ferdinand"), 0x786629a8064b2786.into());
79        sign_offs
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::test_prelude::*;
87
88    impl Closure for Incr {
89        type Args = u64;
90
91        fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
92            let v = pop_encodable::<Self::Args>(stack);
93            let incr = v.checked_add(1).unwrap();
94            push_encodable(stack, &incr);
95        }
96
97        fn pseudorandom_args(
98            &self,
99            seed: [u8; 32],
100            bench_case: Option<BenchmarkCase>,
101        ) -> Self::Args {
102            match bench_case {
103                Some(BenchmarkCase::CommonCase) => (1000 << 32) + 7, // no carry
104                Some(BenchmarkCase::WorstCase) => (1000 << 32) + u64::from(u32::MAX), // carry
105                None => StdRng::from_seed(seed).random(),
106            }
107        }
108
109        fn corner_case_args(&self) -> Vec<Self::Args> {
110            vec![0, u32::MAX.into(), u64::MAX - 1]
111        }
112    }
113
114    #[test]
115    fn rust_shadow() {
116        ShadowedClosure::new(Incr).test();
117    }
118
119    #[test]
120    fn u64_max_crashes_vm() {
121        test_assertion_failure(
122            &ShadowedClosure::new(Incr),
123            InitVmState::with_stack(Incr.set_up_test_stack(u64::MAX)),
124            &[Incr::OVERFLOW_ERROR_ID],
125        );
126    }
127}
128
129#[cfg(test)]
130mod benches {
131    use super::*;
132    use crate::test_prelude::*;
133
134    #[test]
135    fn benchmark() {
136        ShadowedClosure::new(Incr).bench();
137    }
138}