-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathunivariate.py
159 lines (136 loc) · 5.57 KB
/
univariate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from algebra import *
class Polynomial:
def __init__( self, coefficients ):
self.coefficients = [c for c in coefficients]
def degree( self ):
if self.coefficients == []:
return -1
zero = FieldElement(0)
if self.coefficients == [zero] * len(self.coefficients):
return -1
maxindex = 0
for i in range(len(self.coefficients)):
if self.coefficients[i] != zero:
maxindex = i
return maxindex
def __neg__( self ):
return Polynomial([-c for c in self.coefficients])
def __add__( self, other ):
if self.degree() == -1:
return other
elif other.degree() == -1:
return self
field = self.coefficients[0].field
coeffs = [0] * max(len(self.coefficients), len(other.coefficients))
for i in range(len(self.coefficients)):
coeffs[i] = coeffs[i] + self.coefficients[i]
for i in range(len(other.coefficients)):
coeffs[i] = coeffs[i] + other.coefficients[i]
return Polynomial(coeffs)
def __sub__( self, other ):
return self.__add__(-other)
def __mul__(self, other ):
if self.coefficients == [] or other.coefficients == []:
return Polynomial([])
zero = FieldElement(0)
buf = [zero] * (len(self.coefficients) + len(other.coefficients) - 1)
for i in range(len(self.coefficients)):
if self.coefficients[i] == 0:
continue # optimization for sparse polynomials
for j in range(len(other.coefficients)):
buf[i+j] = buf[i+j] + self.coefficients[i] * other.coefficients[j]
return Polynomial(buf)
def __truediv__( self, other ):
quo, rem = Polynomial.divide(self, other)
assert(rem == 0), "cannot perform polynomial division because remainder is not zero"
return quo
def __mod__( self, other ):
quo, rem = Polynomial.divide(self, other)
return rem
def __eq__( self, other ):
if self.degree() != other.degree():
return False
if self.degree() == -1:
return True
return all(self.coefficients[i] == other.coefficients[i] for i in range(len(self.coefficients)))
def __neq__( self, other ):
return not self.__eq__(other)
def is_zero( self ):
if self.degree() == -1:
return True
return False
def __str__( self ):
return "[" + ",".join(s.__str__() for s in self.coefficients) + "]"
def leading_coefficient( self ):
return self.coefficients[self.degree()]
def divide( numerator, denominator ):
if denominator.degree() == -1:
return None
if numerator.degree() < denominator.degree():
return (Polynomial([]), numerator)
remainder = Polynomial([n for n in numerator.coefficients])
quotient_coefficients = [0 for i in range(numerator.degree()-denominator.degree()+1)]
for i in range(numerator.degree()-denominator.degree()+1):
if remainder.degree() < denominator.degree():
break
coefficient = remainder.leading_coefficient() / denominator.leading_coefficient()
shift = remainder.degree() - denominator.degree()
subtractee = Polynomial([0] * shift + [coefficient]) * denominator
quotient_coefficients[shift] = coefficient
remainder = remainder - subtractee
quotient = Polynomial(quotient_coefficients)
return quotient, remainder
def is_zero( self ):
if self.coefficients == []:
return True
for c in self.coefficients:
if not (c == 0):
return False
return True
def interpolate_domain( domain, values ):
assert(len(domain) == len(values)), "number of elements in domain does not match number of values -- cannot interpolate"
assert(len(domain) > 0), "cannot interpolate between zero points"
x = Polynomial([0, 1])
acc = Polynomial([])
for i in range(len(domain)):
prod = Polynomial([values[i]])
for j in range(len(domain)):
if j == i:
continue
prod = prod * (x - Polynomial([domain[j]])) * Polynomial([(domain[i] - domain[j]).inverse()])
acc = acc + prod
return acc
def zerofier_domain( domain ):
field = domain[0].field
x = Polynomial([0, 1])
acc = Polynomial([1])
for d in domain:
acc = acc * (x - Polynomial([d]))
return acc
def evaluate( self, point ):
xi = 1
value = 0
for c in self.coefficients:
value = value + c * xi
xi = xi * point
return value
def evaluate_domain( self, domain ):
return [self.evaluate(d) for d in domain]
def __xor__( self, exponent ):
if self.is_zero():
return Polynomial([])
if exponent == 0:
return Polynomial([self.coefficients[0].field.one()])
acc = Polynomial([self.coefficients[0].field.one()])
for i in reversed(range(len(bin(exponent)[2:]))):
acc = acc * acc
if (1 << i) & exponent != 0:
acc = acc * self
return acc
def scale( self, factor ):
return Polynomial([(factor^i) * self.coefficients[i] for i in range(len(self.coefficients))])
def test_colinearity( points ):
domain = [p[0] for p in points]
values = [p[1] for p in points]
polynomial = Polynomial.interpolate_domain(domain, values)
return polynomial.degree() == 1