tasm_lib/list/
horner_evaluation_dynamic_length.rsuse itertools::Itertools;
use triton_vm::prelude::triton_asm;
use triton_vm::prelude::LabelledInstruction;
use crate::data_type::DataType;
use crate::library::Library;
use crate::list::length::Length;
use crate::traits::basic_snippet::BasicSnippet;
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 entrypoint = self.entrypoint();
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 loop_batches = format!("{entrypoint}_loop_batches");
let loop_remainder = format!("{entrypoint}_loop_remainder");
let length_of_list_of_xfes = library.import(Box::new(Length {
element_type: DataType::Xfe,
}));
triton_asm! {
{entrypoint}:
dup 3 call {length_of_list_of_xfes} 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 test {
use std::collections::HashMap;
use itertools::Itertools;
use rand::prelude::*;
use triton_vm::prelude::*;
use triton_vm::twenty_first::math::polynomial::Polynomial;
use triton_vm::twenty_first::xfe_vec;
use super::HornerEvaluationDynamicLength;
use crate::memory::encode_to_memory;
use crate::prelude::TasmObject;
use crate::snippet_bencher::BenchmarkCase;
use crate::traits::basic_snippet::BasicSnippet;
use crate::traits::function::Function;
use crate::traits::function::FunctionInitialState;
use crate::traits::function::ShadowedFunction;
use crate::traits::rust_shadow::RustShadow;
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 = SeedableRng::from_seed(seed);
let num_coefficients = match bench_case {
Some(BenchmarkCase::CommonCase) => 256,
Some(BenchmarkCase::WorstCase) => 512,
None => rng.gen_range(0..1000),
};
let coefficients = (0..num_coefficients)
.map(|_| rng.gen::<XFieldElement>())
.collect_vec();
let coefficients_pointer = bfe!(rng.gen_range(0..(1u64 << 35)));
let indeterminate = rng.gen::<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::HornerEvaluationDynamicLength;
use crate::traits::function::ShadowedFunction;
use crate::traits::rust_shadow::RustShadow;
#[test]
fn bench() {
ShadowedFunction::new(HornerEvaluationDynamicLength).bench();
}
}