tasm_lib/arithmetic/u64/
shift_right.rs1use 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31pub struct ShiftRight;
32
33impl ShiftRight {
34 pub const SHIFT_AMOUNT_TOO_BIG_ERROR_ID: i128 = 330;
35}
36
37impl BasicSnippet for ShiftRight {
38 fn parameters(&self) -> Vec<(DataType, String)> {
39 let arg = (DataType::U64, "arg".to_string());
40 let shift_amount = (DataType::U32, "shift_amount".to_string());
41
42 vec![arg, shift_amount]
43 }
44
45 fn return_values(&self) -> Vec<(DataType, String)> {
46 vec![(DataType::U64, "shifted_arg".to_string())]
47 }
48
49 fn entrypoint(&self) -> String {
50 "tasmlib_arithmetic_u64_shift_right".to_string()
51 }
52
53 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
54 let entrypoint = self.entrypoint();
55 let shift_amount_gt_32 = format!("{entrypoint}_shift_amount_gt_32");
56
57 triton_asm!(
58 {entrypoint}:
61 push 64
63 dup 1
64 lt
65 assert error_id {Self::SHIFT_AMOUNT_TOO_BIG_ERROR_ID}
66 dup 0
70 push 32
71 lt
72 skiz
75 call {shift_amount_gt_32}
76 push -1
90 mul
91 addi 32
92 push 2
95 pow
96 pick 2
99 dup 1
100 mul
101 split
104 place 3
108 place 3
109 mul
112 split
113 pop 1
114 add
117 return
120
121 {shift_amount_gt_32}:
124 addi -32
125 pick 1
128 pop 1
129 push 0
132 place 2
133 return
136 )
137 }
138
139 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
140 let mut sign_offs = HashMap::new();
141 sign_offs.insert(Reviewer("ferdinand"), 0xa7cfa746b15b2d76.into());
142 sign_offs
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::test_prelude::*;
150
151 impl ShiftRight {
152 pub fn assert_expected_shift_right_behavior(&self, shift_amount: u32, arg: u64) {
153 let initial_stack = self.set_up_test_stack((arg, shift_amount));
154
155 let mut expected_stack = initial_stack.clone();
156 self.rust_shadow(&mut expected_stack).unwrap();
157
158 test_rust_equivalence_given_complete_state(
159 &ShadowedClosure::new(Self),
160 &initial_stack,
161 &[],
162 &NonDeterminism::default(),
163 &None,
164 Some(&expected_stack),
165 );
166 }
167 }
168
169 impl Closure for ShiftRight {
170 type Args = (u64, u32);
171
172 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) -> Result<(), RustShadowError> {
173 let (arg, shift_amount) = pop_encodable::<Self::Args>(stack)?;
174 if shift_amount >= 64 {
175 return Err(RustShadowError::ArithmeticOverflow);
176 }
177 push_encodable(stack, &(arg >> shift_amount));
178 Ok(())
179 }
180
181 fn pseudorandom_args(
182 &self,
183 seed: [u8; 32],
184 bench_case: Option<BenchmarkCase>,
185 ) -> Self::Args {
186 let mut rng = StdRng::from_seed(seed);
187
188 match bench_case {
189 Some(BenchmarkCase::CommonCase) => (0x642, 15),
190 Some(BenchmarkCase::WorstCase) => (0x123, 33),
191 None => (rng.random(), rng.random_range(0..64)),
192 }
193 }
194
195 fn corner_case_args(&self) -> Vec<Self::Args> {
196 (0..64).map(|i| (1 << i, i)).collect()
197 }
198 }
199
200 #[macro_rules_attr::apply(test)]
201 fn rust_shadow() {
202 ShadowedClosure::new(ShiftRight).test()
203 }
204
205 #[macro_rules_attr::apply(test)]
206 fn unit_test() {
207 ShiftRight.assert_expected_shift_right_behavior(2, 8);
208 }
209
210 #[macro_rules_attr::apply(proptest)]
211 fn property_test(arg: u64, #[strategy(0_u32..64)] shift_amount: u32) {
212 ShiftRight.assert_expected_shift_right_behavior(shift_amount, arg);
213 }
214
215 #[macro_rules_attr::apply(proptest)]
216 fn negative_property_test(arg: u64, #[strategy(64_u32..)] shift_amount: u32) {
217 test_assertion_failure(
218 &ShadowedClosure::new(ShiftRight),
219 InitVmState::with_stack(ShiftRight.set_up_test_stack((arg, shift_amount))),
220 &[ShiftRight::SHIFT_AMOUNT_TOO_BIG_ERROR_ID],
221 );
222 }
223}
224
225#[cfg(test)]
226mod benches {
227 use super::*;
228 use crate::test_prelude::*;
229
230 #[macro_rules_attr::apply(test)]
231 fn benchmark() {
232 ShadowedClosure::new(ShiftRight).bench();
233 }
234}