cairo_vm/types/
relocatable.rs

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