tasm_lib/arithmetic/u64/
pow2.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/// Raise 2 to the power of the given exponent.
10///
11/// ### Behavior
12///
13/// ```text
14/// BEFORE: _ [arg: u32]
15/// AFTER:  _ [2^arg: u64]
16/// ```
17///
18/// ### Preconditions
19///
20/// - the input `arg` is less than 64
21///
22/// ### Postconditions
23///
24/// - the output is properly [`BFieldCodec`] encoded
25#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
26pub struct Pow2;
27
28impl Pow2 {
29    pub const INPUT_TOO_LARGE_ERROR_ID: i128 = 360;
30}
31
32impl BasicSnippet for Pow2 {
33    fn inputs(&self) -> Vec<(DataType, String)> {
34        vec![(DataType::U32, "arg".to_string())]
35    }
36
37    fn outputs(&self) -> Vec<(DataType, String)> {
38        vec![(DataType::U64, "(2^arg)".to_string())]
39    }
40
41    fn entrypoint(&self) -> String {
42        "tasmlib_arithmetic_u64_pow2".to_string()
43    }
44
45    fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
46        triton_asm! {
47            {self.entrypoint()}:
48                push 64
49                dup 1
50                lt
51                assert error_id {Self::INPUT_TOO_LARGE_ERROR_ID}
52
53                push 2
54                pow
55                split
56                return
57        }
58    }
59
60    fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
61        let mut sign_offs = HashMap::new();
62        sign_offs.insert(Reviewer("ferdinand"), 0x3ea5807c1e92829b.into());
63        sign_offs
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::test_prelude::*;
71
72    impl Pow2 {
73        fn assert_expected_behavior(&self, arg: u32) {
74            let initial_stack = self.set_up_test_stack(arg);
75
76            let mut expected_stack = initial_stack.clone();
77            self.rust_shadow(&mut expected_stack);
78
79            test_rust_equivalence_given_complete_state(
80                &ShadowedClosure::new(Self),
81                &initial_stack,
82                &[],
83                &NonDeterminism::default(),
84                &None,
85                Some(&expected_stack),
86            );
87        }
88    }
89
90    impl Closure for Pow2 {
91        type Args = u32;
92
93        fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
94            let arg = pop_encodable::<Self::Args>(stack);
95            push_encodable(stack, &2_u64.pow(arg));
96        }
97
98        fn pseudorandom_args(
99            &self,
100            seed: [u8; 32],
101            bench_case: Option<BenchmarkCase>,
102        ) -> Self::Args {
103            let Some(bench_case) = bench_case else {
104                return StdRng::from_seed(seed).random_range(0..64);
105            };
106
107            match bench_case {
108                BenchmarkCase::CommonCase => 31,
109                BenchmarkCase::WorstCase => 63,
110            }
111        }
112    }
113
114    #[test]
115    fn rust_shadow() {
116        ShadowedClosure::new(Pow2).test();
117    }
118
119    #[test]
120    fn unit_test() {
121        for arg in 0..64 {
122            Pow2.assert_expected_behavior(arg);
123        }
124    }
125
126    #[proptest]
127    fn negative_property_test(#[strategy(64_u32..)] arg: u32) {
128        let stack = Pow2.set_up_test_stack(arg);
129
130        test_assertion_failure(
131            &ShadowedClosure::new(Pow2),
132            InitVmState::with_stack(stack),
133            &[Pow2::INPUT_TOO_LARGE_ERROR_ID],
134        );
135    }
136}
137
138#[cfg(test)]
139mod benches {
140    use super::*;
141    use crate::test_prelude::*;
142
143    #[test]
144    fn benchmark() {
145        ShadowedClosure::new(Pow2).bench();
146    }
147}