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 inputs(&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 outputs(&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"), 0xa1531b9db1f3f021.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);
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>) {
173 let (arg, shift_amount) = pop_encodable::<Self::Args>(stack);
174 assert!(shift_amount < 64);
175 push_encodable(stack, &(arg >> shift_amount));
176 }
177
178 fn pseudorandom_args(
179 &self,
180 seed: [u8; 32],
181 bench_case: Option<BenchmarkCase>,
182 ) -> Self::Args {
183 let mut rng = StdRng::from_seed(seed);
184
185 match bench_case {
186 Some(BenchmarkCase::CommonCase) => (0x642, 15),
187 Some(BenchmarkCase::WorstCase) => (0x123, 33),
188 None => (rng.random(), rng.random_range(0..64)),
189 }
190 }
191
192 fn corner_case_args(&self) -> Vec<Self::Args> {
193 (0..64).map(|i| (1 << i, i)).collect()
194 }
195 }
196
197 #[test]
198 fn rust_shadow() {
199 ShadowedClosure::new(ShiftRight).test()
200 }
201
202 #[test]
203 fn unit_test() {
204 ShiftRight.assert_expected_shift_right_behavior(2, 8);
205 }
206
207 #[proptest]
208 fn property_test(arg: u64, #[strategy(0_u32..64)] shift_amount: u32) {
209 ShiftRight.assert_expected_shift_right_behavior(shift_amount, arg);
210 }
211
212 #[proptest]
213 fn negative_property_test(arg: u64, #[strategy(64_u32..)] shift_amount: u32) {
214 test_assertion_failure(
215 &ShadowedClosure::new(ShiftRight),
216 InitVmState::with_stack(ShiftRight.set_up_test_stack((arg, shift_amount))),
217 &[ShiftRight::SHIFT_AMOUNT_TOO_BIG_ERROR_ID],
218 );
219 }
220}
221
222#[cfg(test)]
223mod benches {
224 use super::*;
225 use crate::test_prelude::*;
226
227 #[test]
228 fn benchmark() {
229 ShadowedClosure::new(ShiftRight).bench();
230 }
231}