Skip to content
Open
37 changes: 9 additions & 28 deletions src/libfuncs/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,14 +601,15 @@ fn build_gate_evaluation<'ctx, 'this>(
let circuit_modulus_u768 = block.extui(circuit_modulus, u768_type, location)?;

// Apply egcd to find gcd and inverse
let euclidean_result = runtime_bindings_meta.extended_euclidean_algorithm(
context,
helper.module,
block,
location,
rhs_value,
circuit_modulus_u768,
)?;
let euclidean_result = runtime_bindings_meta
.u384_extended_euclidean_algorithm(
context,
helper.module,
block,
location,
rhs_value,
circuit_modulus_u768,
)?;
// Extract the values from the result struct
let gcd =
block.extract_value(context, location, euclidean_result, u768_type, 0)?;
Expand Down Expand Up @@ -636,26 +637,6 @@ fn build_gate_evaluation<'ctx, 'this>(
));
block = has_inverse_block;

// if the inverse is negative, then add modulus
let zero = block.const_int_from_type(context, location, 0, u768_type)?;
let is_negative = block
.append_operation(arith::cmpi(
context,
CmpiPredicate::Slt,
inverse,
zero,
location,
))
.result(0)?
.into();
let wrapped_inverse = block.addi(inverse, circuit_modulus_u768, location)?;
let inverse = block.append_op_result(arith::select(
is_negative,
wrapped_inverse,
inverse,
location,
))?;

// Truncate back
let inverse = block.trunci(inverse, u384_type, location)?;

