zinc_poly/univariate/
nat_evaluation.rs1use crypto_primitives::{
2 FieldConfig, ProjectPrimitiveIntegersWithConfig, Semiring, SetConfig, SetElement,
3};
4
5use crate::EvaluationError;
6
7#[derive(Clone, Debug, PartialEq)]
9pub struct NatEvaluatedPoly<E: SetElement> {
10 pub evaluations: Vec<E>,
12}
13
14impl<E: SetElement> NatEvaluatedPoly<E> {
15 #[inline(always)]
16 pub const fn new(evaluations: Vec<E>) -> Self {
17 Self { evaluations }
18 }
19
20 #[allow(clippy::arithmetic_side_effects, clippy::cast_possible_wrap)]
29 pub fn evaluate_at_point<C>(&self, cfg: &C, point: &E) -> Result<E, EvaluationError>
30 where
31 C: FieldConfig + ProjectPrimitiveIntegersWithConfig + SetConfig<Element = E>,
32 {
33 let evaluations = &self.evaluations;
34 let zero = cfg.zero();
39 let one = cfg.one();
40
41 let len = evaluations.len();
42
43 let mut evals = vec![];
44
45 let mut prod = point.clone();
46 evals.push(point.clone());
47
48 let mut j = zero.clone();
51 for i in 1..len {
52 if *point == j {
53 return Ok(evaluations[i - 1].clone());
54 }
55 cfg.add_assign(&mut j, &one);
56
57 let tmp = cfg.sub(point, &j);
58 evals.push(tmp.clone());
59 cfg.mul_assign(&mut prod, &tmp);
60 }
61
62 if *point == j {
63 return Ok(evaluations[len - 1].clone());
64 }
65
66 let div = |num: &E, denom: &E| cfg.div(num, denom);
67
68 let mut res = zero;
69 if evaluations.len() <= 20 {
95 let last_denom: E = cfg.project(&factorial(len - 1, u64::from));
96
97 let mut ratio_numerator = 1i64;
98 let mut ratio_denominator = 1u64;
99
100 for i in (0..len).rev() {
101 let ratio_numerator_f = cfg.project(&ratio_numerator);
102 let ratio_denominator_f = cfg.project(&ratio_denominator);
103
104 let num = cfg.mul(&prod, &ratio_denominator_f);
105 let denom = cfg.mul(&cfg.mul(&last_denom, &ratio_numerator_f), &evals[i]);
106 let x = div(&num, &denom);
107
108 let term = cfg.mul(&evaluations[i], &x);
109 cfg.add_assign(&mut res, &term);
110
111 if i != 0 {
113 ratio_numerator *= -(len as i64 - i as i64);
115 ratio_denominator *= i as u64;
116 }
117 }
118 } else if evaluations.len() <= 33 {
119 let last_denom: E = cfg.project(&factorial(len - 1, u128::from));
120 let mut ratio_numerator = 1i128;
121 let mut ratio_denominator = 1u128;
122
123 for i in (0..len).rev() {
124 let ratio_numerator_f = cfg.project(&ratio_numerator);
125 let ratio_denominator_f = cfg.project(&ratio_denominator);
126
127 let num = cfg.mul(&prod, &ratio_denominator_f);
128 let denom = cfg.mul(&cfg.mul(&last_denom, &ratio_numerator_f), &evals[i]);
129 let x = div(&num, &denom);
130
131 let term = cfg.mul(&evaluations[i], &x);
132 cfg.add_assign(&mut res, &term);
133
134 if i != 0 {
136 ratio_numerator *= -(len as i128 - i as i128);
137 ratio_denominator *= i as u128;
138 }
139 }
140 } else {
141 let mut denom_up = cfg.product((1..=(len as u64 - 1)).map(|u: u64| cfg.project(&u)));
144 let mut denom_down = one;
145
146 for i in (0..len).rev() {
147 let num = cfg.mul(&prod, &denom_down);
148 let denom = cfg.mul(&denom_up, &evals[i]);
149 let x = div(&num, &denom);
150
151 let term = cfg.mul(&evaluations[i], &x);
152 cfg.add_assign(&mut res, &term);
153
154 if i != 0 {
156 let denom_up_factor = cfg.project(&((len - i) as u64));
157 denom_up = cfg.neg(&cfg.mul(&denom_up, &denom_up_factor));
158
159 let denom_down_factor = cfg.project(&(i as u64));
160 cfg.mul_assign(&mut denom_down, &denom_down_factor);
161 }
162 }
163 }
164
165 Ok(res)
166 }
167}
168
169#[allow(clippy::arithmetic_side_effects)]
171fn factorial<R, F>(a: usize, from_u64: F) -> R
172where
173 R: Semiring,
174 F: Fn(u64) -> R + Send + Sync,
175{
176 (1..=(a as u64))
177 .map(&from_u64)
178 .reduce(|mut acc, next| {
179 acc *= next;
180 acc
181 })
182 .unwrap_or(from_u64(1))
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use crypto_primitives::{
189 BaseFieldConfig, ProjectElementWithConfig, crypto_bigint_monty::MontyField,
190 crypto_bigint_uint::Uint,
191 };
192 use itertools::Itertools;
193
194 const LIMBS: usize = 4;
195
196 fn test_config() -> MontyField<LIMBS> {
197 let modulus =
198 Uint::from_be_hex("0000000000000000000000000000000000860995AE68FC80E1B1BD1E39D54B33");
199 MontyField::new(&modulus).expect("modulus should be a valid odd prime")
200 }
201
202 #[test]
203 fn evaluate_nat_evaluation() {
204 let cfg = test_config();
205 let field_elem = cfg.project(&100u64);
206
207 let poly = NatEvaluatedPoly::new((0..1024u64).map(|x| cfg.project(&x)).collect_vec());
208
209 let res = poly.evaluate_at_point(&cfg, &field_elem).unwrap();
210
211 assert_eq!(res, cfg.project(&100u64));
212 }
213}