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