forked from ethereum/research
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
vub
authored and
vub
committed
Apr 23, 2017
1 parent
9cf2a9f
commit 3397bd9
Showing
4 changed files
with
217 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
modulus_poly = [1, 0, 0, 0, 0, 0, 0, 0, | ||
0, 0, 1, 0, 1, 0, 0, 1, | ||
1] | ||
modulus_poly_as_int = sum([(v << i) for i, v in enumerate(modulus_poly)]) | ||
degree = len(modulus_poly) - 1 | ||
|
||
two_to_the_degree = 2**degree | ||
two_to_the_degree_m1 = 2**degree - 1 | ||
|
||
def galoistpl(a): | ||
# 2 is not a primitive root, so we have to use 3 as our logarithm base | ||
if a * 2 < two_to_the_degree: | ||
return (a * 2) ^ a | ||
else: | ||
return (a * 2) ^ a ^ modulus_poly_as_int | ||
|
||
# Precomputing a log table for increased speed of addition and multiplication | ||
glogtable = [0] * (two_to_the_degree) | ||
gexptable = [] | ||
v = 1 | ||
for i in range(two_to_the_degree_m1): | ||
glogtable[v] = i | ||
gexptable.append(v) | ||
v = galoistpl(v) | ||
|
||
gexptable += gexptable + gexptable | ||
|
||
# Add two values in the Galois field | ||
def galois_add(x, y): | ||
return x ^ y | ||
|
||
# In binary fields, addition and subtraction are the same thing | ||
galois_sub = galois_add | ||
|
||
# Multiply two values in the Galois field | ||
def galois_mul(x, y): | ||
return 0 if x*y == 0 else gexptable[glogtable[x] + glogtable[y]] | ||
|
||
# Divide two values in the Galois field | ||
def galois_div(x, y): | ||
return 0 if x == 0 else gexptable[(glogtable[x] - glogtable[y]) % two_to_the_degree_m1] | ||
|
||
# Evaluate a polynomial at a point | ||
def eval_poly_at(p, x): | ||
if x == 0: | ||
return p[0] | ||
y = 0 | ||
logx = glogtable[x] | ||
for i, p_coeff in enumerate(p): | ||
if p_coeff: | ||
# Add x**i * coeff | ||
y ^= gexptable[(logx * i + glogtable[p_coeff]) % two_to_the_degree_m1] | ||
return y | ||
|
||
|
||
# Given p+1 y values and x values with no errors, recovers the original | ||
# p+1 degree polynomial. | ||
# Lagrange interpolation works roughly in the following way. | ||
# 1. Suppose you have a set of points, eg. x = [1, 2, 3], y = [2, 5, 10] | ||
# 2. For each x, generate a polynomial which equals its corresponding | ||
# y coordinate at that point and 0 at all other points provided. | ||
# 3. Add these polynomials together. | ||
|
||
def lagrange_interp(pieces, xs): | ||
# Generate master numerator polynomial, eg. (x - x1) * (x - x2) * ... * (x - xn) | ||
root = mk_root_2(xs) | ||
#print(root) | ||
assert len(root) == len(pieces) + 1 | ||
# print(root) | ||
# Generate the derivative | ||
d = derivative(root) | ||
# Generate denominators by evaluating numerator polys at each x | ||
denoms = multi_eval_2(d, xs) | ||
# denoms = [eval_poly_at(d, xs[i]) for i in range(len(xs))] | ||
# Generate output polynomial, which is the sum of the per-value numerator | ||
# polynomials rescaled to have the right y values | ||
return multi_root_derive(xs, [galois_div(p, d) for p, d in zip(pieces, denoms)]) | ||
|
||
def multi_root_derive(xs, muls): | ||
if len(xs) == 1: | ||
return [muls[0]] | ||
R1 = mk_root_2(xs[:len(xs) // 2]) | ||
R2 = mk_root_2(xs[len(xs) // 2:]) | ||
x1 = karatsuba_mul(R1, multi_root_derive(xs[len(xs) // 2:], muls[len(muls) // 2:]) + [0]) | ||
x2 = karatsuba_mul(R2, multi_root_derive(xs[:len(xs) // 2], muls[:len(muls) // 2]) + [0]) | ||
o = [v1 ^ v2 for v1, v2 in zip(x1, x2)][:len(xs)] | ||
# print(len(R1), len(x1), len(xs), len(o)) | ||
return o | ||
|
||
def multi_root_derive_1(xs, muls): | ||
o = [0] * len(xs) | ||
for i in range(len(xs)): | ||
_xs = xs[:i] + xs[(i+1):] | ||
root = mk_root_2(_xs) | ||
for j in range(len(root)): | ||
o[j] ^= galois_mul(root[j], muls[i]) | ||
return o | ||
|
||
a = 124 | ||
b = 8932 | ||
c = 12415 | ||
|
||
assert galois_mul(galois_add(a, b), c) == galois_add(galois_mul(a, c), galois_mul(b, c)) | ||
|
||
def karatsuba_mul(p1, p2): | ||
L = len(p1) | ||
# assert L == len(p2) | ||
if L <= 16: | ||
o = [0] * (L * 2) | ||
for i, v1 in enumerate(p1): | ||
for j, v2 in enumerate(p2): | ||
if v1 and v2: | ||
o[i + j] ^= gexptable[glogtable[v1] + glogtable[v2]] | ||
return o | ||
if L % 2: | ||
p1 = p1 + [0] | ||
p2 = p2 + [0] | ||
L += 1 | ||
halflen = L // 2 | ||
low1 = p1[:halflen] | ||
high1 = p1[halflen:] | ||
sum1 = [l ^ h for l, h in zip(low1, high1)] | ||
low2 = p2[:halflen] | ||
high2 = p2[halflen:] | ||
sum2 = [l ^ h for l, h in zip(low2, high2)] | ||
z2 = karatsuba_mul(high1, high2) | ||
z0 = karatsuba_mul(low1, low2) | ||
z1 = [m ^ _z0 ^ _z2 for m, _z0, _z2 in zip(karatsuba_mul(sum1, sum2), z0, z2)] | ||
o = z0[:halflen] + \ | ||
[a ^ b for a, b in zip(z0[halflen:], z1[:halflen])] + \ | ||
[a ^ b for a, b in zip(z2[:halflen], z1[halflen:])] + \ | ||
z2[halflen:] | ||
return o | ||
|
||
def mk_root_1(xs): | ||
root = [1] | ||
for x in xs: | ||
logx = glogtable[x] | ||
root.insert(0, 0) | ||
for j in range(len(root)-1): | ||
if root[j+1] and x: | ||
root[j] ^= gexptable[glogtable[root[j+1]] + logx] | ||
return root | ||
|
||
def mk_root_2(xs): | ||
if len(xs) >= 128: | ||
return karatsuba_mul(mk_root_2(xs[:len(xs) // 2]), mk_root_2(xs[len(xs) // 2:]))[:len(xs) + 1] | ||
root = [1] | ||
for x in xs: | ||
logx = glogtable[x] | ||
root.insert(0, 0) | ||
for j in range(len(root)-1): | ||
if root[j+1] and x: | ||
root[j] ^= gexptable[glogtable[root[j+1]] + logx] | ||
return root | ||
|
||
def derivative(root): | ||
return [0 if i % 2 else r for i, r in enumerate(root[1:])] | ||
|
||
# Credit to http://people.csail.mit.edu/madhu/ST12/scribe/lect06.pdf for the algorithm | ||
def xn_mod_poly(p): | ||
if len(p) == 1: | ||
return [galois_div(1, p[0])] | ||
halflen = len(p) // 2 | ||
lowinv = xn_mod_poly(p[:halflen]) | ||
submod_high = karatsuba_mul(lowinv, p[:halflen])[halflen:] | ||
med = karatsuba_mul(p[halflen:], lowinv)[:halflen] | ||
med_plus_high = [x ^ y for x, y in zip(med, submod_high)] | ||
highinv = karatsuba_mul(med_plus_high, lowinv) | ||
o = (lowinv + highinv)[:len(p)] | ||
# assert karatsuba_mul(o, p)[:len(p)] == [1] + [0] * (len(p) - 1) | ||
return o | ||
|
||
def mod(a, b): | ||
assert len(a) == 2 * (len(b) - 1) | ||
L = len(b) | ||
inv_rev_b = xn_mod_poly(b[::-1] + [0] * (len(a) - L))[:L] | ||
quot = karatsuba_mul(inv_rev_b, a[::-1][:L])[:L-1][::-1] | ||
subt = karatsuba_mul(b, quot + [0])[:-1] | ||
o = [x ^ y for x, y in zip(a[:L-1], subt[:L-1])] | ||
# assert [x^y for x, y in zip(karatsuba_mul(quot + [0], b), o)] == a | ||
return o | ||
|
||
def multi_eval_1(poly, xs): | ||
return [eval_poly_at(poly, x) for x in xs] | ||
|
||
def multi_eval_2(poly, xs): | ||
if len(xs) <= 1024: | ||
return [eval_poly_at(poly, x) for x in xs] | ||
halflen = len(xs) // 2 | ||
return multi_eval_2(mod(poly, mk_root_2(xs[:halflen])), xs[:halflen]) + \ | ||
multi_eval_2(mod(poly, mk_root_2(xs[halflen:])), xs[halflen:]) | ||
# [eval_poly_at(poly, xs[-2]), eval_poly_at(poly, xs[-1])] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters