1use crate::Felt252;
2use num_bigint::BigUint;
3use num_integer::Integer;
4use num_traits::Zero;
5
6use crate::math_utils::isqrt;
7use crate::stdlib::{boxed::Box, collections::HashMap, prelude::*};
8use crate::types::errors::math_errors::MathError;
9use crate::{
10 hint_processor::hint_processor_definition::HintReference,
11 math_utils::pow2_const_nz,
12 serde::deserialize_program::ApTracking,
13 vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
14};
15
16use super::hint_utils::{
17 get_constant_from_var_name, get_integer_from_var_name, get_relocatable_from_var_name,
18 insert_value_from_var_name, insert_value_into_ap,
19};
20use super::secp::bigint_utils::Uint384;
21pub fn uint384_unsigned_div_rem(
54 vm: &mut VirtualMachine,
55 ids_data: &HashMap<String, HintReference>,
56 ap_tracking: &ApTracking,
57) -> Result<(), HintError> {
58 let a = Uint384::from_var_name("a", vm, ids_data, ap_tracking)?.pack();
59 let div = Uint384::from_var_name("div", vm, ids_data, ap_tracking)?.pack();
60
61 if div.is_zero() {
62 return Err(MathError::DividedByZero.into());
63 }
64 let (quotient, remainder) = a.div_mod_floor(&div);
65
66 let quotient_split = Uint384::split("ient);
67 quotient_split.insert_from_var_name("quotient", vm, ids_data, ap_tracking)?;
68
69 let remainder_split = Uint384::split(&remainder);
70 remainder_split.insert_from_var_name("remainder", vm, ids_data, ap_tracking)
71}
72
73pub fn uint384_split_128(
80 vm: &mut VirtualMachine,
81 ids_data: &HashMap<String, HintReference>,
82 ap_tracking: &ApTracking,
83) -> Result<(), HintError> {
84 let bound = pow2_const_nz(128);
85 let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?;
86 let (high, low) = a.div_rem(bound);
87 insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)?;
88 insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)
89}
90
91pub fn add_no_uint384_check(
102 vm: &mut VirtualMachine,
103 ids_data: &HashMap<String, HintReference>,
104 ap_tracking: &ApTracking,
105 constants: &HashMap<String, Felt252>,
106) -> Result<(), HintError> {
107 let a = Uint384::from_var_name("a", vm, ids_data, ap_tracking)?;
108 let b = Uint384::from_var_name("b", vm, ids_data, ap_tracking)?;
109 let shift = get_constant_from_var_name("SHIFT", constants)?.to_biguint();
111
112 let sum_d0 = (a.limbs[0].as_ref().to_biguint()) + (b.limbs[0].as_ref().to_biguint());
113 let carry_d0 = BigUint::from((sum_d0 >= shift) as usize);
114 let sum_d1 =
115 (a.limbs[1].as_ref().to_biguint()) + (b.limbs[1].as_ref().to_biguint()) + &carry_d0;
116 let carry_d1 = BigUint::from((sum_d1 >= shift) as usize);
117 let sum_d2 =
118 (a.limbs[2].as_ref().to_biguint()) + (b.limbs[2].as_ref().to_biguint()) + &carry_d1;
119 let carry_d2 = Felt252::from((sum_d2 >= shift) as usize);
120
121 insert_value_from_var_name(
122 "carry_d0",
123 Felt252::from(&carry_d0),
124 vm,
125 ids_data,
126 ap_tracking,
127 )?;
128 insert_value_from_var_name(
129 "carry_d1",
130 Felt252::from(&carry_d1),
131 vm,
132 ids_data,
133 ap_tracking,
134 )?;
135 insert_value_from_var_name("carry_d2", carry_d2, vm, ids_data, ap_tracking)
136}
137
138pub fn uint384_sqrt(
163 vm: &mut VirtualMachine,
164 ids_data: &HashMap<String, HintReference>,
165 ap_tracking: &ApTracking,
166) -> Result<(), HintError> {
167 let a = Uint384::from_var_name("a", vm, ids_data, ap_tracking)?.pack();
168
169 let root = isqrt(&a)?;
170
171 if root.is_zero() || root.bits() > 192 {
172 return Err(HintError::AssertionFailed(
173 "assert 0 <= root < 2 ** 192".to_string().into_boxed_str(),
174 ));
175 }
176 let root_split = Uint384::split(&root);
177 root_split.insert_from_var_name("root", vm, ids_data, ap_tracking)
178}
179
180pub fn uint384_signed_nn(
184 vm: &mut VirtualMachine,
185 ids_data: &HashMap<String, HintReference>,
186 ap_tracking: &ApTracking,
187) -> Result<(), HintError> {
188 let a_addr = get_relocatable_from_var_name("a", vm, ids_data, ap_tracking)?;
189 let a_d2 = vm.get_integer((a_addr + 2)?).map_err(|_| {
190 HintError::IdentifierHasNoMember(Box::new(("a".to_string(), "d2".to_string())))
191 })?;
192 let res = Felt252::from((a_d2.bits() <= 127) as u32);
193 insert_value_into_ap(vm, res)
194}
195
196pub fn sub_reduced_a_and_reduced_b(
224 vm: &mut VirtualMachine,
225 ids_data: &HashMap<String, HintReference>,
226 ap_tracking: &ApTracking,
227) -> Result<(), HintError> {
228 let a = Uint384::from_var_name("a", vm, ids_data, ap_tracking)?.pack();
229 let b = Uint384::from_var_name("b", vm, ids_data, ap_tracking)?.pack();
230 let p = Uint384::from_var_name("p", vm, ids_data, ap_tracking)?.pack();
231 let res = if a > b {
232 (a - b).mod_floor(&p)
233 } else {
234 &p - (b - &a).mod_floor(&p)
235 };
236
237 let res_split = Uint384::split(&res);
238 res_split.insert_from_var_name("res", vm, ids_data, ap_tracking)
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use crate::hint_processor::builtin_hint_processor::hint_code;
245
246 use crate::felt_str;
247 use crate::{
248 any_box,
249 hint_processor::{
250 builtin_hint_processor::builtin_hint_processor_definition::{
251 BuiltinHintProcessor, HintProcessorData,
252 },
253 hint_processor_definition::HintProcessorLogic,
254 },
255 types::relocatable::{MaybeRelocatable, Relocatable},
256 utils::test_utils::*,
257 vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine},
258 };
259 use assert_matches::assert_matches;
260
261 #[cfg(target_arch = "wasm32")]
262 use wasm_bindgen_test::*;
263
264 #[test]
265 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
266 fn run_unsigned_div_rem_ok() {
267 let mut vm = vm_with_range_check!();
268 vm.run_context.fp = 10;
270 let ids_data =
272 non_continuous_ids_data![("a", -9), ("div", -6), ("quotient", -3), ("remainder", 0)];
273 vm.segments = segments![
275 ((1, 1), 83434123481193248),
277 ((1, 2), 82349321849739284),
278 ((1, 3), 839243219401320423),
279 ((1, 4), 9283430921839492319493),
281 ((1, 5), 313248123482483248),
282 ((1, 6), 3790328402913840)
283 ];
284 assert_matches!(
286 run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM),
287 Ok(())
288 );
289 check_memory![
291 vm.segments.memory,
292 ((1, 7), 221),
294 ((1, 8), 0),
295 ((1, 9), 0),
296 ((1, 12), 1580642357361782)
300 ];
301 assert_eq!(
302 vm.segments
303 .memory
304 .get_integer((1, 10).into())
305 .unwrap()
306 .as_ref(),
307 &felt_str!("340282366920936411825224315027446796751")
308 );
309 assert_eq!(
310 vm.segments
311 .memory
312 .get_integer((1, 11).into())
313 .unwrap()
314 .as_ref(),
315 &felt_str!("340282366920938463394229121463989152931")
316 );
317 }
318
319 #[test]
320 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
321 fn run_unsigned_div_rem_divide_by_zero() {
322 let mut vm = vm_with_range_check!();
323 vm.run_context.fp = 10;
325 let ids_data =
327 non_continuous_ids_data![("a", -9), ("div", -6), ("quotient", -3), ("remainder", 0)];
328 vm.segments = segments![
330 ((1, 1), 83434123481193248),
332 ((1, 2), 82349321849739284),
333 ((1, 3), 839243219401320423),
334 ((1, 4), 0),
336 ((1, 5), 0),
337 ((1, 6), 0)
338 ];
339 assert_matches!(
341 run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM),
342 Err(HintError::Math(MathError::DividedByZero))
343 );
344 }
345
346 #[test]
347 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
348 fn run_unsigned_div_rem_invalid_memory_insert() {
349 let mut vm = vm_with_range_check!();
350 vm.run_context.fp = 10;
352 let ids_data =
354 non_continuous_ids_data![("a", -9), ("div", -6), ("quotient", -3), ("remainder", 0)];
355 vm.segments = segments![
357 ((1, 1), 83434123481193248),
359 ((1, 2), 82349321849739284),
360 ((1, 3), 839243219401320423),
361 ((1, 4), 9283430921839492319493),
363 ((1, 5), 313248123482483248),
364 ((1, 6), 3790328402913840),
365 ((1, 7), 2)
367 ];
368 assert_matches!(
370 run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM),
371 Err(HintError::Memory(
372 MemoryError::InconsistentMemory(bx)
373 )) if *bx == (Relocatable::from((1, 7)),
374 MaybeRelocatable::from(Felt252::from(2)),
375 MaybeRelocatable::from(Felt252::from(221)))
376 );
377 }
378
379 #[test]
380 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
381 fn run_split_128_ok() {
382 let mut vm = vm_with_range_check!();
383 vm.run_context.fp = 3;
385 let ids_data = ids_data!["a", "low", "high"];
387 vm.segments = segments![((1, 0), 34895349583295832495320945304)];
389 assert_matches!(
391 run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128),
392 Ok(())
393 );
394 check_memory![
396 vm.segments.memory,
397 ((1, 1), 34895349583295832495320945304),
399 ((1, 2), 0)
401 ];
402 }
403
404 #[test]
405 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
406 fn run_split_128_ok_big_number() {
407 let mut vm = vm_with_range_check!();
408 vm.run_context.fp = 3;
410 let ids_data = ids_data!["a", "low", "high"];
412 vm.segments.add();
414 vm.segments.add();
415 vm.segments
416 .memory
417 .insert(
418 (1, 0).into(),
419 Felt252::from(u128::MAX) * Felt252::from(20_u32),
420 )
421 .unwrap();
422 assert_matches!(
424 run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128),
425 Ok(())
426 );
427 check_memory![
429 vm.segments.memory,
430 ((1, 2), 19)
434 ];
435 assert_eq!(
436 vm.segments
437 .memory
438 .get_integer((1, 1).into())
439 .unwrap()
440 .as_ref(),
441 &felt_str!("340282366920938463463374607431768211436")
442 );
443 }
444
445 #[test]
446 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
447 fn run_split_128_invalid_memory_insert() {
448 let mut vm = vm_with_range_check!();
449 vm.run_context.fp = 3;
451 let ids_data = ids_data!["a", "low", "high"];
453 vm.segments = segments![((1, 0), 34895349583295832495320945304), ((1, 1), 2)];
455 assert_matches!(
457 run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128),
458 Err(HintError::Memory(
459 MemoryError::InconsistentMemory(bx)
460 )) if *bx == (Relocatable::from((1, 1)),
461 MaybeRelocatable::from(Felt252::from(2)),
462 MaybeRelocatable::from(Felt252::from(34895349583295832495320945304_i128)))
463 );
464 }
465
466 #[test]
467 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
468 fn run_add_no_check_ok() {
469 let mut vm = vm_with_range_check!();
470 vm.run_context.fp = 10;
472 let ids_data = non_continuous_ids_data![
474 ("a", -10),
475 ("b", -7),
476 ("carry_d0", -4),
477 ("carry_d1", -3),
478 ("carry_d2", -2)
479 ];
480 vm.segments = segments![
482 ((1, 0), 3789423292314891293),
484 ((1, 1), 21894),
485 ((1, 2), 340282366920938463463374607431768211455_u128),
486 ((1, 3), 32838232),
488 ((1, 4), 17),
489 ((1, 5), 8)
490 ];
491 assert_matches!(
493 run_hint!(
494 vm,
495 ids_data,
496 hint_code::ADD_NO_UINT384_CHECK,
497 &mut exec_scopes_ref!(),
498 &[("path.path.path.SHIFT", crate::math_utils::pow2_const(128))]
499 .into_iter()
500 .map(|(k, v)| (k.to_string(), v))
501 .collect()
502 ),
503 Ok(())
504 );
505 check_memory![
507 vm.segments.memory,
508 ((1, 6), 0),
510 ((1, 7), 0),
512 ((1, 8), 1)
514 ];
515 }
516
517 #[test]
518 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
519 fn run_add_no_check_missing_constant() {
520 let mut vm = vm_with_range_check!();
521 vm.run_context.fp = 10;
523 let ids_data = non_continuous_ids_data![
525 ("a", -10),
526 ("b", -7),
527 ("carry_d0", -3),
528 ("carry_d1", -2),
529 ("carry_d2", -1)
530 ];
531 vm.segments = segments![
533 ((1, 0), 3789423292314891293),
535 ((1, 1), 21894),
536 ((1, 2), 340282366920938463463374607431768211455_u128),
537 ((1, 3), 32838232),
539 ((1, 4), 17),
540 ((1, 5), 8)
541 ];
542 assert_matches!(
544 run_hint!(vm, ids_data, hint_code::ADD_NO_UINT384_CHECK),
545 Err(HintError::MissingConstant(bx)) if *bx == "SHIFT"
546 );
547 }
548
549 #[test]
550 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
551 fn run_sqrt_ok() {
552 let mut vm = vm_with_range_check!();
553 vm.run_context.fp = 5;
555 let ids_data = non_continuous_ids_data![("a", -5), ("root", -2)];
557 vm.segments = segments![
559 ((1, 0), 83434123481193248),
561 ((1, 1), 82349321849739284),
562 ((1, 2), 839243219401320423)
563 ];
564 assert_matches!(run_hint!(vm, ids_data, hint_code::UINT384_SQRT), Ok(()));
566 check_memory![
568 vm.segments.memory,
569 ((1, 3), 100835122758113432298839930225328621183),
571 ((1, 4), 916102188),
572 ((1, 5), 0)
573 ];
574 }
575
576 #[test]
577 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
578 fn run_sqrt_assertion_fail() {
579 let mut vm = vm_with_range_check!();
580 vm.run_context.fp = 5;
582 let ids_data = non_continuous_ids_data![("a", -5), ("root", -2)];
584 vm.segments = segments![
587 ((1, 0), (-1)),
589 ((1, 1), (-1)),
590 ((1, 2), (-1))
591 ];
592 assert_matches!(
594 run_hint!(vm, ids_data, hint_code::UINT384_SQRT),
595 Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert 0 <= root < 2 ** 192"
596 );
597 }
598
599 #[test]
600 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
601 fn run_signed_nn_ok_positive() {
602 let mut vm = vm_with_range_check!();
603 vm.run_context.fp = 3;
605 let ids_data = non_continuous_ids_data![("a", -2)];
607 vm.segments = segments![
609 ((1, 3), 1)
611 ];
612 assert!(run_hint!(vm, ids_data, hint_code::UINT384_SIGNED_NN).is_ok());
614 check_memory![
616 vm.segments.memory,
617 ((1, 0), 1)
619 ];
620 }
621
622 #[test]
623 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
624 fn run_signed_nn_missing_identifier() {
625 let mut vm = vm_with_range_check!();
626 vm.run_context.fp = 3;
628 let ids_data = non_continuous_ids_data![("a", -2)];
630 vm.segments = segments![
632 ((1, 1), 1),
634 ((1, 2), 1) ];
637 assert_matches!(run_hint!(vm, ids_data, hint_code::UINT384_SIGNED_NN),
639 Err(HintError::IdentifierHasNoMember(bx)) if *bx == ("a".to_string(), "d2".to_string())
640 );
641 }
642
643 #[test]
644 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
645 fn run_signed_nn_ok_negative() {
646 let mut vm = vm_with_range_check!();
647 vm.run_context.fp = 3;
649 let ids_data = non_continuous_ids_data![("a", -2)];
651 vm.segments = segments![
653 ((1, 3), 170141183460469231731687303715884105729_u128)
655 ];
656 assert!(run_hint!(vm, ids_data, hint_code::UINT384_SIGNED_NN).is_ok());
658 check_memory![
660 vm.segments.memory,
661 ((1, 0), 0)
663 ];
664 }
665
666 #[test]
667 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
668 fn run_uint384_sub_a_b_ok_a_max() {
669 let mut vm = vm_with_range_check!();
670 vm.run_context.fp = 10;
672 let ids_data = non_continuous_ids_data![("a", -10), ("b", -7), ("p", -4), ("res", -1)];
674 vm.segments = segments![
676 ((1, 0), 6),
678 ((1, 1), 6),
679 ((1, 2), 6),
680 ((1, 3), 1),
682 ((1, 4), 1),
683 ((1, 5), 1),
684 ((1, 6), 7),
686 ((1, 7), 7),
687 ((1, 8), 7)
688 ];
689 assert!(run_hint!(vm, ids_data, hint_code::SUB_REDUCED_A_AND_REDUCED_B).is_ok());
691 check_memory![
693 vm.segments.memory,
694 ((1, 9), 5),
696 ((1, 10), 5),
697 ((1, 11), 5)
698 ];
699 }
700
701 #[test]
702 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
703 fn run_uint384_sub_a_b_ok_b_max() {
704 let mut vm = vm_with_range_check!();
705 vm.run_context.fp = 10;
707 let ids_data = non_continuous_ids_data![("a", -10), ("b", -7), ("p", -4), ("res", -1)];
709 vm.segments = segments![
711 ((1, 0), 3),
713 ((1, 1), 3),
714 ((1, 2), 3),
715 ((1, 3), 5),
717 ((1, 4), 5),
718 ((1, 5), 5),
719 ((1, 6), 7),
721 ((1, 7), 7),
722 ((1, 8), 7)
723 ];
724 assert!(run_hint!(vm, ids_data, hint_code::SUB_REDUCED_A_AND_REDUCED_B).is_ok());
726 check_memory![
728 vm.segments.memory,
729 ((1, 9), 5),
731 ((1, 10), 5),
732 ((1, 11), 5)
733 ];
734 }
735}