Skip to content

Commit 0177ca4

Browse files
mfachalMegaRedHandfmolettapefontana
authored andcommitted
Signed functions for felts and sqrt (lambdaclass#1150)
* Add arbitrary function * Update tests * Fix: remove arbitrary feature for BigInt That feature is for compatibility with another `Arbitrary` trait * Fix: construction tests shouldn't receive felt * Fix: lower part was capped by PH instead of PL * Impl `Arbitrary` for `FeltBigInt` and `Felt252` * WIP abs * WIP abs proptests * WIP: signed tests don't contradict implementation * signed functions and sqrt * Uncomment + fix sqrt impl * Fix sqrt_is_inv_square test * Remove sqrt implementation from FeltBigInt * Remove sqrt fn from src/math_utils.rs * Restore proptest after merge * Restore merge changes * Remove now useless line from find_element hint * Remove now useless lines from search_sorted_lower hint * Restore line, remove test * Update to_signed_felt * Fix is_positive hint implementation * Clippy * Add changelog entry * Remove pub * Remove redundant check * Restore fmt * Fix sqrt proptest * Fix SquareRoot hint --------- Co-authored-by: Tomás <[email protected]> Co-authored-by: Tomás <[email protected]> Co-authored-by: Federica <[email protected]> Co-authored-by: fmoletta <[email protected]> Co-authored-by: Pedro Fontana <[email protected]>
1 parent 82c4e7f commit 0177ca4

File tree

8 files changed

+62
-163
lines changed

8 files changed

+62
-163
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
#### Upcoming Changes
44

5+
* fix: Fix felt sqrt and Signed impl [#1150](https://github.com/lambdaclass/cairo-rs/pull/1150)
6+
7+
* BREAKING: Fix `Felt252` methods `abs`, `signum`, `is_positive`, `is_negative` and `sqrt`
8+
* BREAKING: Remove function `math_utils::sqrt`(Now moved to `Felt252::sqrt`)
9+
510
* feat: Add method `CairoRunner::initialize_function_runner_cairo_1` [#1151](https://github.com/lambdaclass/cairo-rs/pull/1151)
611

712
* Add method `pub fn initialize_function_runner_cairo_1(

felt/src/bigint_felt.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl FeltOps for FeltBigInt<FIELD_HIGH, FIELD_LOW> {
183183
}
184184

185185
fn to_signed_felt(&self) -> BigInt {
186-
if self.is_negative() {
186+
if self.val > *SIGNED_FELT_MAX {
187187
BigInt::from_biguint(num_bigint::Sign::Minus, &*CAIRO_PRIME_BIGUINT - &self.val)
188188
} else {
189189
self.val.clone().into()
@@ -198,12 +198,6 @@ impl FeltOps for FeltBigInt<FIELD_HIGH, FIELD_LOW> {
198198
self.val.clone()
199199
}
200200

201-
fn sqrt(&self) -> FeltBigInt<FIELD_HIGH, FIELD_LOW> {
202-
FeltBigInt {
203-
val: self.val.sqrt(),
204-
}
205-
}
206-
207201
fn bits(&self) -> u64 {
208202
self.val.bits()
209203
}
@@ -670,11 +664,7 @@ impl Integer for FeltBigInt<FIELD_HIGH, FIELD_LOW> {
670664

671665
impl Signed for FeltBigInt<FIELD_HIGH, FIELD_LOW> {
672666
fn abs(&self) -> Self {
673-
if self.is_negative() {
674-
self.neg()
675-
} else {
676-
self.clone()
677-
}
667+
self.clone()
678668
}
679669

680670
fn abs_sub(&self, other: &Self) -> Self {
@@ -688,15 +678,13 @@ impl Signed for FeltBigInt<FIELD_HIGH, FIELD_LOW> {
688678
fn signum(&self) -> Self {
689679
if self.is_zero() {
690680
FeltBigInt::zero()
691-
} else if self.is_positive() {
692-
FeltBigInt::one()
693681
} else {
694-
FeltBigInt::max_value()
682+
FeltBigInt::one()
695683
}
696684
}
697685

698686
fn is_positive(&self) -> bool {
699-
!self.is_zero() && self.val < *SIGNED_FELT_MAX
687+
!self.is_zero()
700688
}
701689

702690
fn is_negative(&self) -> bool {

felt/src/lib.rs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ pub(crate) trait FeltOps {
9090
/// ```
9191
fn to_biguint(&self) -> BigUint;
9292

93-
fn sqrt(&self) -> Self;
94-
9593
fn bits(&self) -> u64;
9694

9795
fn prime() -> BigUint;
@@ -228,10 +226,38 @@ impl Felt252 {
228226
self.value.to_biguint()
229227
}
230228
pub fn sqrt(&self) -> Self {
231-
Self {
232-
value: self.value.sqrt(),
229+
// Based on Tonelli-Shanks' algorithm for finding square roots
230+
// and sympy's library implementation of said algorithm.
231+
if self.is_zero() || self.is_one() {
232+
return self.clone();
233+
}
234+
235+
let max_felt = Felt252::max_value();
236+
let trailing_prime = Felt252::max_value() >> 192; // 0x800000000000011
237+
238+
let a = self.pow(&trailing_prime);
239+
let d = (&Felt252::new(3_i32)).pow(&trailing_prime);
240+
let mut m = Felt252::zero();
241+
let mut exponent = Felt252::one() << 191_u32;
242+
let mut adm;
243+
for i in 0..192_u32 {
244+
adm = &a * &(&d).pow(&m);
245+
adm = (&adm).pow(&exponent);
246+
exponent >>= 1;
247+
// if adm ≡ -1 (mod CAIRO_PRIME)
248+
if adm == max_felt {
249+
m += Felt252::one() << i;
250+
}
251+
}
252+
let root_1 = self.pow(&((trailing_prime + 1_u32) >> 1)) * (&d).pow(&(m >> 1));
253+
let root_2 = &max_felt - &root_1 + 1_usize;
254+
if root_1 < root_2 {
255+
root_1
256+
} else {
257+
root_2
233258
}
234259
}
260+
235261
pub fn bits(&self) -> u64 {
236262
self.value.bits()
237263
}
@@ -1377,32 +1403,17 @@ mod test {
13771403
prop_assert_eq!(x, &(&one * x));
13781404
}
13791405

1380-
// Signedness has ambiguous meaning and currently there's a mismatch between
1381-
// the implementation and test's interpretations
1382-
// See: https://github.com/lambdaclass/cairo-rs/issues/1076
1383-
// WIP fix: https://github.com/lambdaclass/cairo-rs/pull/1150
13841406
#[test]
1385-
#[ignore]
1386-
fn non_zero_felt_is_always_positive(x in nonzero_felt252()) {
1407+
fn felt_is_always_positive(x in any::<Felt252>()) {
13871408
prop_assert!(x.is_positive())
13881409
}
13891410

1390-
// Signedness has ambiguous meaning and currently there's a mismatch
1391-
// between the implementation and test's interpretations
1392-
// See: https://github.com/lambdaclass/cairo-rs/issues/1076
1393-
// WIP fix: https://github.com/lambdaclass/cairo-rs/pull/1150
13941411
#[test]
1395-
#[ignore]
13961412
fn felt_is_never_negative(x in any::<Felt252>()) {
13971413
prop_assert!(!x.is_negative())
13981414
}
13991415

1400-
// Signedness has ambiguous meaning and currently there's a mismatch between
1401-
// the implementation and test's interpretations
1402-
// See: https://github.com/lambdaclass/cairo-rs/issues/1076
1403-
// WIP fix: https://github.com/lambdaclass/cairo-rs/pull/1150
14041416
#[test]
1405-
#[ignore]
14061417
fn non_zero_felt_signum_is_always_one(ref x in nonzero_felt252()) {
14071418
let one = Felt252::one();
14081419
prop_assert_eq!(x.signum(), one)
@@ -1415,12 +1426,7 @@ mod test {
14151426
prop_assert_eq!(x.abs_sub(&y), expected_abs_sub)
14161427
}
14171428

1418-
// Signedness has ambiguous meaning and currently there's a mismatch
1419-
// between the implementation and test's interpretations
1420-
// See: https://github.com/lambdaclass/cairo-rs/issues/1076
1421-
// WIP fix: https://github.com/lambdaclass/cairo-rs/pull/1150
14221429
#[test]
1423-
#[ignore]
14241430
fn abs(x in any::<Felt252>()) {
14251431
prop_assert_eq!(&x, &x.abs())
14261432
}
@@ -1443,15 +1449,16 @@ mod test {
14431449
prop_assert!(sqrt < p, "{}", sqrt);
14441450
}
14451451

1446-
// There is a known bug in this implementation where the squared root
1447-
// we compute is that of the underlying integer rather than that of the
1448-
// field element.
1449-
// See: https://github.com/lambdaclass/cairo-rs/issues/1076
1450-
// WIP fix: https://github.com/lambdaclass/cairo-rs/pull/1150
14511452
#[test]
1452-
#[ignore]
14531453
fn sqrt_is_inv_square(x in any::<Felt252>()) {
1454-
prop_assert_eq!((&x * &x).sqrt(), x);
1454+
let x_sq = &x * &x;
1455+
let sqrt = x_sq.sqrt();
1456+
1457+
if sqrt != x {
1458+
prop_assert_eq!(Felt252::max_value() - sqrt + 1_usize, x);
1459+
} else {
1460+
prop_assert_eq!(sqrt, x);
1461+
}
14551462
}
14561463

14571464
#[test]

src/hint_processor/builtin_hint_processor/ec_utils.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ use num_bigint::ToBigInt;
1717
use num_traits::{Bounded, Num, One, Pow, ToPrimitive, Zero};
1818
use sha2::{Digest, Sha256};
1919

20-
use crate::math_utils::sqrt;
21-
2220
use super::hint_utils::get_ptr_from_var_name;
2321

2422
#[derive(Debug, PartialEq)]
@@ -207,7 +205,7 @@ lazy_static! {
207205
fn recover_y(x: &BigUint) -> Option<BigUint> {
208206
let y_squared: BigUint = x.modpow(&BigUint::from(3_u32), &CAIRO_PRIME) + ALPHA * x + &*BETA;
209207
if is_quad_residue(&y_squared) {
210-
Some(sqrt(&Felt252::from(y_squared)).to_biguint())
208+
Some(Felt252::from(y_squared).sqrt().to_biguint())
211209
} else {
212210
None
213211
}

src/hint_processor/builtin_hint_processor/find_element_hint.rs

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::stdlib::{collections::HashMap, prelude::*};
2-
32
use crate::{
43
hint_processor::{
54
builtin_hint_processor::hint_utils::{
@@ -14,7 +13,8 @@ use crate::{
1413
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
1514
};
1615
use felt::Felt252;
17-
use num_traits::{Signed, ToPrimitive};
16+
use num_traits::Signed;
17+
use num_traits::ToPrimitive;
1818

1919
pub fn find_element(
2020
vm: &mut VirtualMachine,
@@ -51,10 +51,6 @@ pub fn find_element(
5151
exec_scopes.delete_variable("find_element_index");
5252
Ok(())
5353
} else {
54-
if n_elms.is_negative() {
55-
return Err(HintError::ValueOutOfRange(n_elms.into_owned()));
56-
}
57-
5854
if let Ok(find_element_max_size) = exec_scopes.get_ref::<Felt252>("find_element_max_size") {
5955
if n_elms.as_ref() > find_element_max_size {
6056
return Err(HintError::FindElemMaxSize(
@@ -103,10 +99,6 @@ pub fn search_sorted_lower(
10399
return Err(HintError::ValueOutOfRange(elm_size.into_owned()));
104100
}
105101

106-
if n_elms.is_negative() {
107-
return Err(HintError::ValueOutOfRange(n_elms.into_owned()));
108-
}
109-
110102
if let Ok(find_element_max_size) = find_element_max_size {
111103
if n_elms.as_ref() > &find_element_max_size {
112104
return Err(HintError::FindElemMaxSize(
@@ -340,7 +332,7 @@ mod tests {
340332
)]));
341333
assert_matches!(
342334
run_hint!(vm, ids_data, hint_code::FIND_ELEMENT),
343-
Err(HintError::ValueOutOfRange(x)) if x == Felt252::new(-1)
335+
Err(HintError::Math(MathError::Felt252ToI32Conversion(x))) if x == Felt252::new(-1)
344336
);
345337
}
346338

@@ -427,19 +419,6 @@ mod tests {
427419
);
428420
}
429421

430-
#[test]
431-
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
432-
fn search_sorted_lower_negative_elm_size() {
433-
let (mut vm, ids_data) = init_vm_ids_data(HashMap::from([(
434-
"elm_size".to_string(),
435-
MaybeRelocatable::Int(Felt252::new(-1)),
436-
)]));
437-
assert_matches!(
438-
run_hint!(vm, ids_data, hint_code::SEARCH_SORTED_LOWER),
439-
Err(HintError::ValueOutOfRange(x)) if x == Felt252::new(-1)
440-
);
441-
}
442-
443422
#[test]
444423
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
445424
fn search_sorted_lower_not_int_n_elms() {
@@ -454,19 +433,6 @@ mod tests {
454433
);
455434
}
456435

457-
#[test]
458-
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
459-
fn search_sorted_lower_negative_n_elms() {
460-
let (mut vm, ids_data) = init_vm_ids_data(HashMap::from([(
461-
"n_elms".to_string(),
462-
MaybeRelocatable::Int(Felt252::new(-1)),
463-
)]));
464-
assert_matches!(
465-
run_hint!(vm, ids_data, hint_code::SEARCH_SORTED_LOWER),
466-
Err(HintError::ValueOutOfRange(x)) if x == Felt252::new(-1)
467-
);
468-
}
469-
470436
#[test]
471437
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
472438
fn search_sorted_lower_empty_scope() {

src/hint_processor/builtin_hint_processor/math_utils.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -374,20 +374,17 @@ pub fn is_positive(
374374
ap_tracking: &ApTracking,
375375
) -> Result<(), HintError> {
376376
let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?;
377+
let value_as_int = value.to_signed_felt();
377378
let range_check_builtin = vm.get_range_check_builtin()?;
378379
//Main logic (assert a is positive)
379380
match &range_check_builtin._bound {
380-
Some(bound) if &value.abs() > bound => {
381+
Some(bound) if value_as_int.abs() > bound.to_bigint() => {
381382
return Err(HintError::ValueOutsideValidRange(value.into_owned()))
382383
}
383384
_ => {}
384385
};
385386

386-
let result = if value.is_positive() {
387-
Felt252::one()
388-
} else {
389-
Felt252::zero()
390-
};
387+
let result = Felt252::from(value_as_int.is_positive() as u8);
391388
insert_value_from_var_name("is_positive", result, vm, ids_data, ap_tracking)
392389
}
393390

@@ -665,11 +662,11 @@ pub fn is_quad_residue(
665662
if x.is_zero() || x.is_one() {
666663
insert_value_from_var_name("y", x.as_ref().clone(), vm, ids_data, ap_tracking)
667664
} else if Pow::pow(x.as_ref(), &(Felt252::max_value() >> 1)).is_one() {
668-
insert_value_from_var_name("y", crate::math_utils::sqrt(&x), vm, ids_data, ap_tracking)
665+
insert_value_from_var_name("y", &x.sqrt(), vm, ids_data, ap_tracking)
669666
} else {
670667
insert_value_from_var_name(
671668
"y",
672-
crate::math_utils::sqrt(&(x.as_ref() / Felt252::new(3_i32))),
669+
(x.as_ref() / Felt252::new(3_i32)).sqrt(),
673670
vm,
674671
ids_data,
675672
ap_tracking,
@@ -2357,9 +2354,9 @@ mod tests {
23572354
if x.is_zero() || x.is_one() {
23582355
assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().as_ref(), x);
23592356
} else if x.pow(&(Felt252::max_value() >> 1)).is_one() {
2360-
assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), crate::math_utils::sqrt(x));
2357+
assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), x.sqrt());
23612358
} else {
2362-
assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), crate::math_utils::sqrt(&(x / Felt252::new(3))));
2359+
assert_eq!(vm.get_integer(Relocatable::from((1, 0))).unwrap().into_owned(), (x / Felt252::new(3)).sqrt());
23632360
}
23642361
}
23652362
}

src/hint_processor/cairo_1_hint_processor/hint_processor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,9 @@ impl Cairo1HintProcessor {
263263
value: &ResOperand,
264264
dst: &CellRef,
265265
) -> Result<(), HintError> {
266-
let value = res_operand_get_val(vm, value)?;
266+
let value = res_operand_get_val(vm, value)?.to_biguint();
267267
let result = value.sqrt();
268-
vm.insert_value(cell_ref_to_relocatable(dst, vm)?, result)
268+
vm.insert_value(cell_ref_to_relocatable(dst, vm)?, Felt252::from(result))
269269
.map_err(HintError::from)
270270
}
271271

0 commit comments

Comments
 (0)