Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
poly.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
16#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
17
18#include <cstddef>
19
20namespace proofs {
21// Fixed-size N-tuples of field elements, interpreted as polynomial coefficients
22// and/or values and/or newton expansion.
23template <size_t N, class Field>
24class Poly {
25 public:
26 static const size_t kN = N;
27 using Elt = typename Field::Elt;
28 using T = Poly;
29
30 // the N-tuple itself
31 Elt t_[N];
32
33 Elt& operator[](size_t i) { return t_[i]; }
34 const Elt& operator[](size_t i) const { return t_[i]; }
35
36 T& add(const T& y, const Field& F) {
37 for (size_t i = 0; i < N; ++i) {
38 F.add(t_[i], y[i]);
39 }
40 return *this;
41 }
42 T& sub(const T& y, const Field& F) {
43 for (size_t i = 0; i < N; ++i) {
44 F.sub(t_[i], y[i]);
45 }
46 return *this;
47 }
48 T& mul(const T& y, const Field& F) {
49 for (size_t i = 0; i < N; ++i) {
50 F.mul(t_[i], y[i]);
51 }
52 return *this;
53 }
54 T& mul_scalar(const Elt& y, const Field& F) {
55 for (size_t i = 0; i < N; ++i) {
56 F.mul(t_[i], y);
57 }
58 return *this;
59 }
60
61 static T extend(const Poly<2, Field>& f, const Field& F) {
62 T g;
63 g[0] = f[0];
64 g[1] = f[1];
65 Elt df = F.subf(f[1], f[0]);
66
67 if (Field::kCharacteristicTwo) {
68 // Assume poly_evaluation_point[0] = 0, poly_evaluation_point[1] = 1,
69 // and the rest are arbitrary.
70 for (size_t i = 2; i < N; ++i) {
71 g[i] = F.addf(g[0], F.mulf(F.poly_evaluation_point(i), df));
72 }
73 } else {
74 // Assume that poly_evaluation_point[] form an arithmetic
75 // progression.
76 for (size_t i = 2; i < N; ++i) {
77 g[i] = F.addf(g[i - 1], df);
78 }
79 }
80
81 return g;
82 }
83
84 // convert Lagrange basis -> Newton forward differences for the
85 // special case of evaluation points 0, 1, 2, ..., N-1.
86 // See interpolation.h for the general case of interpolation.
87 void newton_of_lagrange(const Field& F) {
88 for (size_t i = 1; i < N; i++) {
89 for (size_t k = N; k-- > i;) {
90 F.sub(t_[k], t_[k - 1]);
91 F.mul(t_[k], F.newton_denominator(k, i));
92 }
93 }
94 }
95
96 // Evaluate f(x) for a polynomial in the Newton forward-difference
97 // basis.
98 Elt eval_newton(const Elt& x, const Field& F) const {
99 // Newton interpolation formula
100 Elt e = t_[N - 1];
101 for (size_t i = N - 1; i-- > 0;) {
102 F.mul(e, F.subf(x, F.poly_evaluation_point(i)));
103 F.add(e, t_[i]);
104 }
105
106 return e;
107 }
108
109 Elt eval_lagrange(const Elt& x, const Field& F) const {
110 T tmp(*this); // do not clobber *this
111 tmp.newton_of_lagrange(F);
112 return tmp.eval_newton(x, F);
113 }
114
115 // Evaluate f(r) given a polynomial in the standard basis
116 // f(x)=t_[i]*x^i.
117 Elt eval_monomial(const Elt& x, const Field& F) const {
118 // Horner's algorithm
119 Elt e = t_[N - 1];
120 for (size_t i = N - 1; i-- > 0;) {
121 F.mul(e, x);
122 F.add(e, t_[i]);
123 }
124 return e;
125 }
126
127 static T powers_of(const Elt& e, const Field& F) {
128 T r;
129 r[0] = F.one();
130 for (size_t i = 1; i < N; ++i) {
131 r[i] = F.mulf(r[i - 1], e);
132 }
133 return r;
134 }
135
136 // Interpolation via explicit dot product.
137 //
138 // The combination P.newton_of_lagrange().eval_newton(..., R, ...)
139 // evaluates P at R given the Lagrange basis [P(0), P(1), ..., P(N-1)].
140 //
141 // On the contrary, this class computes a V(R) such that P(R) =
142 // dot(V(R), [P(0), P(1), ..., P(N-1)]) and the caller computes the
143 // inner product, either explicitly or via an inner-product
144 // argument. The construction is pure linear algebra: express the
145 // Lagrange basis P = [P(0), P(1), ..., P(N-1)]^T as I * P where I
146 // is the identity matrix, and interpolate the rows of I
147 // via newton_of_lagrange().eval_newton(). Since newton_of_lagrange()
148 // is O(N^2) and eval_newton() is O(N), pre-compute the eval_newton()
149 // of all rows.
150 class dot_interpolation {
151 // identity_[k] contains the Newton basis of the polynomial P(x) such
152 // that P(k) = 1 and P(i) = 0 for i != k and 0 <= i < N.
153 T identity_[N];
154
155 public:
156 explicit dot_interpolation(const Field& F) {
157 for (size_t k = 0; k < N; ++k) {
158 for (size_t i = 0; i < N; ++i) {
159 identity_[k][i] = (i == k) ? F.one() : F.zero();
160 }
161 identity_[k].newton_of_lagrange(F);
162 }
163 }
164
165 // return V such that P(r) = V^T [P(0), P(1), ..., P(N-1)]
166 T coef(const Elt& x, const Field& F) const {
167 T c;
168 for (size_t k = 0; k < N; ++k) {
169 c[k] = identity_[k].eval_newton(x, F);
170 }
171 return c;
172 }
173 };
174};
175} // namespace proofs
176
177#endif // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
Definition poly.h:24
Definition gf2_128.h:63