use std::ops::Shl;
use std::sync::LazyLock;
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
use cairo_lang_utils::{extract_matches, require};
use num_bigint::BigInt;
use num_traits::{One, Signed, ToPrimitive, Zero};
use super::range_check::RangeCheck96Type;
use super::structure::StructType;
use super::utils::{reinterpret_cast_signature, Range};
use crate::extensions::bounded_int::bounded_int_ty;
use crate::extensions::lib_func::{
BranchSignature, DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature,
SierraApChange, SignatureAndTypeGenericLibfunc, SignatureSpecializationContext,
SpecializationContext, WrapSignatureAndTypeGenericLibfunc,
};
use crate::extensions::type_specialization_context::TypeSpecializationContext;
use crate::extensions::types::{InfoOnlyConcreteType, TypeInfo};
use crate::extensions::{
args_as_single_type, args_as_single_value, args_as_two_types, extract_type_generic_args,
ConcreteType, NamedLibfunc, NamedType, NoGenericArgsGenericLibfunc, NoGenericArgsGenericType,
OutputVarReferenceInfo, SignatureBasedConcreteLibfunc, SpecializationError,
};
use crate::ids::{ConcreteTypeId, GenericTypeId, UserTypeId};
use crate::program::{ConcreteTypeLongId, GenericArg};
use crate::{define_libfunc_hierarchy, define_type_hierarchy};
static CIRCUIT_COMPONENTS: LazyLock<UnorderedHashSet<GenericTypeId>> = LazyLock::new(|| {
UnorderedHashSet::from_iter([
CircuitInput::ID,
AddModGate::ID,
InverseGate::ID,
MulModGate::ID,
SubModGate::ID,
])
});
pub const VALUE_SIZE: usize = 4;
pub const MOD_BUILTIN_INSTANCE_SIZE: usize = 7;
pub const OFFSETS_PER_GATE: usize = 3;
pub const ONE_OFFSET: usize = 0;
define_type_hierarchy! {
pub enum CircuitType {
AddMod(AddModType),
MulMod(MulModType),
AddModGate(AddModGate),
Circuit(Circuit),
CircuitData(CircuitData),
CircuitOutputs(CircuitOutputs),
CircuitPartialOutputs(CircuitPartialOutputs),
CircuitDescriptor(CircuitDescriptor),
CircuitFailureGuarantee(CircuitFailureGuarantee),
CircuitInput(CircuitInput),
CircuitInputAccumulator(CircuitInputAccumulator),
CircuitModulus(CircuitModulus),
InverseGate(InverseGate),
MulModGate(MulModGate),
SubModGate(SubModGate),
U96Guarantee(U96Guarantee),
U96LimbsLessThanGuarantee(U96LimbsLessThanGuarantee),
}, CircuitTypeConcrete
}
define_libfunc_hierarchy! {
pub enum CircuitLibFunc {
AddInput(AddCircuitInputLibFunc),
Eval(EvalCircuitLibFunc),
GetDescriptor(GetCircuitDescriptorLibFunc),
InitCircuitData(InitCircuitDataLibFunc),
GetOutput(GetOutputLibFunc),
TryIntoCircuitModulus(TryIntoCircuitModulusLibFunc),
FailureGuaranteeVerify(CircuitFailureGuaranteeVerifyLibFunc),
IntoU96Guarantee(IntoU96GuaranteeLibFunc),
U96GuaranteeVerify(U96GuaranteeVerifyLibFunc),
U96LimbsLessThanGuaranteeVerify(U96LimbsLessThanGuaranteeVerifyLibfunc),
U96SingleLimbLessThanGuaranteeVerify(U96SingleLimbLessThanGuaranteeVerifyLibfunc),
}, CircuitConcreteLibfunc
}
fn is_circuit_component(
context: &dyn TypeSpecializationContext,
generic_arg: &GenericArg,
) -> Result<bool, SpecializationError> {
let GenericArg::Type(ty) = generic_arg else {
return Err(SpecializationError::UnsupportedGenericArg);
};
let long_id = context.get_type_info(ty.clone())?.long_id;
Ok(CIRCUIT_COMPONENTS.contains(&long_id.generic_id))
}
#[derive(Default)]
pub struct CircuitInput {}
impl NamedType for CircuitInput {
type Concrete = ConcreteCircuitInput;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitInput");
fn specialize(
&self,
_context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let idx = args_as_single_value(args)?
.to_usize()
.ok_or(SpecializationError::UnsupportedGenericArg)?;
Ok(Self::Concrete { info: circuit_component_type_info(Self::ID, args), idx })
}
}
pub struct ConcreteCircuitInput {
pub info: TypeInfo,
pub idx: usize,
}
impl ConcreteType for ConcreteCircuitInput {
fn info(&self) -> &TypeInfo {
&self.info
}
}
fn validate_gate_generic_args(
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<(), SpecializationError> {
if args.len() != 2 {
return Err(SpecializationError::WrongNumberOfGenericArgs);
}
validate_args_are_circuit_components(context, args.iter())
}
#[derive(Default)]
pub struct AddModGate {}
impl NamedType for AddModGate {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("AddModGate");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
validate_gate_generic_args(context, args)?;
Ok(Self::Concrete { info: circuit_component_type_info(Self::ID, args) })
}
}
#[derive(Default)]
pub struct SubModGate {}
impl NamedType for SubModGate {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("SubModGate");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
validate_gate_generic_args(context, args)?;
Ok(Self::Concrete { info: circuit_component_type_info(Self::ID, args) })
}
}
#[derive(Default)]
pub struct MulModGate {}
impl NamedType for MulModGate {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("MulModGate");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
validate_gate_generic_args(context, args)?;
Ok(Self::Concrete { info: circuit_component_type_info(Self::ID, args) })
}
}
#[derive(Default)]
pub struct InverseGate {}
impl NamedType for InverseGate {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("InverseGate");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
if args.len() != 1 {
return Err(SpecializationError::WrongNumberOfGenericArgs);
}
validate_args_are_circuit_components(context, args.iter())?;
Ok(Self::Concrete { info: circuit_component_type_info(Self::ID, args) })
}
}
#[derive(Default)]
pub struct CircuitInputAccumulator {}
impl NamedType for CircuitInputAccumulator {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitInputAccumulator");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let circ_ty = args_as_single_type(args)?;
validate_is_circuit(context, circ_ty)?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId { generic_id: Self::ID, generic_args: args.to_vec() },
duplicatable: false,
droppable: true,
storable: true,
zero_sized: false,
},
})
}
}
#[derive(Default)]
pub struct CircuitModulus {}
impl NoGenericArgsGenericType for CircuitModulus {
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitModulus");
const STORABLE: bool = true;
const DUPLICATABLE: bool = true;
const DROPPABLE: bool = true;
const ZERO_SIZED: bool = false;
}
#[derive(Default)]
pub struct CircuitData {}
impl NamedType for CircuitData {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitData");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let circ_ty = args_as_single_type(args)?;
validate_is_circuit(context, circ_ty)?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId { generic_id: Self::ID, generic_args: args.to_vec() },
duplicatable: false,
droppable: true,
storable: true,
zero_sized: false,
},
})
}
}
#[derive(Default)]
pub struct CircuitOutputs {}
impl NamedType for CircuitOutputs {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitOutputs");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let circ_ty = args_as_single_type(args)?;
validate_is_circuit(context, circ_ty)?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId { generic_id: Self::ID, generic_args: args.to_vec() },
duplicatable: true,
droppable: true,
storable: true,
zero_sized: false,
},
})
}
}
#[derive(Default)]
pub struct CircuitPartialOutputs {}
impl NamedType for CircuitPartialOutputs {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitPartialOutputs");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let circ_ty = args_as_single_type(args)?;
validate_is_circuit(context, circ_ty)?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId { generic_id: Self::ID, generic_args: args.to_vec() },
duplicatable: false,
droppable: true,
storable: true,
zero_sized: false,
},
})
}
}
#[derive(Default)]
pub struct CircuitFailureGuarantee {}
impl NoGenericArgsGenericType for CircuitFailureGuarantee {
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitFailureGuarantee");
const STORABLE: bool = true;
const DUPLICATABLE: bool = false;
const DROPPABLE: bool = false;
const ZERO_SIZED: bool = false;
}
#[derive(Default)]
pub struct U96LimbsLessThanGuarantee {}
impl NamedType for U96LimbsLessThanGuarantee {
type Concrete = ConcreteU96LimbsLessThanGuarantee;
const ID: GenericTypeId = GenericTypeId::new_inline("U96LimbsLtGuarantee");
fn specialize(
&self,
_context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let limb_count = args_as_single_value(args)?
.to_usize()
.ok_or(SpecializationError::UnsupportedGenericArg)?;
require(limb_count >= 1).ok_or(SpecializationError::UnsupportedGenericArg)?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId {
generic_id: <Self as NamedType>::ID,
generic_args: args.to_vec(),
},
duplicatable: false,
droppable: false,
storable: true,
zero_sized: false,
},
limb_count,
})
}
}
pub struct ConcreteU96LimbsLessThanGuarantee {
pub info: TypeInfo,
pub limb_count: usize,
}
impl ConcreteType for ConcreteU96LimbsLessThanGuarantee {
fn info(&self) -> &TypeInfo {
&self.info
}
}
#[derive(Default)]
pub struct U96Guarantee {}
impl NoGenericArgsGenericType for U96Guarantee {
const ID: GenericTypeId = GenericTypeId::new_inline("U96Guarantee");
const STORABLE: bool = true;
const DUPLICATABLE: bool = false;
const DROPPABLE: bool = false;
const ZERO_SIZED: bool = false;
}
#[derive(Default)]
pub struct CircuitDescriptor {}
impl NamedType for CircuitDescriptor {
type Concrete = InfoOnlyConcreteType;
const ID: GenericTypeId = GenericTypeId::new_inline("CircuitDescriptor");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let circ_ty = args_as_single_type(args)?;
validate_is_circuit(context, circ_ty.clone())?;
Ok(Self::Concrete {
info: TypeInfo {
long_id: ConcreteTypeLongId { generic_id: Self::ID, generic_args: args.to_vec() },
duplicatable: true,
droppable: true,
storable: true,
zero_sized: false,
},
})
}
}
#[derive(Default)]
pub struct Circuit {}
impl NamedType for Circuit {
type Concrete = ConcreteCircuit;
const ID: GenericTypeId = GenericTypeId::new_inline("Circuit");
fn specialize(
&self,
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
Self::Concrete::new(context, args)
}
}
pub struct ConcreteCircuit {
pub info: TypeInfo,
pub circuit_info: CircuitInfo,
}
impl ConcreteCircuit {
fn new(
context: &dyn TypeSpecializationContext,
args: &[GenericArg],
) -> Result<Self, SpecializationError> {
let output_tuple = args_as_single_type(args)?;
let circuit_info = get_circuit_info(context, &output_tuple)?;
Ok(Self {
info: TypeInfo {
long_id: ConcreteTypeLongId {
generic_id: "Circuit".into(),
generic_args: args.to_vec(),
},
duplicatable: false,
droppable: false,
storable: false,
zero_sized: true,
},
circuit_info,
})
}
}
impl ConcreteType for ConcreteCircuit {
fn info(&self) -> &TypeInfo {
&self.info
}
}
fn validate_is_circuit(
context: &dyn TypeSpecializationContext,
circ_ty: ConcreteTypeId,
) -> Result<(), SpecializationError> {
if context.get_type_info(circ_ty.clone())?.long_id.generic_id != Circuit::ID {
return Err(SpecializationError::UnsupportedGenericArg);
}
Ok(())
}
fn validate_args_are_circuit_components<'a>(
context: &dyn TypeSpecializationContext,
generic_args: impl Iterator<Item = &'a GenericArg>,
) -> Result<(), SpecializationError> {
for generic_arg in generic_args {
if !is_circuit_component(context, generic_arg)? {
return Err(SpecializationError::UnsupportedGenericArg);
}
}
Ok(())
}
#[derive(Default)]
pub struct InitCircuitDataLibFuncWrapped {}
impl SignatureAndTypeGenericLibfunc for InitCircuitDataLibFuncWrapped {
const STR_ID: &'static str = "init_circuit_data";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let range_check96_type = context.get_concrete_type(RangeCheck96Type::id(), &[])?;
let circuit_input_accumulator_ty =
context.get_concrete_type(CircuitInputAccumulator::id(), &[GenericArg::Type(ty)])?;
Ok(LibfuncSignature::new_non_branch_ex(
vec![ParamSignature::new(range_check96_type.clone()).with_allow_add_const()],
vec![
OutputVarInfo {
ty: range_check96_type.clone(),
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst {
param_idx: 0,
}),
},
OutputVarInfo {
ty: circuit_input_accumulator_ty.clone(),
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
},
],
SierraApChange::Known { new_vars_only: true },
))
}
}
pub type InitCircuitDataLibFunc = WrapSignatureAndTypeGenericLibfunc<InitCircuitDataLibFuncWrapped>;
#[derive(Default)]
pub struct AddCircuitInputLibFuncWrapped {}
impl SignatureAndTypeGenericLibfunc for AddCircuitInputLibFuncWrapped {
const STR_ID: &'static str = "add_circuit_input";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let circuit_input_accumulator_ty = context
.get_concrete_type(CircuitInputAccumulator::id(), &[GenericArg::Type(ty.clone())])?;
let circuit_data_ty =
context.get_concrete_type(CircuitData::id(), &[GenericArg::Type(ty)])?;
let u96_guarantee_ty = context.get_concrete_type(U96Guarantee::id(), &[])?;
let val_ty = context.get_concrete_type(
StructType::id(),
&[
GenericArg::UserType(UserTypeId::from_string("Tuple")),
GenericArg::Type(u96_guarantee_ty.clone()),
GenericArg::Type(u96_guarantee_ty.clone()),
GenericArg::Type(u96_guarantee_ty.clone()),
GenericArg::Type(u96_guarantee_ty),
],
)?;
Ok(LibfuncSignature {
param_signatures: vec![
ParamSignature::new(circuit_input_accumulator_ty.clone()),
ParamSignature::new(val_ty),
],
branch_signatures: vec![
BranchSignature {
vars: vec![OutputVarInfo {
ty: circuit_data_ty,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
}],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![OutputVarInfo {
ty: circuit_input_accumulator_ty,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
}],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
}
pub type AddCircuitInputLibFunc = WrapSignatureAndTypeGenericLibfunc<AddCircuitInputLibFuncWrapped>;
#[derive(Default)]
pub struct GetCircuitDescriptorLibFuncWrapped {}
impl SignatureAndTypeGenericLibfunc for GetCircuitDescriptorLibFuncWrapped {
const STR_ID: &'static str = "get_circuit_descriptor";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let circuit_descriptor_ty =
context.get_concrete_type(CircuitDescriptor::id(), &[GenericArg::Type(ty.clone())])?;
Ok(LibfuncSignature::new_non_branch(
vec![],
vec![OutputVarInfo {
ty: circuit_descriptor_ty,
ref_info: OutputVarReferenceInfo::NewTempVar { idx: 0 },
}],
SierraApChange::Known { new_vars_only: false },
))
}
}
fn get_u96_type(
context: &dyn SignatureSpecializationContext,
) -> Result<ConcreteTypeId, SpecializationError> {
bounded_int_ty(context, BigInt::zero(), BigInt::one().shl(96) - 1)
}
fn get_u384_type(
context: &dyn SignatureSpecializationContext,
) -> Result<ConcreteTypeId, SpecializationError> {
let u96_ty = get_u96_type(context)?;
context.get_concrete_type(
StructType::id(),
&[
GenericArg::UserType(UserTypeId::from_string("core::circuit::u384")),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty),
],
)
}
pub type GetCircuitDescriptorLibFunc =
WrapSignatureAndTypeGenericLibfunc<GetCircuitDescriptorLibFuncWrapped>;
#[derive(Default)]
pub struct EvalCircuitLibFuncWrapped {}
impl SignatureAndTypeGenericLibfunc for EvalCircuitLibFuncWrapped {
const STR_ID: &'static str = "eval_circuit";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let add_mod_builtin_ty = context.get_concrete_type(AddModType::id(), &[])?;
let mul_mod_builtin_ty = context.get_concrete_type(MulModType::id(), &[])?;
let circuit_descriptor_ty =
context.get_concrete_type(CircuitDescriptor::id(), &[GenericArg::Type(ty.clone())])?;
let circuit_data_ty =
context.get_concrete_type(CircuitData::id(), &[GenericArg::Type(ty.clone())])?;
let zero = bounded_int_ty(context, BigInt::zero(), BigInt::zero())?;
let one = bounded_int_ty(context, BigInt::one(), BigInt::one())?;
Ok(LibfuncSignature {
param_signatures: [
add_mod_builtin_ty.clone(),
mul_mod_builtin_ty.clone(),
circuit_descriptor_ty,
circuit_data_ty,
context.get_concrete_type(CircuitModulus::id(), &[])?,
zero,
one,
]
.into_iter()
.map(|ty| ParamSignature::new(ty.clone()))
.collect(),
branch_signatures: vec![
BranchSignature {
vars: vec![
OutputVarInfo::new_builtin(add_mod_builtin_ty.clone(), 0),
OutputVarInfo {
ty: mul_mod_builtin_ty.clone(),
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
},
OutputVarInfo {
ty: context.get_concrete_type(
CircuitOutputs::id(),
&[GenericArg::Type(ty.clone())],
)?,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![
OutputVarInfo::new_builtin(add_mod_builtin_ty, 0),
OutputVarInfo {
ty: mul_mod_builtin_ty,
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
},
OutputVarInfo {
ty: context.get_concrete_type(
CircuitPartialOutputs::id(),
&[GenericArg::Type(ty)],
)?,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
OutputVarInfo {
ty: context.get_concrete_type(CircuitFailureGuarantee::id(), &[])?,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
}
pub type EvalCircuitLibFunc = WrapSignatureAndTypeGenericLibfunc<EvalCircuitLibFuncWrapped>;
#[derive(Default)]
pub struct IntoU96GuaranteeLibFuncWrapped {}
impl SignatureAndTypeGenericLibfunc for IntoU96GuaranteeLibFuncWrapped {
const STR_ID: &'static str = "into_u96_guarantee";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<LibfuncSignature, SpecializationError> {
let range = Range::from_type(context, ty.clone())?;
require(!range.lower.is_negative() && range.upper <= BigInt::one().shl(96))
.ok_or(SpecializationError::UnsupportedGenericArg)?;
let guarantee_ty = context.get_concrete_type(U96Guarantee::id(), &[])?;
Ok(reinterpret_cast_signature(ty, guarantee_ty))
}
}
pub type IntoU96GuaranteeLibFunc =
WrapSignatureAndTypeGenericLibfunc<IntoU96GuaranteeLibFuncWrapped>;
#[derive(Default)]
pub struct U96GuaranteeVerifyLibFunc {}
impl NoGenericArgsGenericLibfunc for U96GuaranteeVerifyLibFunc {
const STR_ID: &'static str = "u96_guarantee_verify";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let range_check96_type = context.get_concrete_type(RangeCheck96Type::id(), &[])?;
let guarantee_ty = context.get_concrete_type(U96Guarantee::id(), &[])?;
Ok(LibfuncSignature::new_non_branch_ex(
vec![
ParamSignature::new(range_check96_type.clone()).with_allow_add_const(),
ParamSignature::new(guarantee_ty),
],
vec![OutputVarInfo::new_builtin(range_check96_type, 0)],
SierraApChange::Known { new_vars_only: true },
))
}
}
#[derive(Default)]
pub struct GetOutputLibFunc {}
impl NamedLibfunc for GetOutputLibFunc {
const STR_ID: &'static str = "get_circuit_output";
type Concrete = ConcreteGetOutputLibFunc;
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (circ_ty, output_ty) = args_as_two_types(args)?;
if !CIRCUIT_COMPONENTS.contains(&context.get_type_info(output_ty)?.long_id.generic_id) {
return Err(SpecializationError::UnsupportedGenericArg);
}
let outputs_ty =
context.get_concrete_type(CircuitOutputs::id(), &[GenericArg::Type(circ_ty)])?;
let u384_ty = get_u384_type(context)?;
let guarantee_ty = u384_less_than_guarantee_ty(context)?;
Ok(LibfuncSignature::new_non_branch(
vec![outputs_ty],
vec![
OutputVarInfo { ty: u384_ty, ref_info: OutputVarReferenceInfo::SimpleDerefs },
OutputVarInfo { ty: guarantee_ty, ref_info: OutputVarReferenceInfo::SimpleDerefs },
],
SierraApChange::Known { new_vars_only: false },
))
}
fn specialize(
&self,
context: &dyn SpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let (circuit_ty, output_ty) = args_as_two_types(args)?;
Ok(ConcreteGetOutputLibFunc {
signature: self.specialize_signature(context.upcast(), args)?,
circuit_ty,
output_ty,
})
}
}
pub struct ConcreteGetOutputLibFunc {
pub signature: LibfuncSignature,
pub circuit_ty: ConcreteTypeId,
pub output_ty: ConcreteTypeId,
}
impl SignatureBasedConcreteLibfunc for ConcreteGetOutputLibFunc {
fn signature(&self) -> &LibfuncSignature {
&self.signature
}
}
#[derive(Default)]
pub struct CircuitFailureGuaranteeVerifyLibFunc {}
impl NoGenericArgsGenericLibfunc for CircuitFailureGuaranteeVerifyLibFunc {
const STR_ID: &'static str = "circuit_failure_guarantee_verify";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let range_check96_type = context.get_concrete_type(RangeCheck96Type::id(), &[])?;
let mul_mod_builtin_ty = context.get_concrete_type(MulModType::id(), &[])?;
let guarantee_ty = context.get_concrete_type(CircuitFailureGuarantee::id(), &[])?;
let zero = bounded_int_ty(context, BigInt::zero(), BigInt::zero())?;
let one = bounded_int_ty(context, BigInt::one(), BigInt::one())?;
Ok(LibfuncSignature::new_non_branch(
vec![range_check96_type.clone(), mul_mod_builtin_ty.clone(), guarantee_ty, zero, one],
vec![
OutputVarInfo::new_builtin(range_check96_type, 0),
OutputVarInfo::new_builtin(mul_mod_builtin_ty, 1),
OutputVarInfo {
ty: u384_less_than_guarantee_ty(context)?,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
],
SierraApChange::Known { new_vars_only: false },
))
}
}
#[derive(Default)]
pub struct U96LimbsLessThanGuaranteeVerifyLibfunc {}
impl NamedLibfunc for U96LimbsLessThanGuaranteeVerifyLibfunc {
const STR_ID: &'static str = "u96_limbs_less_than_guarantee_verify";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let limb_count = args_as_single_value(args)?
.to_usize()
.ok_or(SpecializationError::UnsupportedGenericArg)?;
require(limb_count > 1).ok_or(SpecializationError::UnsupportedGenericArg)?;
let in_guarantee_ty = u96_limbs_less_than_guarantee_ty(context, limb_count)?;
let u96_lt_guarantee_ty = context.get_concrete_type(U96Guarantee::id(), &[])?;
let eq_guarantee_ty = u96_limbs_less_than_guarantee_ty(context, limb_count - 1)?;
Ok(LibfuncSignature {
param_signatures: vec![ParamSignature::new(in_guarantee_ty)],
branch_signatures: vec![
BranchSignature {
vars: vec![OutputVarInfo {
ty: eq_guarantee_ty,
ref_info: OutputVarReferenceInfo::PartialParam { param_idx: 0 },
}],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![OutputVarInfo {
ty: u96_lt_guarantee_ty,
ref_info: OutputVarReferenceInfo::NewTempVar { idx: 0 },
}],
ap_change: SierraApChange::Known { new_vars_only: true },
},
],
fallthrough: Some(0),
})
}
type Concrete = ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc;
fn specialize(
&self,
context: &dyn SpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let limb_count = args_as_single_value(args)?
.to_usize()
.ok_or(SpecializationError::UnsupportedGenericArg)?;
Ok(Self::Concrete {
signature: self.specialize_signature(context.upcast(), args)?,
limb_count,
})
}
}
pub struct ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc {
signature: LibfuncSignature,
pub limb_count: usize,
}
impl SignatureBasedConcreteLibfunc for ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc {
fn signature(&self) -> &LibfuncSignature {
&self.signature
}
}
#[derive(Default)]
pub struct U96SingleLimbLessThanGuaranteeVerifyLibfunc {}
impl NoGenericArgsGenericLibfunc for U96SingleLimbLessThanGuaranteeVerifyLibfunc {
const STR_ID: &'static str = "u96_single_limb_less_than_guarantee_verify";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let in_guarantee_ty = u96_limbs_less_than_guarantee_ty(context, 1)?;
let u96_lt_guarantee_ty = context.get_concrete_type(U96Guarantee::id(), &[])?;
Ok(LibfuncSignature::new_non_branch(
vec![in_guarantee_ty],
vec![OutputVarInfo {
ty: u96_lt_guarantee_ty,
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
}],
SierraApChange::Known { new_vars_only: true },
))
}
}
#[derive(Default)]
pub struct AddModType {}
impl NoGenericArgsGenericType for AddModType {
const ID: GenericTypeId = GenericTypeId::new_inline("AddMod");
const STORABLE: bool = true;
const DUPLICATABLE: bool = false;
const DROPPABLE: bool = false;
const ZERO_SIZED: bool = false;
}
#[derive(Default)]
pub struct MulModType {}
impl NoGenericArgsGenericType for MulModType {
const ID: GenericTypeId = GenericTypeId::new_inline("MulMod");
const STORABLE: bool = true;
const DUPLICATABLE: bool = false;
const DROPPABLE: bool = false;
const ZERO_SIZED: bool = false;
}
#[derive(Default)]
pub struct TryIntoCircuitModulusLibFunc {}
impl NoGenericArgsGenericLibfunc for TryIntoCircuitModulusLibFunc {
const STR_ID: &'static str = "try_into_circuit_modulus";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let u96_ty = get_u96_type(context)?;
let value_type = context.get_concrete_type(
StructType::id(),
&[
GenericArg::UserType(UserTypeId::from_string("Tuple")),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty.clone()),
GenericArg::Type(u96_ty),
],
)?;
Ok(LibfuncSignature {
param_signatures: vec![ParamSignature::new(value_type)],
branch_signatures: vec![
BranchSignature {
vars: vec![OutputVarInfo {
ty: context.get_concrete_type(CircuitModulus::id(), &[])?,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
}],
ap_change: SierraApChange::Known { new_vars_only: false },
},
BranchSignature {
vars: vec![],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
}
fn get_circuit_info(
context: &dyn TypeSpecializationContext,
outputs_tuple_ty: &ConcreteTypeId,
) -> Result<CircuitInfo, SpecializationError> {
let struct_generic_args = extract_type_generic_args::<StructType>(context, outputs_tuple_ty)?;
let mut generic_args = struct_generic_args.iter();
if !matches!(
generic_args.next(),
Some(GenericArg::UserType(ut))
if (*ut == UserTypeId::from_string("Tuple"))
) {
return Err(SpecializationError::UnsupportedGenericArg);
}
let circ_outputs = generic_args;
validate_args_are_circuit_components(context, circ_outputs.clone())?;
let ParsedInputs { reduced_inputs: mut values, mut mul_offsets } =
parse_circuit_inputs(context, circ_outputs.clone())?;
let n_inputs = values.len();
require(n_inputs >= 1).ok_or(SpecializationError::UnsupportedGenericArg)?;
let mut add_offsets = vec![];
let mut stack: Vec<(ConcreteTypeId, bool)> = circ_outputs
.map(|generic_arg| (extract_matches!(generic_arg, GenericArg::Type).clone(), true))
.collect();
while let Some((ty, first_visit)) = stack.pop() {
let long_id = context.get_type_info(ty.clone())?.long_id;
let generic_id = long_id.generic_id;
if values.contains_key(&ty) {
continue;
}
let gate_inputs = long_id
.generic_args
.iter()
.map(|generic_arg| extract_matches!(generic_arg, GenericArg::Type));
if first_visit {
stack.push((ty, false));
stack.extend(gate_inputs.map(|ty| (ty.clone(), true)))
} else {
let output_offset = 1 + n_inputs + values.len();
let mut input_offsets = gate_inputs.map(|ty| values[ty]);
if generic_id == AddModGate::ID {
add_offsets.push(GateOffsets {
lhs: input_offsets.next().unwrap(),
rhs: input_offsets.next().unwrap(),
output: output_offset,
});
} else if generic_id == SubModGate::ID {
let sub_lhs = input_offsets.next().unwrap();
let sub_rhs = input_offsets.next().unwrap();
add_offsets.push(GateOffsets { lhs: output_offset, rhs: sub_rhs, output: sub_lhs });
} else if generic_id == MulModGate::ID {
mul_offsets.push(GateOffsets {
lhs: input_offsets.next().unwrap(),
rhs: input_offsets.next().unwrap(),
output: output_offset,
});
} else if generic_id == InverseGate::ID {
mul_offsets.push(GateOffsets {
lhs: output_offset,
rhs: input_offsets.next().unwrap(),
output: ONE_OFFSET,
});
} else {
return Err(SpecializationError::UnsupportedGenericArg);
};
assert!(input_offsets.next().is_none());
values.insert(ty.clone(), output_offset);
}
}
Ok(CircuitInfo { n_inputs, values, add_offsets, mul_offsets })
}
fn parse_circuit_inputs<'a>(
context: &dyn TypeSpecializationContext,
circuit_outputs: impl Iterator<Item = &'a GenericArg>,
) -> Result<ParsedInputs, SpecializationError> {
let mut stack: Vec<ConcreteTypeId> = circuit_outputs
.map(|generic_arg| extract_matches!(generic_arg, GenericArg::Type).clone())
.collect();
let mut inputs: UnorderedHashMap<usize, ConcreteTypeId> = Default::default();
let mut visited = UnorderedHashSet::<_>::default();
while let Some(ty) = stack.pop() {
if !visited.insert(ty.clone()) {
continue;
}
let long_id = context.get_type_info(ty.clone())?.long_id;
let generic_id = long_id.generic_id;
if generic_id == CircuitInput::ID {
let idx = args_as_single_value(&long_id.generic_args)?
.to_usize()
.ok_or(SpecializationError::UnsupportedGenericArg)?;
assert!(inputs.insert(idx, ty).is_none());
} else {
stack.extend(
long_id
.generic_args
.iter()
.map(|generic_arg| extract_matches!(generic_arg, GenericArg::Type).clone()),
);
}
}
let mut reduced_inputs: UnorderedHashMap<ConcreteTypeId, usize> = Default::default();
let n_inputs = inputs.len();
let reduced_input_start = 1 + n_inputs;
let mut mul_offsets = vec![];
for (idx, (input_idx, ty)) in inputs.iter_sorted().enumerate() {
require(idx == *input_idx).ok_or(SpecializationError::UnsupportedGenericArg)?;
assert!(reduced_inputs.insert(ty.clone(), reduced_input_start + idx).is_none());
mul_offsets.push(GateOffsets {
lhs: ONE_OFFSET,
rhs: 1 + idx,
output: reduced_input_start + idx,
});
}
Ok(ParsedInputs { reduced_inputs, mul_offsets })
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct GateOffsets {
pub lhs: usize,
pub rhs: usize,
pub output: usize,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CircuitInfo {
pub n_inputs: usize,
pub values: UnorderedHashMap<ConcreteTypeId, usize>,
pub add_offsets: Vec<GateOffsets>,
pub mul_offsets: Vec<GateOffsets>,
}
impl CircuitInfo {
pub fn rc96_usage(&self) -> usize {
(1 + self.n_inputs + self.values.len()) * VALUE_SIZE
}
}
struct ParsedInputs {
reduced_inputs: UnorderedHashMap<ConcreteTypeId, usize>,
mul_offsets: Vec<GateOffsets>,
}
fn u384_less_than_guarantee_ty(
context: &dyn SignatureSpecializationContext,
) -> Result<ConcreteTypeId, SpecializationError> {
u96_limbs_less_than_guarantee_ty(context, 4)
}
fn u96_limbs_less_than_guarantee_ty(
context: &dyn SignatureSpecializationContext,
limb_count: usize,
) -> Result<ConcreteTypeId, SpecializationError> {
context
.get_concrete_type(U96LimbsLessThanGuarantee::id(), &[GenericArg::Value(limb_count.into())])
}
fn circuit_component_type_info(generic_id: GenericTypeId, args: &[GenericArg]) -> TypeInfo {
TypeInfo {
long_id: ConcreteTypeLongId { generic_id, generic_args: args.to_vec() },
duplicatable: false,
droppable: false,
storable: false,
zero_sized: true,
}
}