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