cairo_lang_sierra/extensions/modules/
felt252.rs

1use num_bigint::BigInt;
2use num_traits::Zero;
3
4use super::is_zero::{IsZeroLibfunc, IsZeroTraits};
5use super::non_zero::nonzero_ty;
6use crate::define_libfunc_hierarchy;
7use crate::extensions::lib_func::{
8    DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
9    SignatureSpecializationContext, SpecializationContext,
10};
11use crate::extensions::{
12    GenericLibfunc, NamedLibfunc, NamedType, NoGenericArgsGenericType, OutputVarReferenceInfo,
13    SignatureBasedConcreteLibfunc, SpecializationError,
14};
15use crate::ids::{GenericLibfuncId, GenericTypeId};
16use crate::program::GenericArg;
17
18/// Type for felt252.
19/// The native type of the Cairo architecture.
20#[derive(Default)]
21pub struct Felt252Type {}
22impl NoGenericArgsGenericType for Felt252Type {
23    const ID: GenericTypeId = GenericTypeId::new_inline("felt252");
24    const STORABLE: bool = true;
25    const DUPLICATABLE: bool = true;
26    const DROPPABLE: bool = true;
27    const ZERO_SIZED: bool = false;
28}
29
30define_libfunc_hierarchy! {
31    pub enum Felt252Libfunc {
32        BinaryOperation(Felt252BinaryOperationLibfunc),
33        Const(Felt252ConstLibfunc),
34        IsZero(Felt252JumpNotZeroLibfunc),
35    }, Felt252Concrete
36}
37
38define_libfunc_hierarchy! {
39    pub enum Felt252BinaryOperationLibfunc {
40        WithVar(Felt252BinaryOperationWithVarLibfunc),
41        WithConst(Felt252BinaryOperationWithConstLibfunc),
42    }, Felt252BinaryOperationConcrete
43}
44
45#[derive(Default)]
46pub struct Felt252Traits {}
47impl IsZeroTraits for Felt252Traits {
48    const IS_ZERO: &'static str = "felt252_is_zero";
49    const GENERIC_TYPE_ID: GenericTypeId = <Felt252Type as NamedType>::ID;
50}
51pub type Felt252JumpNotZeroLibfunc = IsZeroLibfunc<Felt252Traits>;
52
53/// Felt252 binary operators.
54#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55pub enum Felt252BinaryOperator {
56    Add,
57    Sub,
58    Mul,
59    Div,
60}
61
62/// Libfunc for felt252 binary operations.
63pub struct Felt252BinaryOperationWithVarLibfunc {
64    pub operator: Felt252BinaryOperator,
65}
66impl Felt252BinaryOperationWithVarLibfunc {
67    fn new(operator: Felt252BinaryOperator) -> Self {
68        Self { operator }
69    }
70    const ADD: &'static str = "felt252_add";
71    const SUB: &'static str = "felt252_sub";
72    const MUL: &'static str = "felt252_mul";
73    const DIV: &'static str = "felt252_div";
74}
75impl GenericLibfunc for Felt252BinaryOperationWithVarLibfunc {
76    type Concrete = Felt252BinaryOpConcreteLibfunc;
77
78    fn supported_ids() -> Vec<GenericLibfuncId> {
79        vec![
80            GenericLibfuncId::from(Self::ADD),
81            GenericLibfuncId::from(Self::SUB),
82            GenericLibfuncId::from(Self::MUL),
83            GenericLibfuncId::from(Self::DIV),
84        ]
85    }
86
87    fn by_id(id: &GenericLibfuncId) -> Option<Self> {
88        match id.0.as_str() {
89            Self::ADD => Some(Self::new(Felt252BinaryOperator::Add)),
90            Self::SUB => Some(Self::new(Felt252BinaryOperator::Sub)),
91            Self::MUL => Some(Self::new(Felt252BinaryOperator::Mul)),
92            Self::DIV => Some(Self::new(Felt252BinaryOperator::Div)),
93            _ => None,
94        }
95    }
96
97    fn specialize_signature(
98        &self,
99        context: &dyn SignatureSpecializationContext,
100        args: &[GenericArg],
101    ) -> Result<LibfuncSignature, SpecializationError> {
102        let ty = context.get_concrete_type(Felt252Type::id(), &[])?;
103        let (second_param_type, output_ref_info) =
104            if matches!(self.operator, Felt252BinaryOperator::Div) {
105                (nonzero_ty(context, &ty)?, OutputVarReferenceInfo::NewTempVar { idx: 0 })
106            } else {
107                (ty.clone(), OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic))
108            };
109        match args {
110            [] => Ok(LibfuncSignature::new_non_branch_ex(
111                vec![
112                    ParamSignature::new(ty.clone()),
113                    ParamSignature::new(second_param_type).with_allow_const(),
114                ],
115                vec![OutputVarInfo { ty, ref_info: output_ref_info }],
116                SierraApChange::Known { new_vars_only: true },
117            )),
118            _ => Err(SpecializationError::WrongNumberOfGenericArgs),
119        }
120    }
121
122    fn specialize(
123        &self,
124        context: &dyn SpecializationContext,
125        args: &[GenericArg],
126    ) -> Result<Self::Concrete, SpecializationError> {
127        match args {
128            [] => Ok({
129                Felt252BinaryOpConcreteLibfunc {
130                    operator: self.operator,
131                    signature: self.specialize_signature(context.upcast(), args)?,
132                }
133            }),
134            _ => Err(SpecializationError::WrongNumberOfGenericArgs),
135        }
136    }
137}
138
139/// Libfunc for felt252 binary operations with const.
140pub struct Felt252BinaryOperationWithConstLibfunc {
141    pub operator: Felt252BinaryOperator,
142}
143impl Felt252BinaryOperationWithConstLibfunc {
144    fn new(operator: Felt252BinaryOperator) -> Self {
145        Self { operator }
146    }
147    const ADD: &'static str = "felt252_add_const";
148    const SUB: &'static str = "felt252_sub_const";
149    const MUL: &'static str = "felt252_mul_const";
150    const DIV: &'static str = "felt252_div_const";
151}
152impl GenericLibfunc for Felt252BinaryOperationWithConstLibfunc {
153    type Concrete = Felt252OperationWithConstConcreteLibfunc;
154
155    fn supported_ids() -> Vec<GenericLibfuncId> {
156        vec![
157            GenericLibfuncId::from(Self::ADD),
158            GenericLibfuncId::from(Self::SUB),
159            GenericLibfuncId::from(Self::MUL),
160            GenericLibfuncId::from(Self::DIV),
161        ]
162    }
163
164    fn by_id(id: &GenericLibfuncId) -> Option<Self> {
165        match id.0.as_str() {
166            Self::ADD => Some(Self::new(Felt252BinaryOperator::Add)),
167            Self::SUB => Some(Self::new(Felt252BinaryOperator::Sub)),
168            Self::MUL => Some(Self::new(Felt252BinaryOperator::Mul)),
169            Self::DIV => Some(Self::new(Felt252BinaryOperator::Div)),
170            _ => None,
171        }
172    }
173
174    fn specialize_signature(
175        &self,
176        context: &dyn SignatureSpecializationContext,
177        args: &[GenericArg],
178    ) -> Result<LibfuncSignature, SpecializationError> {
179        let ty = context.get_concrete_type(Felt252Type::id(), &[])?;
180        match args {
181            [GenericArg::Value(c)] => {
182                let output_ref_info = if matches!(self.operator, Felt252BinaryOperator::Div) {
183                    if c.is_zero() {
184                        return Err(SpecializationError::UnsupportedGenericArg);
185                    }
186                    OutputVarReferenceInfo::NewTempVar { idx: 0 }
187                } else {
188                    OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic)
189                };
190
191                Ok(LibfuncSignature::new_non_branch(
192                    vec![ty.clone()],
193                    vec![OutputVarInfo { ty, ref_info: output_ref_info }],
194                    SierraApChange::Known { new_vars_only: true },
195                ))
196            }
197            _ => Err(SpecializationError::WrongNumberOfGenericArgs),
198        }
199    }
200
201    fn specialize(
202        &self,
203        context: &dyn SpecializationContext,
204        args: &[GenericArg],
205    ) -> Result<Self::Concrete, SpecializationError> {
206        match args {
207            [GenericArg::Value(c)] => {
208                if matches!(self.operator, Felt252BinaryOperator::Div) && c.is_zero() {
209                    Err(SpecializationError::UnsupportedGenericArg)
210                } else {
211                    Ok(Felt252OperationWithConstConcreteLibfunc {
212                        operator: self.operator,
213                        c: c.clone(),
214                        signature: self.specialize_signature(context.upcast(), args)?,
215                    })
216                }
217            }
218            _ => Err(SpecializationError::WrongNumberOfGenericArgs),
219        }
220    }
221}
222
223pub struct Felt252BinaryOpConcreteLibfunc {
224    pub operator: Felt252BinaryOperator,
225    pub signature: LibfuncSignature,
226}
227impl SignatureBasedConcreteLibfunc for Felt252BinaryOpConcreteLibfunc {
228    fn signature(&self) -> &LibfuncSignature {
229        &self.signature
230    }
231}
232
233/// Felt252 operations with a const.
234pub struct Felt252OperationWithConstConcreteLibfunc {
235    pub operator: Felt252BinaryOperator,
236    pub c: BigInt,
237    pub signature: LibfuncSignature,
238}
239
240impl SignatureBasedConcreteLibfunc for Felt252OperationWithConstConcreteLibfunc {
241    fn signature(&self) -> &LibfuncSignature {
242        &self.signature
243    }
244}
245
246/// Libfunc for creating a constant felt252.
247#[derive(Default)]
248pub struct Felt252ConstLibfunc {}
249impl NamedLibfunc for Felt252ConstLibfunc {
250    type Concrete = Felt252ConstConcreteLibfunc;
251    const STR_ID: &'static str = "felt252_const";
252
253    fn specialize_signature(
254        &self,
255        context: &dyn SignatureSpecializationContext,
256        _args: &[GenericArg],
257    ) -> Result<LibfuncSignature, SpecializationError> {
258        Ok(LibfuncSignature::new_non_branch(
259            vec![],
260            vec![OutputVarInfo {
261                ty: context.get_concrete_type(Felt252Type::id(), &[])?,
262                ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Const),
263            }],
264            SierraApChange::Known { new_vars_only: true },
265        ))
266    }
267
268    fn specialize(
269        &self,
270        context: &dyn SpecializationContext,
271        args: &[GenericArg],
272    ) -> Result<Self::Concrete, SpecializationError> {
273        match args {
274            [GenericArg::Value(c)] => Ok(Felt252ConstConcreteLibfunc {
275                c: c.clone(),
276                signature: <Self as NamedLibfunc>::specialize_signature(
277                    self,
278                    context.upcast(),
279                    args,
280                )?,
281            }),
282            _ => Err(SpecializationError::UnsupportedGenericArg),
283        }
284    }
285}
286
287pub struct Felt252ConstConcreteLibfunc {
288    pub c: BigInt,
289    pub signature: LibfuncSignature,
290}
291impl SignatureBasedConcreteLibfunc for Felt252ConstConcreteLibfunc {
292    fn signature(&self) -> &LibfuncSignature {
293        &self.signature
294    }
295}