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 #[cfg_attr(feature = "parity-scale-codec", codec(index = 2))]
359 AddMarker { start: ResOperand, end: ResOperand },
360 #[cfg_attr(feature = "parity-scale-codec", codec(index = 3))]
362 AddTrace { flag: ResOperand },
363}
364
365struct DerefOrImmediateFormatter<'a>(&'a DerefOrImmediate);
366impl Display for DerefOrImmediateFormatter<'_> {
367 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
368 match self.0 {
369 DerefOrImmediate::Deref(d) => write!(f, "memory{d}"),
370 DerefOrImmediate::Immediate(i) => write!(f, "{}", i.value),
371 }
372 }
373}
374
375struct ResOperandAsIntegerFormatter<'a>(&'a ResOperand);
376impl Display for ResOperandAsIntegerFormatter<'_> {
377 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
378 match self.0 {
379 ResOperand::Deref(d) => write!(f, "memory{d}"),
380 ResOperand::DoubleDeref(d, i) => write!(f, "memory[memory{d} + {i}]"),
381 ResOperand::Immediate(i) => write!(f, "{}", i.value),
382 ResOperand::BinOp(bin_op) => {
383 write!(
384 f,
385 "(memory{} {} {}) % PRIME",
386 bin_op.a,
387 bin_op.op,
388 DerefOrImmediateFormatter(&bin_op.b)
389 )
390 }
391 }
392 }
393}
394
395struct ResOperandAsAddressFormatter<'a>(&'a ResOperand);
396impl Display for ResOperandAsAddressFormatter<'_> {
397 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
398 match self.0 {
399 ResOperand::Deref(d) => write!(f, "memory{d}"),
400 ResOperand::DoubleDeref(d, i) => write!(f, "memory[memory{d} + {i}]"),
401 ResOperand::Immediate(i) => {
402 unreachable!("Address cannot be an immediate: {}.", i.value)
403 }
404 ResOperand::BinOp(bin_op) => {
405 write!(
406 f,
407 "memory{} {} {}",
408 bin_op.a,
409 bin_op.op,
410 DerefOrImmediateFormatter(&bin_op.b)
411 )
412 }
413 }
414 }
415}
416
417impl PythonicHint for CoreHintBase {
418 fn get_pythonic_hint(&self) -> String {
419 match self {
420 CoreHintBase::Core(hint) => hint.get_pythonic_hint(),
421 CoreHintBase::Deprecated(_) => {
422 unreachable!("Deprecated hints do not have a pythonic version.")
423 }
424 }
425 }
426}
427
428impl PythonicHint for CoreHint {
429 fn get_pythonic_hint(&self) -> String {
430 match self {
431 CoreHint::AllocSegment { dst } => format!("memory{dst} = segments.add()"),
432 CoreHint::AllocFelt252Dict { segment_arena_ptr } => {
433 let segment_arena_ptr = ResOperandAsAddressFormatter(segment_arena_ptr);
434 formatdoc! {"
435
436 if '__dict_manager' not in globals():
437 from starkware.cairo.common.dict import DictManager
438 __dict_manager = DictManager()
439
440 if '__segment_index_to_arena_index' not in globals():
441 # A map from the relocatable value segment index to the index in the
442 # arena.
443 __segment_index_to_arena_index = {{}}
444
445 # {segment_arena_ptr} is the address of the next SegmentArenaBuiltin.
446 # memory[{segment_arena_ptr} - 2] is the number of allocated segments.
447 index = memory[{segment_arena_ptr} - 2]
448
449 segment_start = __dict_manager.new_default_dict(
450 segments, 0, temp_segment=index > 0
451 )
452
453 # Update '__segment_index_to_arena_index'.
454 __segment_index_to_arena_index[segment_start.segment_index] = index
455
456 # Update 'SegmentInfo::start'.
457 # memory[{segment_arena_ptr} - 3] is the address of the segment arena infos
458 # segment. index * 3 is added to get the address of the new SegmentInfo.
459 memory[memory[{segment_arena_ptr} - 3] + index * 3] = segment_start
460 "}
461 }
462 CoreHint::Felt252DictEntryInit { dict_ptr, key } => {
463 let (dict_ptr, key) =
464 (ResOperandAsAddressFormatter(dict_ptr), ResOperandAsIntegerFormatter(key));
465 formatdoc! {"
466
467 dict_tracker = __dict_manager.get_tracker({dict_ptr})
468 dict_tracker.current_ptr += 3
469 memory[{dict_ptr} + 1] = dict_tracker.data[{key}]
470 "}
471 }
472 CoreHint::Felt252DictEntryUpdate { dict_ptr, value } => {
473 let (dict_ptr, value) =
474 (ResOperandAsAddressFormatter(dict_ptr), ResOperandAsIntegerFormatter(value));
475 formatdoc! {"
476
477 dict_tracker = __dict_manager.get_tracker({dict_ptr})
478 dict_tracker.data[memory[{dict_ptr} - 3]] = {value}
479 "}
480 }
481 CoreHint::TestLessThan { lhs, rhs, dst } => {
482 format!(
483 "memory{dst} = {} < {}",
484 ResOperandAsIntegerFormatter(lhs),
485 ResOperandAsIntegerFormatter(rhs)
486 )
487 }
488 CoreHint::TestLessThanOrEqual { lhs, rhs, dst } => format!(
489 "memory{dst} = {} <= {}",
490 ResOperandAsIntegerFormatter(lhs),
491 ResOperandAsIntegerFormatter(rhs)
492 ),
493 CoreHint::TestLessThanOrEqualAddress { lhs, rhs, dst } => format!(
494 "memory{dst} = {} <= {}",
495 ResOperandAsAddressFormatter(lhs),
496 ResOperandAsAddressFormatter(rhs)
497 ),
498 CoreHint::WideMul128 { lhs, rhs, high, low } => format!(
499 "(memory{high}, memory{low}) = divmod({} * {}, 2**128)",
500 ResOperandAsIntegerFormatter(lhs),
501 ResOperandAsIntegerFormatter(rhs)
502 ),
503 CoreHint::DivMod { lhs, rhs, quotient, remainder } => format!(
504 "(memory{quotient}, memory{remainder}) = divmod({}, {})",
505 ResOperandAsIntegerFormatter(lhs),
506 ResOperandAsIntegerFormatter(rhs)
507 ),
508 CoreHint::Uint256DivMod {
509 dividend0,
510 dividend1,
511 quotient0,
512 quotient1,
513 divisor0,
514 divisor1,
515 remainder0,
516 remainder1,
517 } => {
518 let (dividend0, dividend1, divisor0, divisor1) = (
519 ResOperandAsIntegerFormatter(dividend0),
520 ResOperandAsIntegerFormatter(dividend1),
521 ResOperandAsIntegerFormatter(divisor0),
522 ResOperandAsIntegerFormatter(divisor1),
523 );
524 formatdoc! {"
525
526 dividend = {dividend0} + {dividend1} * 2**128
527 divisor = {divisor0} + {divisor1} * 2**128
528 quotient, remainder = divmod(dividend, divisor)
529 memory{quotient0} = quotient & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
530 memory{quotient1} = quotient >> 128
531 memory{remainder0} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
532 memory{remainder1} = remainder >> 128
533 "}
534 }
535 CoreHint::Uint512DivModByUint256 {
536 dividend0,
537 dividend1,
538 dividend2,
539 dividend3,
540 divisor0,
541 divisor1,
542 quotient0,
543 quotient1,
544 quotient2,
545 quotient3,
546 remainder0,
547 remainder1,
548 } => {
549 let [dividend0, dividend1, dividend2, dividend3, divisor0, divisor1] =
550 [dividend0, dividend1, dividend2, dividend3, divisor0, divisor1]
551 .map(ResOperandAsIntegerFormatter);
552 formatdoc! {"
553
554 dividend = {dividend0} + {dividend1} * 2**128 + {dividend2} * 2**256 + \
555 {dividend3} * 2**384
556 divisor = {divisor0} + {divisor1} * 2**128
557 quotient, remainder = divmod(dividend, divisor)
558 memory{quotient0} = quotient & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
559 memory{quotient1} = (quotient >> 128) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
560 memory{quotient2} = (quotient >> 256) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
561 memory{quotient3} = quotient >> 384
562 memory{remainder0} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
563 memory{remainder1} = remainder >> 128
564 "}
565 }
566 CoreHint::SquareRoot { value, dst } => {
567 let value = ResOperandAsIntegerFormatter(value);
568 formatdoc! {"
569
570 import math
571 memory{dst} = math.isqrt({value})
572 "}
573 }
574 CoreHint::Uint256SquareRoot {
575 value_low,
576 value_high,
577 sqrt0,
578 sqrt1,
579 remainder_low,
580 remainder_high,
581 sqrt_mul_2_minus_remainder_ge_u128,
582 } => {
583 let (value_low, value_high) = (
584 ResOperandAsIntegerFormatter(value_low),
585 ResOperandAsIntegerFormatter(value_high),
586 );
587 formatdoc! {"
588
589 import math;
590 value = {value_low} + {value_high} * 2**128
591 root = math.isqrt(value)
592 remainder = value - root ** 2
593 memory{sqrt0} = root & 0xFFFFFFFFFFFFFFFF
594 memory{sqrt1} = root >> 64
595 memory{remainder_low} = remainder & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
596 memory{remainder_high} = remainder >> 128
597 memory{sqrt_mul_2_minus_remainder_ge_u128} = root * 2 - remainder >= 2**128
598 "}
599 }
600 CoreHint::LinearSplit { value, scalar, max_x, x, y } => {
601 let (value, scalar, max_x) = (
602 ResOperandAsIntegerFormatter(value),
603 ResOperandAsIntegerFormatter(scalar),
604 ResOperandAsIntegerFormatter(max_x),
605 );
606 formatdoc! {"
607
608 (value, scalar) = ({value}, {scalar})
609 x = min(value // scalar, {max_x})
610 y = value - x * scalar
611 memory{x} = x
612 memory{y} = y
613 "}
614 }
615 CoreHint::RandomEcPoint { x, y } => {
616 formatdoc! {"
617
618 from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME
619 from starkware.python.math_utils import random_ec_point
620 (memory{x}, memory{y}) = random_ec_point(FIELD_PRIME, ALPHA, BETA)
621 "}
622 }
623 CoreHint::FieldSqrt { val, sqrt } => {
624 let val = ResOperandAsIntegerFormatter(val);
625 formatdoc! {"
626
627 from starkware.crypto.signature.signature import FIELD_PRIME
628 from starkware.python.math_utils import is_quad_residue, sqrt
629
630 val = {val}
631 if is_quad_residue(val, FIELD_PRIME):
632 memory{sqrt} = sqrt(val, FIELD_PRIME)
633 else:
634 memory{sqrt} = sqrt(val * 3, FIELD_PRIME)
635 "}
636 }
637 CoreHint::GetCurrentAccessIndex { range_check_ptr } => {
638 let rc = ResOperandAsAddressFormatter(range_check_ptr);
639 formatdoc! {"
640
641 current_access_indices = sorted(access_indices[key])[::-1]
642 current_access_index = current_access_indices.pop()
643 memory[{rc}] = current_access_index
644 "}
645 }
646 CoreHint::ShouldSkipSquashLoop { should_skip_loop } => {
647 format!("memory{should_skip_loop} = 0 if current_access_indices else 1")
648 }
649 CoreHint::GetCurrentAccessDelta { index_delta_minus1 } => formatdoc! {"
650
651 new_access_index = current_access_indices.pop()
652 memory{index_delta_minus1} = new_access_index - current_access_index - 1
653 current_access_index = new_access_index
654 "},
655 CoreHint::ShouldContinueSquashLoop { should_continue } => {
656 format!("memory{should_continue} = 1 if current_access_indices else 0")
657 }
658 CoreHint::GetNextDictKey { next_key } => formatdoc! {"
659 assert len(keys) > 0, 'No keys left but remaining_accesses > 0.'
660 memory{next_key} = key = keys.pop()
661 "},
662 CoreHint::GetSegmentArenaIndex { dict_end_ptr, dict_index } => {
663 let dict_end_ptr = ResOperandAsAddressFormatter(dict_end_ptr);
664 formatdoc! {"
665
666 memory{dict_index} = __segment_index_to_arena_index[
667 {dict_end_ptr}.segment_index
668 ]
669 "}
670 }
671 CoreHint::InitSquashData {
672 dict_accesses,
673 ptr_diff,
674 n_accesses,
675 big_keys,
676 first_key,
677 } => {
678 let (dict_accesses, ptr_diff, n_accesses) = (
679 ResOperandAsAddressFormatter(dict_accesses),
680 ResOperandAsIntegerFormatter(ptr_diff),
681 ResOperandAsIntegerFormatter(n_accesses),
682 );
683 formatdoc! {"
684
685 dict_access_size = 3
686 address = {dict_accesses}
687 assert {ptr_diff} % dict_access_size == 0, 'Accesses array size must be \
688 divisible by DictAccess.SIZE'
689 n_accesses = {n_accesses}
690 if '__squash_dict_max_size' in globals():
691 assert n_accesses <= __squash_dict_max_size, f'squash_dict() can only be \
692 used with n_accesses<={{__squash_dict_max_size}}. ' f'Got: \
693 n_accesses={{n_accesses}}.'
694 # A map from key to the list of indices accessing it.
695 access_indices = {{}}
696 for i in range(n_accesses):
697 key = memory[address + dict_access_size * i]
698 access_indices.setdefault(key, []).append(i)
699 # Descending list of keys.
700 keys = sorted(access_indices.keys(), reverse=True)
701 # Are the keys used bigger than range_check bound.
702 memory{big_keys} = 1 if keys[0] >= range_check_builtin.bound else 0
703 memory{first_key} = key = keys.pop()
704 "}
705 }
706 CoreHint::AssertLeFindSmallArcs { range_check_ptr, a, b } => {
707 let (range_check_ptr, a, b) = (
708 ResOperandAsAddressFormatter(range_check_ptr),
709 ResOperandAsIntegerFormatter(a),
710 ResOperandAsIntegerFormatter(b),
711 );
712 let prime_over_3_high = 3544607988759775765608368578435044694_u128;
714 let prime_over_2_high = 5316911983139663648412552867652567041_u128;
716 formatdoc! {"
717
718 import itertools
719
720 from starkware.cairo.common.math_utils import assert_integer
721 assert_integer({a})
722 assert_integer({b})
723 a = {a} % PRIME
724 b = {b} % PRIME
725 assert a <= b, f'a = {{a}} is not less than or equal to b = {{b}}.'
726
727 # Find an arc less than PRIME / 3, and another less than PRIME / 2.
728 lengths_and_indices = [(a, 0), (b - a, 1), (PRIME - 1 - b, 2)]
729 lengths_and_indices.sort()
730 assert lengths_and_indices[0][0] <= PRIME // 3 and lengths_and_indices[1][0] \
731 <= PRIME // 2
732 excluded = lengths_and_indices[2][1]
733
734 memory[{range_check_ptr} + 1], memory[{range_check_ptr} + 0] = (
735 divmod(lengths_and_indices[0][0], {prime_over_3_high}))
736 memory[{range_check_ptr} + 3], memory[{range_check_ptr} + 2] = (
737 divmod(lengths_and_indices[1][0], {prime_over_2_high}))
738 "}
739 }
740 CoreHint::AssertLeIsFirstArcExcluded { skip_exclude_a_flag } => {
741 format!("memory{skip_exclude_a_flag} = 1 if excluded != 0 else 0",)
742 }
743 CoreHint::AssertLeIsSecondArcExcluded { skip_exclude_b_minus_a } => {
744 format!("memory{skip_exclude_b_minus_a} = 1 if excluded != 1 else 0",)
745 }
746 CoreHint::DebugPrint { start, end } => {
747 let [start, end] = [start, end].map(ResOperandAsAddressFormatter);
748 formatdoc! {"
749
750 curr = {start}
751 end = {end}
752 while curr != end:
753 print(hex(memory[curr]))
754 curr += 1
755 "}
756 }
757 CoreHint::AllocConstantSize { size, dst } => {
758 let size = ResOperandAsIntegerFormatter(size);
759 formatdoc! {"
760
761 if '__boxed_segment' not in globals():
762 __boxed_segment = segments.add()
763 memory{dst} = __boxed_segment
764 __boxed_segment += {size}
765 "}
766 }
767 CoreHint::U256InvModN {
768 b0,
769 b1,
770 n0,
771 n1,
772 g0_or_no_inv,
773 g1_option,
774 s_or_r0,
775 s_or_r1,
776 t_or_k0,
777 t_or_k1,
778 } => {
779 let [b0, b1, n0, n1] = [b0, b1, n0, n1].map(ResOperandAsIntegerFormatter);
780 formatdoc! {"
781
782 from starkware.python.math_utils import igcdex
783
784 b = {b0} + ({b1} << 128)
785 n = {n0} + ({n1} << 128)
786
787 (_, r, g) = igcdex(n, b)
788 if n == 1:
789 memory{g0_or_no_inv} = 1
790 memory{g1_option} = 0
791 memory{s_or_r0} = {b0}
792 memory{s_or_r1} = {b1}
793 memory{t_or_k0} = 1
794 memory{t_or_k1} = 0
795 elif g != 1:
796 if g % 2 == 0:
797 g = 2
798 s = b // g
799 t = n // g
800 memory{g0_or_no_inv} = g & 0xffffffffffffffffffffffffffffffff
801 memory{g1_option} = g >> 128
802 memory{s_or_r0} = s & 0xffffffffffffffffffffffffffffffff
803 memory{s_or_r1} = s >> 128
804 memory{t_or_k0} = t & 0xffffffffffffffffffffffffffffffff
805 memory{t_or_k1} = t >> 128
806 else:
807 r %= n
808 k = (r * b - 1) // n
809 memory{g0_or_no_inv} = 0
810 memory{s_or_r0} = r & 0xffffffffffffffffffffffffffffffff
811 memory{s_or_r1} = r >> 128
812 memory{t_or_k0} = k & 0xffffffffffffffffffffffffffffffff
813 memory{t_or_k1} = k >> 128
814 "}
815 }
816 CoreHint::EvalCircuit { n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin } => {
817 let n_add_mods = ResOperandAsIntegerFormatter(n_add_mods);
818 let add_mod_builtin = ResOperandAsAddressFormatter(add_mod_builtin);
819 let n_mul_mods = ResOperandAsIntegerFormatter(n_mul_mods);
820 let mul_mod_builtin = ResOperandAsAddressFormatter(mul_mod_builtin);
821 formatdoc! {"
822
823 from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner
824
825 ModBuiltinRunner.fill_memory(
826 memory=memory,
827 add_mod=({add_mod_builtin}, builtin_runners[\"add_mod_builtin\"], {n_add_mods}),
828 mul_mod=({mul_mod_builtin}, builtin_runners[\"mul_mod_builtin\"], {n_mul_mods}),
829 )
830 "}
831 }
832 }
833 }
834}
835
836impl PythonicHint for StarknetHint {
837 fn get_pythonic_hint(&self) -> String {
838 match self {
839 StarknetHint::SystemCall { system } => {
840 format!(
841 "syscall_handler.syscall(syscall_ptr={})",
842 ResOperandAsAddressFormatter(system)
843 )
844 }
845 StarknetHint::Cheatcode { .. } => {
846 r#"raise NotImplementedError("Cheatcode")"#.to_string()
847 }
848 }
849 }
850}
851
852impl PythonicHint for ExternalHint {
853 fn get_pythonic_hint(&self) -> String {
854 match self {
855 Self::AddRelocationRule { src, dst } => {
856 let [src, dst] = [src, dst].map(ResOperandAsAddressFormatter);
857 format!("memory.add_relocation_rule(src_ptr={src}, dest_ptr={dst})")
858 }
859 Self::WriteRunParam { index, dst } => {
860 let index = ResOperandAsIntegerFormatter(index);
861 format!("WriteRunParam {{ dst: {dst}, index: {index} }}",)
862 }
863 Self::AddMarker { start, end } => {
864 let [start, end] = [start, end].map(ResOperandAsAddressFormatter);
865 format!("AddMarker {{ start: {start}, end: {end} }}")
866 }
867 Self::AddTrace { flag } => {
868 format!("AddTrace {{ flag: {} }}", ResOperandAsIntegerFormatter(flag))
869 }
870 }
871 }
872}