tasm_lib/list/
horner_evaluation_dynamic_length.rsuse itertools::Itertools;
use triton_vm::prelude::*;
use crate::list::length::Length;
use crate::prelude::*;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct HornerEvaluationDynamicLength;
impl BasicSnippet for HornerEvaluationDynamicLength {
fn inputs(&self) -> Vec<(DataType, String)> {
vec![
(
DataType::List(Box::new(DataType::Xfe)),
"*coefficients".to_string(),
),
(DataType::Xfe, "indeterminate".to_string()),
]
}
fn outputs(&self) -> Vec<(DataType, String)> {
vec![(DataType::Xfe, "value".to_string())]
}
fn entrypoint(&self) -> String {
"tasmlib_list_horner_evaluation_dynamic_length".to_string()
}
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let horner_iteration = triton_asm! {
dup 5 dup 5 dup 5 xx_mul dup 6 read_mem 3 swap 10 pop 1 xx_add };
let batch_size = 16;
let three_times_batch_size_plus_two = batch_size * 3 + 2;
let many_horner_iterations = (0..batch_size)
.flat_map(|_| horner_iteration.clone())
.collect_vec();
let entrypoint = self.entrypoint();
let loop_batches = format!("{entrypoint}_loop_batches");
let loop_remainder = format!("{entrypoint}_loop_remainder");
let length_of_list = library.import(Box::new(Length));
triton_asm! {
{entrypoint}:
dup 3 call {length_of_list} push 3 mul dup 4 add dup 3 dup 3 dup 3 push 0 push 0 push 0 call {loop_batches}
call {loop_remainder} swap 8 pop 1 swap 8 pop 1 swap 8 pop 5 pop 1 return
{loop_batches}:
dup 6 dup 11 push {three_times_batch_size_plus_two} add
push -1 mul add
split pop 1 skiz return
{&many_horner_iterations}
recurse
{loop_remainder}:
dup 6 dup 11 eq push 1 eq
skiz return
{&horner_iteration}
recurse
}
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use twenty_first::math::polynomial::Polynomial;
use super::*;
use crate::test_prelude::*;
impl HornerEvaluationDynamicLength {
fn prepare_state(
&self,
coefficients: Vec<XFieldElement>,
coefficients_pointer: BFieldElement,
indeterminate: XFieldElement,
) -> FunctionInitialState {
let mut memory = HashMap::default();
let mut stack = self.init_stack_for_isolated_run();
encode_to_memory(&mut memory, coefficients_pointer, &coefficients);
stack.push(coefficients_pointer);
stack.push(indeterminate.coefficients[2]);
stack.push(indeterminate.coefficients[1]);
stack.push(indeterminate.coefficients[0]);
FunctionInitialState { stack, memory }
}
}
impl Function for HornerEvaluationDynamicLength {
fn rust_shadow(
&self,
stack: &mut Vec<BFieldElement>,
memory: &mut HashMap<BFieldElement, BFieldElement>,
) {
let x0 = stack.pop().unwrap();
let x1 = stack.pop().unwrap();
let x2 = stack.pop().unwrap();
let x = XFieldElement::new([x0, x1, x2]);
let address = stack.pop().unwrap();
let coefficients = *Vec::<XFieldElement>::decode_from_memory(memory, address).unwrap();
let polynomial = Polynomial::new(coefficients);
let value = polynomial.evaluate_in_same_field(x);
stack.push(value.coefficients[2]);
stack.push(value.coefficients[1]);
stack.push(value.coefficients[0]);
}
fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
bench_case: Option<BenchmarkCase>,
) -> FunctionInitialState {
let mut rng = StdRng::from_seed(seed);
let num_coefficients = match bench_case {
Some(BenchmarkCase::CommonCase) => 256,
Some(BenchmarkCase::WorstCase) => 512,
None => rng.random_range(0..1000),
};
let coefficients = (0..num_coefficients)
.map(|_| rng.random::<XFieldElement>())
.collect_vec();
let coefficients_pointer = bfe!(rng.random_range(0..(1u64 << 35)));
let indeterminate = rng.random::<XFieldElement>();
self.prepare_state(coefficients, coefficients_pointer, indeterminate)
}
fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
let an_indeterminate = xfe!([1u64 << 45, 47, 49]);
let one_coefficient = self.prepare_state(xfe_vec![19991], bfe!(333), an_indeterminate);
let two_coefficients =
self.prepare_state(xfe_vec![19991, 299999992], bfe!(333), an_indeterminate);
let three_coefficients = self.prepare_state(
xfe_vec![19991, 299999992, 399999993],
bfe!(333),
an_indeterminate,
);
vec![one_coefficient, two_coefficients, three_coefficients]
}
}
#[test]
fn test() {
for _ in 0..10 {
ShadowedFunction::new(HornerEvaluationDynamicLength).test();
}
}
}
#[cfg(test)]
mod bench {
use super::*;
use crate::test_prelude::*;
#[test]
fn benchmark() {
ShadowedFunction::new(HornerEvaluationDynamicLength).bench();
}
}