curv/cryptographic_primitives/secret_sharing/polynomial.rs
1use std::cmp::Ordering;
2use std::convert::TryFrom;
3use std::{iter, ops};
4
5use serde::{Deserialize, Serialize};
6
7use crate::elliptic::curves::{Curve, Scalar};
8
9/// Degree of a [polynomial](Polynomial).
10///
11/// For a polynomial of the form: $f(x) = a_0 + a_1 x^1 + \dots{} + a_{n-1} x^{n-1} + a_n x^n$
12///
13/// The degree of $f(x)$ is defined as the biggest $i$ such that $a_i \neq 0$.
14/// If $f(x) = 0$ it's degree is defined as $\infty$.
15#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
16pub enum PolynomialDegree {
17 Infinity,
18 Finite(u16),
19}
20
21impl From<u16> for PolynomialDegree {
22 fn from(deg: u16) -> Self {
23 PolynomialDegree::Finite(deg)
24 }
25}
26
27impl PartialOrd for PolynomialDegree {
28 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
29 Some(self.cmp(other))
30 }
31}
32
33impl Ord for PolynomialDegree {
34 fn cmp(&self, other: &Self) -> Ordering {
35 match (self, other) {
36 (Self::Infinity, Self::Infinity) => Ordering::Equal,
37 (Self::Infinity, Self::Finite(_)) => Ordering::Greater,
38 (Self::Finite(_), Self::Infinity) => Ordering::Less,
39 (Self::Finite(a), Self::Finite(b)) => a.cmp(b),
40 }
41 }
42}
43
44/// Polynomial of some degree $n$
45///
46/// Polynomial has a form: $f(x) = a_0 + a_1 x^1 + \dots{} + a_{n-1} x^{n-1} + a_n x^n$
47///
48/// Coefficients $a_i$ and indeterminate $x$ are in $\Zq$, ie. they are [`Scalar<E>`](Scalar).
49#[derive(Clone, Debug, Serialize, Deserialize)]
50#[serde(bound = "")]
51pub struct Polynomial<E: Curve> {
52 coefficients: Vec<Scalar<E>>,
53}
54
55impl<E: Curve> Polynomial<E> {
56 /// Constructs polynomial $f(x)$ from list of coefficients $a_0, \dots, a_n$ in $\Zq$
57 ///
58 /// ## Order
59 ///
60 /// $a_i$ should corresponds to polynomial $i^{\text{th}}$ coefficient $f(x) = \dots{} + a_i x^i + \dots$
61 ///
62 /// ## Polynomial degree
63 ///
64 /// Note that it's not guaranteed that constructed polynomial degree equals to `coefficients.len()-1`
65 /// as it's allowed to end with zero coefficients. Actual polynomial degree equals to index of last
66 /// non-zero coefficient or zero if all the coefficients are zero.
67 ///
68 /// ## Example
69 ///
70 /// ```rust
71 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
72 /// use curv::elliptic::curves::{Scalar, Point, Secp256k1};
73 ///
74 /// let coefs = vec![Scalar::random(), Scalar::random()];
75 /// let poly = Polynomial::<Secp256k1>::from_coefficients(coefs.clone());
76 ///
77 /// assert_eq!(coefs, poly.coefficients());
78 /// ```
79 pub fn from_coefficients(coefficients: Vec<Scalar<E>>) -> Self {
80 Self { coefficients }
81 }
82
83 /// Sample a random polynomial of given degree
84 ///
85 /// ## Example
86 /// ```rust
87 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
88 /// use curv::elliptic::curves::Secp256k1;
89 /// use curv::cryptographic_primitives::secret_sharing::PolynomialDegree;
90 ///
91 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(3);
92 /// assert_eq!(polynomial.degree(), 3.into());
93 ///
94 /// let zero_polynomial = Polynomial::<Secp256k1>::sample_exact(PolynomialDegree::Infinity);
95 /// assert_eq!(zero_polynomial.degree(), PolynomialDegree::Infinity);
96 /// ```
97 pub fn sample_exact(degree: impl Into<PolynomialDegree>) -> Self {
98 match degree.into() {
99 PolynomialDegree::Finite(degree) => Self::from_coefficients(
100 iter::repeat_with(Scalar::random)
101 .take(usize::from(degree) + 1)
102 .collect(),
103 ),
104 PolynomialDegree::Infinity => Self::from_coefficients(vec![]),
105 }
106 }
107
108 /// Samples random polynomial of degree $n$ with fixed constant term (ie. $a_0 = \text{constant\\_term}$)
109 ///
110 /// ## Example
111 /// ```rust
112 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
113 /// use curv::elliptic::curves::{Secp256k1, Scalar};
114 ///
115 /// let const_term = Scalar::<Secp256k1>::random();
116 /// let polynomial = Polynomial::<Secp256k1>::sample_exact_with_fixed_const_term(3, const_term.clone());
117 /// assert_eq!(polynomial.degree(), 3.into());
118 /// assert_eq!(polynomial.evaluate(&Scalar::zero()), const_term);
119 /// ```
120 pub fn sample_exact_with_fixed_const_term(n: u16, const_term: Scalar<E>) -> Self {
121 if n == 0 {
122 Self::from_coefficients(vec![const_term])
123 } else {
124 let random_coefficients = iter::repeat_with(Scalar::random).take(usize::from(n));
125 Self::from_coefficients(iter::once(const_term).chain(random_coefficients).collect())
126 }
127 }
128
129 /// Returns degree $d$ of polynomial $f(x)$: $d = \deg f$
130 ///
131 /// ```rust
132 /// # use curv::cryptographic_primitives::secret_sharing::{Polynomial, PolynomialDegree};
133 /// use curv::elliptic::curves::{Secp256k1, Scalar};
134 ///
135 /// let polynomial = Polynomial::<Secp256k1>::from_coefficients(vec![
136 /// Scalar::from(1), Scalar::from(2),
137 /// ]);
138 /// assert_eq!(polynomial.degree(), 1.into());
139 ///
140 /// let polynomial = Polynomial::<Secp256k1>::from_coefficients(vec![
141 /// Scalar::zero()
142 /// ]);
143 /// assert_eq!(polynomial.degree(), PolynomialDegree::Infinity);
144 /// ```
145 pub fn degree(&self) -> PolynomialDegree {
146 self.coefficients()
147 .iter()
148 .enumerate()
149 .rev()
150 .find(|(_, a)| !a.is_zero())
151 .map(|(i, _)| {
152 PolynomialDegree::Finite(
153 u16::try_from(i).expect("polynomial degree guaranteed to fit into u16"),
154 )
155 })
156 .unwrap_or(PolynomialDegree::Infinity)
157 }
158
159 /// Takes scalar $x$ and evaluates $f(x)$
160 ///
161 /// ## Example
162 /// ```rust
163 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
164 /// use curv::elliptic::curves::{Secp256k1, Scalar};
165 ///
166 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(2);
167 ///
168 /// let x = Scalar::from(10);
169 /// let y = polynomial.evaluate(&x);
170 ///
171 /// let a = polynomial.coefficients();
172 /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x);
173 /// ```
174 pub fn evaluate(&self, point_x: &Scalar<E>) -> Scalar<E> {
175 let mut reversed_coefficients = self.coefficients.iter().rev();
176 let head = reversed_coefficients
177 .next()
178 .expect("at least one coefficient is guaranteed to be present");
179 let tail = reversed_coefficients;
180 tail.fold(head.clone(), |partial, coef| {
181 let partial_times_point_x = partial * point_x;
182 partial_times_point_x + coef
183 })
184 }
185
186 /// Takes point $x$ that's convertable to BigInt, and evaluates $f(x)$
187 ///
188 /// ## Example
189 /// ```rust
190 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
191 /// use curv::elliptic::curves::{Secp256k1, Scalar};
192 ///
193 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(2);
194 ///
195 /// let x: u16 = 10;
196 /// let y: Scalar<Secp256k1> = polynomial.evaluate_bigint(x);
197 ///
198 /// let a = polynomial.coefficients();
199 /// let x = Scalar::from(x);
200 /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x);
201 /// ```
202 pub fn evaluate_bigint<B>(&self, point_x: B) -> Scalar<E>
203 where
204 Scalar<E>: From<B>,
205 {
206 self.evaluate(&Scalar::from(point_x))
207 }
208
209 /// Takes list of points $x$ and returns iterator over $f(x_i)$
210 ///
211 /// ## Example
212 /// ```rust
213 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
214 /// use curv::elliptic::curves::{Secp256k1, Scalar};
215 ///
216 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(2);
217 ///
218 /// let xs = &[Scalar::from(10), Scalar::from(11)];
219 /// let ys = polynomial.evaluate_many(xs);
220 ///
221 /// let a = polynomial.coefficients();
222 /// for (y, x) in ys.zip(xs) {
223 /// assert_eq!(y, &a[0] + &a[1] * x + &a[2] * x*x);
224 /// }
225 /// ```
226 pub fn evaluate_many<'i, I>(&'i self, points_x: I) -> impl Iterator<Item = Scalar<E>> + 'i
227 where
228 I: IntoIterator<Item = &'i Scalar<E>> + 'i,
229 {
230 points_x.into_iter().map(move |x| self.evaluate(x))
231 }
232
233 /// Takes a list of points $x$ that are convertable to BigInt, and returns iterator over
234 /// $f(x_i)$.
235 ///
236 /// ## Example
237 /// ```rust
238 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
239 /// use curv::elliptic::curves::{Secp256k1, Scalar};
240 ///
241 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(2);
242 ///
243 /// let xs: &[u16] = &[10, 11];
244 /// let ys = polynomial.evaluate_many_bigint(xs.iter().copied());
245 ///
246 /// let a = polynomial.coefficients();
247 /// for (y, x) in ys.zip(xs) {
248 /// let x = Scalar::from(*x);
249 /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x);
250 /// }
251 /// ```
252 pub fn evaluate_many_bigint<'i, B, I>(
253 &'i self,
254 points_x: I,
255 ) -> impl Iterator<Item = Scalar<E>> + 'i
256 where
257 I: IntoIterator<Item = B> + 'i,
258 Scalar<E>: From<B>,
259 {
260 points_x.into_iter().map(move |x| self.evaluate_bigint(x))
261 }
262
263 /// Returns list of polynomial coefficients $a$: $a_i$ corresponds to $i^{\text{th}}$ coefficient of
264 /// polynomial $f(x) = \dots{} + a_i x^i + \dots{}$
265 ///
266 /// ## Example
267 ///
268 /// ```rust
269 /// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
270 /// use curv::elliptic::curves::{Secp256k1, Scalar};
271 ///
272 /// let polynomial = Polynomial::<Secp256k1>::sample_exact(3);
273 /// let a = polynomial.coefficients();
274 /// let x = Scalar::<Secp256k1>::random();
275 /// assert_eq!(polynomial.evaluate(&x), &a[0] + &a[1] * &x + &a[2] * &x*&x + &a[3] * &x*&x*&x);
276 /// ```
277 pub fn coefficients(&self) -> &[Scalar<E>] {
278 &self.coefficients
279 }
280
281 /// Evaluates lagrange basis polynomial
282 ///
283 /// $$l_{X,j}(x) = \prod_{\substack{0 \leq m \leq t,\\\\m \ne j}} \frac{x - X_m}{X_j - X_m}$$
284 ///
285 /// Lagrange basis polynomials are mainly used for Lagrange interpolation, ie. calculating $L(x)$
286 /// where polynomial $L$ is defined as set of $t+1$ distinct points $(x_i, y_i)$ ($t = \deg f$).
287 /// Example section shows how Lagrange interpolation can be implemented using this function.
288 ///
289 /// ## Panics
290 /// This function will panic if elements in `xs` are not pairwise distinct, or `j ≥ xs.len()`
291 ///
292 /// ## Example
293 /// If you have polynomial $f$ defined as $t+1$ points $(x_0, y_0), \dots, (x_t, y_t)$ (and polynomial
294 /// degree is $t$), then you can, for instance, calculate $f(15)$ using Lagrange interpolation:
295 ///
296 /// ```rust
297 /// use curv::cryptographic_primitives::secret_sharing::Polynomial;
298 /// # use curv::elliptic::curves::*;
299 ///
300 /// # let t = 3;
301 /// # let f = Polynomial::<Secp256r1>::sample_exact(t);
302 /// # let (x_0, x_1, x_2, x_3) = (Scalar::from(1), Scalar::from(2), Scalar::from(3), Scalar::from(4));
303 /// # let (y_0, y_1, y_2, y_3) = (f.evaluate(&x_0), f.evaluate(&x_1), f.evaluate(&x_2), f.evaluate(&x_3));
304 /// let xs = &[x_0, x_1, x_2, x_3];
305 /// let ys = &[y_0, y_1, y_2, y_3];
306 ///
307 /// let f_15: Scalar<_> = (0..).zip(ys)
308 /// .map(|(j, y_j)| y_j * Polynomial::lagrange_basis(&Scalar::from(15), j, xs))
309 /// .sum();
310 /// assert_eq!(f_15, f.evaluate(&Scalar::from(15)));
311 /// ```
312 ///
313 /// Generally, formula of Lagrange interpolation is:
314 ///
315 /// $$ L_{X,Y}(x) = \sum^t_{j=0} Y\_j \cdot l_{X,j}(x) $$
316 pub fn lagrange_basis(x: &Scalar<E>, j: u16, xs: &[Scalar<E>]) -> Scalar<E> {
317 let x_j = &xs[usize::from(j)];
318 let num: Scalar<E> = (0u16..)
319 .zip(xs)
320 .filter(|(m, _)| *m != j)
321 .map(|(_, x_m)| x - x_m)
322 .product();
323 let denum: Scalar<E> = (0u16..)
324 .zip(xs)
325 .filter(|(m, _)| *m != j)
326 .map(|(_, x_m)| x_j - x_m)
327 .product();
328 let denum = denum
329 .invert()
330 .expect("elements in xs are not pairwise distinct");
331 num * denum
332 }
333}
334
335/// Multiplies polynomial `f(x)` at scalar `s`, returning resulting polynomial `g(x) = s * f(x)`
336///
337/// ## Example
338///
339/// ```rust
340/// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
341/// use curv::elliptic::curves::{Secp256k1, Scalar};
342///
343/// let f = Polynomial::<Secp256k1>::sample_exact(3);
344///
345/// let s = Scalar::<Secp256k1>::random();
346/// let g = &f * &s;
347///
348/// for (f_coef, g_coef) in f.coefficients().iter().zip(g.coefficients()) {
349/// assert_eq!(&(f_coef * &s), g_coef);
350/// }
351/// ```
352impl<E: Curve> ops::Mul<&Scalar<E>> for &Polynomial<E> {
353 type Output = Polynomial<E>;
354 fn mul(self, scalar: &Scalar<E>) -> Self::Output {
355 let coefficients = self.coefficients.iter().map(|c| c * scalar).collect();
356 Polynomial::from_coefficients(coefficients)
357 }
358}
359
360/// Adds two polynomial `f(x)` and `g(x)` returning resulting polynomial `h(x) = f(x) + g(x)`
361///
362/// ## Example
363///
364/// ```rust
365/// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
366/// use curv::elliptic::curves::{Secp256k1, Scalar};
367///
368/// let f = Polynomial::<Secp256k1>::sample_exact(2);
369/// let g = Polynomial::<Secp256k1>::sample_exact(3);
370/// let h = &f + &g;
371///
372/// let x = Scalar::<Secp256k1>::from(10);
373/// assert_eq!(h.evaluate(&x), f.evaluate(&x) + g.evaluate(&x));
374/// ```
375impl<E: Curve> ops::Add for &Polynomial<E> {
376 type Output = Polynomial<E>;
377 fn add(self, g: Self) -> Self::Output {
378 let len1 = self.coefficients.len();
379 let len2 = g.coefficients.len();
380
381 let overlapped = self
382 .coefficients()
383 .iter()
384 .zip(g.coefficients())
385 .map(|(f_coef, g_coef)| f_coef + g_coef);
386 let tail = if len1 < len2 {
387 &g.coefficients()[len1..]
388 } else {
389 &self.coefficients()[len2..]
390 };
391
392 Polynomial::from_coefficients(overlapped.chain(tail.iter().cloned()).collect())
393 }
394}
395
396/// Subtracts two polynomial `g(x)` from `f(x)` returning resulting polynomial `h(x) = f(x) - g(x)`
397///
398/// ## Example
399///
400/// ```rust
401/// # use curv::cryptographic_primitives::secret_sharing::Polynomial;
402/// use curv::elliptic::curves::{Secp256k1, Scalar};
403///
404/// let f = Polynomial::<Secp256k1>::sample_exact(2);
405/// let g = Polynomial::<Secp256k1>::sample_exact(3);
406/// let h = &f - &g;
407///
408/// let x = Scalar::<Secp256k1>::from(10);
409/// assert_eq!(h.evaluate(&x), f.evaluate(&x) - &g.evaluate(&x));
410/// ```
411impl<E: Curve> ops::Sub for &Polynomial<E> {
412 type Output = Polynomial<E>;
413 fn sub(self, g: Self) -> Self::Output {
414 let len1 = self.coefficients.len();
415 let len2 = g.coefficients.len();
416
417 let overlapped = self
418 .coefficients()
419 .iter()
420 .zip(g.coefficients())
421 .map(|(f_coef, g_coef)| f_coef - g_coef);
422 let tail = if len1 < len2 {
423 g.coefficients()[len1..].iter().map(|x| -x).collect()
424 } else {
425 self.coefficients()[len2..].to_vec()
426 };
427
428 Polynomial::from_coefficients(overlapped.chain(tail.into_iter()).collect())
429 }
430}