Skip to main content

cairo_vm/types/
relocatable.rs

1use std::{
2    fmt::{self, Display},
3    ops::{Add, AddAssign, Sub},
4};
5
6use crate::Felt252;
7use crate::{
8    relocatable, types::errors::math_errors::MathError, vm::errors::memory_errors::MemoryError,
9};
10use num_traits::ToPrimitive;
11use serde::{Deserialize, Serialize};
12
13#[cfg(feature = "test_utils")]
14use arbitrary::Arbitrary;
15
16#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
17#[derive(
18    Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Copy, Debug, Serialize, Deserialize, Default,
19)]
20pub struct Relocatable {
21    pub segment_index: isize,
22    pub offset: usize,
23}
24
25#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
26#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
27pub enum MaybeRelocatable {
28    RelocatableValue(Relocatable),
29    Int(Felt252),
30}
31
32// NOTE: implemented manually so we can format the felt properly
33impl fmt::Debug for MaybeRelocatable {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            MaybeRelocatable::RelocatableValue(v) => {
37                f.debug_tuple("RelocatableValue").field(&v).finish()
38            }
39            MaybeRelocatable::Int(v) => {
40                f.debug_tuple("Int").field(&format_args!("{}", &v)).finish()
41            }
42        }
43    }
44}
45
46impl From<(isize, usize)> for Relocatable {
47    fn from(index_offset: (isize, usize)) -> Self {
48        Relocatable {
49            segment_index: index_offset.0,
50            offset: index_offset.1,
51        }
52    }
53}
54
55impl From<(isize, usize)> for MaybeRelocatable {
56    fn from(index_offset: (isize, usize)) -> Self {
57        MaybeRelocatable::RelocatableValue(Relocatable::from(index_offset))
58    }
59}
60
61impl From<usize> for MaybeRelocatable {
62    fn from(num: usize) -> Self {
63        MaybeRelocatable::Int(Felt252::from(num))
64    }
65}
66
67impl From<Felt252> for MaybeRelocatable {
68    fn from(num: Felt252) -> Self {
69        MaybeRelocatable::Int(num)
70    }
71}
72
73impl From<&Relocatable> for MaybeRelocatable {
74    fn from(rel: &Relocatable) -> Self {
75        MaybeRelocatable::RelocatableValue(*rel)
76    }
77}
78
79impl From<&Relocatable> for Relocatable {
80    fn from(other: &Relocatable) -> Self {
81        *other
82    }
83}
84
85impl From<&Felt252> for MaybeRelocatable {
86    fn from(val: &Felt252) -> Self {
87        MaybeRelocatable::Int(*val)
88    }
89}
90
91impl From<Relocatable> for MaybeRelocatable {
92    fn from(rel: Relocatable) -> Self {
93        MaybeRelocatable::RelocatableValue(rel)
94    }
95}
96
97impl Display for MaybeRelocatable {
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        match self {
100            MaybeRelocatable::RelocatableValue(rel) => rel.fmt(f),
101            MaybeRelocatable::Int(num) => write!(f, "{num}"),
102        }
103    }
104}
105
106impl Display for Relocatable {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        write!(f, "{}:{}", self.segment_index, self.offset)
109    }
110}
111
112impl Add<usize> for Relocatable {
113    type Output = Result<Relocatable, MathError>;
114    fn add(self, other: usize) -> Result<Self, MathError> {
115        self.offset
116            .checked_add(other)
117            .map(|x| Relocatable::from((self.segment_index, x)))
118            .ok_or_else(|| MathError::RelocatableAddUsizeOffsetExceeded(Box::new((self, other))))
119    }
120}
121
122/// Warning: may panic if self.offset + rhs exceeds usize::MAX
123impl AddAssign<usize> for Relocatable {
124    fn add_assign(&mut self, rhs: usize) {
125        self.offset += rhs
126    }
127}
128
129impl Add<u32> for Relocatable {
130    type Output = Result<Relocatable, MathError>;
131    fn add(self, other: u32) -> Result<Self, MathError> {
132        self + other as usize
133    }
134}
135
136impl Add<i32> for Relocatable {
137    type Output = Result<Relocatable, MathError>;
138    fn add(self, other: i32) -> Result<Self, MathError> {
139        if other >= 0 {
140            self + other as usize
141        } else {
142            self - other.unsigned_abs() as usize
143        }
144    }
145}
146impl Add<&Felt252> for Relocatable {
147    type Output = Result<Relocatable, MathError>;
148    fn add(self, other: &Felt252) -> Result<Relocatable, MathError> {
149        let new_offset = (self.offset as u64 + other)
150            .and_then(|x| x.to_usize())
151            .ok_or_else(|| {
152                MathError::RelocatableAddFelt252OffsetExceeded(Box::new((self, *other)))
153            })?;
154        Ok((self.segment_index, new_offset).into())
155    }
156}
157
158/// Adds a MaybeRelocatable to self
159/// Cant add two relocatable values
160impl Add<&MaybeRelocatable> for Relocatable {
161    type Output = Result<Relocatable, MathError>;
162    fn add(self, other: &MaybeRelocatable) -> Result<Relocatable, MathError> {
163        let num_ref = match other {
164            MaybeRelocatable::RelocatableValue(rel) => {
165                return Err(MathError::RelocatableAdd(Box::new((self, *rel))))
166            }
167            MaybeRelocatable::Int(num) => num,
168        };
169        self + num_ref
170    }
171}
172
173impl Sub<usize> for Relocatable {
174    type Output = Result<Relocatable, MathError>;
175    fn sub(self, other: usize) -> Result<Self, MathError> {
176        if self.offset < other {
177            return Err(MathError::RelocatableSubUsizeNegOffset(Box::new((
178                self, other,
179            ))));
180        }
181        let new_offset = self.offset - other;
182        Ok(relocatable!(self.segment_index, new_offset))
183    }
184}
185
186impl Sub<Relocatable> for Relocatable {
187    type Output = Result<usize, MathError>;
188    fn sub(self, other: Self) -> Result<usize, MathError> {
189        if self.segment_index != other.segment_index {
190            return Err(MathError::RelocatableSubDiffIndex(Box::new((self, other))));
191        }
192        if self.offset < other.offset {
193            return Err(MathError::RelocatableSubUsizeNegOffset(Box::new((
194                self,
195                other.offset,
196            ))));
197        }
198        let result = self.offset - other.offset;
199        Ok(result)
200    }
201}
202
203impl TryInto<Relocatable> for MaybeRelocatable {
204    type Error = MemoryError;
205    fn try_into(self) -> Result<Relocatable, MemoryError> {
206        match self {
207            MaybeRelocatable::RelocatableValue(rel) => Ok(rel),
208            _ => Err(MemoryError::AddressNotRelocatable),
209        }
210    }
211}
212
213impl From<&MaybeRelocatable> for MaybeRelocatable {
214    fn from(other: &MaybeRelocatable) -> Self {
215        other.clone()
216    }
217}
218
219impl TryFrom<&MaybeRelocatable> for Relocatable {
220    type Error = MathError;
221    fn try_from(other: &MaybeRelocatable) -> Result<Self, MathError> {
222        match other {
223            MaybeRelocatable::RelocatableValue(rel) => Ok(*rel),
224            MaybeRelocatable::Int(num) => Err(MathError::Felt252ToRelocatable(Box::new(*num))),
225        }
226    }
227}
228
229impl MaybeRelocatable {
230    /// Adds a Felt252 to self
231    pub fn add_int(&self, other: &Felt252) -> Result<MaybeRelocatable, MathError> {
232        match *self {
233            MaybeRelocatable::Int(ref value) => Ok(MaybeRelocatable::Int(value + other)),
234            MaybeRelocatable::RelocatableValue(rel) => {
235                Ok(MaybeRelocatable::RelocatableValue((rel + other)?))
236            }
237        }
238    }
239
240    /// Adds a usize to self
241    pub fn add_usize(&self, other: usize) -> Result<MaybeRelocatable, MathError> {
242        Ok(match *self {
243            MaybeRelocatable::Int(ref value) => MaybeRelocatable::Int(value + other as u64),
244            MaybeRelocatable::RelocatableValue(rel) => (rel + other)?.into(),
245        })
246    }
247
248    /// Adds a MaybeRelocatable to self
249    /// Cant add two relocatable values
250    pub fn add(&self, other: &MaybeRelocatable) -> Result<MaybeRelocatable, MathError> {
251        match (self, other) {
252            (MaybeRelocatable::Int(num_a_ref), MaybeRelocatable::Int(num_b)) => {
253                Ok(MaybeRelocatable::Int(num_a_ref + num_b))
254            }
255            (
256                &MaybeRelocatable::RelocatableValue(rel_a),
257                &MaybeRelocatable::RelocatableValue(rel_b),
258            ) => Err(MathError::RelocatableAdd(Box::new((rel_a, rel_b)))),
259            (&MaybeRelocatable::RelocatableValue(rel), &MaybeRelocatable::Int(ref num_ref))
260            | (&MaybeRelocatable::Int(ref num_ref), &MaybeRelocatable::RelocatableValue(rel)) => {
261                Ok((rel + num_ref)?.into())
262            }
263        }
264    }
265
266    /// Subs a usize from self
267    pub fn sub_usize(&self, other: usize) -> Result<MaybeRelocatable, MathError> {
268        Ok(match *self {
269            MaybeRelocatable::Int(ref value) => MaybeRelocatable::Int(value - other as u64),
270            MaybeRelocatable::RelocatableValue(rel) => (rel - other)?.into(),
271        })
272    }
273
274    /// Substracts two MaybeRelocatable values and returns the result as a MaybeRelocatable value.
275    /// Only values of the same type may be substracted.
276    /// Relocatable values can only be substracted if they belong to the same segment.
277    pub fn sub(&self, other: &MaybeRelocatable) -> Result<MaybeRelocatable, MathError> {
278        match (self, other) {
279            (MaybeRelocatable::Int(num_a), MaybeRelocatable::Int(num_b)) => {
280                Ok(MaybeRelocatable::Int(num_a - num_b))
281            }
282            (
283                MaybeRelocatable::RelocatableValue(rel_a),
284                MaybeRelocatable::RelocatableValue(rel_b),
285            ) => {
286                if rel_a.segment_index == rel_b.segment_index {
287                    return Ok(MaybeRelocatable::from(Felt252::from(
288                        rel_a.offset as i128 - rel_b.offset as i128,
289                    )));
290                }
291                Err(MathError::RelocatableSubDiffIndex(Box::new((
292                    *rel_a, *rel_b,
293                ))))
294            }
295            (MaybeRelocatable::RelocatableValue(rel_a), MaybeRelocatable::Int(ref num_b)) => {
296                Ok(MaybeRelocatable::from((
297                    rel_a.segment_index,
298                    (rel_a.offset as u64 - num_b)
299                        .and_then(|x| x.to_usize())
300                        .ok_or_else(|| {
301                            MathError::RelocatableSubFelt252NegOffset(Box::new((*rel_a, *num_b)))
302                        })?,
303                )))
304            }
305            (MaybeRelocatable::Int(int), MaybeRelocatable::RelocatableValue(rel)) => {
306                Err(MathError::SubRelocatableFromInt(Box::new((*int, *rel))))
307            }
308        }
309    }
310
311    /// Performs integer division and module on a MaybeRelocatable::Int by another
312    /// MaybeRelocatable::Int and returns the quotient and reminder.
313    pub fn divmod(
314        &self,
315        other: &MaybeRelocatable,
316    ) -> Result<(MaybeRelocatable, MaybeRelocatable), MathError> {
317        match (self, other) {
318            (MaybeRelocatable::Int(val), MaybeRelocatable::Int(div)) => Ok((
319                MaybeRelocatable::from(
320                    val.field_div(&div.try_into().map_err(|_| MathError::DividedByZero)?),
321                ),
322                // NOTE: elements on a field element always have multiplicative inverse
323                MaybeRelocatable::from(Felt252::ZERO),
324            )),
325            _ => Err(MathError::DivModWrongType(Box::new((
326                self.clone(),
327                other.clone(),
328            )))),
329        }
330    }
331
332    // TODO: Check if its more performant to use get_int instead
333    /// Returns a reference to the inner value if it is a Felt252, returns None otherwise.
334    pub fn get_int_ref(&self) -> Option<&Felt252> {
335        match self {
336            MaybeRelocatable::Int(num) => Some(num),
337            MaybeRelocatable::RelocatableValue(_) => None,
338        }
339    }
340
341    /// Returns the inner value if it is a Felt252, returns None otherwise.
342    pub fn get_int(&self) -> Option<Felt252> {
343        match self {
344            MaybeRelocatable::Int(num) => Some(*num),
345            MaybeRelocatable::RelocatableValue(_) => None,
346        }
347    }
348
349    /// Returns the inner value if it is a Relocatable, returns None otherwise.
350    pub fn get_relocatable(&self) -> Option<Relocatable> {
351        match self {
352            MaybeRelocatable::RelocatableValue(rel) => Some(*rel),
353            MaybeRelocatable::Int(_) => None,
354        }
355    }
356}
357
358/// Turns a MaybeRelocatable into a Felt252 value.
359/// If the value is an Int, it will extract the Felt252 value from it.
360/// If the value is RelocatableValue, it will relocate it according to the relocation_table
361pub fn relocate_value(
362    value: MaybeRelocatable,
363    relocation_table: &[usize],
364) -> Result<Felt252, MemoryError> {
365    match value {
366        MaybeRelocatable::Int(num) => Ok(num),
367        MaybeRelocatable::RelocatableValue(relocatable) => Ok(Felt252::from(relocate_address(
368            relocatable,
369            relocation_table,
370        )?)),
371    }
372}
373
374// Relocates a Relocatable value according to the relocation_table
375pub fn relocate_address(
376    relocatable: Relocatable,
377    relocation_table: &[usize],
378) -> Result<usize, MemoryError> {
379    let (segment_index, offset) = if relocatable.segment_index >= 0 {
380        (relocatable.segment_index as usize, relocatable.offset)
381    } else {
382        return Err(MemoryError::TemporarySegmentInRelocation(
383            relocatable.segment_index,
384        ));
385    };
386
387    if relocation_table.len() <= segment_index {
388        return Err(MemoryError::Relocation);
389    }
390
391    Ok(relocation_table[segment_index] + offset)
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397    use crate::{felt_hex, felt_str};
398    use crate::{relocatable, utils::test_utils::mayberelocatable};
399
400    use proptest::prelude::*;
401
402    proptest! {
403        #[test]
404        fn add_relocatable_felt(offset in any::<u64>(), ref bigint in any::<[u8; 32]>()) {
405            let big = &Felt252::from_bytes_be(bigint);
406            let rel = Relocatable::from((0, offset as usize));
407
408            let sum = (big + offset).to_usize()
409                .map(|offset| (0, offset).into());
410            prop_assert_eq!((rel + big).ok(), sum);
411        }
412
413        #[test]
414        fn add_relocatable_felt_extremes(offset in any::<u64>()) {
415            let big_zero = &Felt252::ZERO;
416            let big_max = &Felt252::MAX;
417            let big_min = &(big_zero + (i64::MIN as u64));
418            let rel = Relocatable::from((0, offset as usize));
419
420            let sum_max = (big_max + offset).to_usize()
421                .map(|offset| (0, offset).into());
422            prop_assert_eq!((rel + big_max).ok(), sum_max);
423            let sum_min = (big_min + offset).to_usize()
424                .map(|offset| (0, offset).into());
425            prop_assert_eq!((rel + big_min).ok(), sum_min);
426            let sum_zero = (big_zero + offset).to_usize()
427                .map(|offset| (0, offset).into());
428            prop_assert_eq!((rel + big_zero).ok(), sum_zero);
429        }
430    }
431
432    #[test]
433    fn add_bigint_to_int() {
434        let addr = MaybeRelocatable::from(Felt252::from(7i32));
435        let added_addr = addr.add_int(&Felt252::from(2i32));
436        assert_eq!(added_addr, Ok(MaybeRelocatable::Int(Felt252::from(9))));
437    }
438
439    #[test]
440    fn add_usize_to_int() {
441        let addr = MaybeRelocatable::from(Felt252::from(7_i32));
442        let added_addr = addr.add_usize(2).unwrap();
443        assert_eq!(MaybeRelocatable::Int(Felt252::from(9)), added_addr);
444    }
445
446    #[test]
447    fn add_bigint_to_relocatable() {
448        let addr = MaybeRelocatable::RelocatableValue(relocatable!(7, 65));
449        let added_addr = addr.add_int(&Felt252::from(2));
450        assert_eq!(
451            added_addr,
452            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
453                segment_index: 7,
454                offset: 67
455            }))
456        );
457    }
458
459    #[test]
460    fn add_int_mod_offset_exceeded() {
461        let addr = MaybeRelocatable::from((0, 0));
462        let error = addr.add_int(&felt_hex!("0x10000000000000000"));
463        assert_eq!(
464            error,
465            Err(MathError::RelocatableAddFelt252OffsetExceeded(Box::new((
466                relocatable!(0, 0),
467                felt_hex!("0x10000000000000000")
468            ))))
469        );
470    }
471
472    #[test]
473    fn add_usize_to_relocatable() {
474        let addr = MaybeRelocatable::RelocatableValue(relocatable!(7, 65));
475        let added_addr = addr.add_usize(2);
476        assert_eq!(
477            added_addr,
478            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
479                segment_index: 7,
480                offset: 67
481            }))
482        );
483    }
484
485    #[test]
486    fn add_bigint_to_int_prime_mod() {
487        // Felt252::MAX is PRIME - 1; adding 5 wraps around to 4
488        let addr = MaybeRelocatable::Int(Felt252::MAX);
489        let added_addr = addr.add_int(&Felt252::from(5));
490        assert_eq!(added_addr, Ok(MaybeRelocatable::Int(Felt252::from(4))));
491    }
492
493    #[test]
494    fn add_bigint_to_relocatable_prime() {
495        let addr = MaybeRelocatable::from((1, 9));
496        let added_addr = addr.add_int(&felt_str!(
497            "3618502788666131213697322783095070105623107215331596699973092056135872020481"
498        ));
499        assert_eq!(
500            added_addr,
501            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
502                segment_index: 1,
503                offset: 9
504            }))
505        );
506    }
507
508    #[test]
509    fn add_int_to_int() {
510        let addr_a = &MaybeRelocatable::from(felt_str!(
511            "3618502788666131213697322783095070105623107215331596699973092056135872020488"
512        ));
513        let addr_b = &MaybeRelocatable::from(Felt252::from(17_i32));
514        let added_addr = addr_a.add(addr_b);
515        assert_eq!(added_addr, Ok(MaybeRelocatable::Int(Felt252::from(24))));
516    }
517
518    #[test]
519    fn add_relocatable_to_relocatable_should_fail() {
520        let addr_a = &MaybeRelocatable::from((7, 5));
521        let addr_b = &MaybeRelocatable::RelocatableValue(relocatable!(7, 10));
522        let error = addr_a.add(addr_b);
523        assert_eq!(
524            error,
525            Err(MathError::RelocatableAdd(Box::new((
526                relocatable!(7, 5),
527                relocatable!(7, 10)
528            ))))
529        );
530    }
531
532    #[test]
533    fn add_int_to_relocatable() {
534        let addr_a = &MaybeRelocatable::from((7, 7));
535        let addr_b = &MaybeRelocatable::from(Felt252::from(10));
536        let added_addr = addr_a.add(addr_b);
537        assert_eq!(
538            added_addr,
539            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
540                segment_index: 7,
541                offset: 17
542            }))
543        );
544    }
545
546    #[test]
547    fn add_relocatable_to_int() {
548        let addr_a = &MaybeRelocatable::from(Felt252::from(10_i32));
549        let addr_b = &MaybeRelocatable::RelocatableValue(relocatable!(7, 7));
550        let added_addr = addr_a.add(addr_b);
551        assert_eq!(
552            added_addr,
553            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
554                segment_index: 7,
555                offset: 17
556            }))
557        );
558    }
559
560    #[test]
561    fn add_int_to_relocatable_prime() {
562        let addr_a = &MaybeRelocatable::from((7, 14));
563        let addr_b = &MaybeRelocatable::Int(felt_hex!(
564            "800000000000011000000000000000000000000000000000000000000000001"
565        ));
566        let added_addr = addr_a.add(addr_b);
567        assert_eq!(
568            added_addr,
569            Ok(MaybeRelocatable::RelocatableValue(Relocatable {
570                segment_index: 7,
571                offset: 14
572            }))
573        );
574    }
575
576    #[test]
577    fn add_int_rel_int_offset_exceeded() {
578        let addr = MaybeRelocatable::from((0, 0));
579        let error = addr.add(&MaybeRelocatable::from(felt_hex!("0x10000000000000000")));
580        assert_eq!(
581            error,
582            Err(MathError::RelocatableAddFelt252OffsetExceeded(Box::new((
583                relocatable!(0, 0),
584                felt_hex!("0x10000000000000000")
585            ))))
586        );
587    }
588
589    #[test]
590    fn add_int_int_rel_offset_exceeded() {
591        let addr = MaybeRelocatable::Int(felt_hex!("0x10000000000000000"));
592        let relocatable = Relocatable {
593            offset: 0,
594            segment_index: 0,
595        };
596        let error = addr.add(&MaybeRelocatable::RelocatableValue(relocatable));
597        assert_eq!(
598            error,
599            Err(MathError::RelocatableAddFelt252OffsetExceeded(Box::new((
600                relocatable!(0, 0),
601                felt_hex!("0x10000000000000000")
602            ))))
603        );
604    }
605
606    #[test]
607    fn sub_int_from_int() {
608        let addr_a = &MaybeRelocatable::from(Felt252::from(7));
609        let addr_b = &MaybeRelocatable::from(Felt252::from(5));
610        let sub_addr = addr_a.sub(addr_b);
611        assert_eq!(sub_addr, Ok(MaybeRelocatable::Int(Felt252::from(2))));
612    }
613
614    #[test]
615    fn sub_relocatable_from_relocatable_same_offset() {
616        let addr_a = &MaybeRelocatable::from((7, 17));
617        let addr_b = &MaybeRelocatable::from((7, 7));
618        let sub_addr = addr_a.sub(addr_b);
619        assert_eq!(sub_addr, Ok(MaybeRelocatable::Int(Felt252::from(10))));
620    }
621
622    #[test]
623    fn sub_relocatable_from_relocatable_diff_offset() {
624        let addr_a = &MaybeRelocatable::from((7, 17));
625        let addr_b = &MaybeRelocatable::from((8, 7));
626        let error = addr_a.sub(addr_b);
627        assert_eq!(
628            error,
629            Err(MathError::RelocatableSubDiffIndex(Box::new((
630                relocatable!(7, 17),
631                relocatable!(8, 7)
632            ))))
633        );
634    }
635
636    #[test]
637    fn sub_int_addr_ref_from_relocatable_addr_ref() {
638        let addr_a = &MaybeRelocatable::from((7, 17));
639        let addr_b = &MaybeRelocatable::from(Felt252::from(5_i32));
640        let addr_c = addr_a.sub(addr_b);
641        assert_eq!(addr_c, Ok(MaybeRelocatable::from((7, 12))));
642    }
643
644    #[test]
645    fn sub_rel_to_int_error() {
646        assert_eq!(
647            MaybeRelocatable::from(Felt252::from(7_i32)).sub(&MaybeRelocatable::from((7, 10))),
648            Err(MathError::SubRelocatableFromInt(Box::new((
649                Felt252::from(7_i32),
650                Relocatable::from((7, 10))
651            ))))
652        );
653    }
654
655    #[test]
656    fn divmod_working() {
657        let value = &MaybeRelocatable::from(Felt252::from(10));
658        let div = &MaybeRelocatable::from(Felt252::from(3));
659        let (q, r) = value.divmod(div).expect("Unexpected error in divmod");
660        assert_eq!(
661            q,
662            MaybeRelocatable::from(
663                Felt252::from(10).field_div(&Felt252::from(3).try_into().unwrap())
664            )
665        );
666        assert_eq!(r, MaybeRelocatable::from(Felt252::ZERO));
667    }
668
669    #[test]
670    fn divmod_bad_type() {
671        let value = &MaybeRelocatable::from(Felt252::from(10));
672        let div = &MaybeRelocatable::from((2, 7));
673        assert_eq!(
674            value.divmod(div),
675            Err(MathError::DivModWrongType(Box::new((
676                MaybeRelocatable::from(Felt252::from(10)),
677                MaybeRelocatable::from((2, 7))
678            ))))
679        );
680    }
681
682    #[test]
683    fn relocate_relocatable_value() {
684        let value = MaybeRelocatable::from((2, 7));
685        let relocation_table = vec![1, 2, 5];
686        assert_eq!(
687            relocate_value(value, &relocation_table),
688            Ok(Felt252::from(12))
689        );
690    }
691
692    #[test]
693    fn relocate_relocatable_in_temp_segment_value() {
694        let value = MaybeRelocatable::from((-1, 7));
695        let relocation_table = vec![1, 2, 5];
696        assert_eq!(
697            relocate_value(value, &relocation_table),
698            Err(MemoryError::TemporarySegmentInRelocation(-1)),
699        );
700    }
701
702    #[test]
703    fn relocate_relocatable_in_temp_segment_value_with_offset() {
704        let value = MaybeRelocatable::from((-1, 7));
705        let relocation_table = vec![1, 2, 5];
706        assert_eq!(
707            relocate_value(value, &relocation_table),
708            Err(MemoryError::TemporarySegmentInRelocation(-1)),
709        );
710    }
711
712    #[test]
713    fn relocate_relocatable_in_temp_segment_value_error() {
714        let value = MaybeRelocatable::from((-1, 7));
715        let relocation_table = vec![1, 2, 5];
716        assert_eq!(
717            relocate_value(value, &relocation_table),
718            Err(MemoryError::TemporarySegmentInRelocation(-1))
719        );
720    }
721
722    #[test]
723    fn relocate_int_value() {
724        let value = MaybeRelocatable::from(Felt252::from(7));
725        let relocation_table = vec![1, 2, 5];
726        assert_eq!(
727            relocate_value(value, &relocation_table),
728            Ok(Felt252::from(7))
729        );
730    }
731
732    #[test]
733    fn relocate_relocatable_value_no_relocation() {
734        let value = MaybeRelocatable::from((2, 7));
735        let relocation_table = vec![1, 2];
736        assert_eq!(
737            relocate_value(value, &relocation_table),
738            Err(MemoryError::Relocation)
739        );
740    }
741
742    #[test]
743    fn relocatable_add_int() {
744        assert_eq!(
745            relocatable!(1, 2) + &Felt252::from(4),
746            Ok(relocatable!(1, 6))
747        );
748        assert_eq!(relocatable!(3, 2) + &Felt252::ZERO, Ok(relocatable!(3, 2)));
749    }
750
751    #[test]
752    fn relocatable_add_int_mod_offset_exceeded_error() {
753        assert_eq!(
754            relocatable!(0, 0) + &(Felt252::from(usize::MAX) + 1_u64),
755            Err(MathError::RelocatableAddFelt252OffsetExceeded(Box::new((
756                relocatable!(0, 0),
757                Felt252::from(usize::MAX) + 1_u64
758            ))))
759        );
760    }
761
762    #[test]
763    fn relocatable_add_i32() {
764        let reloc = relocatable!(1, 5);
765
766        assert_eq!(reloc + 3, Ok(relocatable!(1, 8)));
767        assert_eq!(reloc + (-3), Ok(relocatable!(1, 2)));
768    }
769
770    #[test]
771    fn relocatable_add_i32_with_overflow() {
772        let reloc = relocatable!(1, 1);
773
774        assert_eq!(
775            reloc + (-3),
776            Err(MathError::RelocatableSubUsizeNegOffset(Box::new((
777                relocatable!(1, 1),
778                3
779            ))))
780        );
781    }
782
783    #[test]
784    fn mayberelocatable_try_into_reloctable() {
785        let address = mayberelocatable!(1, 2);
786        assert_eq!(Ok(relocatable!(1, 2)), address.try_into());
787
788        let value = mayberelocatable!(1);
789        let err: Result<Relocatable, _> = value.try_into();
790        assert_eq!(Err(MemoryError::AddressNotRelocatable), err)
791    }
792
793    #[test]
794    fn relocatable_sub_rel_test() {
795        let reloc = relocatable!(7, 6);
796        assert_eq!(reloc - relocatable!(7, 5), Ok(1));
797        assert_eq!(
798            reloc - relocatable!(7, 9),
799            Err(MathError::RelocatableSubUsizeNegOffset(Box::new((
800                relocatable!(7, 6),
801                9
802            ))))
803        );
804    }
805
806    #[test]
807    fn sub_rel_different_indexes() {
808        let a = relocatable!(7, 6);
809        let b = relocatable!(8, 6);
810        assert_eq!(
811            a - b,
812            Err(MathError::RelocatableSubDiffIndex(Box::new((a, b))))
813        );
814    }
815
816    #[test]
817    fn add_maybe_mod_ok() {
818        assert_eq!(
819            relocatable!(1, 0) + &mayberelocatable!(2),
820            Ok(relocatable!(1, 2))
821        );
822        assert_eq!(
823            relocatable!(0, 29) + &mayberelocatable!(100),
824            Ok(relocatable!(0, 129))
825        );
826        assert_eq!(
827            relocatable!(2, 12) + &mayberelocatable!(104),
828            Ok(relocatable!(2, 116))
829        );
830        assert_eq!(
831            relocatable!(1, 0) + &mayberelocatable!(0),
832            Ok(relocatable!(1, 0))
833        );
834        assert_eq!(
835            relocatable!(1, 2) + &mayberelocatable!(71),
836            Ok(relocatable!(1, 73))
837        );
838    }
839
840    #[test]
841    fn add_maybe_mod_add_two_relocatable_error() {
842        assert_eq!(
843            relocatable!(1, 0) + &mayberelocatable!(1, 2),
844            Err(MathError::RelocatableAdd(Box::new((
845                relocatable!(1, 0),
846                relocatable!(1, 2)
847            ))))
848        );
849    }
850
851    #[test]
852    fn add_maybe_mod_offset_exceeded_error() {
853        assert_eq!(
854            relocatable!(1, 0) + &mayberelocatable!(usize::MAX as i128 + 1),
855            Err(MathError::RelocatableAddFelt252OffsetExceeded(Box::new((
856                relocatable!(1, 0),
857                Felt252::from(usize::MAX) + 1_u64
858            ))))
859        );
860    }
861
862    #[test]
863    fn get_relocatable_test() {
864        assert_eq!(
865            mayberelocatable!(1, 2).get_relocatable(),
866            Some(relocatable!(1, 2))
867        );
868        assert_eq!(mayberelocatable!(3).get_relocatable(), None)
869    }
870
871    #[test]
872    fn relocatable_display() {
873        assert_eq!(
874            format!("{}", Relocatable::from((1, 0))),
875            String::from("1:0")
876        )
877    }
878
879    #[test]
880    fn maybe_relocatable_relocatable_display() {
881        assert_eq!(
882            format!("{}", MaybeRelocatable::from((1, 0))),
883            String::from("1:0")
884        )
885    }
886
887    #[test]
888    fn maybe_relocatable_int_display() {
889        assert_eq!(
890            format!("{}", MaybeRelocatable::from(Felt252::from(6))),
891            String::from("6")
892        )
893    }
894
895    #[test]
896    fn relocatable_add_assign_usize() {
897        let mut addr = Relocatable::from((1, 0));
898        addr += 1;
899        assert_eq!(addr, Relocatable::from((1, 1)))
900    }
901}