Skip to content

Commit f78fe9a

Browse files
authored
[Move] Compute Lagrange denominator once (#367)
1 parent f8ad2d8 commit f78fe9a

File tree

1 file changed

+77
-22
lines changed

1 file changed

+77
-22
lines changed

move/seal/sources/polynomial.move

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module seal::polynomial;
55

66
use seal::gf256;
7+
use std::u64::range_do_eq;
78

89
const EIncomatibleInputLengths: u64 = 1;
910

@@ -15,10 +16,13 @@ public struct Polynomial has copy, drop, store {
1516

1617
/// Evaluate a polynomial at a given point.
1718
public 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.
5794
public(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-
108144
fun 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)
113149
fun 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]
139185
fun 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

Comments
 (0)