use super::super::{AdviceSource, ExecutionError, Felt, HostResponse};
use crate::{AdviceProvider, Ext2InttError, FieldElement, ProcessState, ZERO};
use alloc::vec::Vec;
use vm_core::{QuadExtension, SignatureKind};
use winter_prover::math::fft;
type QuadFelt = QuadExtension<Felt>;
pub(crate) fn copy_merkle_node_to_adv_stack<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
let depth = process.get_stack_item(0);
let index = process.get_stack_item(1);
let root = [
process.get_stack_item(5),
process.get_stack_item(4),
process.get_stack_item(3),
process.get_stack_item(2),
];
let node = advice_provider.get_tree_node(root, &depth, &index)?;
advice_provider.push_stack(AdviceSource::Value(node[3]))?;
advice_provider.push_stack(AdviceSource::Value(node[2]))?;
advice_provider.push_stack(AdviceSource::Value(node[1]))?;
advice_provider.push_stack(AdviceSource::Value(node[0]))?;
Ok(HostResponse::None)
}
pub(crate) fn copy_map_value_to_adv_stack<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
include_len: bool,
key_offset: usize,
) -> Result<HostResponse, ExecutionError> {
if key_offset > 12 {
return Err(ExecutionError::InvalidStackWordOffset(key_offset));
}
let key = [
process.get_stack_item(key_offset + 3),
process.get_stack_item(key_offset + 2),
process.get_stack_item(key_offset + 1),
process.get_stack_item(key_offset),
];
advice_provider.push_stack(AdviceSource::Map { key, include_len })?;
Ok(HostResponse::None)
}
pub(crate) fn push_u64_div_result<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
let divisor_hi = process.get_stack_item(0).as_int();
let divisor_lo = process.get_stack_item(1).as_int();
let divisor = (divisor_hi << 32) + divisor_lo;
if divisor == 0 {
return Err(ExecutionError::DivideByZero(process.clk()));
}
let dividend_hi = process.get_stack_item(2).as_int();
let dividend_lo = process.get_stack_item(3).as_int();
let dividend = (dividend_hi << 32) + dividend_lo;
let quotient = dividend / divisor;
let remainder = dividend - quotient * divisor;
let (q_hi, q_lo) = u64_to_u32_elements(quotient);
let (r_hi, r_lo) = u64_to_u32_elements(remainder);
advice_provider.push_stack(AdviceSource::Value(r_hi))?;
advice_provider.push_stack(AdviceSource::Value(r_lo))?;
advice_provider.push_stack(AdviceSource::Value(q_hi))?;
advice_provider.push_stack(AdviceSource::Value(q_lo))?;
Ok(HostResponse::None)
}
pub(crate) fn push_ext2_inv_result<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
let coef0 = process.get_stack_item(1);
let coef1 = process.get_stack_item(0);
let element = QuadFelt::new(coef0, coef1);
if element == QuadFelt::ZERO {
return Err(ExecutionError::DivideByZero(process.clk()));
}
let result = element.inv().to_base_elements();
advice_provider.push_stack(AdviceSource::Value(result[1]))?;
advice_provider.push_stack(AdviceSource::Value(result[0]))?;
Ok(HostResponse::None)
}
pub(crate) fn push_ext2_intt_result<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
let output_size = process.get_stack_item(0).as_int() as usize;
let input_size = process.get_stack_item(1).as_int() as usize;
let input_start_ptr = process.get_stack_item(2).as_int();
if input_size <= 1 {
return Err(Ext2InttError::DomainSizeTooSmall(input_size as u64).into());
}
if !input_size.is_power_of_two() {
return Err(Ext2InttError::DomainSizeNotPowerOf2(input_size as u64).into());
}
if input_start_ptr >= u32::MAX as u64 {
return Err(Ext2InttError::InputStartAddressTooBig(input_start_ptr).into());
}
if input_size > u32::MAX as usize {
return Err(Ext2InttError::InputSizeTooBig(input_size as u64).into());
}
let input_end_ptr = input_start_ptr + (input_size / 2) as u64;
if input_end_ptr > u32::MAX as u64 {
return Err(Ext2InttError::InputEndAddressTooBig(input_end_ptr).into());
}
if output_size == 0 {
return Err(Ext2InttError::OutputSizeIsZero.into());
}
if output_size > input_size {
return Err(Ext2InttError::OutputSizeTooBig(output_size, input_size).into());
}
let mut poly = Vec::with_capacity(input_size);
for addr in (input_start_ptr as u32)..(input_end_ptr as u32) {
let word = process
.get_mem_value(process.ctx(), addr)
.ok_or(Ext2InttError::UninitializedMemoryAddress(addr))?;
poly.push(QuadFelt::new(word[0], word[1]));
poly.push(QuadFelt::new(word[2], word[3]));
}
let twiddles = fft::get_inv_twiddles::<Felt>(input_size);
fft::interpolate_poly::<Felt, QuadFelt>(&mut poly, &twiddles);
for element in QuadFelt::slice_as_base_elements(&poly[..output_size]).iter().rev() {
advice_provider.push_stack(AdviceSource::Value(*element))?;
}
Ok(HostResponse::None)
}
pub(crate) fn push_signature<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
kind: SignatureKind,
) -> Result<HostResponse, ExecutionError> {
let pub_key = process.get_stack_word(0);
let msg = process.get_stack_word(1);
let result: Vec<Felt> = advice_provider.get_signature(kind, pub_key, msg)?;
for r in result {
advice_provider.push_stack(AdviceSource::Value(r))?;
}
Ok(HostResponse::None)
}
pub(crate) fn push_leading_zeros<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
push_transformed_stack_top(advice_provider, process, |stack_top| {
Felt::from(stack_top.leading_zeros())
})
}
pub(crate) fn push_trailing_zeros<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
push_transformed_stack_top(advice_provider, process, |stack_top| {
Felt::from(stack_top.trailing_zeros())
})
}
pub(crate) fn push_leading_ones<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
push_transformed_stack_top(advice_provider, process, |stack_top| {
Felt::from(stack_top.leading_ones())
})
}
pub(crate) fn push_trailing_ones<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
push_transformed_stack_top(advice_provider, process, |stack_top| {
Felt::from(stack_top.trailing_ones())
})
}
pub(crate) fn push_ilog2<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
) -> Result<HostResponse, ExecutionError> {
let n = process.get_stack_item(0).as_int();
if n == 0 {
return Err(ExecutionError::LogArgumentZero(process.clk()));
}
let ilog2 = Felt::from(n.ilog2());
advice_provider.push_stack(AdviceSource::Value(ilog2))?;
Ok(HostResponse::None)
}
fn u64_to_u32_elements(value: u64) -> (Felt, Felt) {
let hi = Felt::from((value >> 32) as u32);
let lo = Felt::from(value as u32);
(hi, lo)
}
fn push_transformed_stack_top<S: ProcessState, A: AdviceProvider>(
advice_provider: &mut A,
process: &S,
f: impl FnOnce(u32) -> Felt,
) -> Result<HostResponse, ExecutionError> {
let stack_top = process.get_stack_item(0);
let stack_top: u32 = stack_top
.as_int()
.try_into()
.map_err(|_| ExecutionError::NotU32Value(stack_top, ZERO))?;
let transformed_stack_top = f(stack_top);
advice_provider.push_stack(AdviceSource::Value(transformed_stack_top))?;
Ok(HostResponse::None)
}