1use crate::{
2 serde::deserialize_program::{ApTracking, OffsetValue},
3 types::{
4 errors::math_errors::MathError,
5 instruction::Register,
6 relocatable::{MaybeRelocatable, Relocatable},
7 },
8 vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
9};
10
11use super::hint_processor_definition::HintReference;
12use crate::Felt252;
13
14use num_traits::ToPrimitive;
15
16pub fn insert_value_from_reference(
18 value: impl Into<MaybeRelocatable>,
19 vm: &mut VirtualMachine,
20 hint_reference: &HintReference,
21 ap_tracking: &ApTracking,
22) -> Result<(), HintError> {
23 let addr = compute_addr_from_reference(hint_reference, vm, ap_tracking)
24 .ok_or(HintError::UnknownIdentifierInternal)?;
25 vm.insert_value(addr, value).map_err(HintError::Memory)
26}
27
28pub fn get_integer_from_reference(
31 vm: &VirtualMachine,
32 hint_reference: &HintReference,
33 ap_tracking: &ApTracking,
34) -> Result<Felt252, HintError> {
35 get_maybe_relocatable_from_reference(vm, hint_reference, ap_tracking)
36 .ok_or(HintError::UnknownIdentifierInternal)?
37 .get_int()
38 .ok_or(HintError::WrongIdentifierTypeInternal)
39}
40
41pub fn get_ptr_from_reference(
43 vm: &VirtualMachine,
44 hint_reference: &HintReference,
45 ap_tracking: &ApTracking,
46) -> Result<Relocatable, HintError> {
47 get_maybe_relocatable_from_reference(vm, hint_reference, ap_tracking)
48 .ok_or(HintError::UnknownIdentifierInternal)?
49 .get_relocatable()
50 .ok_or(HintError::WrongIdentifierTypeInternal)
51}
52
53pub fn get_maybe_relocatable_from_reference(
55 vm: &VirtualMachine,
56 hint_reference: &HintReference,
57 ap_tracking: &ApTracking,
58) -> Option<MaybeRelocatable> {
59 let offset1 = get_offset_value(
60 vm,
61 &hint_reference.offset1,
62 &hint_reference.ap_tracking_data,
63 ap_tracking,
64 )?;
65 let offset2 = get_offset_value(
66 vm,
67 &hint_reference.offset2,
68 &hint_reference.ap_tracking_data,
69 ap_tracking,
70 )?;
71 let mut val = match hint_reference.offset2 {
72 OffsetValue::Reference(_, _, _, true)
73 | OffsetValue::Immediate(_)
74 | OffsetValue::Value(_) => offset1.add(&offset2).ok()?,
75 OffsetValue::Reference(_, _, _, false) => offset1.sub(&offset2).ok()?,
76 };
77 let deref_count =
78 u8::from(hint_reference.inner_dereference) + u8::from(hint_reference.outer_dereference);
79 for _ in 0..deref_count {
80 val = vm.get_maybe(&val)?;
81 }
82 Some(val)
83}
84
85pub fn compute_addr_from_reference(
87 hint_reference: &HintReference,
88 vm: &VirtualMachine,
89 ap_tracking: &ApTracking,
90) -> Option<Relocatable> {
91 let offset1 = get_offset_value(
92 vm,
93 &hint_reference.offset1,
94 &hint_reference.ap_tracking_data,
95 ap_tracking,
96 )?;
97 let offset2 = get_offset_value(
98 vm,
99 &hint_reference.offset2,
100 &hint_reference.ap_tracking_data,
101 ap_tracking,
102 )?;
103 let mut val = offset1.add(&offset2).ok()?;
104 if hint_reference.inner_dereference {
105 val = vm.get_maybe(&val)?;
106 };
107 val.get_relocatable()
108}
109
110fn apply_ap_tracking_correction(
111 ap: Relocatable,
112 ref_ap_tracking: &ApTracking,
113 hint_ap_tracking: &ApTracking,
114) -> Option<Relocatable> {
115 if ref_ap_tracking.group != hint_ap_tracking.group {
117 return None;
118 }
119 let ap_diff = hint_ap_tracking.offset - ref_ap_tracking.offset;
120 (ap - ap_diff).ok()
121}
122
123pub fn felt_to_usize(felt: &Felt252) -> Result<usize, MathError> {
125 felt.to_usize()
126 .ok_or_else(|| MathError::Felt252ToUsizeConversion(Box::new(*felt)))
127}
128
129pub fn felt_to_u32(felt: &Felt252) -> Result<u32, MathError> {
131 felt.to_u32()
132 .ok_or_else(|| MathError::Felt252ToU32Conversion(Box::new(*felt)))
133}
134
135fn get_offset_value(
136 vm: &VirtualMachine,
137 offset_value: &OffsetValue,
138 reference_ap_tracking: &Option<ApTracking>,
139 hint_ap_tracking: &ApTracking,
140) -> Option<MaybeRelocatable> {
141 match offset_value {
142 OffsetValue::Immediate(f) => Some(f.into()),
143 OffsetValue::Value(v) => Some(Felt252::from(*v).into()),
144 OffsetValue::Reference(register, offset, deref, _) => {
145 let addr = (if matches!(register, Register::FP) {
146 vm.get_fp()
147 } else {
148 apply_ap_tracking_correction(
149 vm.get_ap(),
150 reference_ap_tracking.as_ref()?,
151 hint_ap_tracking,
152 )?
153 } + *offset)
154 .ok()?;
155
156 if *deref {
157 vm.get_maybe(&addr)
158 } else {
159 Some(addr.into())
160 }
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 use crate::{relocatable, utils::test_utils::*, vm::vm_memory::memory::Memory};
170 use assert_matches::assert_matches;
171
172 #[test]
173 fn get_integer_from_reference_with_immediate_value() {
174 let mut vm = vm!();
176 vm.segments = segments![((1, 0), 0)];
177 let mut hint_ref = HintReference::new(0, 0, false, false, true);
178 hint_ref.offset1 = OffsetValue::Immediate(Felt252::from(2));
179
180 assert_eq!(
181 get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
182 .expect("Unexpected get integer fail"),
183 Felt252::from(2)
184 );
185 }
186
187 #[test]
188 fn get_offset_value_reference_valid() {
189 let mut vm = vm!();
190 vm.segments = segments![((1, 0), 0)];
191 let mut hint_ref = HintReference::new(0, 0, false, true, true);
192 hint_ref.offset1 = OffsetValue::Reference(Register::FP, 2_i32, false, true);
193
194 assert_matches!(
195 get_offset_value(&vm, &hint_ref.offset1, &hint_ref.ap_tracking_data, &ApTracking::new()),
196 Some(x) if x == mayberelocatable!(1, 2)
197 );
198 }
199
200 #[test]
201 fn get_offset_value_invalid() {
202 let mut vm = vm!();
203 vm.segments = segments![((1, 0), 0)];
204 let mut hint_ref = HintReference::new(0, 0, false, true, true);
205 hint_ref.offset1 = OffsetValue::Reference(Register::FP, -2_i32, false, true);
206
207 assert_matches!(
208 get_offset_value(
209 &vm,
210 &hint_ref.offset1,
211 &hint_ref.ap_tracking_data,
212 &ApTracking::new()
213 ),
214 None
215 );
216 }
217
218 #[test]
219 fn get_ptr_from_reference_short_path() {
220 let mut vm = vm!();
221 vm.segments = segments![((1, 0), (2, 0))];
222
223 assert_matches!(
224 get_ptr_from_reference(
225 &vm,
226 &HintReference::new(0, 0, false, false, true),
227 &ApTracking::new()
228 ),
229 Ok(x) if x == relocatable!(1, 0)
230 );
231 }
232
233 #[test]
234 fn get_ptr_from_reference_with_dereference() {
235 let mut vm = vm!();
236 vm.segments = segments![((1, 0), (3, 0))];
237
238 assert_matches!(
239 get_ptr_from_reference(
240 &vm,
241 &HintReference::new(0, 0, false, true, true),
242 &ApTracking::new()
243 ),
244 Ok(x) if x == relocatable!(3, 0)
245 );
246 }
247
248 #[test]
249 fn get_ptr_from_reference_with_dereference_and_imm() {
250 let mut vm = vm!();
251 vm.segments = segments![((1, 0), (4, 0))];
252 let mut hint_ref = HintReference::new(0, 0, true, false, true);
253 hint_ref.offset2 = OffsetValue::Value(2);
254
255 assert_matches!(
256 get_ptr_from_reference(&vm, &hint_ref, &ApTracking::new()),
257 Ok(x) if x == relocatable!(4, 2)
258 );
259 }
260
261 #[test]
262 fn compute_addr_from_reference_no_regiter_in_reference() {
263 let mut vm = vm!();
264 vm.segments = segments![((1, 0), (4, 0))];
265 let mut hint_reference = HintReference::new(0, 0, false, false, true);
266 hint_reference.offset1 = OffsetValue::Immediate(Felt252::from(2_i32));
267
268 assert!(compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()).is_none());
269 }
270
271 #[test]
272 fn compute_addr_from_reference_failed_to_get_ids() {
273 let mut vm = vm!();
274 vm.segments = segments![((1, 0), 4)];
275 let mut hint_reference = HintReference::new(0, 0, false, false, true);
277 hint_reference.offset1 = OffsetValue::Reference(Register::FP, -1, true, true);
278
279 assert_matches!(
280 compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()),
281 None
282 );
283 }
284
285 #[test]
286 fn tracking_correction_valid() {
287 let mut ref_ap_tracking = ApTracking::new();
288 ref_ap_tracking.group = 1;
289 let mut hint_ap_tracking = ApTracking::new();
290 hint_ap_tracking.group = 1;
291
292 assert_matches!(
293 apply_ap_tracking_correction(relocatable!(1, 0), &ref_ap_tracking, &hint_ap_tracking),
294 Some(relocatable!(1, 0))
295 );
296 }
297
298 #[test]
299 fn tracking_correction_invalid_group() {
300 let mut ref_ap_tracking = ApTracking::new();
301 ref_ap_tracking.group = 1;
302 let mut hint_ap_tracking = ApTracking::new();
303 hint_ap_tracking.group = 2;
304
305 assert!(apply_ap_tracking_correction(
306 relocatable!(1, 0),
307 &ref_ap_tracking,
308 &hint_ap_tracking
309 )
310 .is_none());
311 }
312
313 #[test]
314 fn tracking_correction_invalid_offset() {
315 let mut ref_ap_tracking = ApTracking::new();
318 ref_ap_tracking.group = 1;
319 ref_ap_tracking.offset = 0;
320 let mut hint_ap_tracking = ApTracking::new();
321 hint_ap_tracking.group = 1;
322 hint_ap_tracking.offset = 5;
323
324 assert!(apply_ap_tracking_correction(
325 relocatable!(1, 2),
326 &ref_ap_tracking,
327 &hint_ap_tracking
328 )
329 .is_none());
330 }
331
332 #[test]
333 fn get_maybe_relocatable_from_reference_valid() {
334 let mut vm = vm!();
335 vm.segments = segments![((1, 0), (0, 0))];
336 let hint_ref = HintReference::new_simple(0);
337 assert_matches!(
338 get_maybe_relocatable_from_reference(&vm, &hint_ref, &ApTracking::new()),
339 Some(x) if x == mayberelocatable!(0, 0)
340 );
341 }
342
343 #[test]
344 fn get_maybe_relocatable_from_reference_invalid() {
345 let mut vm = vm!();
346 vm.segments.memory = Memory::new();
347 let hint_ref = HintReference::new_simple(0);
348 assert_matches!(
349 get_maybe_relocatable_from_reference(&vm, &hint_ref, &ApTracking::new()),
350 None
351 );
352 }
353
354 #[test]
355 fn get_integer_from_reference_with_triple_deref() {
356 let mut vm = vm!();
358 vm.segments = segments![
359 ((1, 2), (0, 0)), ((0, 2), (0, 5)), ((0, 5), 3) ];
363 let hint_ref = HintReference {
364 offset1: OffsetValue::Reference(Register::FP, 2, true, true),
365 offset2: OffsetValue::Value(2),
366 outer_dereference: true,
367 inner_dereference: true,
368 ap_tracking_data: Default::default(),
369 cairo_type: None,
370 };
371
372 assert_eq!(
373 get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
374 .expect("Unexpected get integer fail"),
375 Felt252::THREE
376 );
377 }
378
379 #[test]
380 fn get_integer_from_reference_without_outer_defer() {
381 let mut vm = vm!();
383 vm.segments = segments![
384 ((1, 4), 8), ];
386 let hint_ref = HintReference {
388 offset1: OffsetValue::Reference(Register::FP, 4, true, true),
389 offset2: OffsetValue::Immediate(Felt252::from(-5)),
390 outer_dereference: false,
391 inner_dereference: false,
392 ap_tracking_data: Default::default(),
393 cairo_type: None,
394 };
395
396 assert_eq!(
397 get_integer_from_reference(&vm, &hint_ref, &ApTracking::new())
398 .expect("Unexpected get integer fail"),
399 Felt252::THREE
400 );
401 }
402}