tasm_lib/array/
horner_evaluation.rsuse std::collections::HashMap;
use itertools::Itertools;
use triton_vm::prelude::*;
use crate::data_type::ArrayType;
use crate::prelude::*;
use crate::traits::basic_snippet::Reviewer;
use crate::traits::basic_snippet::SignOffFingerprint;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct HornerEvaluation {
pub num_coefficients: usize,
}
impl HornerEvaluation {
pub fn new(num_coefficients: usize) -> Self {
Self { num_coefficients }
}
}
impl BasicSnippet for HornerEvaluation {
fn inputs(&self) -> Vec<(DataType, String)> {
let coefficients_ty = DataType::Array(Box::new(ArrayType {
element_type: DataType::Xfe,
length: self.num_coefficients,
}));
vec![
(coefficients_ty, "*coefficients".to_string()),
(DataType::Xfe, "indeterminate".to_string()),
]
}
fn outputs(&self) -> Vec<(DataType, String)> {
vec![(DataType::Xfe, "evaluation".to_string())]
}
fn entrypoint(&self) -> String {
let n = self.num_coefficients;
format!("tasmlib_array_horner_evaluation_with_{n}_coefficients")
}
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
let update_running_evaluation = triton_asm! {
dup 5 dup 5 dup 5 xx_mul pick 6 read_mem 3 place 9 xx_add };
let update_running_evaluation_for_each_coefficient = (0..self.num_coefficients)
.flat_map(|_| update_running_evaluation.clone())
.collect_vec();
triton_asm! {
{self.entrypoint()}:
pick 3 addi {(self.num_coefficients * 3).saturating_sub(1)}
place 3 push 0 push 0 push 0 {&update_running_evaluation_for_each_coefficient}
place 6 place 6 place 6 pop 4 return
}
}
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
let mut sign_offs = HashMap::new();
if self.num_coefficients == 4 {
sign_offs.insert(Reviewer("ferdinand"), 0xec460e65f9c22a87.into());
}
sign_offs
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rust_shadowing_helper_functions::array::array_get;
use crate::rust_shadowing_helper_functions::array::insert_as_array;
use crate::test_prelude::*;
use crate::twenty_first::prelude::Polynomial;
impl Accessor for HornerEvaluation {
fn rust_shadow(
&self,
stack: &mut Vec<BFieldElement>,
memory: &HashMap<BFieldElement, BFieldElement>,
) {
let indeterminate = pop_encodable(stack);
let coefficient_ptr = stack.pop().unwrap();
let coefficients = (0..self.num_coefficients)
.map(|i| array_get(coefficient_ptr, i, memory, 3))
.map(|bfes| XFieldElement::new(bfes.try_into().unwrap()))
.collect();
let polynomial = Polynomial::new(coefficients);
let evaluation = polynomial.evaluate_in_same_field(indeterminate);
push_encodable(stack, &evaluation);
}
fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
_: Option<BenchmarkCase>,
) -> AccessorInitialState {
let mut rng = StdRng::from_seed(seed);
let coefficients = (0..self.num_coefficients)
.map(|_| rng.random::<XFieldElement>())
.collect();
let address = rng.random();
let mut memory = HashMap::new();
insert_as_array(address, &mut memory, coefficients);
let mut stack = self.init_stack_for_isolated_run();
stack.push(address);
push_encodable(&mut stack, &rng.random::<XFieldElement>()); AccessorInitialState { stack, memory }
}
}
#[test]
fn rust_shadow() {
for n in [0, 1, 4, 20, 587, 1000] {
ShadowedAccessor::new(HornerEvaluation::new(n)).test();
}
}
}
#[cfg(test)]
mod benches {
use super::*;
use crate::test_prelude::*;
#[test]
fn bench() {
for n in [100, 587] {
ShadowedAccessor::new(HornerEvaluation::new(n)).bench();
}
}
}