Skip to content

Commit cd358d1

Browse files
MegaRedHandOppen
authored andcommitted
feat(hints): add NewHint#13 (lambdaclass#1006)
* Add NewHint#13 * Update changelog * Add test for when m is too high * Restrict the guard clause in quad_bit * Simplify match with unwrap_or Feels dirty! Co-authored-by: Mario Rugiero <[email protected]> * Fix build errors --------- Co-authored-by: Mario Rugiero <[email protected]>
1 parent e09e71e commit cd358d1

File tree

6 files changed

+161
-7
lines changed

6 files changed

+161
-7
lines changed

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@
7979
* Added dynamic layout [#879](https://github.com/lambdaclass/cairo-rs/pull/879)
8080
* `get_segment_size` was exposed [#934](https://github.com/lambdaclass/cairo-rs/pull/934)
8181

82+
* Add missing hint on cairo_secp lib [#1006](https://github.com/lambdaclass/cairo-rs/pull/1006):
83+
84+
`BuiltinHintProcessor` now supports the following hint:
85+
86+
```python
87+
ids.quad_bit = (
88+
8 * ((ids.scalar_v >> ids.m) & 1)
89+
+ 4 * ((ids.scalar_u >> ids.m) & 1)
90+
+ 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
91+
+ ((ids.scalar_u >> (ids.m - 1)) & 1)
92+
)
93+
```
94+
8295
* Add missing hint on cairo_secp lib [#1003](https://github.com/lambdaclass/cairo-rs/pull/1003):
8396

8497
`BuiltinHintProcessor` now supports the following hint:
@@ -98,9 +111,10 @@
98111
value = x_inv = div_mod(1, x, SECP_P)
99112
```
100113

101-
* Add missing hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994)::
114+
* Add missing hints on cairo_secp lib [#994](https://github.com/lambdaclass/cairo-rs/pull/994):
102115

103116
`BuiltinHintProcessor` now supports the following hints:
117+
104118
```python
105119
from starkware.cairo.common.cairo_secp.secp_utils import pack
106120
from starkware.python.math_utils import div_mod, safe_div

cairo_programs/quad_bit.cairo

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
%builtins range_check
2+
3+
func get_quad_bit{range_check_ptr}(scalar_u: felt, scalar_v: felt, m: felt) -> felt {
4+
alloc_locals;
5+
local quad_bit: felt;
6+
%{
7+
ids.quad_bit = (
8+
8 * ((ids.scalar_v >> ids.m) & 1)
9+
+ 4 * ((ids.scalar_u >> ids.m) & 1)
10+
+ 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
11+
+ ((ids.scalar_u >> (ids.m - 1)) & 1)
12+
)
13+
%}
14+
return quad_bit;
15+
}
16+
17+
func test_quad_bit{range_check_ptr}() {
18+
let u = 4194304; // 1 << 22
19+
let v = 8388608; // 1 << 23
20+
21+
// 8 * 1 + 4 * 0 + 2 * 0 + 1 * 1
22+
assert get_quad_bit(u, v, 23) = 9;
23+
24+
return ();
25+
}
26+
27+
func main{range_check_ptr}() {
28+
test_quad_bit();
29+
30+
return ();
31+
}

src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
ec_utils::{
3232
compute_doubling_slope, compute_slope, ec_double_assign_new_x,
3333
ec_double_assign_new_y, ec_mul_inner, ec_negate, fast_ec_add_assign_new_x,
34-
fast_ec_add_assign_new_y,
34+
fast_ec_add_assign_new_y, quad_bit,
3535
},
3636
field_utils::{
3737
is_zero_assign_scope_variables, is_zero_assign_scope_variables_external_const,
@@ -537,6 +537,7 @@ impl HintProcessor for BuiltinHintProcessor {
537537
hint_code::UINT256_MUL_DIV_MOD => {
538538
uint256_mul_div_mod(vm, &hint_data.ids_data, &hint_data.ap_tracking)
539539
}
540+
hint_code::QUAD_BIT => quad_bit(vm, &hint_data.ids_data, &hint_data.ap_tracking),
540541
#[cfg(feature = "skip_next_instruction_hint")]
541542
hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm),
542543
code => Err(HintError::UnknownHint(code.to_string())),

src/hint_processor/builtin_hint_processor/hint_code.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,5 +819,12 @@ ids.remainder.d1 = remainder_split[1]
819819
ids.remainder.d2 = remainder_split[2]";
820820
pub const UINT384_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0";
821821

822+
pub const QUAD_BIT: &str = r#"ids.quad_bit = (
823+
8 * ((ids.scalar_v >> ids.m) & 1)
824+
+ 4 * ((ids.scalar_u >> ids.m) & 1)
825+
+ 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
826+
+ ((ids.scalar_u >> (ids.m - 1)) & 1)
827+
)"#;
828+
822829
#[cfg(feature = "skip_next_instruction_hint")]
823830
pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()";

src/hint_processor/builtin_hint_processor/secp/ec_utils.rs

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ use crate::{
22
hint_processor::{
33
builtin_hint_processor::{
44
hint_utils::{
5-
get_integer_from_var_name, get_relocatable_from_var_name, insert_value_into_ap,
5+
get_integer_from_var_name, get_relocatable_from_var_name,
6+
insert_value_from_var_name, insert_value_into_ap,
7+
},
8+
secp::{
9+
bigint_utils::BigInt3,
10+
secp_utils::{pack, SECP_P},
611
},
7-
secp::secp_utils::pack,
812
},
913
hint_processor_definition::HintReference,
1014
},
@@ -17,9 +21,7 @@ use crate::{
1721
use felt::Felt252;
1822
use num_bigint::BigInt;
1923
use num_integer::Integer;
20-
use num_traits::{One, Zero};
21-
22-
use super::{bigint_utils::BigInt3, secp_utils::SECP_P};
24+
use num_traits::{One, ToPrimitive, Zero};
2325

2426
#[derive(Debug, PartialEq)]
2527
struct EcPoint<'a> {
@@ -271,6 +273,51 @@ pub fn ec_mul_inner(
271273
insert_value_into_ap(vm, scalar)
272274
}
273275

276+
/*
277+
Implements hint:
278+
%{
279+
ids.quad_bit = (
280+
8 * ((ids.scalar_v >> ids.m) & 1)
281+
+ 4 * ((ids.scalar_u >> ids.m) & 1)
282+
+ 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
283+
+ ((ids.scalar_u >> (ids.m - 1)) & 1)
284+
)
285+
%}
286+
*/
287+
pub fn quad_bit(
288+
vm: &mut VirtualMachine,
289+
ids_data: &HashMap<String, HintReference>,
290+
ap_tracking: &ApTracking,
291+
) -> Result<(), HintError> {
292+
let scalar_v_cow = get_integer_from_var_name("scalar_v", vm, ids_data, ap_tracking)?;
293+
let scalar_u_cow = get_integer_from_var_name("scalar_u", vm, ids_data, ap_tracking)?;
294+
let m_cow = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?;
295+
296+
let scalar_v = scalar_v_cow.as_ref();
297+
let scalar_u = scalar_u_cow.as_ref();
298+
299+
// If m is too high the shift result will always be zero
300+
let m = m_cow.as_ref().to_u32().unwrap_or(253);
301+
if m >= 253 {
302+
return insert_value_from_var_name("quad_bit", 0, vm, ids_data, ap_tracking);
303+
}
304+
305+
let one = &Felt252::one();
306+
307+
// 8 * ((ids.scalar_v >> ids.m) & 1)
308+
let quad_bit_3 = ((scalar_v >> m) & one) << 3u32;
309+
// 4 * ((ids.scalar_u >> ids.m) & 1)
310+
let quad_bit_2 = ((scalar_u >> m) & one) << 2u32;
311+
// 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
312+
let quad_bit_1 = ((scalar_v >> (m - 1)) & one) << 1u32;
313+
// 1 * ((ids.scalar_u >> (ids.m - 1)) & 1)
314+
let quad_bit_0 = (scalar_u >> (m - 1)) & one;
315+
316+
let res = quad_bit_0 + quad_bit_1 + quad_bit_2 + quad_bit_3;
317+
318+
insert_value_from_var_name("quad_bit", res, vm, ids_data, ap_tracking)
319+
}
320+
274321
#[cfg(test)]
275322
mod tests {
276323
use super::*;
@@ -808,4 +855,51 @@ mod tests {
808855
let r = EcPoint::from_var_name("e", &vm, &ids_data, &ap_tracking);
809856
assert_matches!(r, Err(HintError::UnknownIdentifier(x)) if x == "e")
810857
}
858+
859+
#[test]
860+
fn run_quad_bit_ok() {
861+
let hint_code = "ids.quad_bit = (\n 8 * ((ids.scalar_v >> ids.m) & 1)\n + 4 * ((ids.scalar_u >> ids.m) & 1)\n + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)\n + ((ids.scalar_u >> (ids.m - 1)) & 1)\n)";
862+
let mut vm = vm_with_range_check!();
863+
864+
let scalar_u = 89712;
865+
let scalar_v = 1478396;
866+
let m = 4;
867+
// Insert ids.scalar into memory
868+
vm.segments = segments![((1, 0), scalar_u), ((1, 1), scalar_v), ((1, 2), m)];
869+
870+
// Initialize RunContext
871+
run_context!(vm, 0, 4, 4);
872+
873+
let ids_data = ids_data!["scalar_u", "scalar_v", "m", "quad_bit"];
874+
875+
// Execute the hint
876+
assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
877+
878+
// Check hint memory inserts
879+
check_memory![vm.segments.memory, ((1, 3), 14)];
880+
}
881+
882+
#[test]
883+
fn run_quad_bit_with_max_m_ok() {
884+
let hint_code = "ids.quad_bit = (\n 8 * ((ids.scalar_v >> ids.m) & 1)\n + 4 * ((ids.scalar_u >> ids.m) & 1)\n + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)\n + ((ids.scalar_u >> (ids.m - 1)) & 1)\n)";
885+
let mut vm = vm_with_range_check!();
886+
887+
let scalar_u = 89712;
888+
let scalar_v = 1478396;
889+
// Value is so high the result will always be zero
890+
let m = i128::MAX;
891+
// Insert ids.scalar into memory
892+
vm.segments = segments![((1, 0), scalar_u), ((1, 1), scalar_v), ((1, 2), m)];
893+
894+
// Initialize RunContext
895+
run_context!(vm, 0, 4, 4);
896+
897+
let ids_data = ids_data!["scalar_u", "scalar_v", "m", "quad_bit"];
898+
899+
// Execute the hint
900+
assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
901+
902+
// Check hint memory inserts
903+
check_memory![vm.segments.memory, ((1, 3), 0)];
904+
}
811905
}

src/tests/cairo_run_test.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,3 +1350,10 @@ fn cairo_run_is_zero_pack() {
13501350
let program_data = include_bytes!("../../cairo_programs/is_zero_pack.json");
13511351
run_program_simple(program_data.as_slice());
13521352
}
1353+
1354+
#[test]
1355+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1356+
fn cairo_run_quad_bit() {
1357+
let program_data = include_bytes!("../../cairo_programs/quad_bit.json");
1358+
run_program_simple(program_data.as_slice());
1359+
}

0 commit comments

Comments
 (0)