44module seal ::polynomial ;
55
66use seal::gf256;
7+ use std::u64::range_do_eq;
78
89const EIncomatibleInputLengths : u64 = 1 ;
910
@@ -15,10 +16,13 @@ public struct Polynomial has copy, drop, store {
1516
1617/// Evaluate a polynomial at a given point.
1718public fun evaluate (p: &Polynomial , x: u8 ): u8 {
18- let mut result = 0 ;
19+ if (p.coefficients.is_empty ()) {
20+ return 0
21+ };
1922 let n = p.coefficients.length ();
20- n.do !(|i| {
21- result = gf256::add (gf256::mul (result, x), p.coefficients[n - i - 1 ]);
23+ let mut result = p.coefficients[n - 1 ];
24+ (n - 1 ).do !(|i| {
25+ result = gf256::add (gf256::mul (result, x), p.coefficients[n - i - 2 ]);
2226 });
2327 result
2428}
@@ -28,39 +32,75 @@ public(package) fun get_constant_term(p: &Polynomial): u8 {
2832 else p.coefficients[0 ]
2933}
3034
31- /// Interpolate a polynomial p such that p(x_i) = y[i] for all i.
32- /// Panics if the lengths of x and y are not the same.
33- /// Panics if x contains duplicate values.
34- public (package ) fun interpolate (x: &vector <u8 >, y: &vector <u8 >): Polynomial {
35+ // Divide a polynomial by the monic linear polynomial x + c.
36+ // This assumes that the polynomial is divisible by the monic linear polynomial,
37+ // and it's not clear what the result will be otherwise.
38+ // Aborts if the polynomial is empty.
39+ fun div_exact_by_monic_linear (x: &Polynomial , c: u8 ): Polynomial {
40+ let n = x.coefficients.length ();
41+ let mut coefficients = vector ::empty ();
42+
43+ let mut previous = x.coefficients[n - 1 ];
44+ coefficients.push_back (previous);
45+
46+ range_do_eq !(1 , n - 2 , |i| {
47+ previous = gf256::sub (x.coefficients[n - i - 1 ], gf256::mul (previous, c));
48+ coefficients.push_back (previous);
49+ });
50+ coefficients.reverse ();
51+ Polynomial { coefficients }
52+ }
53+
54+ /// Same as interpolate, but the numerator product, \prod_i (x - x_i), is precomputed.
55+ fun interpolate_with_numerators (
56+ x: &vector <u8 >,
57+ y: &vector <u8 >,
58+ numerators: &vector <Polynomial >,
59+ ): Polynomial {
3560 assert !(x.length () == y.length (), EIncomatibleInputLengths );
3661 let n = x.length ();
3762 let mut sum = Polynomial { coefficients: vector [] };
3863 n.do !(|j| {
39- let mut product = Polynomial { coefficients: vector [ 1 ] } ;
64+ let mut denominator = 1 ;
4065 n.do !(|i| {
4166 if (i != j) {
42- product =
43- mul (
44- &product,
45- &div (&monic_linear (&x[i]), gf256::sub (x[j], x[i])),
46- );
67+ denominator = gf256::mul (denominator, gf256::sub (x[j], x[i]));
4768 };
4869 });
49- sum = add (&sum, &scale (&product, y[j]));
70+ sum =
71+ add (
72+ &sum,
73+ &numerators[j].scale (
74+ gf256::div (y[j], denominator),
75+ ),
76+ );
5077 });
5178 sum
5279}
5380
81+ /// Compute the numerators of the Lagrange polynomials for the given x values.
82+ fun compute_numerators (x: vector <u8 >): vector <Polynomial > {
83+ // The full numerator depends only on x, so we can compute it here
84+ let full_numerator = x.fold !(Polynomial { coefficients: vector [1 ] }, |product, x_j| {
85+ product.mul (&monic_linear (&x_j))
86+ });
87+ x.map_ref !(|x_j| div_exact_by_monic_linear (&full_numerator, *x_j))
88+ }
89+
5490/// Interpolate l polynomials p_1, ..., p_l such that p_i(x_j) = y[j][i] for all i, j.
5591/// The length of the input vectors must be the same.
5692/// The length of each vector in y must be the same (equal to the l above).
93+ /// Aborts if the input lengths are not compatible or if the vectors are empty.
5794public (package ) fun interpolate_all (x: &vector <u8 >, y: &vector <vector <u8 >>): vector <Polynomial > {
5895 assert !(x.length () == y.length (), EIncomatibleInputLengths );
5996 let l = y[0 ].length ();
6097 assert !(y.all !(|yi| yi.length () == l), EIncomatibleInputLengths );
98+
99+ // The numerators depend only on x, so we can compute them here
100+ let numerators = compute_numerators (*x);
61101 vector ::tabulate !(l, |i| {
62102 let yi = y.map_ref !(|yj| yj[i]);
63- interpolate (x, &yi)
103+ interpolate_with_numerators (x, &yi, &numerators )
64104 })
65105}
66106
@@ -101,17 +141,13 @@ fun mul(x: &Polynomial, y: &Polynomial): Polynomial {
101141 Polynomial { coefficients }
102142}
103143
104- fun div (x: &Polynomial , s: u8 ): Polynomial {
105- x.scale (gf256::div (1 , s))
106- }
107-
108144fun scale (x: &Polynomial , s: u8 ): Polynomial {
109145 Polynomial { coefficients: x.coefficients.map_ref !(|c| gf256::mul (*c, s)) }
110146}
111147
112- /// Return x - c
148+ /// Return x - c (same as x + c since GF256 is a binary field)
113149fun monic_linear (c: &u8 ): Polynomial {
114- Polynomial { coefficients: vector [gf256:: sub ( 0 , *c) , 1 ] }
150+ Polynomial { coefficients: vector [*c , 1 ] }
115151}
116152
117153#[test]
@@ -133,13 +169,23 @@ fun test_evaluate() {
133169 assert !(p.evaluate (1 ) == 0 );
134170 assert !(p.evaluate (2 ) == 9 );
135171 assert !(p.evaluate (3 ) == 8 );
172+
173+ // Test zero polynomial
174+ let p = Polynomial { coefficients: vector [] };
175+ assert !(p.evaluate (0 ) == 0 );
176+
177+ let p = Polynomial { coefficients: vector [3 ] };
178+ assert !(p.evaluate (0 ) == 3 );
179+ assert !(p.evaluate (1 ) == 3 );
180+ assert !(p.evaluate (2 ) == 3 );
181+ assert !(p.evaluate (3 ) == 3 );
136182}
137183
138184#[test]
139185fun test_interpolate () {
140186 let x = vector [1 , 2 , 3 ];
141187 let y = vector [7 , 11 , 17 ];
142- let p = interpolate (&x, &y);
188+ let p = interpolate_with_numerators (&x, &y, & compute_numerators (x) );
143189 assert !(p.coefficients == x"1d150f ");
144190 x.zip_do !(y, |x, y| assert !(p.evaluate (x) == y));
145191}
@@ -155,3 +201,12 @@ fun test_interpolate_all() {
155201 assert !(ps[1 ].evaluate (x) == y[1 ]);
156202 });
157203}
204+
205+ #[test]
206+ fun test_div_exact_by_monic_linear () {
207+ let x = Polynomial { coefficients: vector [1 , 2 , 3 , 4 , 5 , 6 , 7 ] };
208+ let monic_linear = monic_linear (&2 );
209+ let y = mul (&x, &monic_linear);
210+ let z = div_exact_by_monic_linear (&y, 2 );
211+ assert !(z == x);
212+ }
0 commit comments