tasm_lib/arithmetic/u64/
shift_left.rs1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::arithmetic::u64::shift_right::ShiftRight;
6use crate::prelude::*;
7use crate::traits::basic_snippet::Reviewer;
8use crate::traits::basic_snippet::SignOffFingerprint;
9
10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
32pub struct ShiftLeft;
33
34impl ShiftLeft {
35 pub const SHIFT_AMOUNT_TOO_BIG_ERROR_ID: i128 = 370;
36}
37
38impl BasicSnippet for ShiftLeft {
39 fn parameters(&self) -> Vec<(DataType, String)> {
40 ShiftRight.parameters()
41 }
42
43 fn return_values(&self) -> Vec<(DataType, String)> {
44 ShiftRight.return_values()
45 }
46
47 fn entrypoint(&self) -> String {
48 "tasmlib_arithmetic_u64_shift_left".to_string()
49 }
50
51 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
52 let entrypoint = self.entrypoint();
53 let handle_hi_shift = format!("{entrypoint}_handle_hi_shift");
54
55 triton_asm!(
56 {entrypoint}:
59 push 64
61 dup 1
62 lt
63 assert error_id {Self::SHIFT_AMOUNT_TOO_BIG_ERROR_ID}
64 dup 0
68 push 32
69 lt
70 skiz call {handle_hi_shift}
73 push 2
77 pow
78 pick 2
81 dup 1
82 mul
83 split
86 place 3
87 pop 1
88 mul
91 split
92 place 2
95 add
96 pick 1
97 return
100
101 {handle_hi_shift}:
104 addi -32
105 pick 2
108 pop 1
109 push 0
110 place 1
111 return
115 )
116 }
117
118 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
119 let mut sign_offs = HashMap::new();
120 sign_offs.insert(Reviewer("ferdinand"), 0x4a03ab75c8546370.into());
121 sign_offs
122 }
123}
124
125#[cfg(test)]
126pub(crate) mod tests {
127 use super::*;
128 use crate::test_prelude::*;
129
130 impl ShiftLeft {
131 pub fn assert_expected_behavior(&self, shift_amount: u32, arg: u64) {
132 let initial_stack = self.set_up_test_stack((arg, shift_amount));
133
134 let mut expected_stack = initial_stack.clone();
135 self.rust_shadow(&mut expected_stack).unwrap();
136
137 test_rust_equivalence_given_complete_state(
138 &ShadowedClosure::new(Self),
139 &initial_stack,
140 &[],
141 &NonDeterminism::default(),
142 &None,
143 Some(&expected_stack),
144 );
145 }
146 }
147
148 impl Closure for ShiftLeft {
149 type Args = (u64, u32);
150
151 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) -> Result<(), RustShadowError> {
152 let (arg, shift_amount) = pop_encodable::<Self::Args>(stack)?;
153 if shift_amount >= 64 {
154 return Err(RustShadowError::ArithmeticOverflow);
155 }
156 push_encodable(stack, &(arg << shift_amount));
157 Ok(())
158 }
159
160 fn pseudorandom_args(
161 &self,
162 seed: [u8; 32],
163 bench_case: Option<BenchmarkCase>,
164 ) -> Self::Args {
165 let mut rng = StdRng::from_seed(seed);
166
167 match bench_case {
168 Some(BenchmarkCase::CommonCase) => (0x642, 15),
169 Some(BenchmarkCase::WorstCase) => (0x123456789abcdef, 33),
170 None => (rng.random(), rng.random_range(0..64)),
171 }
172 }
173
174 fn corner_case_args(&self) -> Vec<Self::Args> {
175 (0..64).map(|i| (1, i)).collect()
176 }
177 }
178
179 #[macro_rules_attr::apply(test)]
180 fn rust_shadow() {
181 ShadowedClosure::new(ShiftLeft).test()
182 }
183
184 #[macro_rules_attr::apply(test)]
185 fn edge_cases() {
186 for i in 0..64 {
187 ShiftLeft.assert_expected_behavior(i, u64::MAX);
188 }
189 }
190
191 #[macro_rules_attr::apply(proptest)]
192 fn property_test(arg: u64, #[strategy(0_u32..64)] shift_amount: u32) {
193 ShiftLeft.assert_expected_behavior(shift_amount, arg);
194 }
195
196 #[macro_rules_attr::apply(proptest)]
197 fn negative_property_test(arg: u64, #[strategy(64_u32..)] shift_amount: u32) {
198 test_assertion_failure(
199 &ShadowedClosure::new(ShiftLeft),
200 InitVmState::with_stack(ShiftLeft.set_up_test_stack((arg, shift_amount))),
201 &[ShiftLeft::SHIFT_AMOUNT_TOO_BIG_ERROR_ID],
202 );
203 }
204}
205
206#[cfg(test)]
207mod benches {
208 use super::*;
209 use crate::test_prelude::*;
210
211 #[macro_rules_attr::apply(test)]
212 fn benchmark() {
213 ShadowedClosure::new(ShiftLeft).bench();
214 }
215}