1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::list::new::New;
6use crate::prelude::*;
7use crate::traits::basic_snippet::Reviewer;
8use crate::traits::basic_snippet::SignOffFingerprint;
9
10#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct Range;
31
32impl Range {
33 pub const INVALID_ERROR_ID: i128 = 550;
34}
35
36impl BasicSnippet for Range {
37 fn parameters(&self) -> Vec<(DataType, String)> {
38 ["minimum", "supremum"]
39 .map(|s| (DataType::U32, s.to_string()))
40 .to_vec()
41 }
42
43 fn return_values(&self) -> Vec<(DataType, String)> {
44 let list_type = DataType::List(Box::new(DataType::U32));
45 vec![(list_type, "*list".to_string())]
46 }
47
48 fn entrypoint(&self) -> String {
49 "tasmlib_list_range".into()
50 }
51
52 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
53 let new_list = library.import(Box::new(New));
54
55 let entrypoint = self.entrypoint();
56 let inner_loop = format!("{entrypoint}_loop");
57
58 triton_asm!(
59 {entrypoint}:
62 dup 0 addi 1 dup 2 lt assert error_id {Self::INVALID_ERROR_ID}
65
66 dup 0 dup 2 push -1 mul add call {new_list} dup 0 place 4 write_mem 1 call {inner_loop} pop 3 return
78
79 {inner_loop}:
83 dup 2 dup 2 eq skiz return dup 2 place 1 write_mem 1 pick 2 addi 1 place 2 recurse
91 )
92 }
93
94 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
95 let mut sign_offs = HashMap::new();
96 sign_offs.insert(Reviewer("ferdinand"), 0x39a164b082c968d.into());
97 sign_offs
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator;
105 use crate::test_prelude::*;
106
107 impl Range {
108 fn set_up_initial_state(&self, minimum: u32, supremum: u32) -> FunctionInitialState {
109 let mut stack = self.init_stack_for_isolated_run();
110 stack.push(bfe!(minimum));
111 stack.push(bfe!(supremum));
112
113 FunctionInitialState {
114 stack,
115 ..Default::default()
116 }
117 }
118 }
119
120 impl Function for Range {
121 fn rust_shadow(
122 &self,
123 stack: &mut Vec<BFieldElement>,
124 memory: &mut HashMap<BFieldElement, BFieldElement>,
125 ) -> Result<(), RustShadowError> {
126 let supremum = pop_encodable::<u32>(stack)?;
127 let minimum = pop_encodable::<u32>(stack)?;
128 if minimum > supremum {
129 return Err(RustShadowError::Other);
130 }
131
132 let list_pointer = dynamic_allocator(memory);
133 let list = (minimum..supremum).collect_vec();
134 encode_to_memory(memory, list_pointer, &list);
135
136 stack.push(list_pointer);
137 Ok(())
138 }
139
140 fn pseudorandom_initial_state(
141 &self,
142 seed: [u8; 32],
143 bench_case: Option<BenchmarkCase>,
144 ) -> FunctionInitialState {
145 let (minimum, supremum) = match bench_case {
146 Some(BenchmarkCase::CommonCase) => (0, 45),
147 Some(BenchmarkCase::WorstCase) => (0, 250),
148 None => {
149 let mut rng = StdRng::from_seed(seed);
150 let supremum = rng.random_range(0..=400);
151 let minimum = rng.random_range(0..=supremum);
152 (minimum, supremum)
153 }
154 };
155
156 self.set_up_initial_state(minimum, supremum)
157 }
158
159 fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
160 [(0, 0), (0, 1), (0, 10), (5, 15), (15, 15)]
161 .map(|(min, sup)| self.set_up_initial_state(min, sup))
162 .to_vec()
163 }
164 }
165
166 #[macro_rules_attr::apply(test)]
167 fn rust_shadow() {
168 ShadowedFunction::new(Range).test();
169 }
170
171 #[macro_rules_attr::apply(proptest)]
172 fn invalid_range_crashes_vm(
173 #[strategy(1_u32..)] minimum: u32,
174 #[strategy(..#minimum)] supremum: u32,
175 ) {
176 test_assertion_failure(
177 &ShadowedFunction::new(Range),
178 Range.set_up_initial_state(minimum, supremum).into(),
179 &[Range::INVALID_ERROR_ID],
180 );
181 }
182}
183
184#[cfg(test)]
185mod benches {
186 use super::*;
187 use crate::test_prelude::*;
188
189 #[macro_rules_attr::apply(test)]
190 fn benchmark() {
191 ShadowedFunction::new(Range).bench();
192 }
193}