Expand Down
151 changes: 31 additions & 120 deletions src/libfuncs/felt252.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use super::LibfuncHelper;
use crate::{
error::Result,
metadata::MetadataStorage,
error::{panic::ToNativeAssertError, Result},
metadata::{runtime_bindings::RuntimeBindingsMeta, MetadataStorage},
utils::{ProgramRegistryExt, PRIME},
};
use cairo_lang_sierra::{
Expand All @@ -19,12 +19,9 @@ use cairo_lang_sierra::{
program_registry::ProgramRegistry,
};
use melior::{
dialect::{
arith::{self, CmpiPredicate},
cf,
},
helpers::{ArithBlockExt, BuiltinBlockExt},
ir::{r#type::IntegerType, Block, BlockLike, Location, Value, ValueLike},
dialect::arith::{self, CmpiPredicate},
helpers::{ArithBlockExt, BuiltinBlockExt, LlvmBlockExt},
ir::{r#type::IntegerType, Block, Location, Value, ValueLike},
Context,
};
use num_bigint::{BigInt, Sign};
Expand Down Expand Up @@ -149,130 +146,44 @@ pub fn build_binary_operation<'ctx, 'this>(
entry.trunci(result, felt252_ty, location)?
}
Felt252BinaryOperator::Div => {
// The extended euclidean algorithm calculates the greatest common divisor of two integers,
// as well as the bezout coefficients x and y such that for inputs a and b, ax+by=gcd(a,b)
// We use this in felt division to find the modular inverse of a given number
// If a is the number we're trying to find the inverse of, we can do
// ax+y*PRIME=gcd(a,PRIME)=1 => ax = 1 (mod PRIME)
// Hence for input a, we return x
// The input MUST be non-zero
// See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
let start_block = helper.append_block(Block::new(&[(i512, location)]));
let loop_block = helper.append_block(Block::new(&[
(i512, location),
(i512, location),
(i512, location),
(i512, location),
]));
let negative_check_block = helper.append_block(Block::new(&[]));
// Block containing final result
let inverse_result_block = helper.append_block(Block::new(&[(i512, location)]));
// Egcd works by calculating a series of remainders, each the remainder of dividing the previous two
// For the initial setup, r0 = PRIME, r1 = a
// This order is chosen because if we reverse them, then the first iteration will just swap them
let prev_remainder =
start_block.const_int_from_type(context, location, PRIME.clone(), i512)?;
let remainder = start_block.arg(0)?;
// Similarly we'll calculate another series which starts 0,1,... and from which we will retrieve the modular inverse of a
let prev_inverse = start_block.const_int_from_type(context, location, 0, i512)?;
let inverse = start_block.const_int_from_type(context, location, 1, i512)?;
start_block.append_operation(cf::br(
loop_block,
&[prev_remainder, remainder, prev_inverse, inverse],
location,
));

//---Loop body---
// Arguments are rem_(i-1), rem, inv_(i-1), inv
let prev_remainder = loop_block.arg(0)?;
let remainder = loop_block.arg(1)?;
let prev_inverse = loop_block.arg(2)?;
let inverse = loop_block.arg(3)?;

// First calculate q = rem_(i-1)/rem_i, rounded down
let quotient =
loop_block.append_op_result(arith::divui(prev_remainder, remainder, location))?;
// Then r_(i+1) = r_(i-1) - q * r_i, and inv_(i+1) = inv_(i-1) - q * inv_i
let rem_times_quo = loop_block.muli(remainder, quotient, location)?;
let inv_times_quo = loop_block.muli(inverse, quotient, location)?;
let next_remainder = loop_block.append_op_result(arith::subi(
prev_remainder,
rem_times_quo,
location,
))?;
let next_inverse =
loop_block.append_op_result(arith::subi(prev_inverse, inv_times_quo, location))?;

// If r_(i+1) is 0, then inv_i is the inverse
let zero = loop_block.const_int_from_type(context, location, 0, i512)?;
let next_remainder_eq_zero =
loop_block.cmpi(context, CmpiPredicate::Eq, next_remainder, zero, location)?;
loop_block.append_operation(cf::cond_br(
context,
next_remainder_eq_zero,
negative_check_block,
loop_block,
&[],
&[remainder, next_remainder, inverse, next_inverse],
location,
));

// egcd sometimes returns a negative number for the inverse,
// in such cases we must simply wrap it around back into [0, PRIME)
// this suffices because |inv_i| <= divfloor(PRIME,2)
let zero = negative_check_block.const_int_from_type(context, location, 0, i512)?;

let is_negative = negative_check_block
.append_operation(arith::cmpi(
context,
CmpiPredicate::Slt,
inverse,
zero,
location,
))
.result(0)?
.into();
// if the inverse is < 0, add PRIME
let prime =
negative_check_block.const_int_from_type(context, location, PRIME.clone(), i512)?;
let wrapped_inverse = negative_check_block.addi(inverse, prime, location)?;
let inverse = negative_check_block.append_op_result(arith::select(
is_negative,
wrapped_inverse,
inverse,
location,
))?;
negative_check_block.append_operation(cf::br(
inverse_result_block,
&[inverse],
location,
));
let runtime_bindings_meta = metadata
.get_mut::<RuntimeBindingsMeta>()
.to_native_assert_error(
"Unable to get the RuntimeBindingsMeta from MetadataStorage",
)?;

// Div Logic Start
// Fetch operands
let prime = entry.const_int_from_type(context, location, PRIME.clone(), i512)?;
let lhs = entry.extui(lhs, i512, location)?;
let rhs = entry.extui(rhs, i512, location)?;
// Calculate inverse of rhs, callling the inverse implementation's starting block
entry.append_operation(cf::br(start_block, &[rhs], location));
// Fetch the inverse result from the result block
let inverse = inverse_result_block.arg(0)?;
// Peform lhs * (1/ rhs)
let result = inverse_result_block.muli(lhs, inverse, location)?;

// Find 1 / rhs.
let euclidean_result = runtime_bindings_meta.u252_extended_euclidean_algorithm(
context,
helper.module,
entry,
location,
rhs,
prime,
)?;

let inverse = entry.extract_value(context, location, euclidean_result, i512, 1)?;

// Peform lhs * (1 / rhs)
let result = entry.muli(lhs, inverse, location)?;
// Apply modulo and convert result to felt252
let result_mod =
inverse_result_block.append_op_result(arith::remui(result, prime, location))?;
let result_mod = entry.append_op_result(arith::remui(result, prime, location))?;
let is_out_of_range =
inverse_result_block.cmpi(context, CmpiPredicate::Uge, result, prime, location)?;
entry.cmpi(context, CmpiPredicate::Uge, result, prime, location)?;

let result = inverse_result_block.append_op_result(arith::select(
let result = entry.append_op_result(arith::select(
is_out_of_range,
result_mod,
result,
location,
))?;
let result = inverse_result_block.trunci(result, felt252_ty, location)?;
let result = entry.trunci(result, felt252_ty, location)?;

return helper.br(inverse_result_block, 0, &[result], location);
return helper.br(entry, 0, &[result], location);
}
};

Expand Down
Loading
Loading