tasm_lib/arithmetic/u128/
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, Default, Copy, Clone, Eq, PartialEq, Hash)]
31pub struct ShiftRight;
32
33impl ShiftRight {
34 pub const SHIFT_AMOUNT_TOO_BIG_ERROR_ID: i128 = 540;
35}
36
37impl BasicSnippet for ShiftRight {
38 fn parameters(&self) -> Vec<(DataType, String)> {
39 let arg = (DataType::U128, "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::U128, "shifted_arg".to_string())]
47 }
48
49 fn entrypoint(&self) -> String {
50 "tasmlib_arithmetic_u128_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 128
63 dup 1
64 lt
65 assert error_id {Self::SHIFT_AMOUNT_TOO_BIG_ERROR_ID}
66 dup 0
70 push 32
71 lt skiz
73 call {shift_amount_gt_32}
74 push -1
78 mul
79 addi 32 push 2
81 pow dup 0
84 pick 5
85 mul place 4
87 xb_mul pick 3
90 split pick 4
92 split pick 5
94 split pick 6
96 split pop 1 add place 4 add place 3 add place 2 return
107
108 {shift_amount_gt_32}:
112 addi -32 pick 1
114 pop 1 push 0
116 place 4 dup 0
119 push 32
120 lt
121 skiz
122 recurse
123 return
124 )
125 }
126
127 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
128 let mut sign_offs = HashMap::new();
129 sign_offs.insert(Reviewer("ferdinand"), 0x68440e40d1aa55e9.into());
130 sign_offs
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use rand::rngs::StdRng;
137
138 use super::*;
139 use crate::test_prelude::*;
140
141 impl Closure for ShiftRight {
142 type Args = (u128, u32);
143
144 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) -> Result<(), RustShadowError> {
145 let (arg, shift_amount) = pop_encodable::<Self::Args>(stack)?;
146 if shift_amount >= 128 {
147 return Err(RustShadowError::ArithmeticOverflow);
148 }
149 push_encodable(stack, &(arg >> shift_amount));
150 Ok(())
151 }
152
153 fn pseudorandom_args(
154 &self,
155 seed: [u8; 32],
156 bench_case: Option<BenchmarkCase>,
157 ) -> Self::Args {
158 let mut rng = StdRng::from_seed(seed);
159
160 match bench_case {
161 Some(BenchmarkCase::CommonCase) => (0x642, 20),
162 Some(BenchmarkCase::WorstCase) => (0x123, 127),
163 None => (rng.random(), rng.random_range(0..128)),
164 }
165 }
166
167 fn corner_case_args(&self) -> Vec<Self::Args> {
168 [0, 1 << 3, 1 << 64, u64::MAX.into(), 1 << 127, u128::MAX]
169 .into_iter()
170 .cartesian_product(0..128)
171 .collect()
172 }
173 }
174
175 #[macro_rules_attr::apply(test)]
176 fn rust_shadow() {
177 ShadowedClosure::new(ShiftRight).test()
178 }
179
180 #[macro_rules_attr::apply(proptest)]
181 fn too_large_shift_crashes_vm(arg: u128, #[strategy(128_u32..)] shift_amount: u32) {
182 test_assertion_failure(
183 &ShadowedClosure::new(ShiftRight),
184 InitVmState::with_stack(ShiftRight.set_up_test_stack((arg, shift_amount))),
185 &[ShiftRight::SHIFT_AMOUNT_TOO_BIG_ERROR_ID],
186 )
187 }
188}
189
190#[cfg(test)]
191mod benches {
192 use super::*;
193 use crate::test_prelude::*;
194
195 #[macro_rules_attr::apply(test)]
196 fn benchmark() {
197 ShadowedClosure::new(ShiftRight).bench();
198 }
199}