1#[cfg(not(feature = "std"))]
2use alloc::{
3 format,
4 string::{String, ToString},
5};
6use core::fmt::{Display, Formatter};
7
8use cairo_lang_utils::bigint::BigIntAsHex;
9use indoc::formatdoc;
10
11use crate::operand::{CellRef, DerefOrImmediate, ResOperand};
12
13#[cfg(test)]
14mod test;
15
16#[derive(Debug, Eq, PartialEq, Clone)]
20#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(untagged))]
21#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
22#[cfg_attr(
23 feature = "parity-scale-codec",
24 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
25)]
26pub enum Hint {
27 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
28 Core(CoreHintBase),
29 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
30 Starknet(StarknetHint),
31 #[cfg_attr(feature = "parity-scale-codec", codec(index = 2))]
32 #[cfg_attr(feature = "schemars", schemars(skip))]
33 External(ExternalHint),
34}
35
36impl Hint {
37 pub fn representing_string(&self) -> String {
38 format!("{:?}", self)
39 }
40}
41
42impl From<CoreHint> for Hint {
43 fn from(value: CoreHint) -> Self {
44 Hint::Core(value.into())
45 }
46}
47impl From<StarknetHint> for Hint {
48 fn from(value: StarknetHint) -> Self {
49 Hint::Starknet(value)
50 }
51}
52impl From<ExternalHint> for Hint {
53 fn from(value: ExternalHint) -> Self {
54 Hint::External(value)
55 }
56}
57
58pub trait PythonicHint {
61 fn get_pythonic_hint(&self) -> String;
62}
63
64impl PythonicHint for Hint {
65 fn get_pythonic_hint(&self) -> String {
66 match self {
67 Hint::Core(hint) => hint.get_pythonic_hint(),
68 Hint::Starknet(hint) => hint.get_pythonic_hint(),
69 Hint::External(hint) => hint.get_pythonic_hint(),
70 }
71 }
72}
73
74#[derive(Debug, Eq, PartialEq, Clone)]
76#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
77#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
78#[cfg_attr(
79 feature = "parity-scale-codec",
80 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
81)]
82pub enum StarknetHint {
83 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
84 SystemCall { system: ResOperand },
85 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
86 #[cfg_attr(feature = "schemars", schemars(skip))]
87 Cheatcode {
88 selector: BigIntAsHex,
89 input_start: ResOperand,
90 input_end: ResOperand,
91 output_start: CellRef,
92 output_end: CellRef,
93 },
94}
95
96#[derive(Debug, Eq, PartialEq, Clone)]
98#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(untagged))]
99#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
100#[cfg_attr(
101 feature = "parity-scale-codec",
102 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
103)]
104pub enum CoreHintBase {
105 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
106 Core(CoreHint),
107 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
108 Deprecated(DeprecatedHint),
109}
110
111impl From<CoreHint> for CoreHintBase {
112 fn from(value: CoreHint) -> Self {
113 CoreHintBase::Core(value)
114 }
115}
116impl From<DeprecatedHint> for CoreHintBase {
117 fn from(value: DeprecatedHint) -> Self {
118 CoreHintBase::Deprecated(value)
119 }
120}
121
122#[derive(Debug, Eq, PartialEq, Clone)]
123#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
124#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
125#[cfg_attr(
126 feature = "parity-scale-codec",
127 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
128)]
129pub enum CoreHint {
130 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
131 AllocSegment { dst: CellRef },
132 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
133 TestLessThan { lhs: ResOperand, rhs: ResOperand, dst: CellRef },
134 #[cfg_attr(feature = "parity-scale-codec", codec(index = 2))]
135 TestLessThanOrEqual { lhs: ResOperand, rhs: ResOperand, dst: CellRef },
136 #[cfg_attr(feature = "parity-scale-codec", codec(index = 28))]
138 TestLessThanOrEqualAddress { lhs: ResOperand, rhs: ResOperand, dst: CellRef },
139 #[cfg_attr(feature = "parity-scale-codec", codec(index = 3))]
142 WideMul128 { lhs: ResOperand, rhs: ResOperand, high: CellRef, low: CellRef },
143 #[cfg_attr(feature = "parity-scale-codec", codec(index = 4))]
147 DivMod { lhs: ResOperand, rhs: ResOperand, quotient: CellRef, remainder: CellRef },
148 #[cfg_attr(feature = "parity-scale-codec", codec(index = 5))]
152 Uint256DivMod {
153 dividend0: ResOperand,
154 dividend1: ResOperand,
155 divisor0: ResOperand,
156 divisor1: ResOperand,
157 quotient0: CellRef,
158 quotient1: CellRef,
159 remainder0: CellRef,
160 remainder1: CellRef,
161 },
162 #[cfg_attr(feature = "parity-scale-codec", codec(index = 6))]
167 Uint512DivModByUint256 {
168 dividend0: ResOperand,
169 dividend1: ResOperand,
170 dividend2: ResOperand,
171 dividend3: ResOperand,
172 divisor0: ResOperand,
173 divisor1: ResOperand,
174 quotient0: CellRef,
175 quotient1: CellRef,
176 quotient2: CellRef,
177 quotient3: CellRef,
178 remainder0: CellRef,
179 remainder1: CellRef,
180 },
181 #[cfg_attr(feature = "parity-scale-codec", codec(index = 7))]
182 SquareRoot { value: ResOperand, dst: CellRef },
183 #[cfg_attr(feature = "parity-scale-codec", codec(index = 8))]
188 Uint256SquareRoot {
189 value_low: ResOperand,
190 value_high: ResOperand,
191 sqrt0: CellRef,
192 sqrt1: CellRef,
193 remainder_low: CellRef,
194 remainder_high: CellRef,
195 sqrt_mul_2_minus_remainder_ge_u128: CellRef,
196 },
197 #[cfg_attr(feature = "parity-scale-codec", codec(index = 9))]
199 LinearSplit { value: ResOperand, scalar: ResOperand, max_x: ResOperand, x: CellRef, y: CellRef },
200 #[cfg_attr(feature = "parity-scale-codec", codec(index = 10))]
202 AllocFelt252Dict { segment_arena_ptr: ResOperand },
203 #[cfg_attr(feature = "parity-scale-codec", codec(index = 11))]
205 Felt252DictEntryInit { dict_ptr: ResOperand, key: ResOperand },
206 #[cfg_attr(feature = "parity-scale-codec", codec(index = 12))]
209 Felt252DictEntryUpdate { dict_ptr: ResOperand, value: ResOperand },
210 #[cfg_attr(feature = "parity-scale-codec", codec(index = 13))]
212 GetSegmentArenaIndex { dict_end_ptr: ResOperand, dict_index: CellRef },
213 #[cfg_attr(feature = "parity-scale-codec", codec(index = 14))]
215 InitSquashData {
216 dict_accesses: ResOperand,
217 ptr_diff: ResOperand,
218 n_accesses: ResOperand,
219 big_keys: CellRef,
220 first_key: CellRef,
221 },
222 #[cfg_attr(feature = "parity-scale-codec", codec(index = 15))]
224 GetCurrentAccessIndex { range_check_ptr: ResOperand },
225 #[cfg_attr(feature = "parity-scale-codec", codec(index = 16))]
227 ShouldSkipSquashLoop { should_skip_loop: CellRef },
228 #[cfg_attr(feature = "parity-scale-codec", codec(index = 17))]
230 GetCurrentAccessDelta { index_delta_minus1: CellRef },
231 #[cfg_attr(feature = "parity-scale-codec", codec(index = 18))]
233 ShouldContinueSquashLoop { should_continue: CellRef },
234 #[cfg_attr(feature = "parity-scale-codec", codec(index = 19))]
236 GetNextDictKey { next_key: CellRef },
237 #[cfg_attr(feature = "parity-scale-codec", codec(index = 20))]
240 AssertLeFindSmallArcs { range_check_ptr: ResOperand, a: ResOperand, b: ResOperand },
241 #[cfg_attr(feature = "parity-scale-codec", codec(index = 21))]
243 AssertLeIsFirstArcExcluded { skip_exclude_a_flag: CellRef },
244 #[cfg_attr(feature = "parity-scale-codec", codec(index = 22))]
246 AssertLeIsSecondArcExcluded { skip_exclude_b_minus_a: CellRef },
247 #[cfg_attr(feature = "parity-scale-codec", codec(index = 23))]
249 RandomEcPoint { x: CellRef, y: CellRef },
250 #[cfg_attr(feature = "parity-scale-codec", codec(index = 24))]
256 FieldSqrt { val: ResOperand, sqrt: CellRef },
257 #[cfg_attr(feature = "parity-scale-codec", codec(index = 25))]
260 DebugPrint { start: ResOperand, end: ResOperand },
261 #[cfg_attr(feature = "parity-scale-codec", codec(index = 26))]
263 AllocConstantSize { size: ResOperand, dst: CellRef },
264 #[cfg_attr(feature = "parity-scale-codec", codec(index = 27))]
284 U256InvModN {
285 b0: ResOperand,
286 b1: ResOperand,
287 n0: ResOperand,
288 n1: ResOperand,
289 g0_or_no_inv: CellRef,
290 g1_option: CellRef,
291 s_or_r0: CellRef,
292 s_or_r1: CellRef,
293 t_or_k0: CellRef,
294 t_or_k1: CellRef,
295 },
296
297 #[cfg_attr(feature = "parity-scale-codec", codec(index = 29))]
298 EvalCircuit {
299 n_add_mods: ResOperand,
300 add_mod_builtin: ResOperand,
301 n_mul_mods: ResOperand,
302 mul_mod_builtin: ResOperand,
303 },
304}
305
306#[derive(Debug, Eq, PartialEq, Clone)]
309#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
310#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
311#[cfg_attr(
312 feature = "parity-scale-codec",
313 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
314)]
315pub enum DeprecatedHint {
316 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
318 AssertCurrentAccessIndicesIsEmpty,
319 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
322 AssertAllAccessesUsed { n_used_accesses: CellRef },
323 #[cfg_attr(feature = "parity-scale-codec", codec(index = 2))]
325 AssertAllKeysUsed,
326 #[cfg_attr(feature = "parity-scale-codec", codec(index = 3))]
328 AssertLeAssertThirdArcExcluded,
329 #[cfg_attr(feature = "parity-scale-codec", codec(index = 4))]
331 AssertLtAssertValidInput { a: ResOperand, b: ResOperand },
332 #[cfg_attr(feature = "parity-scale-codec", codec(index = 5))]
335 Felt252DictRead { dict_ptr: ResOperand, key: ResOperand, value_dst: CellRef },
336 #[cfg_attr(feature = "parity-scale-codec", codec(index = 6))]
338 Felt252DictWrite { dict_ptr: ResOperand, key: ResOperand, value: ResOperand },
339}
340
341#[derive(Debug, Eq, PartialEq, Clone)]
345#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
346#[cfg_attr(
347 feature = "parity-scale-codec",
348 derive(parity_scale_codec::Encode, parity_scale_codec::Decode)
349)]
350pub enum ExternalHint {
351 #[cfg_attr(feature = "parity-scale-codec", codec(index = 0))]
353 AddRelocationRule { src: ResOperand, dst: ResOperand },
354 #[cfg_attr(feature = "parity-scale-codec", codec(index = 1))]
356 WriteRunParam { index: ResOperand, dst: CellRef },
357}
358
359struct DerefOrImmediateFormatter<'a>(&'a DerefOrImmediate);
360impl Display for DerefOrImmediateFormatter<'_> {
361 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
362 match self.0 {
363 DerefOrImmediate::Deref(d) => write!(f, "memory{d}"),
364 DerefOrImmediate::Immediate(i) => write!(f, "{}", i.value),
365 }
366 }
367}
368
369struct ResOperandAsIntegerFormatter<'a>(&'a ResOperand);
370impl Display for ResOperandAsIntegerFormatter<'_> {
371 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
372 match self.0 {
373 ResOperand::Deref(d) => write!(f, "memory{d}"),
374 ResOperand::DoubleDeref(d, i) => write!(f, "memory[memory{d} + {i}]"),
375 ResOperand::Immediate(i) => write!(f, "{}", i.value),
376 ResOperand::BinOp(bin_op) => {
377 write!(
378 f,
379 "(memory{} {} {}) % PRIME",
380 bin_op.a,
381 bin_op.op,
382 DerefOrImmediateFormatter(&bin_op.b)
383 )
384 }
385 }
386 }
387}
388
389struct ResOperandAsAddressFormatter<'a>(&'a ResOperand);
390impl Display for ResOperandAsAddressFormatter<'_> {
391 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
392 match self.0 {
393 ResOperand::Deref(d) => write!(f, "memory{d}"),
394 ResOperand::DoubleDeref(d, i) => write!(f, "memory[memory{d} + {i}]"),
395 ResOperand::Immediate(i) => {
396 unreachable!("Address cannot be an immediate: {}.", i.value)
397 }
398 ResOperand::BinOp(bin_op) => {
399 write!(
400 f,
401 "memory{} {} {}",
402 bin_op.a,
403 bin_op.op,
404 DerefOrImmediateFormatter(&bin_op.b)
405 )
406 }
407 }
408 }
409}
410
411impl PythonicHint for CoreHintBase {
412 fn get_pythonic_hint(&self) -> String {
413 match self {
414 CoreHintBase::Core(hint) => hint.get_pythonic_hint(),
415 CoreHintBase::Deprecated(_) => {
416 unreachable!("Deprecated hints do not have a pythonic version.")
417 }
418 }
419 }
420}
421
422impl PythonicHint for CoreHint {
423 fn get_pythonic_hint(&self) -> String {
424 match self {
425 CoreHint::AllocSegment { dst } => format!("memory{dst} = segments.add()"),
426 CoreHint::AllocFelt252Dict { segment_arena_ptr } => {
427 let segment_arena_ptr = ResOperandAsAddressFormatter(segment_arena_ptr);
428 formatdoc! {"
429
430 if '__dict_manager' not in globals():
431 from starkware.cairo.common.dict import DictManager
432 __dict_manager = DictManager()
433
434 if '__segment_index_to_arena_index' not in globals():
435 # A map from the relocatable value segment index to the index in the
436 # arena.
437 __segment_index_to_arena_index = {{}}
438
439 # {segment_arena_ptr} is the address of the next SegmentArenaBuiltin.
440 # memory[{segment_arena_ptr} - 2] is the number of allocated segments.
441 index = memory[{segment_arena_ptr} - 2]
442
443 segment_start = __dict_manager.new_default_dict(
444 segments, 0, temp_segment=index > 0
445 )
446
447 # Update '__segment_index_to_arena_index'.
448 __segment_index_to_arena_index[segment_start.segment_index] = index
449
450 # Update 'SegmentInfo::start'.
451 # memory[{segment_arena_ptr} - 3] is the address of the segment arena infos
452 # segment. index * 3 is added to get the address of the new SegmentInfo.
453 memory[memory[{segment_arena_ptr} - 3] + index * 3] = segment_start
454 "}
455 }
456 CoreHint::Felt252DictEntryInit { dict_ptr, key } => {
457 let (dict_ptr, key) =
458 (ResOperandAsAddressFormatter(dict_ptr), ResOperandAsIntegerFormatter(key));
459 formatdoc! {"
460
461 dict_tracker = __dict_manager.get_tracker({dict_ptr})
462 dict_tracker.current_ptr += 3
463 memory[{dict_ptr} + 1] = dict_tracker.data[{key}]
464 "}
465 }
466 CoreHint::Felt252DictEntryUpdate { dict_ptr, value } => {
467 let (dict_ptr, value) =
468 (ResOperandAsAddressFormatter(dict_ptr), ResOperandAsIntegerFormatter(value));
469 formatdoc! {"
470
471 dict_tracker = __dict_manager.get_tracker({dict_ptr})
472 dict_tracker.data[memory[{dict_ptr} - 3]] = {value}
473 "}
474 }
475 CoreHint::TestLessThan { lhs, rhs, dst } => {
476 format!(
477 "memory{dst} = {} < {}",
478 ResOperandAsIntegerFormatter(lhs),
479 ResOperandAsIntegerFormatter(rhs)
480 )
481 }
482 CoreHint::TestLessThanOrEqual { lhs, rhs, dst } => format!(
483 "memory{dst} = {} <= {}",
484 ResOperandAsIntegerFormatter(lhs),
485 ResOperandAsIntegerFormatter(rhs)
486 ),
487 CoreHint::TestLessThanOrEqualAddress { lhs, rhs, dst } => format!(
488 "memory{dst} = {} <= {}",
489 ResOperandAsAddressFormatter(lhs),
490 ResOperandAsAddressFormatter(rhs)
491 ),
492 CoreHint::WideMul128 { lhs, rhs, high, low } => format!(
493 "(memory{high}, memory{low}) = divmod({} * {}, 2**128)",
494 ResOperandAsIntegerFormatter(lhs),
495 ResOperandAsIntegerFormatter(rhs)
496 ),
497 CoreHint::DivMod { lhs, rhs, quotient, remainder } => format!(
498 "(memory{quotient}, memory{remainder}) = divmod({}, {})",
499 ResOperandAsIntegerFormatter(lhs),
500 ResOperandAsIntegerFormatter(rhs)
501 ),
502 CoreHint::Uint256DivMod {
503 dividend0,
504 dividend1,
505 quotient0,
506 quotient1,
507 divisor0,
508 divisor1,
509 remainder0,
510 remainder1,
511 } => {
512 let (dividend0, dividend1, divisor0, divisor1) = (
513 ResOperandAsIntegerFormatter(dividend0),
514 ResOperandAsIntegerFormatter(dividend1),
515 ResOperandAsIntegerFormatter(divisor0),
516 ResOperandAsIntegerFormatter(divisor1),
517 );
518 formatdoc! {"
519
520 dividend = {dividend0} + {dividend1} * 2**128
521 divisor = {divisor0} + {divisor1} * 2**128
522 quotient, remainder = divmod(dividend, divisor)
523 memory{quotient0} = quotient & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
524 memory{quotient1} = quotient >> 128
525 memory{remainder0} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
526 memory{remainder1} = remainder >> 128
527 "}
528 }
529 CoreHint::Uint512DivModByUint256 {
530 dividend0,
531 dividend1,
532 dividend2,
533 dividend3,
534 divisor0,
535 divisor1,
536 quotient0,
537 quotient1,
538 quotient2,
539 quotient3,
540 remainder0,
541 remainder1,
542 } => {
543 let [dividend0, dividend1, dividend2, dividend3, divisor0, divisor1] =
544 [dividend0, dividend1, dividend2, dividend3, divisor0, divisor1]
545 .map(ResOperandAsIntegerFormatter);
546 formatdoc! {"
547
548 dividend = {dividend0} + {dividend1} * 2**128 + {dividend2} * 2**256 + \
549 {dividend3} * 2**384
550 divisor = {divisor0} + {divisor1} * 2**128
551 quotient, remainder = divmod(dividend, divisor)
552 memory{quotient0} = quotient & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
553 memory{quotient1} = (quotient >> 128) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
554 memory{quotient2} = (quotient >> 256) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
555 memory{quotient3} = quotient >> 384
556 memory{remainder0} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
557 memory{remainder1} = remainder >> 128
558 "}
559 }
560 CoreHint::SquareRoot { value, dst } => {
561 let value = ResOperandAsIntegerFormatter(value);
562 formatdoc! {"
563
564 import math
565 memory{dst} = math.isqrt({value})
566 "}
567 }
568 CoreHint::Uint256SquareRoot {
569 value_low,
570 value_high,
571 sqrt0,
572 sqrt1,
573 remainder_low,
574 remainder_high,
575 sqrt_mul_2_minus_remainder_ge_u128,
576 } => {
577 let (value_low, value_high) = (
578 ResOperandAsIntegerFormatter(value_low),
579 ResOperandAsIntegerFormatter(value_high),
580 );
581 formatdoc! {"
582
583 import math;
584 value = {value_low} + {value_high} * 2**128
585 root = math.isqrt(value)
586 remainder = value - root ** 2
587 memory{sqrt0} = root & 0xFFFFFFFFFFFFFFFF
588 memory{sqrt1} = root >> 64
589 memory{remainder_low} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
590 memory{remainder_high} = remainder >> 128
591 memory{sqrt_mul_2_minus_remainder_ge_u128} = root * 2 - remainder >= 2**128
592 "}
593 }
594 CoreHint::LinearSplit { value, scalar, max_x, x, y } => {
595 let (value, scalar, max_x) = (
596 ResOperandAsIntegerFormatter(value),
597 ResOperandAsIntegerFormatter(scalar),
598 ResOperandAsIntegerFormatter(max_x),
599 );
600 formatdoc! {"
601
602 (value, scalar) = ({value}, {scalar})
603 x = min(value // scalar, {max_x})
604 y = value - x * scalar
605 memory{x} = x
606 memory{y} = y
607 "}
608 }
609 CoreHint::RandomEcPoint { x, y } => {
610 formatdoc! {"
611
612 from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME
613 from starkware.python.math_utils import random_ec_point
614 (memory{x}, memory{y}) = random_ec_point(FIELD_PRIME, ALPHA, BETA)
615 "}
616 }
617 CoreHint::FieldSqrt { val, sqrt } => {
618 let val = ResOperandAsIntegerFormatter(val);
619 formatdoc! {"
620
621 from starkware.crypto.signature.signature import FIELD_PRIME
622 from starkware.python.math_utils import is_quad_residue, sqrt
623
624 val = {val}
625 if is_quad_residue(val, FIELD_PRIME):
626 memory{sqrt} = sqrt(val, FIELD_PRIME)
627 else:
628 memory{sqrt} = sqrt(val * 3, FIELD_PRIME)
629 "}
630 }
631 CoreHint::GetCurrentAccessIndex { range_check_ptr } => {
632 let rc = ResOperandAsAddressFormatter(range_check_ptr);
633 formatdoc! {"
634
635 current_access_indices = sorted(access_indices[key])[::-1]
636 current_access_index = current_access_indices.pop()
637 memory[{rc}] = current_access_index
638 "}
639 }
640 CoreHint::ShouldSkipSquashLoop { should_skip_loop } => {
641 format!("memory{should_skip_loop} = 0 if current_access_indices else 1")
642 }
643 CoreHint::GetCurrentAccessDelta { index_delta_minus1 } => formatdoc! {"
644
645 new_access_index = current_access_indices.pop()
646 memory{index_delta_minus1} = new_access_index - current_access_index - 1
647 current_access_index = new_access_index
648 "},
649 CoreHint::ShouldContinueSquashLoop { should_continue } => {
650 format!("memory{should_continue} = 1 if current_access_indices else 0")
651 }
652 CoreHint::GetNextDictKey { next_key } => formatdoc! {"
653 assert len(keys) > 0, 'No keys left but remaining_accesses > 0.'
654 memory{next_key} = key = keys.pop()
655 "},
656 CoreHint::GetSegmentArenaIndex { dict_end_ptr, dict_index } => {
657 let dict_end_ptr = ResOperandAsAddressFormatter(dict_end_ptr);
658 formatdoc! {"
659
660 memory{dict_index} = __segment_index_to_arena_index[
661 {dict_end_ptr}.segment_index
662 ]
663 "}
664 }
665 CoreHint::InitSquashData {
666 dict_accesses,
667 ptr_diff,
668 n_accesses,
669 big_keys,
670 first_key,
671 } => {
672 let (dict_accesses, ptr_diff, n_accesses) = (
673 ResOperandAsAddressFormatter(dict_accesses),
674 ResOperandAsIntegerFormatter(ptr_diff),
675 ResOperandAsIntegerFormatter(n_accesses),
676 );
677 formatdoc! {"
678
679 dict_access_size = 3
680 address = {dict_accesses}
681 assert {ptr_diff} % dict_access_size == 0, 'Accesses array size must be \
682 divisible by DictAccess.SIZE'
683 n_accesses = {n_accesses}
684 if '__squash_dict_max_size' in globals():
685 assert n_accesses <= __squash_dict_max_size, f'squash_dict() can only be \
686 used with n_accesses<={{__squash_dict_max_size}}. ' f'Got: \
687 n_accesses={{n_accesses}}.'
688 # A map from key to the list of indices accessing it.
689 access_indices = {{}}
690 for i in range(n_accesses):
691 key = memory[address + dict_access_size * i]
692 access_indices.setdefault(key, []).append(i)
693 # Descending list of keys.
694 keys = sorted(access_indices.keys(), reverse=True)
695 # Are the keys used bigger than range_check bound.
696 memory{big_keys} = 1 if keys[0] >= range_check_builtin.bound else 0
697 memory{first_key} = key = keys.pop()
698 "}
699 }
700 CoreHint::AssertLeFindSmallArcs { range_check_ptr, a, b } => {
701 let (range_check_ptr, a, b) = (
702 ResOperandAsAddressFormatter(range_check_ptr),
703 ResOperandAsIntegerFormatter(a),
704 ResOperandAsIntegerFormatter(b),
705 );
706 formatdoc! {"
707
708 import itertools
709
710 from starkware.cairo.common.math_utils import assert_integer
711 assert_integer({a})
712 assert_integer({b})
713 a = {a} % PRIME
714 b = {b} % PRIME
715 assert a <= b, f'a = {{a}} is not less than or equal to b = {{b}}.'
716
717 # Find an arc less than PRIME / 3, and another less than PRIME / 2.
718 lengths_and_indices = [(a, 0), (b - a, 1), (PRIME - 1 - b, 2)]
719 lengths_and_indices.sort()
720 assert lengths_and_indices[0][0] <= PRIME // 3 and lengths_and_indices[1][0] \
721 <= PRIME // 2
722 excluded = lengths_and_indices[2][1]
723
724 memory[{range_check_ptr} + 1], memory[{range_check_ptr} + 0] = (
725 divmod(lengths_and_indices[0][0], 3544607988759775765608368578435044694))
726 memory[{range_check_ptr} + 3], memory[{range_check_ptr} + 2] = (
727 divmod(lengths_and_indices[1][0], 5316911983139663648412552867652567041))
728 "}
729 }
730 CoreHint::AssertLeIsFirstArcExcluded { skip_exclude_a_flag } => {
731 format!("memory{skip_exclude_a_flag} = 1 if excluded != 0 else 0",)
732 }
733 CoreHint::AssertLeIsSecondArcExcluded { skip_exclude_b_minus_a } => {
734 format!("memory{skip_exclude_b_minus_a} = 1 if excluded != 1 else 0",)
735 }
736 CoreHint::DebugPrint { start, end } => {
737 let [start, end] = [start, end].map(ResOperandAsAddressFormatter);
738 formatdoc! {"
739
740 curr = {start}
741 end = {end}
742 while curr != end:
743 print(hex(memory[curr]))
744 curr += 1
745 "}
746 }
747 CoreHint::AllocConstantSize { size, dst } => {
748 let size = ResOperandAsIntegerFormatter(size);
749 formatdoc! {"
750
751 if '__boxed_segment' not in globals():
752 __boxed_segment = segments.add()
753 memory{dst} = __boxed_segment
754 __boxed_segment += {size}
755 "}
756 }
757 CoreHint::U256InvModN {
758 b0,
759 b1,
760 n0,
761 n1,
762 g0_or_no_inv,
763 g1_option,
764 s_or_r0,
765 s_or_r1,
766 t_or_k0,
767 t_or_k1,
768 } => {
769 let [b0, b1, n0, n1] = [b0, b1, n0, n1].map(ResOperandAsIntegerFormatter);
770 formatdoc! {"
771
772 from starkware.python.math_utils import igcdex
773
774 b = {b0} + ({b1} << 128)
775 n = {n0} + ({n1} << 128)
776
777 (_, r, g) = igcdex(n, b)
778 if n == 1:
779 memory{g0_or_no_inv} = 1
780 memory{g1_option} = 0
781 memory{s_or_r0} = {b0}
782 memory{s_or_r1} = {b1}
783 memory{t_or_k0} = 1
784 memory{t_or_k1} = 0
785 elif g != 1:
786 if g % 2 == 0:
787 g = 2
788 s = b // g
789 t = n // g
790 memory{g0_or_no_inv} = g & 0xffffffffffffffffffffffffffffffff
791 memory{g1_option} = g >> 128
792 memory{s_or_r0} = s & 0xffffffffffffffffffffffffffffffff
793 memory{s_or_r1} = s >> 128
794 memory{t_or_k0} = t & 0xffffffffffffffffffffffffffffffff
795 memory{t_or_k1} = t >> 128
796 else:
797 r %= n
798 k = (r * b - 1) // n
799 memory{g0_or_no_inv} = 0
800 memory{s_or_r0} = r & 0xffffffffffffffffffffffffffffffff
801 memory{s_or_r1} = r >> 128
802 memory{t_or_k0} = k & 0xffffffffffffffffffffffffffffffff
803 memory{t_or_k1} = k >> 128
804 "}
805 }
806 CoreHint::EvalCircuit { n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin } => {
807 let n_add_mods = ResOperandAsIntegerFormatter(n_add_mods);
808 let add_mod_builtin = ResOperandAsAddressFormatter(add_mod_builtin);
809 let n_mul_mods = ResOperandAsIntegerFormatter(n_mul_mods);
810 let mul_mod_builtin = ResOperandAsAddressFormatter(mul_mod_builtin);
811 formatdoc! {"
812
813 from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner
814
815 ModBuiltinRunner.fill_memory(
816 memory=memory,
817 add_mod=({add_mod_builtin}, builtin_runners[\"add_mod_builtin\"], {n_add_mods}),
818 mul_mod=({mul_mod_builtin}, builtin_runners[\"mul_mod_builtin\"], {n_mul_mods}),
819 )
820 "}
821 }
822 }
823 }
824}
825
826impl PythonicHint for StarknetHint {
827 fn get_pythonic_hint(&self) -> String {
828 match self {
829 StarknetHint::SystemCall { system } => {
830 format!(
831 "syscall_handler.syscall(syscall_ptr={})",
832 ResOperandAsAddressFormatter(system)
833 )
834 }
835 StarknetHint::Cheatcode { .. } => {
836 r#"raise NotImplementedError("Cheatcode")"#.to_string()
837 }
838 }
839 }
840}
841
842impl PythonicHint for ExternalHint {
843 fn get_pythonic_hint(&self) -> String {
844 match self {
845 Self::AddRelocationRule { src, dst } => {
846 let [src, dst] = [src, dst].map(ResOperandAsAddressFormatter);
847 format!("memory.add_relocation_rule(src_ptr={src}, dest_ptr={dst})")
848 }
849 Self::WriteRunParam { index, dst } => {
850 let index = ResOperandAsIntegerFormatter(index);
851 format!(r#"raise NotImplementedError("memory{dst}.. = params[{index}])")"#)
852 }
853 }
854 }
855}