cairo_vm/vm/vm_memory/
memory.rs

1use crate::stdlib::{borrow::Cow, collections::HashMap, fmt, prelude::*};
2
3use crate::types::errors::math_errors::MathError;
4use crate::vm::runners::cairo_pie::CairoPieMemory;
5use crate::Felt252;
6use crate::{
7    types::relocatable::{MaybeRelocatable, Relocatable},
8    utils::from_relocatable_to_indexes,
9    vm::errors::memory_errors::MemoryError,
10};
11use bitvec::prelude as bv;
12use core::cmp::Ordering;
13use num_traits::ToPrimitive;
14
15pub struct ValidationRule(
16    #[allow(clippy::type_complexity)]
17    pub  Box<dyn Fn(&Memory, Relocatable) -> Result<Vec<Relocatable>, MemoryError>>,
18);
19
20/// [`MemoryCell`] represents an optimized storage layout for the VM memory.
21/// It's specified to have both size an alignment of 32 bytes to optimize cache access.
22/// Typical cache sizes are 64 bytes, a few cases might be 128 bytes, meaning 32 bytes aligned to
23/// 32 bytes boundaries will never get split into two separate lines, avoiding double stalls and
24/// reducing false sharing and evictions.
25/// The trade off is extra computation for conversion to our "in-flight" `MaybeRelocatable` and
26/// `Felt252` as well as some extra copies. Empirically, this seems to be offset by the improved
27/// locality of the bigger structure for Lambdaworks. There is a big hit from the conversions when
28/// using the `BigUint` implementation, since those force allocations on the heap, but since that's
29/// dropped in later versions anyway it's not a priority. For Lambdaworks the new copies are mostly
30/// to the stack, which is typically already in the cache.
31/// The layout uses the 4 MSB in the first `u64` as flags:
32/// - BIT63: NONE flag, 1 when the cell is actually empty.
33/// - BIT62: ACCESS flag, 1 when the cell has been accessed in a way observable to Cairo.
34/// - BIT61: RELOCATABLE flag, 1 when the contained value is a `Relocatable`, 0 when it is a
35///   `Felt252`.
36///
37/// `Felt252` values are stored in big-endian order to keep the flag bits free.
38/// `Relocatable` values are stored as native endian, with the 3rd word storing the segment index
39/// and the 4th word storing the offset.
40#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Debug)]
41#[repr(align(32))]
42pub(crate) struct MemoryCell([u64; 4]);
43
44impl MemoryCell {
45    pub const NONE_MASK: u64 = 1 << 63;
46    pub const ACCESS_MASK: u64 = 1 << 62;
47    pub const RELOCATABLE_MASK: u64 = 1 << 61;
48    pub const NONE: Self = Self([Self::NONE_MASK, 0, 0, 0]);
49
50    pub fn new(value: MaybeRelocatable) -> Self {
51        value.into()
52    }
53
54    pub fn is_none(&self) -> bool {
55        self.0[0] & Self::NONE_MASK == Self::NONE_MASK
56    }
57
58    pub fn is_some(&self) -> bool {
59        !self.is_none()
60    }
61
62    pub fn mark_accessed(&mut self) {
63        self.0[0] |= Self::ACCESS_MASK;
64    }
65
66    pub fn is_accessed(&self) -> bool {
67        self.0[0] & Self::ACCESS_MASK == Self::ACCESS_MASK
68    }
69
70    pub fn get_value(&self) -> Option<MaybeRelocatable> {
71        self.is_some().then(|| (*self).into())
72    }
73}
74
75impl From<MaybeRelocatable> for MemoryCell {
76    fn from(value: MaybeRelocatable) -> Self {
77        match value {
78            MaybeRelocatable::Int(x) => Self(x.to_raw()),
79            MaybeRelocatable::RelocatableValue(x) => Self([
80                Self::RELOCATABLE_MASK,
81                0,
82                // NOTE: hack around signedness
83                usize::from_ne_bytes(x.segment_index.to_ne_bytes()) as u64,
84                x.offset as u64,
85            ]),
86        }
87    }
88}
89
90impl From<MemoryCell> for MaybeRelocatable {
91    fn from(cell: MemoryCell) -> Self {
92        debug_assert!(cell.is_some());
93        let flags = cell.0[0];
94        match flags & MemoryCell::RELOCATABLE_MASK {
95            MemoryCell::RELOCATABLE_MASK => Self::from((
96                // NOTE: hack around signedness
97                isize::from_ne_bytes((cell.0[2] as usize).to_ne_bytes()),
98                cell.0[3] as usize,
99            )),
100            _ => {
101                let mut value = cell.0;
102                // Remove all flag bits
103                value[0] &= 0x0fffffffffffffff;
104                Self::Int(Felt252::from_raw(value))
105            }
106        }
107    }
108}
109
110pub struct AddressSet(Vec<bv::BitVec>);
111
112impl AddressSet {
113    pub(crate) fn new() -> Self {
114        Self(Vec::new())
115    }
116
117    pub(crate) fn contains(&self, addr: &Relocatable) -> bool {
118        let segment = addr.segment_index;
119        if segment.is_negative() {
120            return false;
121        }
122
123        self.0
124            .get(segment as usize)
125            .and_then(|segment| segment.get(addr.offset))
126            .map(|bit| *bit)
127            .unwrap_or(false)
128    }
129
130    pub(crate) fn extend(&mut self, addresses: &[Relocatable]) {
131        for addr in addresses {
132            let segment = addr.segment_index;
133            if segment.is_negative() {
134                continue;
135            }
136            let segment = segment as usize;
137            if segment >= self.0.len() {
138                self.0.resize(segment + 1, bv::BitVec::new());
139            }
140
141            let offset = addr.offset;
142            if offset >= self.0[segment].len() {
143                self.0[segment].resize(offset + 1, false);
144            }
145            self.0[segment].replace(offset, true);
146        }
147    }
148}
149
150#[cfg(test)]
151impl AddressSet {
152    pub(crate) fn len(&self) -> usize {
153        self.0
154            .iter()
155            .map(|segment| segment.iter().map(|bit| *bit as usize).sum::<usize>())
156            .sum()
157    }
158}
159
160pub struct Memory {
161    pub(crate) data: Vec<Vec<MemoryCell>>,
162    /// Temporary segments are used when it's necessary to write data, but we
163    /// don't know yet where it will be located. These segments will eventually
164    /// be relocated to the main memory according to the `relocation_rules`. For
165    /// example, dictionaries are required to be contiguous, so each is stored in a
166    /// temporary segment and eventually relocated to a single segment.
167    pub(crate) temp_data: Vec<Vec<MemoryCell>>,
168    // relocation_rules's keys map to temp_data's indices and therefore begin at
169    // zero; that is, segment_index = -1 maps to key 0, -2 to key 1...
170    #[cfg(not(feature = "extensive_hints"))]
171    pub(crate) relocation_rules: HashMap<usize, Relocatable>,
172    #[cfg(feature = "extensive_hints")]
173    pub(crate) relocation_rules: HashMap<usize, MaybeRelocatable>,
174    pub validated_addresses: AddressSet,
175    validation_rules: Vec<Option<ValidationRule>>,
176}
177
178impl Memory {
179    pub fn new() -> Memory {
180        Memory {
181            data: Vec::new(),
182            temp_data: Vec::new(),
183            relocation_rules: HashMap::new(),
184            validated_addresses: AddressSet::new(),
185            validation_rules: Vec::with_capacity(7),
186        }
187    }
188
189    /// Inserts a value into a memory address
190    /// Will return an Error if the segment index given by the address corresponds to a non-allocated segment,
191    /// or if the inserted value is inconsistent with the current value at the memory cell
192    /// If the address isnt contiguous with previously inserted data, memory gaps will be represented by None values
193    pub fn insert<V>(&mut self, key: Relocatable, val: V) -> Result<(), MemoryError>
194    where
195        MaybeRelocatable: From<V>,
196    {
197        let val = MaybeRelocatable::from(val);
198        let (value_index, value_offset) = from_relocatable_to_indexes(key);
199
200        let data = if key.segment_index.is_negative() {
201            &mut self.temp_data
202        } else {
203            &mut self.data
204        };
205
206        let data_len = data.len();
207        let segment = data
208            .get_mut(value_index)
209            .ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((value_index, data_len))))?;
210
211        //Check if the element is inserted next to the last one on the segment
212        //Forgoing this check would allow data to be inserted in a different index
213        let (len, capacity) = (segment.len(), segment.capacity());
214        if len <= value_offset {
215            let new_len = value_offset
216                .checked_add(1)
217                .ok_or(MemoryError::VecCapacityExceeded)?;
218            segment
219                .try_reserve(new_len.saturating_sub(capacity))
220                .map_err(|_| MemoryError::VecCapacityExceeded)?;
221            segment.resize(new_len, MemoryCell::NONE);
222        }
223        // At this point there's *something* in there
224
225        match segment[value_offset].get_value() {
226            None => segment[value_offset] = MemoryCell::new(val),
227            Some(current_cell) => {
228                if current_cell != val {
229                    //Existing memory cannot be changed
230                    return Err(MemoryError::InconsistentMemory(Box::new((
231                        key,
232                        current_cell,
233                        val,
234                    ))));
235                }
236            }
237        };
238        self.validate_memory_cell(key)
239    }
240
241    /// Retrieve a value from memory (either normal or temporary) and apply relocation rules
242    pub(crate) fn get<'a, 'b: 'a, K: 'a>(&'b self, key: &'a K) -> Option<Cow<'b, MaybeRelocatable>>
243    where
244        Relocatable: TryFrom<&'a K>,
245    {
246        let relocatable: Relocatable = key.try_into().ok()?;
247
248        let data = if relocatable.segment_index.is_negative() {
249            &self.temp_data
250        } else {
251            &self.data
252        };
253        let (i, j) = from_relocatable_to_indexes(relocatable);
254        let value = data.get(i)?.get(j)?.get_value()?;
255        Some(Cow::Owned(self.relocate_value(&value).ok()?.into_owned()))
256    }
257
258    // Version of Memory.relocate_value() that doesn't require a self reference
259    #[cfg(not(feature = "extensive_hints"))]
260    fn relocate_address(
261        addr: Relocatable,
262        relocation_rules: &HashMap<usize, Relocatable>,
263    ) -> Result<MaybeRelocatable, MemoryError> {
264        if addr.segment_index < 0 {
265            // Adjust the segment index to begin at zero, as per the struct field's
266            // comment.
267            if let Some(x) = relocation_rules.get(&(-(addr.segment_index + 1) as usize)) {
268                return Ok((*x + addr.offset)?.into());
269            }
270        }
271        Ok(addr.into())
272    }
273
274    #[cfg(feature = "extensive_hints")]
275    fn relocate_address(
276        addr: Relocatable,
277        relocation_rules: &HashMap<usize, MaybeRelocatable>,
278    ) -> Result<MaybeRelocatable, MemoryError> {
279        if addr.segment_index < 0 {
280            // Adjust the segment index to begin at zero, as per the struct field's
281            // comment.
282            if let Some(x) = relocation_rules.get(&(-(addr.segment_index + 1) as usize)) {
283                return Ok(match x {
284                    MaybeRelocatable::RelocatableValue(r) => (*r + addr.offset)?.into(),
285                    MaybeRelocatable::Int(i) => i.into(),
286                });
287            }
288        }
289        Ok(addr.into())
290    }
291
292    #[cfg(not(feature = "extensive_hints"))]
293    fn flatten_relocation_rules(&mut self) -> Result<(), MemoryError> {
294        let keys: Vec<usize> = self.relocation_rules.keys().copied().collect();
295        let max_hops = self.relocation_rules.len().saturating_add(1);
296        for key in keys {
297            let mut dst = *self
298                .relocation_rules
299                .get(&key)
300                .expect("key taken from keys vec must exist");
301
302            let mut hops = 0;
303            while dst.segment_index < 0 {
304                let next_key = (-(dst.segment_index + 1)) as usize;
305                let next = *self
306                    .relocation_rules
307                    .get(&next_key)
308                    .ok_or(MemoryError::UnmappedTemporarySegment(dst.segment_index))?;
309                dst = (next + dst.offset).map_err(MemoryError::Math)?;
310                hops += 1;
311                if hops > max_hops {
312                    return Err(MemoryError::Relocation); // cycle guard
313                }
314            }
315            self.relocation_rules.insert(key, dst);
316        }
317        Ok(())
318    }
319
320    #[cfg(feature = "extensive_hints")]
321    fn flatten_relocation_rules(&mut self) -> Result<(), MemoryError> {
322        let keys: Vec<usize> = self.relocation_rules.keys().copied().collect();
323        let max_hops = self.relocation_rules.len().saturating_add(1);
324        for key in keys {
325            let mut dst = self
326                .relocation_rules
327                .get(&key)
328                .expect("key taken from keys vec must exist")
329                .clone();
330
331            let mut hops = 0;
332            loop {
333                match dst {
334                    MaybeRelocatable::RelocatableValue(relocatable)
335                        if relocatable.segment_index < 0 =>
336                    {
337                        let next_key = (-(relocatable.segment_index + 1)) as usize;
338                        let next = self
339                            .relocation_rules
340                            .get(&next_key)
341                            .ok_or(MemoryError::UnmappedTemporarySegment(
342                                relocatable.segment_index,
343                            ))?
344                            .clone();
345
346                        match next {
347                            MaybeRelocatable::RelocatableValue(next_relocatable) => {
348                                dst = MaybeRelocatable::RelocatableValue(
349                                    (next_relocatable + relocatable.offset)
350                                        .map_err(MemoryError::Math)?,
351                                );
352                            }
353                            MaybeRelocatable::Int(i) => {
354                                if relocatable.offset != 0 {
355                                    return Err(MemoryError::NonZeroOffset(relocatable.offset));
356                                }
357                                dst = MaybeRelocatable::Int(i);
358                            }
359                        }
360                    }
361                    _ => break,
362                }
363                hops += 1;
364                if hops > max_hops {
365                    return Err(MemoryError::Relocation);
366                }
367            }
368            self.relocation_rules.insert(key, dst);
369        }
370        Ok(())
371    }
372
373    /// Relocates the memory according to the relocation rules and clears `self.relocaction_rules`.
374    pub fn relocate_memory(&mut self) -> Result<(), MemoryError> {
375        if self.relocation_rules.is_empty() || self.temp_data.is_empty() {
376            return Ok(());
377        }
378
379        // flatten chains (temp->temp->...->real).
380        self.flatten_relocation_rules()?;
381
382        // Relocate temporary addresses in memory
383        for segment in self.data.iter_mut().chain(self.temp_data.iter_mut()) {
384            for cell in segment.iter_mut() {
385                let value = cell.get_value();
386                match value {
387                    Some(MaybeRelocatable::RelocatableValue(addr)) if addr.segment_index < 0 => {
388                        let mut new_cell = MemoryCell::new(Memory::relocate_address(
389                            addr,
390                            &self.relocation_rules,
391                        )?);
392                        if cell.is_accessed() {
393                            new_cell.mark_accessed();
394                        }
395                        *cell = new_cell;
396                    }
397                    _ => {}
398                }
399            }
400        }
401        // Move relocated temporary memory into the real memory
402        for index in (0..self.temp_data.len()).rev() {
403            if let Some(base_addr) = self.relocation_rules.get(&index) {
404                let data_segment = self.temp_data.remove(index);
405
406                #[cfg(feature = "extensive_hints")]
407                let base_addr = match base_addr {
408                    MaybeRelocatable::RelocatableValue(addr) => addr,
409                    MaybeRelocatable::Int(_) => {
410                        continue;
411                    }
412                };
413
414                // Insert the to-be relocated segment into the real memory
415                let mut addr = *base_addr;
416                if let Some(s) = self.data.get_mut(addr.segment_index as usize) {
417                    s.reserve_exact(data_segment.len())
418                }
419                for cell in data_segment {
420                    if let Some(v) = cell.get_value() {
421                        // Rely on Memory::insert to catch memory inconsistencies
422                        self.insert(addr, v)?;
423                        // If the cell is accessed, mark the relocated one as accessed too
424                        if cell.is_accessed() {
425                            self.mark_as_accessed(addr)
426                        }
427                    }
428                    addr = (addr + 1)?;
429                }
430            }
431        }
432        self.relocation_rules.clear();
433        Ok(())
434    }
435
436    /// Add a new relocation rule.
437    ///
438    /// When using feature "extensive_hints" the destination is allowed to be an Integer (via
439    /// MaybeRelocatable). Relocating memory to anything other than a `Relocatable` is generally
440    /// not useful, but it does make the implementation consistent with the pythonic version.
441    ///
442    /// Will return an error if any of the following conditions are not met:
443    ///   - Source address's segment must be negative (temporary).
444    ///   - Source address's offset must be zero.
445    ///   - There shouldn't already be relocation at the source segment.
446    #[cfg(not(feature = "extensive_hints"))]
447    pub(crate) fn add_relocation_rule(
448        &mut self,
449        src_ptr: Relocatable,
450        dst_ptr: Relocatable,
451    ) -> Result<(), MemoryError> {
452        if src_ptr.segment_index >= 0 {
453            return Err(MemoryError::AddressNotInTemporarySegment(
454                src_ptr.segment_index,
455            ));
456        }
457        if src_ptr.offset != 0 {
458            return Err(MemoryError::NonZeroOffset(src_ptr.offset));
459        }
460
461        // Adjust the segment index to begin at zero, as per the struct field's
462        // comment.
463        let segment_index = -(src_ptr.segment_index + 1) as usize;
464        if self.relocation_rules.contains_key(&segment_index) {
465            return Err(MemoryError::DuplicatedRelocation(src_ptr.segment_index));
466        }
467
468        self.relocation_rules.insert(segment_index, dst_ptr);
469        Ok(())
470    }
471    #[cfg(feature = "extensive_hints")]
472    pub(crate) fn add_relocation_rule(
473        &mut self,
474        src_ptr: Relocatable,
475        dst: MaybeRelocatable,
476    ) -> Result<(), MemoryError> {
477        if src_ptr.segment_index >= 0 {
478            return Err(MemoryError::AddressNotInTemporarySegment(
479                src_ptr.segment_index,
480            ));
481        }
482        if src_ptr.offset != 0 {
483            return Err(MemoryError::NonZeroOffset(src_ptr.offset));
484        }
485
486        // Adjust the segment index to begin at zero, as per the struct field's
487        // comment.
488        let segment_index = -(src_ptr.segment_index + 1) as usize;
489        if self.relocation_rules.contains_key(&segment_index) {
490            return Err(MemoryError::DuplicatedRelocation(src_ptr.segment_index));
491        }
492
493        self.relocation_rules.insert(segment_index, dst);
494        Ok(())
495    }
496
497    /// Gets the value from memory address as a Felt252 value.
498    /// Returns an Error if the value at the memory address is missing or not a Felt252.
499    pub fn get_integer(&'_ self, key: Relocatable) -> Result<Cow<'_, Felt252>, MemoryError> {
500        match self
501            .get(&key)
502            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
503        {
504            Cow::Borrowed(MaybeRelocatable::Int(int)) => Ok(Cow::Borrowed(int)),
505            Cow::Owned(MaybeRelocatable::Int(int)) => Ok(Cow::Owned(int)),
506            _ => Err(MemoryError::ExpectedInteger(Box::new(key))),
507        }
508    }
509
510    /// Gets a u32 value from memory address.
511    /// Returns an Error if the value at the memory address is missing or not a u32.
512    pub fn get_u32(&self, key: Relocatable) -> Result<u32, MemoryError> {
513        let felt = self.get_integer(key)?.into_owned();
514        felt.to_u32()
515            .ok_or_else(|| MemoryError::Math(MathError::Felt252ToU32Conversion(Box::new(felt))))
516    }
517
518    /// Gets the value from memory address as a usize.
519    /// Returns an Error if the value at the memory address is missing not a Felt252, or can't be converted to usize.
520    pub fn get_usize(&self, key: Relocatable) -> Result<usize, MemoryError> {
521        let felt = self.get_integer(key)?.into_owned();
522        felt.to_usize()
523            .ok_or_else(|| MemoryError::Math(MathError::Felt252ToUsizeConversion(Box::new(felt))))
524    }
525
526    /// Gets the value from memory address as a Relocatable value.
527    /// Returns an Error if the value at the memory address is missing or not a Relocatable.
528    pub fn get_relocatable(&self, key: Relocatable) -> Result<Relocatable, MemoryError> {
529        match self
530            .get(&key)
531            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
532        {
533            Cow::Borrowed(MaybeRelocatable::RelocatableValue(rel)) => Ok(*rel),
534            Cow::Owned(MaybeRelocatable::RelocatableValue(rel)) => Ok(rel),
535            _ => Err(MemoryError::ExpectedRelocatable(Box::new(key))),
536        }
537    }
538
539    /// Gets the value from memory address as a MaybeRelocatable value.
540    /// Returns an Error if the value at the memory address is missing or not a MaybeRelocatable.
541    pub fn get_maybe_relocatable(&self, key: Relocatable) -> Result<MaybeRelocatable, MemoryError> {
542        match self
543            .get(&key)
544            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
545        {
546            // Note: the `Borrowed` variant will never occur.
547            Cow::Borrowed(maybe_rel) => Ok(maybe_rel.clone()),
548            Cow::Owned(maybe_rel) => Ok(maybe_rel),
549        }
550    }
551
552    /// Inserts a value into memory
553    /// Returns an error if the memory cell asignment is invalid
554    pub fn insert_value<T: Into<MaybeRelocatable>>(
555        &mut self,
556        key: Relocatable,
557        val: T,
558    ) -> Result<(), MemoryError> {
559        self.insert(key, &val.into())
560    }
561
562    pub fn add_validation_rule(&mut self, segment_index: usize, rule: ValidationRule) {
563        if segment_index >= self.validation_rules.len() {
564            // Fill gaps
565            self.validation_rules
566                .resize_with(segment_index + 1, || None);
567        }
568        self.validation_rules.insert(segment_index, Some(rule));
569    }
570
571    fn validate_memory_cell(&mut self, addr: Relocatable) -> Result<(), MemoryError> {
572        if let Some(Some(rule)) = addr
573            .segment_index
574            .to_usize()
575            .and_then(|x| self.validation_rules.get(x))
576        {
577            if !self.validated_addresses.contains(&addr) {
578                self.validated_addresses
579                    .extend(rule.0(self, addr)?.as_slice());
580            }
581        }
582        Ok(())
583    }
584
585    ///Applies validation_rules to the current memory
586    pub fn validate_existing_memory(&mut self) -> Result<(), MemoryError> {
587        for (index, rule) in self.validation_rules.iter().enumerate() {
588            if index >= self.data.len() {
589                continue;
590            }
591            let Some(rule) = rule else {
592                continue;
593            };
594            for offset in 0..self.data[index].len() {
595                let addr = Relocatable::from((index as isize, offset));
596                if !self.validated_addresses.contains(&addr) {
597                    self.validated_addresses
598                        .extend(rule.0(self, addr)?.as_slice());
599                }
600            }
601        }
602        Ok(())
603    }
604
605    /// Compares two ranges of values in memory of length `len`
606    /// Returns the ordering and the first relative position at which they differ
607    /// Special cases:
608    /// - `lhs` exists in memory but `rhs` doesn't -> (Ordering::Greater, 0)
609    /// - `rhs` exists in memory but `lhs` doesn't -> (Ordering::Less, 0)
610    /// - None of `lhs` or `rhs` exist in memory -> (Ordering::Equal, 0)
611    ///
612    /// Everything else behaves much like `memcmp` in C.
613    /// This is meant as an optimization for hints to avoid allocations.
614    pub(crate) fn memcmp(
615        &self,
616        lhs: Relocatable,
617        rhs: Relocatable,
618        len: usize,
619    ) -> (Ordering, usize) {
620        let get_segment = |idx: isize| {
621            if idx.is_negative() {
622                self.temp_data.get(-(idx + 1) as usize)
623            } else {
624                self.data.get(idx as usize)
625            }
626        };
627        match (
628            get_segment(lhs.segment_index),
629            get_segment(rhs.segment_index),
630        ) {
631            (None, None) => {
632                return (Ordering::Equal, 0);
633            }
634            (Some(_), None) => {
635                return (Ordering::Greater, 0);
636            }
637            (None, Some(_)) => {
638                return (Ordering::Less, 0);
639            }
640            (Some(lhs_segment), Some(rhs_segment)) => {
641                let (lhs_start, rhs_start) = (lhs.offset, rhs.offset);
642                for i in 0..len {
643                    let (lhs, rhs) = (
644                        lhs_segment.get(lhs_start + i),
645                        rhs_segment.get(rhs_start + i),
646                    );
647                    let ord = lhs.cmp(&rhs);
648                    if ord == Ordering::Equal {
649                        continue;
650                    }
651                    return (ord, i);
652                }
653            }
654        };
655        (Ordering::Equal, len)
656    }
657
658    /// Compares two ranges of values in memory of length `len`
659    /// Returns the ordering and the first relative position at which they differ
660    /// Special cases:
661    /// - `lhs` exists in memory but `rhs` doesn't -> (Ordering::Greater, 0)
662    /// - `rhs` exists in memory but `lhs` doesn't -> (Ordering::Less, 0)
663    /// - None of `lhs` or `rhs` exist in memory -> (Ordering::Equal, 0)
664    ///   Everything else behaves much like `memcmp` in C.
665    ///   This is meant as an optimization for hints to avoid allocations.
666    pub(crate) fn mem_eq(&self, lhs: Relocatable, rhs: Relocatable, len: usize) -> bool {
667        if lhs == rhs {
668            return true;
669        }
670        let get_segment = |idx: isize| {
671            if idx.is_negative() {
672                self.temp_data.get(-(idx + 1) as usize)
673            } else {
674                self.data.get(idx as usize)
675            }
676        };
677        match (
678            get_segment(lhs.segment_index).and_then(|s| s.get(lhs.offset..)),
679            get_segment(rhs.segment_index).and_then(|s| s.get(rhs.offset..)),
680        ) {
681            (Some(lhs), Some(rhs)) => {
682                let (lhs_len, rhs_len) = (lhs.len().min(len), rhs.len().min(len));
683                if lhs_len != rhs_len {
684                    return false;
685                }
686                lhs[..lhs_len] == rhs[..rhs_len]
687            }
688            (None, None) => true,
689            _ => false,
690        }
691    }
692
693    /// Gets a range of memory values from addr to addr + size
694    /// The outputed range may contain gaps if the original memory has them
695    pub fn get_range(
696        &'_ self,
697        addr: Relocatable,
698        size: usize,
699    ) -> Vec<Option<Cow<'_, MaybeRelocatable>>> {
700        let mut values = Vec::new();
701
702        for i in 0..size {
703            values.push((addr + i).ok().and_then(|x| self.get(&x)));
704        }
705
706        values
707    }
708
709    /// Gets a range of memory values from addr to addr + size
710    /// Fails if there if any of the values inside the range is missing (memory gap)
711    pub fn get_continuous_range(
712        &self,
713        addr: Relocatable,
714        size: usize,
715    ) -> Result<Vec<MaybeRelocatable>, MemoryError> {
716        let mut values = Vec::with_capacity(size);
717
718        for i in 0..size {
719            values.push(match self.get(&(addr + i)?) {
720                Some(elem) => elem.into_owned(),
721                None => return Err(MemoryError::GetRangeMemoryGap(Box::new((addr, size)))),
722            });
723        }
724
725        Ok(values)
726    }
727
728    /// Gets a range of Felt252 memory values from addr to addr + size
729    /// Fails if there if any of the values inside the range is missing (memory gap),
730    /// or is not a Felt252
731    pub fn get_integer_range(
732        &'_ self,
733        addr: Relocatable,
734        size: usize,
735    ) -> Result<Vec<Cow<'_, Felt252>>, MemoryError> {
736        let mut values = Vec::new();
737
738        for i in 0..size {
739            values.push(self.get_integer((addr + i)?)?);
740        }
741
742        Ok(values)
743    }
744
745    /// Gets a range of u32 memory values from addr to addr + size
746    /// Fails if any of the values inside the range is missing (memory gap) or is not a u32
747    pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result<Vec<u32>, MemoryError> {
748        let mut values = Vec::new();
749
750        for i in 0..size {
751            values.push(self.get_u32((addr + i)?)?);
752        }
753
754        Ok(values)
755    }
756
757    fn get_cell(&self, addr: Relocatable) -> Option<&MemoryCell> {
758        let (i, j) = from_relocatable_to_indexes(addr);
759        let data = if addr.segment_index < 0 {
760            &self.temp_data
761        } else {
762            &self.data
763        };
764        data.get(i)?.get(j)
765    }
766
767    pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
768        Ok(self
769            .get_cell(*addr)
770            .ok_or(MemoryError::UnknownMemoryCell(Box::new(*addr)))?
771            .is_accessed())
772    }
773
774    pub fn mark_as_accessed(&mut self, addr: Relocatable) {
775        let (i, j) = from_relocatable_to_indexes(addr);
776        let data = if addr.segment_index < 0 {
777            &mut self.temp_data
778        } else {
779            &mut self.data
780        };
781        let cell = data.get_mut(i).and_then(|x| x.get_mut(j));
782        if let Some(cell) = cell {
783            cell.mark_accessed()
784        }
785    }
786
787    pub fn get_amount_of_accessed_addresses_for_segment(
788        &self,
789        segment_index: usize,
790    ) -> Option<usize> {
791        let segment = self.data.get(segment_index)?;
792        Some(
793            segment
794                .iter()
795                .filter(|x| x.is_some() && x.is_accessed())
796                .count(),
797        )
798    }
799
800    // Inserts a value into memory & inmediately marks it as accessed if insertion was succesful
801    // Used by ModBuiltinRunner, as it accesses memory outside of it's segment when operating
802    pub(crate) fn insert_as_accessed<V>(
803        &mut self,
804        key: Relocatable,
805        val: V,
806    ) -> Result<(), MemoryError>
807    where
808        MaybeRelocatable: From<V>,
809    {
810        self.insert(key, val)?;
811        self.mark_as_accessed(key);
812        Ok(())
813    }
814}
815
816impl From<&Memory> for CairoPieMemory {
817    fn from(mem: &Memory) -> CairoPieMemory {
818        let mut pie_memory = Vec::default();
819        for (i, segment) in mem.data.iter().enumerate() {
820            for (j, cell) in segment.iter().enumerate() {
821                if let Some(value) = cell.get_value() {
822                    pie_memory.push(((i, j), value))
823                }
824            }
825        }
826        CairoPieMemory(pie_memory)
827    }
828}
829
830impl fmt::Display for Memory {
831    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
832        for (i, segment) in self.temp_data.iter().enumerate() {
833            for (j, cell) in segment.iter().enumerate() {
834                if let Some(elem) = cell.get_value() {
835                    let temp_segment = i + 1;
836                    writeln!(f, "(-{temp_segment},{j}) : {elem}")?;
837                }
838            }
839        }
840        for (i, segment) in self.data.iter().enumerate() {
841            for (j, cell) in segment.iter().enumerate() {
842                if let Some(elem) = cell.get_value() {
843                    writeln!(f, "({i},{j}) : {elem}")?;
844                }
845            }
846        }
847        Ok(())
848    }
849}
850
851/// Applies `relocation_rules` to a value
852pub(crate) trait RelocateValue<'a, Input: 'a, Output: 'a> {
853    fn relocate_value(&self, value: Input) -> Result<Output, MemoryError>;
854}
855
856#[cfg(not(feature = "extensive_hints"))]
857impl RelocateValue<'_, Relocatable, Relocatable> for Memory {
858    fn relocate_value(&self, addr: Relocatable) -> Result<Relocatable, MemoryError> {
859        if addr.segment_index < 0 {
860            // Adjust the segment index to begin at zero, as per the struct field's
861            // comment.
862            if let Some(x) = self
863                .relocation_rules
864                .get(&(-(addr.segment_index + 1) as usize))
865            {
866                return (*x + addr.offset).map_err(MemoryError::Math);
867            }
868        }
869        Ok(addr)
870    }
871}
872#[cfg(feature = "extensive_hints")]
873impl RelocateValue<'_, Relocatable, MaybeRelocatable> for Memory {
874    fn relocate_value(&self, addr: Relocatable) -> Result<MaybeRelocatable, MemoryError> {
875        if addr.segment_index < 0 {
876            // Adjust the segment index to begin at zero, as per the struct field's
877            // comment.
878            if let Some(x) = self
879                .relocation_rules
880                .get(&(-(addr.segment_index + 1) as usize))
881            {
882                return Ok(match x {
883                    MaybeRelocatable::RelocatableValue(r) => {
884                        (*r + addr.offset).map_err(MemoryError::Math)?.into()
885                    }
886                    MaybeRelocatable::Int(i) => i.into(),
887                });
888            }
889        }
890        Ok(addr.into())
891    }
892}
893
894impl<'a> RelocateValue<'a, &'a Felt252, &'a Felt252> for Memory {
895    fn relocate_value(&self, value: &'a Felt252) -> Result<&'a Felt252, MemoryError> {
896        Ok(value)
897    }
898}
899
900impl<'a> RelocateValue<'a, &'a MaybeRelocatable, Cow<'a, MaybeRelocatable>> for Memory {
901    fn relocate_value(
902        &self,
903        value: &'a MaybeRelocatable,
904    ) -> Result<Cow<'a, MaybeRelocatable>, MemoryError> {
905        Ok(match value {
906            MaybeRelocatable::Int(_) => Cow::Borrowed(value),
907            MaybeRelocatable::RelocatableValue(addr) => {
908                #[cfg(not(feature = "extensive_hints"))]
909                let v = self.relocate_value(*addr)?.into();
910                #[cfg(feature = "extensive_hints")]
911                let v = self.relocate_value(*addr)?;
912
913                Cow::Owned(v)
914            }
915        })
916    }
917}
918
919impl Default for Memory {
920    fn default() -> Self {
921        Self::new()
922    }
923}
924
925#[cfg(test)]
926mod memory_tests {
927
928    use super::*;
929    use crate::{
930        felt_hex, relocatable,
931        utils::test_utils::*,
932        vm::{
933            runners::builtin_runner::{
934                RangeCheckBuiltinRunner, SignatureBuiltinRunner, RC_N_PARTS_STANDARD,
935            },
936            vm_memory::memory_segments::MemorySegmentManager,
937        },
938    };
939    use assert_matches::assert_matches;
940
941    use crate::vm::errors::memory_errors::MemoryError;
942
943    use crate::utils::test_utils::memory_from_memory;
944    use crate::utils::test_utils::memory_inner;
945
946    #[cfg(target_arch = "wasm32")]
947    use wasm_bindgen_test::*;
948
949    #[test]
950    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
951    fn insert_and_get_succesful() {
952        let key = Relocatable::from((0, 0));
953        let val = MaybeRelocatable::from(Felt252::from(5_u64));
954        let mut memory = Memory::new();
955        memory.data.push(Vec::new());
956        memory.insert(key, &val).unwrap();
957        assert_eq!(
958            memory.get(&key).unwrap().as_ref(),
959            &MaybeRelocatable::from(Felt252::from(5_u64))
960        );
961    }
962
963    #[test]
964    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
965    fn get_valuef_from_temp_segment() {
966        let mut memory = Memory::new();
967        memory.temp_data = vec![vec![
968            MemoryCell::NONE,
969            MemoryCell::NONE,
970            MemoryCell::new(mayberelocatable!(8)),
971        ]];
972        assert_eq!(
973            memory.get(&mayberelocatable!(-1, 2)).unwrap().as_ref(),
974            &mayberelocatable!(8),
975        );
976    }
977
978    #[test]
979    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
980    fn insert_value_in_temp_segment() {
981        let key = Relocatable::from((-1, 3));
982        let val = MaybeRelocatable::from(Felt252::from(8_u64));
983        let mut memory = Memory::new();
984        memory.temp_data.push(Vec::new());
985        memory.insert(key, &val).unwrap();
986        assert_eq!(
987            memory.temp_data[0][3],
988            MemoryCell::new(MaybeRelocatable::from(Felt252::from(8_u64)))
989        );
990    }
991
992    #[test]
993    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
994    fn insert_and_get_from_temp_segment_succesful() {
995        let key = Relocatable::from((-1, 0));
996        let val = MaybeRelocatable::from(Felt252::from(5_u64));
997        let mut memory = Memory::new();
998        memory.temp_data.push(Vec::new());
999        memory.insert(key, &val).unwrap();
1000        assert_eq!(
1001            memory.get(&key).unwrap().as_ref(),
1002            &MaybeRelocatable::from(Felt252::from(5_u64)),
1003        );
1004    }
1005
1006    #[test]
1007    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1008    fn insert_and_get_from_temp_segment_failed() {
1009        let key = relocatable!(-1, 1);
1010        let mut memory = Memory::new();
1011        memory.temp_data = vec![vec![
1012            MemoryCell::NONE,
1013            MemoryCell::new(mayberelocatable!(8)),
1014        ]];
1015        assert_eq!(
1016            memory.insert(key, &mayberelocatable!(5)),
1017            Err(MemoryError::InconsistentMemory(Box::new((
1018                relocatable!(-1, 1),
1019                mayberelocatable!(8),
1020                mayberelocatable!(5)
1021            ))))
1022        );
1023    }
1024
1025    #[test]
1026    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1027    fn get_non_allocated_memory() {
1028        let key = Relocatable::from((0, 0));
1029        let memory = Memory::new();
1030        assert_eq!(memory.get(&key), None);
1031    }
1032
1033    #[test]
1034    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1035    fn get_non_existant_element() {
1036        let key = Relocatable::from((0, 0));
1037        let memory = Memory::new();
1038        assert_eq!(memory.get(&key), None);
1039    }
1040
1041    #[test]
1042    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1043    fn insert_non_allocated_memory() {
1044        let key = Relocatable::from((0, 0));
1045        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1046        let mut memory = Memory::new();
1047        let error = memory.insert(key, &val);
1048        assert_eq!(
1049            error,
1050            Err(MemoryError::UnallocatedSegment(Box::new((0, 0))))
1051        );
1052    }
1053
1054    #[test]
1055    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1056    fn insert_inconsistent_memory() {
1057        let key = Relocatable::from((0, 0));
1058        let val_a = MaybeRelocatable::from(Felt252::from(5_u64));
1059        let val_b = MaybeRelocatable::from(Felt252::from(6_u64));
1060        let mut memory = Memory::new();
1061        memory.data.push(Vec::new());
1062        memory
1063            .insert(key, &val_a)
1064            .expect("Unexpected memory insert fail");
1065        let error = memory.insert(key, &val_b);
1066        assert_eq!(
1067            error,
1068            Err(MemoryError::InconsistentMemory(Box::new((
1069                key, val_a, val_b
1070            ))))
1071        );
1072    }
1073
1074    #[test]
1075    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1076    fn insert_non_contiguous_element() {
1077        let key_a = Relocatable::from((0, 0));
1078        let key_b = Relocatable::from((0, 2));
1079        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1080        let mut memory = Memory::new();
1081        memory.data.push(Vec::new());
1082        memory.insert(key_a, &val).unwrap();
1083        memory.insert(key_b, &val).unwrap();
1084        assert_eq!(memory.get(&key_b).unwrap().as_ref(), &val);
1085    }
1086
1087    #[test]
1088    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1089    fn insert_non_contiguous_element_memory_gaps_none() {
1090        let key_a = Relocatable::from((0, 0));
1091        let key_b = Relocatable::from((0, 5));
1092        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1093        let mut memory = Memory::new();
1094        memory.data.push(Vec::new());
1095        memory.insert(key_a, &val).unwrap();
1096        memory.insert(key_b, &val).unwrap();
1097        assert_eq!(memory.get(&key_b).unwrap().as_ref(), &val);
1098        assert_eq!(memory.get(&MaybeRelocatable::from((0, 1))), None);
1099        assert_eq!(memory.get(&MaybeRelocatable::from((0, 2))), None);
1100        assert_eq!(memory.get(&MaybeRelocatable::from((0, 3))), None);
1101        assert_eq!(memory.get(&MaybeRelocatable::from((0, 4))), None);
1102    }
1103
1104    #[test]
1105    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1106    fn validate_existing_memory_for_range_check_within_bounds() {
1107        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1108        let mut segments = MemorySegmentManager::new();
1109        builtin.initialize_segments(&mut segments);
1110        builtin.add_validation_rule(&mut segments.memory);
1111        for _ in 0..3 {
1112            segments.add();
1113        }
1114
1115        segments
1116            .memory
1117            .insert(
1118                Relocatable::from((0, 0)),
1119                &MaybeRelocatable::from(Felt252::from(45_u64)),
1120            )
1121            .unwrap();
1122        segments.memory.validate_existing_memory().unwrap();
1123        assert!(segments
1124            .memory
1125            .validated_addresses
1126            .contains(&Relocatable::from((0, 0))));
1127    }
1128
1129    #[test]
1130    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1131    fn validate_existing_memory_for_range_check_outside_bounds() {
1132        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1133        let mut segments = MemorySegmentManager::new();
1134        segments.add();
1135        builtin.initialize_segments(&mut segments);
1136        segments
1137            .memory
1138            .insert(
1139                Relocatable::from((1, 0)),
1140                &MaybeRelocatable::from(Felt252::from(-10)),
1141            )
1142            .unwrap();
1143        builtin.add_validation_rule(&mut segments.memory);
1144        let error = segments.memory.validate_existing_memory();
1145        assert_eq!(
1146            error,
1147            Err(MemoryError::RangeCheckNumOutOfBounds(Box::new((
1148                Felt252::from(-10),
1149                Felt252::TWO.pow(128_u128)
1150            ))))
1151        );
1152    }
1153
1154    #[test]
1155    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1156    fn validate_existing_memory_for_invalid_signature() {
1157        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
1158        let mut segments = MemorySegmentManager::new();
1159        builtin.initialize_segments(&mut segments);
1160        segments.memory = memory![
1161            (
1162                (0, 0),
1163                (
1164                    "874739451078007766457464989774322083649278607533249481151382481072868806602",
1165                    10
1166                )
1167            ),
1168            (
1169                (0, 1),
1170                (
1171                    "-1472574760335685482768423018116732869320670550222259018541069375211356613248",
1172                    10
1173                )
1174            )
1175        ];
1176        builtin.add_validation_rule(&mut segments.memory);
1177        let error = segments.memory.validate_existing_memory();
1178        assert_eq!(
1179            error,
1180            Err(MemoryError::SignatureNotFound(Box::new((0, 0).into())))
1181        );
1182    }
1183
1184    #[test]
1185    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1186    fn validate_existing_memory_for_valid_signature() {
1187        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
1188
1189        let signature_r =
1190            felt_hex!("0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20");
1191        let signature_s =
1192            felt_hex!("0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b");
1193
1194        builtin
1195            .add_signature(Relocatable::from((1, 0)), &(signature_r, signature_s))
1196            .unwrap();
1197
1198        let mut segments = MemorySegmentManager::new();
1199
1200        segments.memory = memory![
1201            (
1202                (1, 0),
1203                (
1204                    "874739451078007766457464989774322083649278607533249481151382481072868806602",
1205                    10
1206                )
1207            ),
1208            ((1, 1), 2)
1209        ];
1210
1211        builtin.initialize_segments(&mut segments);
1212
1213        builtin.add_validation_rule(&mut segments.memory);
1214
1215        let result = segments.memory.validate_existing_memory();
1216
1217        assert_eq!(result, Ok(()))
1218    }
1219
1220    #[test]
1221    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1222    fn validate_existing_memory_for_range_check_relocatable_value() {
1223        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1224        let mut segments = MemorySegmentManager::new();
1225        builtin.initialize_segments(&mut segments);
1226        segments.memory = memory![((0, 0), (0, 4))];
1227        builtin.add_validation_rule(&mut segments.memory);
1228        let error = segments.memory.validate_existing_memory();
1229        assert_eq!(
1230            error,
1231            Err(MemoryError::RangeCheckFoundNonInt(Box::new(relocatable!(
1232                0, 0
1233            ))))
1234        );
1235    }
1236
1237    #[test]
1238    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1239    fn validate_existing_memory_for_range_check_out_of_bounds_diff_segment() {
1240        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1241        let mut segments = MemorySegmentManager::new();
1242        segments.memory = Memory::new();
1243        segments.add();
1244        builtin.initialize_segments(&mut segments);
1245        segments
1246            .memory
1247            .insert(
1248                Relocatable::from((0, 0)),
1249                &MaybeRelocatable::from(Felt252::from(-45_i128)),
1250            )
1251            .unwrap();
1252        builtin.add_validation_rule(&mut segments.memory);
1253        assert_eq!(segments.memory.validate_existing_memory(), Ok(()));
1254    }
1255
1256    #[test]
1257    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1258    fn get_integer_valid() {
1259        let memory = memory![((0, 0), 10)];
1260        assert_eq!(
1261            memory
1262                .get_integer(Relocatable::from((0, 0)))
1263                .unwrap()
1264                .as_ref(),
1265            &Felt252::from(10)
1266        );
1267    }
1268
1269    #[test]
1270    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1271    fn get_integer_invalid_expected_integer() {
1272        let mut segments = MemorySegmentManager::new();
1273        segments.add();
1274        segments
1275            .memory
1276            .insert(Relocatable::from((0, 0)), &MaybeRelocatable::from((0, 10)))
1277            .unwrap();
1278        assert_matches!(
1279            segments.memory.get_integer(Relocatable::from((0, 0))),
1280            Err(MemoryError::ExpectedInteger(
1281                bx
1282            )) if *bx == Relocatable::from((0, 0))
1283        );
1284    }
1285
1286    #[test]
1287    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1288    fn get_u32_too_big() {
1289        let mut segments = MemorySegmentManager::new();
1290        segments.add();
1291        segments
1292            .memory
1293            .insert(Relocatable::from((0, 0)), &Felt252::from(1_u64 << 32))
1294            .unwrap();
1295        assert_matches!(
1296            segments.memory.get_u32(Relocatable::from((0, 0))),
1297            Err(MemoryError::Math(MathError::Felt252ToU32Conversion(
1298                bx
1299            ))) if *bx == Felt252::from(1_u64 << 32)
1300        );
1301    }
1302
1303    #[test]
1304    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1305    fn get_maybe_relocatable_valid_relocatable() {
1306        let memory = memory![((0, 0), (1, 0))];
1307        assert_eq!(
1308            memory
1309                .get_maybe_relocatable(Relocatable::from((0, 0)))
1310                .unwrap(),
1311            Relocatable::from((1, 0)).into()
1312        );
1313    }
1314
1315    #[test]
1316    fn get_maybe_relocatable_valid_integer() {
1317        let memory = memory![((0, 0), 10)];
1318        assert_eq!(
1319            memory
1320                .get_maybe_relocatable(Relocatable::from((0, 0)))
1321                .unwrap(),
1322            10.into()
1323        );
1324    }
1325
1326    #[test]
1327    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1328    fn default_memory() {
1329        let mem: Memory = Default::default();
1330        assert_eq!(mem.data.len(), 0);
1331    }
1332
1333    #[test]
1334    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1335    fn insert_and_get_temporary_succesful() {
1336        let mut memory = Memory::new();
1337        memory.temp_data.push(Vec::new());
1338
1339        let key = Relocatable::from((-1, 0));
1340        let val = MaybeRelocatable::from(Felt252::from(5));
1341        memory.insert(key, &val).unwrap();
1342
1343        assert_eq!(memory.get(&key).unwrap().as_ref(), &val);
1344    }
1345
1346    #[test]
1347    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1348    fn add_relocation_rule() {
1349        let mut memory = Memory::new();
1350
1351        assert_eq!(
1352            memory.add_relocation_rule((-1, 0).into(), (1, 2).into()),
1353            Ok(()),
1354        );
1355        assert_eq!(
1356            memory.add_relocation_rule((-2, 0).into(), (-1, 1).into()),
1357            Ok(()),
1358        );
1359        assert_eq!(
1360            memory.add_relocation_rule((5, 0).into(), (0, 0).into()),
1361            Err(MemoryError::AddressNotInTemporarySegment(5)),
1362        );
1363        assert_eq!(
1364            memory.add_relocation_rule((-3, 6).into(), (0, 0).into()),
1365            Err(MemoryError::NonZeroOffset(6)),
1366        );
1367        assert_eq!(
1368            memory.add_relocation_rule((-1, 0).into(), (0, 0).into()),
1369            Err(MemoryError::DuplicatedRelocation(-1)),
1370        );
1371    }
1372
1373    #[test]
1374    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1375    fn relocate_value_bigint() {
1376        let mut memory = Memory::new();
1377        memory
1378            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1379            .unwrap();
1380        memory
1381            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1382            .unwrap();
1383
1384        // Test when value is Some(BigInt):
1385        assert_eq!(
1386            memory
1387                .relocate_value(&MaybeRelocatable::Int(Felt252::from(0)))
1388                .unwrap(),
1389            Cow::Owned(MaybeRelocatable::Int(Felt252::from(0))),
1390        );
1391    }
1392
1393    #[test]
1394    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1395    fn relocate_value_mayberelocatable() {
1396        let mut memory = Memory::new();
1397        memory
1398            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1399            .unwrap();
1400        memory
1401            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1402            .unwrap();
1403
1404        // Test when value is Some(MaybeRelocatable) with segment_index >= 0:
1405        assert_eq!(
1406            memory
1407                .relocate_value(&MaybeRelocatable::RelocatableValue((0, 0).into()))
1408                .unwrap(),
1409            Cow::Owned(MaybeRelocatable::RelocatableValue((0, 0).into())),
1410        );
1411        assert_eq!(
1412            memory
1413                .relocate_value(&MaybeRelocatable::RelocatableValue((5, 0).into()))
1414                .unwrap(),
1415            Cow::Owned(MaybeRelocatable::RelocatableValue((5, 0).into())),
1416        );
1417    }
1418
1419    #[test]
1420    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1421    fn relocate_value_mayberelocatable_temporary_segment_no_rules() {
1422        let mut memory = Memory::new();
1423        memory
1424            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1425            .unwrap();
1426        memory
1427            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1428            .unwrap();
1429
1430        // Test when value is Some(MaybeRelocatable) with segment_index < 0 and
1431        // there are no applicable relocation rules:
1432        assert_eq!(
1433            memory
1434                .relocate_value(&MaybeRelocatable::RelocatableValue((-5, 0).into()))
1435                .unwrap(),
1436            Cow::Owned(MaybeRelocatable::RelocatableValue((-5, 0).into())),
1437        );
1438    }
1439
1440    #[test]
1441    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1442    fn relocate_value_mayberelocatable_temporary_segment_rules() {
1443        let mut memory = Memory::new();
1444        memory
1445            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1446            .unwrap();
1447        memory
1448            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1449            .unwrap();
1450
1451        // Test when value is Some(MaybeRelocatable) with segment_index < 0 and
1452        // there are applicable relocation rules:
1453        assert_eq!(
1454            memory
1455                .relocate_value(&MaybeRelocatable::RelocatableValue((-1, 0).into()))
1456                .unwrap(),
1457            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 0).into())),
1458        );
1459        assert_eq!(
1460            memory
1461                .relocate_value(&MaybeRelocatable::RelocatableValue((-2, 0).into()))
1462                .unwrap(),
1463            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 2).into())),
1464        );
1465        assert_eq!(
1466            memory
1467                .relocate_value(&MaybeRelocatable::RelocatableValue((-1, 5).into()))
1468                .unwrap(),
1469            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 5).into())),
1470        );
1471        assert_eq!(
1472            memory
1473                .relocate_value(&MaybeRelocatable::RelocatableValue((-2, 5).into()))
1474                .unwrap(),
1475            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 7).into())),
1476        );
1477    }
1478
1479    #[test]
1480    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1481    fn get_range_for_continuous_memory() {
1482        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 2), 4)];
1483
1484        let value1 = MaybeRelocatable::from(Felt252::from(2));
1485        let value2 = MaybeRelocatable::from(Felt252::from(3));
1486        let value3 = MaybeRelocatable::from(Felt252::from(4));
1487
1488        let expected_vec = vec![
1489            Some(Cow::Borrowed(&value1)),
1490            Some(Cow::Borrowed(&value2)),
1491            Some(Cow::Borrowed(&value3)),
1492        ];
1493        assert_eq!(memory.get_range(Relocatable::from((1, 0)), 3), expected_vec);
1494    }
1495
1496    #[test]
1497    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1498    fn get_range_for_non_continuous_memory() {
1499        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 3), 4)];
1500
1501        let value1 = MaybeRelocatable::from(Felt252::from(2));
1502        let value2 = MaybeRelocatable::from(Felt252::from(3));
1503        let value3 = MaybeRelocatable::from(Felt252::from(4));
1504
1505        let expected_vec = vec![
1506            Some(Cow::Borrowed(&value1)),
1507            Some(Cow::Borrowed(&value2)),
1508            None,
1509            Some(Cow::Borrowed(&value3)),
1510        ];
1511        assert_eq!(memory.get_range(Relocatable::from((1, 0)), 4), expected_vec);
1512    }
1513
1514    #[test]
1515    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1516    fn get_continuous_range_for_continuous_memory() {
1517        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 2), 4)];
1518
1519        let value1 = MaybeRelocatable::from(Felt252::from(2));
1520        let value2 = MaybeRelocatable::from(Felt252::from(3));
1521        let value3 = MaybeRelocatable::from(Felt252::from(4));
1522
1523        let expected_vec = vec![value1, value2, value3];
1524        assert_eq!(
1525            memory.get_continuous_range(Relocatable::from((1, 0)), 3),
1526            Ok(expected_vec)
1527        );
1528    }
1529
1530    #[test]
1531    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1532    fn get_continuous_range_for_non_continuous_memory() {
1533        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 3), 4)];
1534
1535        assert_eq!(
1536            memory.get_continuous_range(Relocatable::from((1, 0)), 3),
1537            Err(MemoryError::GetRangeMemoryGap(Box::new(((1, 0).into(), 3))))
1538        );
1539    }
1540
1541    #[test]
1542    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1543    fn get_u32_range_ok() {
1544        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)];
1545        let expected_vector = vec![1, 4294967295];
1546        assert_eq!(memory.get_u32_range((0, 1).into(), 2), Ok(expected_vector));
1547    }
1548
1549    #[test]
1550    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1551    fn get_u32_range_relocatable() {
1552        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)];
1553        assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into());
1554    }
1555
1556    #[test]
1557    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1558    fn get_u32_range_over_32_bits() {
1559        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)];
1560        assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64));
1561    }
1562
1563    #[test]
1564    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1565    fn get_u32_range_memory_gap() {
1566        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)];
1567        assert_matches!(memory.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into());
1568    }
1569
1570    /// Test that relocate_memory() works when there are no relocation rules.
1571    #[test]
1572    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1573    fn relocate_memory_empty_relocation_rules() {
1574        let mut memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3)];
1575
1576        assert_eq!(memory.relocate_memory(), Ok(()));
1577        check_memory!(memory, ((0, 0), 1), ((0, 1), 2), ((0, 2), 3));
1578    }
1579
1580    #[test]
1581    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1582    fn relocate_memory_new_segment_with_gap() {
1583        let mut memory = memory![
1584            ((0, 0), 1),
1585            ((0, 1), (-1, 0)),
1586            ((0, 2), 3),
1587            ((1, 0), (-1, 1)),
1588            ((1, 1), 5),
1589            ((1, 2), (-1, 2)),
1590            ((-1, 0), 7),
1591            ((-1, 1), 8),
1592            ((-1, 2), 9)
1593        ];
1594        memory
1595            .add_relocation_rule((-1, 0).into(), (2, 1).into())
1596            .unwrap();
1597        memory.data.push(vec![]);
1598
1599        assert_eq!(memory.relocate_memory(), Ok(()));
1600        check_memory!(
1601            memory,
1602            ((0, 0), 1),
1603            ((0, 1), (2, 1)),
1604            ((0, 2), 3),
1605            ((1, 0), (2, 2)),
1606            ((1, 1), 5),
1607            ((1, 2), (2, 3)),
1608            ((2, 1), 7),
1609            ((2, 2), 8),
1610            ((2, 3), 9)
1611        );
1612        assert!(memory.temp_data.is_empty());
1613    }
1614
1615    #[test]
1616    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1617    fn relocate_memory_new_segment() {
1618        let mut memory = memory![
1619            ((0, 0), 1),
1620            ((0, 1), (-1, 0)),
1621            ((0, 2), 3),
1622            ((1, 0), (-1, 1)),
1623            ((1, 1), 5),
1624            ((1, 2), (-1, 2)),
1625            ((-1, 0), 7),
1626            ((-1, 1), 8),
1627            ((-1, 2), 9)
1628        ];
1629        memory
1630            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1631            .unwrap();
1632        memory.data.push(vec![]);
1633
1634        assert_eq!(memory.relocate_memory(), Ok(()));
1635
1636        check_memory!(
1637            memory,
1638            ((0, 0), 1),
1639            ((0, 1), (2, 0)),
1640            ((0, 2), 3),
1641            ((1, 0), (2, 1)),
1642            ((1, 1), 5),
1643            ((1, 2), (2, 2)),
1644            ((2, 0), 7),
1645            ((2, 1), 8),
1646            ((2, 2), 9)
1647        );
1648        assert!(memory.temp_data.is_empty());
1649    }
1650
1651    #[test]
1652    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1653    fn relocate_memory_new_segment_unallocated() {
1654        let mut memory = memory![
1655            ((0, 0), 1),
1656            ((0, 1), (-1, 0)),
1657            ((0, 2), 3),
1658            ((1, 0), (-1, 1)),
1659            ((1, 1), 5),
1660            ((1, 2), (-1, 2)),
1661            ((-1, 0), 7),
1662            ((-1, 1), 8),
1663            ((-1, 2), 9)
1664        ];
1665        memory
1666            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1667            .unwrap();
1668
1669        assert_eq!(
1670            memory.relocate_memory(),
1671            Err(MemoryError::UnallocatedSegment(Box::new((2, 2))))
1672        );
1673    }
1674
1675    #[test]
1676    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1677    fn relocate_memory_into_existing_segment() {
1678        let mut memory = memory![
1679            ((0, 0), 1),
1680            ((0, 1), (-1, 0)),
1681            ((0, 2), 3),
1682            ((1, 0), (-1, 1)),
1683            ((1, 1), 5),
1684            ((1, 2), (-1, 2)),
1685            ((-1, 0), 7),
1686            ((-1, 1), 8),
1687            ((-1, 2), 9)
1688        ];
1689        memory
1690            .add_relocation_rule((-1, 0).into(), (1, 3).into())
1691            .unwrap();
1692
1693        assert_eq!(memory.relocate_memory(), Ok(()));
1694
1695        check_memory!(
1696            memory,
1697            ((0, 0), 1),
1698            ((0, 1), (1, 3)),
1699            ((0, 2), 3),
1700            ((1, 0), (1, 4)),
1701            ((1, 1), 5),
1702            ((1, 2), (1, 5)),
1703            ((1, 3), 7),
1704            ((1, 4), 8),
1705            ((1, 5), 9)
1706        );
1707        assert!(memory.temp_data.is_empty());
1708    }
1709
1710    #[test]
1711    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1712    fn relocate_memory_into_existing_segment_inconsistent_memory() {
1713        let mut memory = memory![
1714            ((0, 0), 1),
1715            ((0, 1), (-1, 0)),
1716            ((0, 2), 3),
1717            ((1, 0), (-1, 1)),
1718            ((1, 1), 5),
1719            ((1, 2), (-1, 2)),
1720            ((-1, 0), 7),
1721            ((-1, 1), 8),
1722            ((-1, 2), 9)
1723        ];
1724        memory
1725            .add_relocation_rule((-1, 0).into(), (1, 0).into())
1726            .unwrap();
1727
1728        assert_eq!(
1729            memory.relocate_memory(),
1730            Err(MemoryError::InconsistentMemory(Box::new((
1731                (1, 0).into(),
1732                (1, 1).into(),
1733                7.into(),
1734            ))))
1735        );
1736    }
1737
1738    #[test]
1739    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1740    fn relocate_memory_new_segment_2_temporary_segments_one_relocated() {
1741        let mut memory = memory![
1742            ((0, 0), 1),
1743            ((0, 1), (-1, 0)),
1744            ((0, 2), 3),
1745            ((1, 0), (-1, 1)),
1746            ((1, 1), 5),
1747            ((1, 2), (-1, 2)),
1748            ((-1, 0), 7),
1749            ((-1, 1), 8),
1750            ((-1, 2), 9),
1751            ((-2, 0), 10),
1752            ((-2, 1), 11)
1753        ];
1754        memory
1755            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1756            .unwrap();
1757        memory.data.push(vec![]);
1758
1759        assert_eq!(memory.relocate_memory(), Ok(()));
1760        check_memory!(
1761            memory,
1762            ((0, 0), 1),
1763            ((0, 1), (2, 0)),
1764            ((0, 2), 3),
1765            ((1, 0), (2, 1)),
1766            ((1, 1), 5),
1767            ((1, 2), (2, 2)),
1768            ((2, 0), 7),
1769            ((2, 1), 8),
1770            ((2, 2), 9),
1771            ((-1, 0), 10),
1772            ((-1, 1), 11)
1773        );
1774    }
1775
1776    #[test]
1777    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1778    fn relocate_memory_new_segment_2_temporary_segments_relocated() {
1779        let mut memory = memory![
1780            ((0, 0), 1),
1781            ((0, 1), (-1, 0)),
1782            ((0, 2), 3),
1783            ((1, 0), (-1, 1)),
1784            ((1, 1), 5),
1785            ((1, 2), (-1, 2)),
1786            ((-1, 0), 7),
1787            ((-1, 1), 8),
1788            ((-1, 2), 9),
1789            ((-2, 0), 10),
1790            ((-2, 1), 11)
1791        ];
1792        memory.data.push(vec![]);
1793        memory
1794            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1795            .unwrap();
1796        memory.data.push(vec![]);
1797        memory
1798            .add_relocation_rule((-2, 0).into(), (3, 0).into())
1799            .unwrap();
1800
1801        assert_eq!(memory.relocate_memory(), Ok(()));
1802
1803        check_memory!(
1804            memory,
1805            ((0, 0), 1),
1806            ((0, 1), (2, 0)),
1807            ((0, 2), 3),
1808            ((1, 0), (2, 1)),
1809            ((1, 1), 5),
1810            ((1, 2), (2, 2)),
1811            ((2, 0), 7),
1812            ((2, 1), 8),
1813            ((2, 2), 9),
1814            ((3, 0), 10),
1815            ((3, 1), 11)
1816        );
1817        assert!(memory.temp_data.is_empty());
1818    }
1819
1820    #[test]
1821    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1822    fn test_memory_display() {
1823        let memory = memory![
1824            ((0, 0), 1),
1825            ((0, 1), (-1, 0)),
1826            ((0, 2), 3),
1827            ((1, 0), (-1, 1)),
1828            ((1, 1), 5),
1829            ((1, 2), (-1, 2)),
1830            ((-1, 0), (-1, 0)),
1831            ((-1, 1), 8),
1832            ((-1, 2), 9)
1833        ];
1834
1835        assert_eq!(
1836            format!("{}", memory),
1837            "(-1,0) : -1:0\n(-1,1) : 8\n(-1,2) : 9\n(0,0) : 1\n(0,1) : -1:0\n(0,2) : 3\n(1,0) : -1:1\n(1,1) : 5\n(1,2) : -1:2\n");
1838    }
1839
1840    #[test]
1841    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1842    fn relocate_memory_into_existing_segment_temporary_values_in_temporary_memory() {
1843        let mut memory = memory![
1844            ((0, 0), 1),
1845            ((0, 1), (-1, 0)),
1846            ((0, 2), 3),
1847            ((1, 0), (-1, 1)),
1848            ((1, 1), 5),
1849            ((1, 2), (-1, 2)),
1850            ((-1, 0), (-1, 0)),
1851            ((-1, 1), 8),
1852            ((-1, 2), 9)
1853        ];
1854        memory
1855            .add_relocation_rule((-1, 0).into(), (1, 3).into())
1856            .unwrap();
1857
1858        assert_eq!(memory.relocate_memory(), Ok(()));
1859        check_memory!(
1860            memory,
1861            ((0, 0), 1),
1862            ((0, 1), (1, 3)),
1863            ((0, 2), 3),
1864            ((1, 0), (1, 4)),
1865            ((1, 1), 5),
1866            ((1, 2), (1, 5)),
1867            ((1, 3), (1, 3)),
1868            ((1, 4), 8),
1869            ((1, 5), 9)
1870        );
1871        assert!(memory.temp_data.is_empty());
1872    }
1873
1874    #[test]
1875    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1876    fn relocate_address_with_rules() {
1877        let mut memory = Memory::new();
1878        memory
1879            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1880            .unwrap();
1881        memory
1882            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1883            .unwrap();
1884
1885        assert_eq!(
1886            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1887            MaybeRelocatable::RelocatableValue((2, 0).into()),
1888        );
1889        assert_eq!(
1890            Memory::relocate_address((-2, 1).into(), &memory.relocation_rules).unwrap(),
1891            MaybeRelocatable::RelocatableValue((2, 3).into()),
1892        );
1893    }
1894
1895    #[test]
1896    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1897    fn relocate_address_no_rules() {
1898        let memory = Memory::new();
1899        assert_eq!(
1900            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1901            MaybeRelocatable::RelocatableValue((-1, 0).into()),
1902        );
1903        assert_eq!(
1904            Memory::relocate_address((-2, 1).into(), &memory.relocation_rules).unwrap(),
1905            MaybeRelocatable::RelocatableValue((-2, 1).into()),
1906        );
1907    }
1908
1909    #[test]
1910    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1911    fn relocate_address_real_addr() {
1912        let memory = Memory::new();
1913        assert_eq!(
1914            Memory::relocate_address((1, 0).into(), &memory.relocation_rules).unwrap(),
1915            MaybeRelocatable::RelocatableValue((1, 0).into()),
1916        );
1917        assert_eq!(
1918            Memory::relocate_address((1, 1).into(), &memory.relocation_rules).unwrap(),
1919            MaybeRelocatable::RelocatableValue((1, 1).into()),
1920        );
1921    }
1922
1923    #[test]
1924    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1925    #[cfg(feature = "extensive_hints")]
1926    fn relocate_address_to_integer() {
1927        let mut memory = Memory::new();
1928        memory
1929            .add_relocation_rule((-1, 0).into(), 0.into())
1930            .unwrap();
1931        memory
1932            .add_relocation_rule((-2, 0).into(), 42.into())
1933            .unwrap();
1934
1935        assert_eq!(
1936            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1937            MaybeRelocatable::Int(0.into()),
1938        );
1939        assert_eq!(
1940            Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(),
1941            MaybeRelocatable::Int(42.into()),
1942        );
1943    }
1944
1945    #[test]
1946    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1947    #[cfg(feature = "extensive_hints")]
1948    fn relocate_address_integer_no_duplicates() {
1949        let mut memory = Memory::new();
1950        memory
1951            .add_relocation_rule((-1, 0).into(), 1.into())
1952            .unwrap();
1953        assert_eq!(
1954            memory.add_relocation_rule((-1, 0).into(), 42.into()),
1955            Err(MemoryError::DuplicatedRelocation(-1))
1956        );
1957        assert_eq!(
1958            memory.add_relocation_rule((-1, 0).into(), (2, 0).into()),
1959            Err(MemoryError::DuplicatedRelocation(-1))
1960        );
1961
1962        assert_eq!(
1963            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1964            MaybeRelocatable::Int(1.into()),
1965        );
1966
1967        memory
1968            .add_relocation_rule((-2, 0).into(), (3, 0).into())
1969            .unwrap();
1970        assert_eq!(
1971            memory.add_relocation_rule((-2, 0).into(), 1.into()),
1972            Err(MemoryError::DuplicatedRelocation(-2))
1973        );
1974
1975        assert_eq!(
1976            Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(),
1977            MaybeRelocatable::RelocatableValue((3, 0).into()),
1978        );
1979    }
1980
1981    #[test]
1982    fn mark_address_as_accessed() {
1983        let mut memory = memory![((0, 0), 0)];
1984        assert!(!memory.data[0][0].is_accessed());
1985        memory.mark_as_accessed(relocatable!(0, 0));
1986        assert!(memory.data[0][0].is_accessed());
1987    }
1988
1989    #[test]
1990    fn get_amount_of_accessed_addresses_for_segment_valid() {
1991        let mut memory = memory![((0, 0), 0)];
1992        assert_eq!(
1993            memory.get_amount_of_accessed_addresses_for_segment(0),
1994            Some(0)
1995        );
1996        memory.mark_as_accessed(relocatable!(0, 0));
1997        assert_eq!(
1998            memory.get_amount_of_accessed_addresses_for_segment(0),
1999            Some(1)
2000        );
2001    }
2002
2003    #[test]
2004    fn get_amount_of_accessed_addresses_for_segment_invalid_segment() {
2005        let memory = memory![((0, 0), 0)];
2006        assert_eq!(memory.get_amount_of_accessed_addresses_for_segment(1), None);
2007    }
2008
2009    #[test]
2010    fn memory_cell_new_is_not_accessed() {
2011        let cell = MemoryCell::new(mayberelocatable!(1));
2012        assert!(!cell.is_accessed())
2013    }
2014
2015    #[test]
2016    fn memory_cell_mark_accessed() {
2017        let mut cell = MemoryCell::new(mayberelocatable!(1));
2018        cell.mark_accessed();
2019        assert!(cell.is_accessed())
2020    }
2021
2022    #[test]
2023    fn memory_cell_get_value() {
2024        let cell = MemoryCell::new(mayberelocatable!(1));
2025        assert_eq!(cell.get_value(), Some(mayberelocatable!(1)));
2026    }
2027
2028    use core::cmp::Ordering::*;
2029
2030    fn check_memcmp(
2031        lhs: (isize, usize),
2032        rhs: (isize, usize),
2033        len: usize,
2034        ord: Ordering,
2035        pos: usize,
2036    ) {
2037        let mem = memory![
2038            ((-2, 0), 1),
2039            ((-2, 1), (1, 1)),
2040            ((-2, 3), 0),
2041            ((-2, 4), 0),
2042            ((-1, 0), 1),
2043            ((-1, 1), (1, 1)),
2044            ((-1, 3), 0),
2045            ((-1, 4), 3),
2046            ((0, 0), 1),
2047            ((0, 1), (1, 1)),
2048            ((0, 3), 0),
2049            ((0, 4), 0),
2050            ((1, 0), 1),
2051            ((1, 1), (1, 1)),
2052            ((1, 3), 0),
2053            ((1, 4), 3)
2054        ];
2055        assert_eq!((ord, pos), mem.memcmp(lhs.into(), rhs.into(), len));
2056    }
2057
2058    #[test]
2059    fn insert_alloc_fails_gracefully() {
2060        let mut mem = memory![((0, 0), 1)];
2061        let err = mem.insert((0, usize::MAX >> 1).into(), Felt252::ONE);
2062        assert_eq!(err, Err(MemoryError::VecCapacityExceeded));
2063    }
2064
2065    #[test]
2066    fn insert_overflow_fails_gracefully() {
2067        let mut mem = memory![((0, 0), 1)];
2068        let err = mem.insert((0, usize::MAX).into(), Felt252::ONE);
2069        assert_eq!(err, Err(MemoryError::VecCapacityExceeded));
2070    }
2071
2072    #[test]
2073    fn memcmp() {
2074        check_memcmp((0, 0), (0, 0), 3, Equal, 3);
2075        check_memcmp((0, 0), (1, 0), 3, Equal, 3);
2076        check_memcmp((0, 0), (1, 0), 5, Less, 4);
2077        check_memcmp((1, 0), (0, 0), 5, Greater, 4);
2078        check_memcmp((2, 2), (2, 5), 8, Equal, 0);
2079        check_memcmp((0, 0), (2, 5), 8, Greater, 0);
2080        check_memcmp((2, 5), (0, 0), 8, Less, 0);
2081        check_memcmp((-2, 0), (-2, 0), 3, Equal, 3);
2082        check_memcmp((-2, 0), (-1, 0), 3, Equal, 3);
2083        check_memcmp((-2, 0), (-1, 0), 5, Less, 4);
2084        check_memcmp((-1, 0), (-2, 0), 5, Greater, 4);
2085        check_memcmp((-3, 2), (-3, 5), 8, Equal, 0);
2086        check_memcmp((-2, 0), (-3, 5), 8, Greater, 0);
2087        check_memcmp((-3, 5), (-2, 0), 8, Less, 0);
2088    }
2089
2090    #[test]
2091    fn cairo_pie_memory_from_memory() {
2092        let memory = memory![((8, 9), 3), ((1, 2), 5), ((7, 6), (1, 2))];
2093
2094        assert_eq!(
2095            CairoPieMemory::from(&memory),
2096            CairoPieMemory(vec![
2097                ((1, 2), MaybeRelocatable::from(5)),
2098                ((7, 6), MaybeRelocatable::from((1, 2))),
2099                ((8, 9), MaybeRelocatable::from(3))
2100            ])
2101        )
2102    }
2103
2104    #[test]
2105    #[cfg(not(feature = "extensive_hints"))]
2106    fn flatten_relocation_rules_chain_happy() {
2107        let mut mem = Memory::new();
2108        // temp segments just to keep indices sensible (not strictly required here)
2109        mem.temp_data = vec![vec![], vec![]];
2110
2111        //  key 0 -> (-2, 3) ; key 1 -> (10, 4)
2112        //  after flatten: key 0 -> (10, 7)
2113        mem.relocation_rules.insert(0, Relocatable::from((-2, 3)));
2114        mem.relocation_rules.insert(1, Relocatable::from((10, 4)));
2115        // sanity: an unrelated rule stays as-is
2116        mem.relocation_rules.insert(2, Relocatable::from((7, 1)));
2117
2118        assert_eq!(mem.flatten_relocation_rules(), Ok(()));
2119        assert_eq!(
2120            *mem.relocation_rules.get(&0).unwrap(),
2121            Relocatable::from((10, 7))
2122        );
2123        assert_eq!(
2124            *mem.relocation_rules.get(&1).unwrap(),
2125            Relocatable::from((10, 4))
2126        );
2127        assert_eq!(
2128            *mem.relocation_rules.get(&2).unwrap(),
2129            Relocatable::from((7, 1))
2130        );
2131    }
2132
2133    #[test]
2134    #[cfg(not(feature = "extensive_hints"))]
2135    fn flatten_relocation_rules_cycle_err() {
2136        let mut mem = Memory::new();
2137        mem.temp_data = vec![vec![]];
2138
2139        // Self-loop: key 0 -> (-1, 0)  (i.e., points back to itself)
2140        mem.relocation_rules.insert(0, Relocatable::from((-1, 0)));
2141
2142        assert_eq!(mem.flatten_relocation_rules(), Err(MemoryError::Relocation));
2143    }
2144
2145    #[test]
2146    #[cfg(feature = "extensive_hints")]
2147    fn flatten_relocation_rules_chain_happy_extensive_reloc_and_int() {
2148        let mut mem = Memory::new();
2149        mem.temp_data = vec![vec![], vec![], vec![], vec![], vec![]];
2150
2151        // ---- Chain A (ends at relocatable) ----
2152        // key 0 -> (-2, 3)  (=> key 1)
2153        // key 1 -> (10, 4)  (real)
2154        // Expect: key 0 -> (10, 7)
2155        mem.relocation_rules.insert(
2156            0,
2157            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 3))),
2158        );
2159        mem.relocation_rules.insert(
2160            1,
2161            MaybeRelocatable::RelocatableValue(Relocatable::from((10, 4))),
2162        );
2163
2164        // ---- Chain B (ends at int) ----
2165        // key 2 -> (-4, 0)  (=> key 3)
2166        // key 3 -> (-5, 0)  (=> key 4)
2167        // key 4 -> 99       (final Int)
2168        // Expect: key 2 -> 99  (offset is zero along the way)
2169        mem.relocation_rules.insert(
2170            2,
2171            MaybeRelocatable::RelocatableValue(Relocatable::from((-4, 0))),
2172        );
2173        mem.relocation_rules.insert(
2174            3,
2175            MaybeRelocatable::RelocatableValue(Relocatable::from((-5, 0))),
2176        );
2177        mem.relocation_rules
2178            .insert(4, MaybeRelocatable::Int(Felt252::from(99)));
2179
2180        assert_eq!(mem.flatten_relocation_rules(), Ok(()));
2181
2182        assert_eq!(
2183            mem.relocation_rules.get(&0),
2184            Some(&MaybeRelocatable::RelocatableValue(Relocatable::from((
2185                10, 7
2186            ))))
2187        );
2188        assert_eq!(
2189            mem.relocation_rules.get(&1),
2190            Some(&MaybeRelocatable::RelocatableValue(Relocatable::from((
2191                10, 4
2192            ))))
2193        );
2194        assert_eq!(
2195            mem.relocation_rules.get(&2),
2196            Some(&MaybeRelocatable::Int(Felt252::from(99)))
2197        );
2198    }
2199
2200    #[test]
2201    #[cfg(feature = "extensive_hints")]
2202    fn flatten_relocation_rules_int_with_non_zero_offset_err() {
2203        let mut mem = Memory::new();
2204        // temp_data length only matters for error messages; keep it >= biggest temp key + 1
2205        mem.temp_data = vec![vec![], vec![], vec![]];
2206
2207        // key 0 -> (-2, 5)  (points to key 1, offset 5)
2208        // key 1 -> (-3, 0)  (points to key 2, offset 2)
2209        // key 2 -> 7        (final Int)
2210        mem.relocation_rules.insert(
2211            0,
2212            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 5))),
2213        );
2214        mem.relocation_rules.insert(
2215            1,
2216            MaybeRelocatable::RelocatableValue(Relocatable::from((-3, 0))),
2217        );
2218        mem.relocation_rules
2219            .insert(2, MaybeRelocatable::Int(Felt252::from(7)));
2220
2221        assert_eq!(
2222            mem.flatten_relocation_rules(),
2223            Err(MemoryError::NonZeroOffset(5))
2224        );
2225    }
2226
2227    #[test]
2228    #[cfg(feature = "extensive_hints")]
2229    fn flatten_relocation_rules_cycle_err_extensive() {
2230        let mut mem = Memory::new();
2231        mem.temp_data = vec![vec![], vec![]];
2232
2233        // 2-cycle:
2234        //   key 0 -> (-1, 0)  (points to key 0)
2235        //   key 1 -> (-2, 0)  (points to key 1)
2236        mem.relocation_rules.insert(
2237            0,
2238            MaybeRelocatable::RelocatableValue(Relocatable::from((-1, 0))),
2239        );
2240        mem.relocation_rules.insert(
2241            1,
2242            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 0))),
2243        );
2244
2245        assert_eq!(mem.flatten_relocation_rules(), Err(MemoryError::Relocation));
2246    }
2247
2248    #[test]
2249    #[cfg(not(feature = "extensive_hints"))]
2250    fn flatten_relocation_rules_missing_next_err() {
2251        let mut mem = Memory::new();
2252        mem.temp_data = vec![vec![], vec![], vec![]];
2253
2254        // key 0 -> (-4, 1)  => next_key = -( -4 + 1 ) = 3
2255        // No rule for key 3, so we expect UnmappedTemporarySegment(-4).
2256        mem.relocation_rules.insert(0, Relocatable::from((-4, 1)));
2257
2258        assert_eq!(
2259            mem.flatten_relocation_rules(),
2260            Err(MemoryError::UnmappedTemporarySegment(-4))
2261        );
2262    }
2263
2264    #[test]
2265    #[cfg(feature = "extensive_hints")]
2266    fn flatten_relocation_rules_missing_next_err_extensive() {
2267        let mut mem = Memory::new();
2268        mem.temp_data = vec![vec![], vec![], vec![]];
2269
2270        // key 0 -> (-4, 0)  => next_key = 3
2271        // No rule for key 3, so we expect UnmappedTemporarySegment(-4).
2272        mem.relocation_rules.insert(
2273            0,
2274            MaybeRelocatable::RelocatableValue(Relocatable::from((-4, 0))),
2275        );
2276
2277        assert_eq!(
2278            mem.flatten_relocation_rules(),
2279            Err(MemoryError::UnmappedTemporarySegment(-4))
2280        );
2281    }
2282
2283    #[test]
2284    #[cfg(not(feature = "extensive_hints"))]
2285    fn relocate_memory_temp_chain_multi_hop() {
2286        let mut memory = Memory::new();
2287
2288        // temp segments:
2289        //  -1: [100, 200]
2290        //  -2: [7, 8, 9]
2291        memory.temp_data = vec![
2292            vec![
2293                MemoryCell::new(mayberelocatable!(100)),
2294                MemoryCell::new(mayberelocatable!(200)),
2295            ],
2296            vec![
2297                MemoryCell::new(mayberelocatable!(7)),
2298                MemoryCell::new(mayberelocatable!(8)),
2299                MemoryCell::new(mayberelocatable!(9)),
2300            ],
2301        ];
2302
2303        // Rules:
2304        //   key 0 (-1) -> (-2, 3)
2305        //   key 1 (-2) -> (5, 10)
2306        // Chain: -1 -> -2-> (5,10)  => -1 relocates to (5,13)
2307        memory
2308            .relocation_rules
2309            .insert(0, Relocatable::from((-2, 3)));
2310        memory
2311            .relocation_rules
2312            .insert(1, Relocatable::from((5, 10)));
2313
2314        // Real memory references that point into temp segments
2315        // Expect after relocation:
2316        //   (0,0): (-1,0) -> (5,13)
2317        //   (0,1): (-1,1) -> (5,14)
2318        //   (0,2): (-2,0) -> (5,10)
2319        memory.data.push(vec![
2320            MemoryCell::new(mayberelocatable!(-1, 0)),
2321            MemoryCell::new(mayberelocatable!(-1, 1)),
2322            MemoryCell::new(mayberelocatable!(-2, 0)),
2323        ]);
2324
2325        // Allocate destination segment 5
2326        while memory.data.len() <= 5 {
2327            memory.data.push(Vec::new());
2328        }
2329
2330        assert_eq!(memory.relocate_memory(), Ok(()));
2331
2332        // References updated
2333        check_memory!(
2334            memory,
2335            ((0, 0), (5, 13)),
2336            ((0, 1), (5, 14)),
2337            ((0, 2), (5, 10)),
2338            ((5, 10), 7),
2339            ((5, 11), 8),
2340            ((5, 12), 9),
2341            ((5, 13), 100),
2342            ((5, 14), 200)
2343        );
2344        assert!(memory.temp_data.is_empty());
2345    }
2346
2347    #[test]
2348    #[cfg(feature = "extensive_hints")]
2349    fn relocate_memory_temp_chain_to_int_multi_hop() {
2350        let mut memory = Memory::new();
2351
2352        // temp segments:
2353        //  -1: [123]
2354        //  -2: [456]
2355        memory.temp_data = vec![
2356            vec![MemoryCell::new(mayberelocatable!(123))],
2357            vec![MemoryCell::new(mayberelocatable!(456))],
2358        ];
2359
2360        // Rules:
2361        //   key 0 (-1) -> (-2, 0)
2362        //   key 1 (-2) -> 99 (Int)
2363        // Chain: -1 -> -2 -> 99  (offsets are zero, so collapse to Int)
2364        memory.relocation_rules.insert(
2365            0,
2366            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 0))),
2367        );
2368        memory
2369            .relocation_rules
2370            .insert(1, MaybeRelocatable::Int(Felt252::from(99)));
2371
2372        // Real memory has a reference into temp
2373        // Expect after relocation: (0,0) becomes Int(99)
2374        memory
2375            .data
2376            .push(vec![MemoryCell::new(mayberelocatable!(-1, 0))]);
2377
2378        assert_eq!(memory.relocate_memory(), Ok(()));
2379
2380        // Reference collapsed to Int
2381        assert_eq!(
2382            memory.data[0][0].get_value(),
2383            Some(MaybeRelocatable::Int(Felt252::from(99)))
2384        );
2385
2386        // The temp segments whose rules end at Int are not moved
2387        assert!(memory.temp_data.is_empty());
2388    }
2389}