use std::any::Any;
use std::cmp::min;
use std::collections::HashMap;
use std::path::PathBuf;
use super::types::Task;
use super::PROGRAM_INPUT;
use crate::hints::fact_topologies::GPS_FACT_TOPOLOGY;
use crate::hints::types::ProgramIdentifiers;
use crate::utils::ProgramInput;
use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::get_ptr_from_var_name;
use cairo_vm::hint_processor::hint_processor_definition::HintReference;
use cairo_vm::serde::deserialize_program::{ApTracking, Identifier};
use cairo_vm::types::exec_scope::ExecutionScopes;
use cairo_vm::types::program::Program;
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_vm::vm::errors::hint_errors::HintError;
use cairo_vm::vm::errors::memory_errors::MemoryError;
use cairo_vm::vm::runners::builtin_runner::OutputBuiltinRunner;
use cairo_vm::vm::runners::cairo_pie::StrippedProgram;
use cairo_vm::vm::vm_core::VirtualMachine;
use serde::de::DeserializeOwned;
#[macro_export]
macro_rules! maybe_relocatable_box {
($val:expr) => {
Box::new(MaybeRelocatable::from($val)) as Box<dyn Any>
};
}
pub fn get_program_identifies(
exec_scopes: &ExecutionScopes,
program: &str,
) -> Result<ProgramIdentifiers, HintError> {
if let Ok(program) = exec_scopes.get::<Program>(program) {
return Ok(program
.iter_identifiers()
.map(|(k, v)| (k.to_string(), v.clone()))
.collect());
}
Err(HintError::VariableNotInScopeError(
program.to_string().into_boxed_str(),
))
}
fn parse_program_input_from_str<T: DeserializeOwned>(json: &str) -> Result<T, HintError> {
serde_json::from_str(json).map_err(|e| {
HintError::CustomHint(format!("Failed to parse program input JSON: {e}").into())
})
}
fn parse_program_input_from_path<T: DeserializeOwned>(path: &PathBuf) -> Result<T, HintError> {
let json = std::fs::read_to_string(path).map_err(|e| {
HintError::CustomHint(format!("Failed to read program input from {path:?}: {e}").into())
})?;
parse_program_input_from_str(&json)
}
pub fn get_program_input_value<T>(exec_scopes: &ExecutionScopes) -> Result<T, HintError>
where
T: DeserializeOwned + Clone + 'static,
{
let program_input = exec_scopes
.get_ref::<ProgramInput>(PROGRAM_INPUT)
.map_err(|_| {
HintError::CustomHint("Program input was not found in execution scopes.".into())
})?;
match program_input {
ProgramInput::Json(json) => parse_program_input_from_str(json),
ProgramInput::Path(path) => parse_program_input_from_path(path),
ProgramInput::Value(value) => {
if let Some(typed) = value.downcast_ref::<T>() {
return Ok(typed.clone());
}
Err(HintError::CustomHint(
"Program input value has unsupported in-memory type.".into(),
))
}
}
}
pub fn get_identifier(
identifiers: &HashMap<String, Identifier>,
name: &str,
) -> Result<usize, HintError> {
if let Some(identifier) = identifiers.get(name) {
if let Some(pc) = identifier.pc {
return Ok(pc);
}
}
Err(HintError::VariableNotInScopeError(
name.to_string().into_boxed_str(),
))
}
pub fn gen_arg(
vm: &mut VirtualMachine,
args: &Vec<Box<dyn Any>>,
) -> Result<Relocatable, MemoryError> {
let base = vm.segments.add();
let mut ptr = base;
for arg in args {
if let Some(value) = arg.downcast_ref::<MaybeRelocatable>() {
ptr = vm.segments.load_data(ptr, std::slice::from_ref(value))?;
} else if let Some(vector) = arg.downcast_ref::<Vec<Box<dyn Any>>>() {
let nested_base = gen_arg(vm, vector)?;
ptr = vm.segments.load_data(ptr, &[nested_base.into()])?;
} else {
return Err(MemoryError::GenArgInvalidType);
}
}
Ok(base)
}
pub fn get_program_from_task(task: &Task) -> Result<StrippedProgram, HintError> {
task.get_program()
.map_err(|e| HintError::CustomHint(e.to_string().into_boxed_str()))
}
pub fn split_outputs_to_pages(
output_start: Relocatable,
output_ptr: Relocatable,
output_builtin: &mut OutputBuiltinRunner,
page_size: usize,
) -> Result<usize, HintError> {
let mut next_page_start = min((output_start + page_size)?, output_ptr);
let mut next_page_id = 1;
while next_page_start < output_ptr {
let current_page_size = min(output_ptr.offset - next_page_start.offset, page_size);
output_builtin
.add_page(next_page_id, next_page_start, current_page_size)
.map_err(|e| {
HintError::CustomHint(format!("Failed to add page to output builtin: {e:?}").into())
})?;
next_page_start = (next_page_start + page_size)?;
next_page_id += 1;
}
Ok(next_page_id)
}
pub fn add_fact_topology(output_builtin: &mut OutputBuiltinRunner, n_pages: usize) {
if n_pages == 1 {
output_builtin.add_attribute(GPS_FACT_TOPOLOGY.into(), [1, 0].to_vec());
} else {
output_builtin.add_attribute(
GPS_FACT_TOPOLOGY.into(),
[n_pages, n_pages - 1, 0, 2].to_vec(),
);
}
}
pub fn output_builtin_set_pages_by_size_and_fact_topology(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
page_size: usize,
) -> Result<(), HintError> {
let output_start = get_ptr_from_var_name("output_start", vm, ids_data, ap_tracking)?;
let output_ptr = get_ptr_from_var_name("output_ptr", vm, ids_data, ap_tracking)?;
let output_builtin = vm.get_output_builtin_mut()?;
let n_pages = split_outputs_to_pages(output_start, output_ptr, output_builtin, page_size)?;
add_fact_topology(output_builtin, n_pages);
Ok(())
}