1use std::collections::HashMap;
2
3use crate::{
4 types::relocatable::{MaybeRelocatable, Relocatable},
5 vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
6};
7
8#[derive(PartialEq, Eq, Debug, Clone)]
9pub struct DictManager {
12 pub trackers: HashMap<isize, DictTracker>,
13}
14
15#[derive(PartialEq, Eq, Debug, Clone)]
16pub struct DictTracker {
18 pub data: Dictionary,
20 pub current_ptr: Relocatable,
22}
23
24#[derive(PartialEq, Eq, Debug, Clone)]
25pub enum Dictionary {
26 SimpleDictionary(HashMap<MaybeRelocatable, MaybeRelocatable>),
27 DefaultDictionary {
28 dict: HashMap<MaybeRelocatable, MaybeRelocatable>,
29 default_value: MaybeRelocatable,
30 },
31}
32
33impl Dictionary {
34 fn get(&mut self, key: &MaybeRelocatable) -> Option<&MaybeRelocatable> {
35 match self {
36 Self::SimpleDictionary(dict) => dict.get(key),
37 Self::DefaultDictionary {
38 dict,
39 default_value,
40 } => Some(
41 dict.entry(key.clone())
42 .or_insert_with(|| default_value.clone()),
43 ),
44 }
45 }
46
47 fn insert(&mut self, key: &MaybeRelocatable, value: &MaybeRelocatable) {
48 let dict = match self {
49 Self::SimpleDictionary(dict) => dict,
50 Self::DefaultDictionary {
51 dict,
52 default_value: _,
53 } => dict,
54 };
55 dict.insert(key.clone(), value.clone());
56 }
57}
58
59impl DictManager {
60 pub fn new() -> Self {
61 DictManager {
62 trackers: HashMap::<isize, DictTracker>::new(),
63 }
64 }
65 pub fn new_dict(
68 &mut self,
69 vm: &mut VirtualMachine,
70 initial_dict: HashMap<MaybeRelocatable, MaybeRelocatable>,
71 ) -> Result<MaybeRelocatable, HintError> {
72 let base = vm.add_memory_segment();
73 if self.trackers.contains_key(&base.segment_index) {
74 return Err(HintError::CantCreateDictionaryOnTakenSegment(
75 base.segment_index,
76 ));
77 };
78
79 self.trackers.insert(
80 base.segment_index,
81 DictTracker::new_with_initial(base, initial_dict),
82 );
83 Ok(MaybeRelocatable::RelocatableValue(base))
84 }
85
86 pub fn new_default_dict(
88 &mut self,
89 vm: &mut VirtualMachine,
90 default_value: &MaybeRelocatable,
91 initial_dict: Option<HashMap<MaybeRelocatable, MaybeRelocatable>>,
92 ) -> Result<MaybeRelocatable, HintError> {
93 let base = vm.add_memory_segment();
94 if self.trackers.contains_key(&base.segment_index) {
95 return Err(HintError::CantCreateDictionaryOnTakenSegment(
96 base.segment_index,
97 ));
98 }
99 self.trackers.insert(
100 base.segment_index,
101 DictTracker::new_default_dict(base, default_value, initial_dict),
102 );
103 Ok(MaybeRelocatable::RelocatableValue(base))
104 }
105
106 pub fn get_tracker_mut(
108 &mut self,
109 dict_ptr: Relocatable,
110 ) -> Result<&mut DictTracker, HintError> {
111 let tracker = self
112 .trackers
113 .get_mut(&dict_ptr.segment_index)
114 .ok_or(HintError::NoDictTracker(dict_ptr.segment_index))?;
115 if tracker.current_ptr != dict_ptr {
116 return Err(HintError::MismatchedDictPtr(Box::new((
117 tracker.current_ptr,
118 dict_ptr,
119 ))));
120 }
121 Ok(tracker)
122 }
123
124 pub fn get_tracker(&self, dict_ptr: Relocatable) -> Result<&DictTracker, HintError> {
126 let tracker = self
127 .trackers
128 .get(&dict_ptr.segment_index)
129 .ok_or(HintError::NoDictTracker(dict_ptr.segment_index))?;
130 if tracker.current_ptr != dict_ptr {
131 return Err(HintError::MismatchedDictPtr(Box::new((
132 tracker.current_ptr,
133 dict_ptr,
134 ))));
135 }
136 Ok(tracker)
137 }
138}
139
140impl Default for DictManager {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146impl DictTracker {
147 pub fn new_empty(base: Relocatable) -> Self {
148 DictTracker {
149 data: Dictionary::SimpleDictionary(HashMap::new()),
150 current_ptr: base,
151 }
152 }
153
154 pub fn new_default_dict(
155 base: Relocatable,
156 default_value: &MaybeRelocatable,
157 initial_dict: Option<HashMap<MaybeRelocatable, MaybeRelocatable>>,
158 ) -> Self {
159 DictTracker {
160 data: Dictionary::DefaultDictionary {
161 dict: initial_dict.unwrap_or_default(),
162 default_value: default_value.clone(),
163 },
164 current_ptr: base,
165 }
166 }
167
168 pub fn new_with_initial(
169 base: Relocatable,
170 initial_dict: HashMap<MaybeRelocatable, MaybeRelocatable>,
171 ) -> Self {
172 DictTracker {
173 data: Dictionary::SimpleDictionary(initial_dict),
174 current_ptr: base,
175 }
176 }
177
178 pub fn get_dictionary_copy(&self) -> HashMap<MaybeRelocatable, MaybeRelocatable> {
180 match &self.data {
181 Dictionary::SimpleDictionary(dict) => dict.clone(),
182 Dictionary::DefaultDictionary {
183 dict,
184 default_value: _,
185 } => dict.clone(),
186 }
187 }
188
189 pub fn get_dictionary_ref(&self) -> &HashMap<MaybeRelocatable, MaybeRelocatable> {
191 match &self.data {
192 Dictionary::SimpleDictionary(dict) => dict,
193 Dictionary::DefaultDictionary {
194 dict,
195 default_value: _,
196 } => dict,
197 }
198 }
199
200 pub fn get_value(&mut self, key: &MaybeRelocatable) -> Result<&MaybeRelocatable, HintError> {
201 self.data
202 .get(key)
203 .ok_or_else(|| HintError::NoValueForKey(Box::new(key.clone())))
204 }
205
206 pub fn insert_value(&mut self, key: &MaybeRelocatable, val: &MaybeRelocatable) {
207 self.data.insert(key, val)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::{relocatable, utils::test_utils::*};
215 use assert_matches::assert_matches;
216
217 #[test]
218 fn create_dict_manager() {
219 let dict_manager = DictManager::new();
220 assert_eq!(dict_manager.trackers, HashMap::new());
221 }
222
223 #[test]
224 fn create_dict_tracker_empty() {
225 let dict_tracker = DictTracker::new_empty(relocatable!(1, 0));
226 assert_eq!(
227 dict_tracker.data,
228 Dictionary::SimpleDictionary(HashMap::new())
229 );
230 assert_eq!(dict_tracker.current_ptr, relocatable!(1, 0));
231 }
232
233 #[test]
234 fn create_dict_tracker_default() {
235 let dict_tracker =
236 DictTracker::new_default_dict(relocatable!(1, 0), &MaybeRelocatable::from(5), None);
237 assert_eq!(
238 dict_tracker.data,
239 Dictionary::DefaultDictionary {
240 dict: HashMap::new(),
241 default_value: MaybeRelocatable::from(5)
242 }
243 );
244 assert_eq!(dict_tracker.current_ptr, relocatable!(1, 0));
245 }
246
247 #[test]
248 fn dict_manager_new_dict_empty() {
249 let mut vm = vm!();
250 let mut dict_manager = DictManager::new();
251 let base = dict_manager.new_dict(&mut vm, HashMap::new());
252 assert_matches!(base, Ok(x) if x == MaybeRelocatable::from((0, 0)));
253 assert!(dict_manager.trackers.contains_key(&0));
254 assert_eq!(
255 dict_manager.trackers.get(&0),
256 Some(&DictTracker::new_empty(relocatable!(0, 0)))
257 );
258 assert_eq!(vm.segments.num_segments(), 1);
259 }
260
261 #[test]
262 fn dict_manager_new_dict_default() {
263 let mut dict_manager = DictManager::new();
264 let mut vm = vm!();
265 let base = dict_manager.new_default_dict(&mut vm, &MaybeRelocatable::from(5), None);
266 assert_matches!(base, Ok(x) if x == MaybeRelocatable::from((0, 0)));
267 assert!(dict_manager.trackers.contains_key(&0));
268 assert_eq!(
269 dict_manager.trackers.get(&0),
270 Some(&DictTracker::new_default_dict(
271 relocatable!(0, 0),
272 &MaybeRelocatable::from(5),
273 None
274 ))
275 );
276 assert_eq!(vm.segments.num_segments(), 1);
277 }
278
279 #[test]
280 fn dict_manager_new_dict_with_initial_dict() {
281 let mut dict_manager = DictManager::new();
282 let mut vm = vm!();
283 let mut initial_dict = HashMap::<MaybeRelocatable, MaybeRelocatable>::new();
284 initial_dict.insert(MaybeRelocatable::from(5), MaybeRelocatable::from(5));
285 let base = dict_manager.new_dict(&mut vm, initial_dict.clone());
286 assert_matches!(base, Ok(x) if x == MaybeRelocatable::from((0, 0)));
287 assert!(dict_manager.trackers.contains_key(&0));
288 assert_eq!(
289 dict_manager.trackers.get(&0),
290 Some(&DictTracker::new_with_initial(
291 relocatable!(0, 0),
292 initial_dict
293 ))
294 );
295 assert_eq!(vm.segments.num_segments(), 1);
296 }
297
298 #[test]
299 fn dict_manager_new_default_dict_with_initial_dict() {
300 let mut dict_manager = DictManager::new();
301 let mut initial_dict = HashMap::<MaybeRelocatable, MaybeRelocatable>::new();
302 let mut vm = vm!();
303 initial_dict.insert(MaybeRelocatable::from(5), MaybeRelocatable::from(5));
304 let base = dict_manager.new_default_dict(
305 &mut vm,
306 &MaybeRelocatable::from(7),
307 Some(initial_dict.clone()),
308 );
309 assert_matches!(base, Ok(x) if x == MaybeRelocatable::from((0, 0)));
310 assert!(dict_manager.trackers.contains_key(&0));
311 assert_eq!(
312 dict_manager.trackers.get(&0),
313 Some(&DictTracker::new_default_dict(
314 relocatable!(0, 0),
315 &MaybeRelocatable::from(7),
316 Some(initial_dict)
317 ))
318 );
319 assert_eq!(vm.segments.num_segments(), 1);
320 }
321
322 #[test]
323 fn dict_manager_new_dict_empty_same_segment() {
324 let mut dict_manager = DictManager::new();
325 dict_manager
326 .trackers
327 .insert(0, DictTracker::new_empty(relocatable!(0, 0)));
328 let mut vm = vm!();
329 assert_matches!(
330 dict_manager.new_dict(&mut vm, HashMap::new()),
331 Err(HintError::CantCreateDictionaryOnTakenSegment(0))
332 );
333 }
334
335 #[test]
336 fn dict_manager_new_default_dict_empty_same_segment() {
337 let mut dict_manager = DictManager::new();
338 dict_manager.trackers.insert(
339 0,
340 DictTracker::new_default_dict(relocatable!(0, 0), &MaybeRelocatable::from(6), None),
341 );
342 let mut vm = vm!();
343 assert_matches!(
344 dict_manager.new_dict(&mut vm, HashMap::new()),
345 Err(HintError::CantCreateDictionaryOnTakenSegment(0))
346 );
347 }
348
349 #[test]
350 fn dictionary_get_insert_simple() {
351 let mut dictionary = Dictionary::SimpleDictionary(HashMap::new());
352 dictionary.insert(&MaybeRelocatable::from(1), &MaybeRelocatable::from(2));
353 assert_eq!(
354 dictionary.get(&MaybeRelocatable::from(1)),
355 Some(&MaybeRelocatable::from(2))
356 );
357 assert_eq!(dictionary.get(&MaybeRelocatable::from(2)), None);
358 }
359
360 #[test]
361 fn dictionary_get_insert_default() {
362 let mut dictionary = Dictionary::DefaultDictionary {
363 dict: HashMap::new(),
364 default_value: MaybeRelocatable::from(7),
365 };
366 dictionary.insert(&MaybeRelocatable::from(1), &MaybeRelocatable::from(2));
367 assert_eq!(
368 dictionary.get(&MaybeRelocatable::from(1)),
369 Some(&MaybeRelocatable::from(2))
370 );
371 assert_eq!(
372 dictionary.get(&MaybeRelocatable::from(2)),
373 Some(&MaybeRelocatable::from(7))
374 );
375 }
376}