cairo_lang_sierra/extensions/modules/
utils.rs

1use std::ops::Shl;
2
3use cairo_lang_utils::casts::IntoOrPanic;
4use itertools::{chain, repeat_n};
5use num_bigint::BigInt;
6use num_traits::One;
7use starknet_types_core::felt::CAIRO_PRIME_BIGINT;
8
9use super::bounded_int::BoundedIntType;
10use super::bytes31::Bytes31Type;
11use super::int::signed::{Sint8Type, Sint16Type, Sint32Type, Sint64Type};
12use super::int::signed128::Sint128Type;
13use super::int::unsigned::{Uint8Type, Uint16Type, Uint32Type, Uint64Type};
14use super::int::unsigned128::Uint128Type;
15use super::structure::StructType;
16use crate::extensions::felt252::Felt252Type;
17use crate::extensions::lib_func::{
18    LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange, SignatureSpecializationContext,
19};
20use crate::extensions::types::TypeInfo;
21use crate::extensions::{NamedType, OutputVarReferenceInfo, SpecializationError};
22use crate::ids::{ConcreteTypeId, UserTypeId};
23use crate::program::GenericArg;
24
25/// Returns a libfunc signature that casts from one type to another, without changing the internal
26/// representation.
27/// The implementation must be the identity function (no CASM output).
28pub fn reinterpret_cast_signature(
29    from_ty: ConcreteTypeId,
30    to_ty: ConcreteTypeId,
31) -> LibfuncSignature {
32    LibfuncSignature::new_non_branch_ex(
33        vec![ParamSignature::new(from_ty).with_allow_all()],
34        vec![OutputVarInfo {
35            ty: to_ty,
36            ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
37        }],
38        SierraApChange::Known { new_vars_only: true },
39    )
40}
41
42/// A range of integers (`[lower, upper)`).
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct Range {
45    /// The lower bound (Inclusive).
46    pub lower: BigInt,
47    /// The upper bound (Exclusive).
48    pub upper: BigInt,
49}
50impl Range {
51    /// Creates a closed range i.e. `[lower, upper]`.
52    pub fn closed(lower: impl Into<BigInt>, upper: impl Into<BigInt>) -> Self {
53        Self::half_open(lower, upper.into() as BigInt + 1)
54    }
55    /// Creates a half-closed range i.e. `[lower, upper)`.
56    pub fn half_open(lower: impl Into<BigInt>, upper: impl Into<BigInt>) -> Self {
57        let result = Self { lower: lower.into(), upper: upper.into() };
58        assert!(result.lower < result.upper, "Invalid range: {:?}", result);
59        result
60    }
61    /// Returns the [Range] bounds from the given type info.
62    pub fn from_type_info(ty_info: &TypeInfo) -> Result<Self, SpecializationError> {
63        Ok(match (&ty_info.long_id.generic_id, &ty_info.long_id.generic_args[..]) {
64            (id, []) if *id == Felt252Type::id() => {
65                let prime: BigInt = CAIRO_PRIME_BIGINT.clone();
66                Self::half_open(1 - &prime, prime)
67            }
68            (id, []) if *id == Uint8Type::id() => Self::closed(u8::MIN, u8::MAX),
69            (id, []) if *id == Uint16Type::id() => Self::closed(u16::MIN, u16::MAX),
70            (id, []) if *id == Uint32Type::id() => Self::closed(u32::MIN, u32::MAX),
71            (id, []) if *id == Uint64Type::id() => Self::closed(u64::MIN, u64::MAX),
72            (id, []) if *id == Uint128Type::id() => Self::closed(u128::MIN, u128::MAX),
73            (id, []) if *id == Sint8Type::id() => Self::closed(i8::MIN, i8::MAX),
74            (id, []) if *id == Sint16Type::id() => Self::closed(i16::MIN, i16::MAX),
75            (id, []) if *id == Sint32Type::id() => Self::closed(i32::MIN, i32::MAX),
76            (id, []) if *id == Sint64Type::id() => Self::closed(i64::MIN, i64::MAX),
77            (id, []) if *id == Sint128Type::id() => Self::closed(i128::MIN, i128::MAX),
78            (id, []) if *id == Bytes31Type::id() => Self::half_open(0, BigInt::one().shl(248)),
79            (id, [GenericArg::Value(min), GenericArg::Value(max)])
80                if *id == BoundedIntType::id() =>
81            {
82                Self::closed(min.clone(), max.clone())
83            }
84            _ => return Err(SpecializationError::UnsupportedGenericArg),
85        })
86    }
87    /// Returns the Range bounds from the given type.
88    pub fn from_type(
89        context: &dyn SignatureSpecializationContext,
90        ty: ConcreteTypeId,
91    ) -> Result<Self, SpecializationError> {
92        Self::from_type_info(&context.get_type_info(ty)?)
93    }
94    /// Returns true if this range is smaller than the RangeCheck range.
95    pub fn is_small_range(&self) -> bool {
96        self.size() <= BigInt::one().shl(128)
97    }
98    /// Returns true if this range can contain all possible values of a CASM cell.
99    pub fn is_full_felt252_range(&self) -> bool {
100        self.size() >= *CAIRO_PRIME_BIGINT
101    }
102    /// Returns the size of the range.
103    pub fn size(&self) -> BigInt {
104        &self.upper - &self.lower
105    }
106    /// Returns the intersection of `self` and `other`.
107    ///
108    /// If the intersection is empty, returns `None`.
109    pub fn intersection(&self, other: &Self) -> Option<Self> {
110        let lower = std::cmp::max(&self.lower, &other.lower).clone();
111        let upper = std::cmp::min(&self.upper, &other.upper).clone();
112        if lower < upper { Some(Self::half_open(lower, upper)) } else { None }
113    }
114}
115
116/// Returns a fixed type array of the given type and size.
117pub fn fixed_size_array_ty(
118    context: &dyn SignatureSpecializationContext,
119    ty: ConcreteTypeId,
120    size: i16,
121) -> Result<ConcreteTypeId, SpecializationError> {
122    let args: Vec<GenericArg> = chain!(
123        [GenericArg::UserType(UserTypeId::from_string("Tuple"))],
124        repeat_n(GenericArg::Type(ty), size.into_or_panic())
125    )
126    .collect();
127    context.get_concrete_type(StructType::id(), &args)
128}