use crate::Felt252;
use num_bigint::{BigUint, ToBigInt};
use num_integer::Integer;
use num_traits::Zero;
use super::hint_utils::insert_value_from_var_name;
use super::secp::bigint_utils::Uint384;
use super::uint256_utils::Uint256;
use crate::math_utils::{is_quad_residue, mul_inv, sqrt_prime_power};
use crate::serde::deserialize_program::ApTracking;
use crate::types::errors::math_errors::MathError;
use crate::vm::errors::hint_errors::HintError;
use crate::{
hint_processor::hint_processor_definition::HintReference, vm::vm_core::VirtualMachine,
};
use std::collections::HashMap;
pub fn u384_get_square_root(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let generator = Uint384::from_var_name("generator", vm, ids_data, ap_tracking)?.pack();
let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack();
let p = Uint384::from_var_name("p", vm, ids_data, ap_tracking)?.pack();
let success_x = is_quad_residue(&x, &p)?;
let root_x = if success_x {
sqrt_prime_power(&x, &p).unwrap_or_default()
} else {
BigUint::zero()
};
let gx = generator * &x;
let success_gx = is_quad_residue(&gx, &p)?;
let root_gx = if success_gx {
sqrt_prime_power(&gx, &p).unwrap_or_default()
} else {
BigUint::zero()
};
if !&x.is_zero() && !(success_x ^ success_gx) {
return Err(HintError::AssertionFailed(
"assert success_x + success_gx ==1"
.to_string()
.into_boxed_str(),
));
}
insert_value_from_var_name(
"success_x",
Felt252::from(success_x as u8),
vm,
ids_data,
ap_tracking,
)?;
insert_value_from_var_name(
"success_gx",
Felt252::from(success_gx as u8),
vm,
ids_data,
ap_tracking,
)?;
Uint384::split(&root_x).insert_from_var_name("sqrt_x", vm, ids_data, ap_tracking)?;
Uint384::split(&root_gx).insert_from_var_name("sqrt_gx", vm, ids_data, ap_tracking)?;
Ok(())
}
pub fn u256_get_square_root(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let generator = Uint256::from_var_name("generator", vm, ids_data, ap_tracking)?.pack();
let x = Uint256::from_var_name("x", vm, ids_data, ap_tracking)?.pack();
let p = Uint256::from_var_name("p", vm, ids_data, ap_tracking)?.pack();
let success_x = is_quad_residue(&x, &p)?;
let root_x = if success_x {
sqrt_prime_power(&x, &p).unwrap_or_default()
} else {
BigUint::zero()
};
let gx = generator * &x;
let success_gx = is_quad_residue(&gx, &p)?;
let root_gx = if success_gx {
sqrt_prime_power(&gx, &p).unwrap_or_default()
} else {
BigUint::zero()
};
if !&x.is_zero() && !(success_x ^ success_gx) {
return Err(HintError::AssertionFailed(
"assert success_x + success_gx ==1"
.to_string()
.into_boxed_str(),
));
}
insert_value_from_var_name(
"success_x",
Felt252::from(success_x as u8),
vm,
ids_data,
ap_tracking,
)?;
insert_value_from_var_name(
"success_gx",
Felt252::from(success_gx as u8),
vm,
ids_data,
ap_tracking,
)?;
Uint256::split(&root_x).insert_from_var_name("sqrt_x", vm, ids_data, ap_tracking)?;
Uint256::split(&root_gx).insert_from_var_name("sqrt_gx", vm, ids_data, ap_tracking)?;
Ok(())
}
pub fn uint384_div(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let b = Uint384::from_var_name("b", vm, ids_data, ap_tracking)?
.pack()
.to_bigint()
.unwrap_or_default();
let p = Uint384::from_var_name("p", vm, ids_data, ap_tracking)?
.pack()
.to_bigint()
.unwrap_or_default();
if b.is_zero() {
return Err(MathError::DividedByZero.into());
}
let b_inverse_mod_p = mul_inv(&b, &p)
.mod_floor(&p)
.to_biguint()
.unwrap_or_default();
let b_inverse_mod_p_split = Uint384::split(&b_inverse_mod_p);
b_inverse_mod_p_split.insert_from_var_name("b_inverse_mod_p", vm, ids_data, ap_tracking)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hint_processor::builtin_hint_processor::hint_code;
use crate::vm::errors::memory_errors::MemoryError;
use crate::{
any_box,
hint_processor::{
builtin_hint_processor::builtin_hint_processor_definition::{
BuiltinHintProcessor, HintProcessorData,
},
hint_processor_definition::HintProcessorLogic,
},
utils::test_utils::*,
vm::vm_core::VirtualMachine,
};
use assert_matches::assert_matches;
#[test]
fn run_u384_get_square_ok_goldilocks_prime() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2)
];
vm.segments = segments![
((1, 0), 18446744069414584321),
((1, 1), 0),
((1, 2), 0),
((1, 3), 25),
((1, 4), 0),
((1, 5), 0),
((1, 6), 7),
((1, 7), 0),
((1, 8), 0)
];
assert_matches!(
run_hint!(vm, ids_data, hint_code::UINT384_GET_SQUARE_ROOT),
Ok(())
);
check_memory![
vm.segments.memory,
((1, 9), 5),
((1, 10), 0),
((1, 11), 0),
((1, 12), 0),
((1, 13), 0),
((1, 14), 0),
((1, 15), 1),
((1, 16), 0)
];
}
#[test]
fn run_u384_get_square_no_successes() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2)
];
vm.segments = segments![
((1, 0), 3),
((1, 1), 0),
((1, 2), 0),
((1, 3), 17),
((1, 4), 0),
((1, 5), 0),
((1, 6), 1),
((1, 7), 0),
((1, 8), 0)
];
assert_matches!(run_hint!(vm, ids_data, hint_code::UINT384_GET_SQUARE_ROOT),
Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert success_x + success_gx ==1"
);
}
#[test]
fn run_u384_get_square_ok_success_gx() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2),
];
vm.segments = segments![
((1, 0), 3),
((1, 1), 0),
((1, 2), 0),
((1, 3), 17),
((1, 4), 0),
((1, 5), 0),
((1, 6), 71),
((1, 7), 0),
((1, 8), 0),
];
assert_matches!(
run_hint!(vm, ids_data, hint_code::UINT384_GET_SQUARE_ROOT),
Ok(())
);
check_memory![
vm.segments.memory,
((1, 9), 0),
((1, 10), 0),
((1, 11), 0),
((1, 12), 1),
((1, 13), 0),
((1, 14), 0),
((1, 15), 0),
((1, 16), 1),
];
}
#[test]
fn run_u256_get_square_ok_goldilocks_prime() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2),
];
vm.segments = segments![
((1, 0), 18446744069414584321),
((1, 1), 0),
((1, 3), 25),
((1, 4), 0),
((1, 6), 7),
((1, 7), 0),
];
assert!(run_hint!(vm, ids_data, hint_code::UINT256_GET_SQUARE_ROOT).is_ok());
check_memory![
vm.segments.memory,
((1, 9), 5),
((1, 10), 0),
((1, 12), 0),
((1, 13), 0),
((1, 15), 1),
((1, 16), 0),
];
}
#[test]
fn run_u256_get_square_no_successes() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2),
];
vm.segments = segments![
((1, 0), 3),
((1, 1), 0),
((1, 3), 17),
((1, 4), 0),
((1, 6), 1),
((1, 7), 0),
];
assert_matches!(run_hint!(vm, ids_data, hint_code::UINT256_GET_SQUARE_ROOT),
Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert success_x + success_gx ==1"
);
}
#[test]
fn run_u256_get_square_ok_success_gx() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 14;
let ids_data = non_continuous_ids_data![
("p", -14),
("x", -11),
("generator", -8),
("sqrt_x", -5),
("sqrt_gx", -2),
("success_x", 1),
("success_gx", 2),
];
vm.segments = segments![
((1, 0), 3),
((1, 1), 0),
((1, 3), 17),
((1, 4), 0),
((1, 6), 71),
((1, 7), 0),
];
assert!(run_hint!(vm, ids_data, hint_code::UINT256_GET_SQUARE_ROOT).is_ok());
check_memory![
vm.segments.memory,
((1, 9), 0),
((1, 10), 0),
((1, 12), 1),
((1, 13), 0),
((1, 15), 0),
((1, 16), 1),
];
}
#[test]
fn run_uint384_div_ok() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 11;
let ids_data =
non_continuous_ids_data![("a", -11), ("b", -8), ("p", -5), ("b_inverse_mod_p", -2)];
vm.segments = segments![
((1, 0), 25),
((1, 1), 0),
((1, 2), 0),
((1, 3), 5),
((1, 4), 0),
((1, 5), 0),
((1, 6), 31),
((1, 7), 0),
((1, 8), 0)
];
assert_matches!(run_hint!(vm, ids_data, hint_code::UINT384_DIV), Ok(()));
check_memory![
vm.segments.memory,
((1, 9), 25),
((1, 10), 0),
((1, 11), 0)
];
}
#[test]
fn run_uint384_div_b_is_zero() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 11;
let ids_data =
non_continuous_ids_data![("a", -11), ("b", -8), ("p", -5), ("b_inverse_mod_p", -2)];
vm.segments = segments![
((1, 0), 25),
((1, 1), 0),
((1, 2), 0),
((1, 3), 0),
((1, 4), 0),
((1, 5), 0),
((1, 6), 31),
((1, 7), 0),
((1, 8), 0)
];
assert_matches!(
run_hint!(vm, ids_data, hint_code::UINT384_DIV),
Err(HintError::Math(MathError::DividedByZero))
);
}
#[test]
fn run_uint384_div_inconsistent_memory() {
let mut vm = vm_with_range_check!();
vm.run_context.fp = 11;
let ids_data =
non_continuous_ids_data![("a", -11), ("b", -8), ("p", -5), ("b_inverse_mod_p", -2)];
vm.segments = segments![
((1, 0), 25),
((1, 1), 0),
((1, 2), 0),
((1, 3), 5),
((1, 4), 0),
((1, 5), 0),
((1, 6), 31),
((1, 7), 0),
((1, 8), 0),
((1, 9), 0)
];
assert_matches!(
run_hint!(vm, ids_data, hint_code::UINT384_DIV),
Err(HintError::Memory(MemoryError::InconsistentMemory(_)))
);
}
}