tasm_lib/arithmetic/u64/
div2.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)]
26pub struct Div2;
27
28impl BasicSnippet for Div2 {
29 fn inputs(&self) -> Vec<(DataType, String)> {
30 vec![(DataType::U64, "arg".to_string())]
31 }
32
33 fn outputs(&self) -> Vec<(DataType, String)> {
34 vec![(DataType::U64, "(arg/2)".to_string())]
35 }
36
37 fn entrypoint(&self) -> String {
38 "tasmlib_arithmetic_u64_div2".to_string()
39 }
40
41 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
42 triton_asm!(
43 {self.entrypoint()}:
46 push 2
48 pick 1
49 div_mod
50 pop 1
51 push 2
55 pick 2
56 div_mod
57 push {1_u32 << 31}
61 hint two_pow_31: u32 = stack[0]
62 mul
63 hint carry: u32 = stack[0]
64 pick 2
67 add
68 return
71 )
72 }
73
74 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
75 let mut sign_offs = HashMap::new();
76 sign_offs.insert(Reviewer("ferdinand"), 0xe77a12ba30ef339b.into());
77 sign_offs
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::test_helpers::negative_test;
85 use crate::test_prelude::*;
86
87 impl Div2 {
88 fn assert_expected_behavior(&self, arg: u64) {
89 let initial_stack = self.set_up_test_stack(arg);
90
91 let mut expected_stack = initial_stack.clone();
92 self.rust_shadow(&mut expected_stack);
93
94 test_rust_equivalence_given_complete_state(
95 &ShadowedClosure::new(Self),
96 &initial_stack,
97 &[],
98 &NonDeterminism::default(),
99 &None,
100 Some(&expected_stack),
101 );
102 }
103 }
104
105 impl Closure for Div2 {
106 type Args = u64;
107
108 fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
109 let arg = pop_encodable::<Self::Args>(stack);
110 push_encodable(stack, &(arg / 2));
111 }
112
113 fn pseudorandom_args(
114 &self,
115 seed: [u8; 32],
116 bench_case: Option<BenchmarkCase>,
117 ) -> Self::Args {
118 match bench_case {
119 Some(BenchmarkCase::CommonCase) => 0x8000_0000,
120 Some(BenchmarkCase::WorstCase) => 0xf000_0001_f000_0000,
121 None => StdRng::from_seed(seed).random(),
122 }
123 }
124 }
125
126 #[test]
127 fn rust_shadow() {
128 ShadowedClosure::new(Div2).test();
129 }
130
131 #[proptest]
132 fn lo_is_not_u32(hi: u32, #[strategy(1_u64 << 32..)] lo: u64) {
133 let stack = [Div2.init_stack_for_isolated_run(), bfe_vec![hi, lo]].concat();
134
135 let error = InstructionError::OpStackError(OpStackError::FailedU32Conversion(bfe!(lo)));
136 negative_test(
137 &ShadowedClosure::new(Div2),
138 InitVmState::with_stack(stack),
139 &[error],
140 );
141 }
142
143 #[proptest]
144 fn hi_is_not_u32(#[strategy(1_u64 << 32..)] hi: u64, lo: u32) {
145 let stack = [Div2.init_stack_for_isolated_run(), bfe_vec![hi, lo]].concat();
146
147 let error = InstructionError::OpStackError(OpStackError::FailedU32Conversion(bfe!(hi)));
148 negative_test(
149 &ShadowedClosure::new(Div2),
150 InitVmState::with_stack(stack),
151 &[error],
152 );
153 }
154
155 #[test]
156 fn div_2_test() {
157 let small_args = 0..9;
158 let mid_args = (0..9).map(|offset| (1 << 32) + offset);
159 let large_args = [0, 4, 1 << 31, 0b111 << 31].map(|offset| (1 << 63) + offset);
160
161 for arg in small_args.chain(mid_args).chain(large_args) {
162 Div2.assert_expected_behavior(arg);
163 }
164 }
165}
166
167#[cfg(test)]
168mod benches {
169 use super::*;
170 use crate::test_prelude::*;
171
172 #[test]
173 fn benchmark() {
174 ShadowedClosure::new(Div2).bench();
175 }
176}