symath 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +616 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/symath/definition/abs.rb +48 -0
- data/lib/symath/definition/arccos.rb +25 -0
- data/lib/symath/definition/arccot.rb +23 -0
- data/lib/symath/definition/arccsc.rb +24 -0
- data/lib/symath/definition/arcsec.rb +24 -0
- data/lib/symath/definition/arcsin.rb +25 -0
- data/lib/symath/definition/arctan.rb +23 -0
- data/lib/symath/definition/bounds.rb +39 -0
- data/lib/symath/definition/codiff.rb +31 -0
- data/lib/symath/definition/constant.rb +111 -0
- data/lib/symath/definition/cos.rb +17 -0
- data/lib/symath/definition/cot.rb +17 -0
- data/lib/symath/definition/csc.rb +17 -0
- data/lib/symath/definition/curl.rb +27 -0
- data/lib/symath/definition/d.rb +62 -0
- data/lib/symath/definition/div.rb +27 -0
- data/lib/symath/definition/exp.rb +112 -0
- data/lib/symath/definition/fact.rb +55 -0
- data/lib/symath/definition/flat.rb +31 -0
- data/lib/symath/definition/function.rb +197 -0
- data/lib/symath/definition/grad.rb +23 -0
- data/lib/symath/definition/hodge.rb +23 -0
- data/lib/symath/definition/int.rb +75 -0
- data/lib/symath/definition/laplacian.rb +23 -0
- data/lib/symath/definition/lmd.rb +97 -0
- data/lib/symath/definition/ln.rb +45 -0
- data/lib/symath/definition/number.rb +51 -0
- data/lib/symath/definition/operator.rb +228 -0
- data/lib/symath/definition/sec.rb +17 -0
- data/lib/symath/definition/sharp.rb +31 -0
- data/lib/symath/definition/sin.rb +17 -0
- data/lib/symath/definition/sqrt.rb +62 -0
- data/lib/symath/definition/tan.rb +17 -0
- data/lib/symath/definition/trig.rb +95 -0
- data/lib/symath/definition/variable.rb +284 -0
- data/lib/symath/definition/xd.rb +28 -0
- data/lib/symath/definition.rb +205 -0
- data/lib/symath/equation.rb +67 -0
- data/lib/symath/fraction.rb +177 -0
- data/lib/symath/matrix.rb +252 -0
- data/lib/symath/minus.rb +125 -0
- data/lib/symath/operation/differential.rb +167 -0
- data/lib/symath/operation/distributivelaw.rb +367 -0
- data/lib/symath/operation/exterior.rb +64 -0
- data/lib/symath/operation/integration.rb +329 -0
- data/lib/symath/operation/match.rb +166 -0
- data/lib/symath/operation/normalization.rb +458 -0
- data/lib/symath/operation.rb +36 -0
- data/lib/symath/operator.rb +163 -0
- data/lib/symath/parser.rb +473 -0
- data/lib/symath/parser.y +129 -0
- data/lib/symath/poly/dup.rb +835 -0
- data/lib/symath/poly/galois.rb +621 -0
- data/lib/symath/poly.rb +142 -0
- data/lib/symath/power.rb +224 -0
- data/lib/symath/product.rb +183 -0
- data/lib/symath/sum.rb +174 -0
- data/lib/symath/type.rb +282 -0
- data/lib/symath/value.rb +372 -0
- data/lib/symath/version.rb +3 -0
- data/lib/symath/wedge.rb +48 -0
- data/lib/symath.rb +157 -0
- data/symath.gemspec +39 -0
- metadata +160 -0
@@ -0,0 +1,835 @@
|
|
1
|
+
require 'symath/poly'
|
2
|
+
require 'symath/poly/galois'
|
3
|
+
require 'cmath'
|
4
|
+
|
5
|
+
module SyMath
|
6
|
+
# Class representing a univariate polynomial. The polynomial is represented
|
7
|
+
# by an array in of the polynomial coefficients in 'dense form', i.e.
|
8
|
+
# zero-coefficients are included in the list. The coefficients are stored
|
9
|
+
# in decreasing order starting with the highest degree, and ending with the
|
10
|
+
# constant.
|
11
|
+
class Poly::DUP < Poly
|
12
|
+
def initialize(args)
|
13
|
+
if args.is_a?(SyMath::Value)
|
14
|
+
init_from_exp(args)
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
if args.key?(:arr)
|
19
|
+
init_from_array(args[:arr], args[:var])
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
raise 'Bad arguments for Poly::DUP constructor'
|
24
|
+
end
|
25
|
+
|
26
|
+
def init_from_array(arr, var)
|
27
|
+
@arr = arr
|
28
|
+
@var = var
|
29
|
+
end
|
30
|
+
|
31
|
+
def init_from_exp(e)
|
32
|
+
# From this point, expect e to be a SyMath::Value
|
33
|
+
error = 'Expression ' + e.to_s + ' is not an univariate polynomial'
|
34
|
+
max_degree = 0
|
35
|
+
terms = {}
|
36
|
+
|
37
|
+
# Assert that this really is an univariate polynomial, and build
|
38
|
+
# the dup array representation.
|
39
|
+
e.terms.each do |s|
|
40
|
+
var = nil
|
41
|
+
c = 1
|
42
|
+
d = 0
|
43
|
+
|
44
|
+
s.factors.each do |f|
|
45
|
+
if f.is_a?(SyMath::Fraction)
|
46
|
+
raise error
|
47
|
+
end
|
48
|
+
|
49
|
+
if f == -1
|
50
|
+
c *= -1
|
51
|
+
elsif f.is_number?
|
52
|
+
c *= f.value
|
53
|
+
else
|
54
|
+
if !var.nil?
|
55
|
+
raise error
|
56
|
+
end
|
57
|
+
|
58
|
+
var = f.base
|
59
|
+
if !var.is_a?(SyMath::Definition::Variable)
|
60
|
+
raise error
|
61
|
+
end
|
62
|
+
|
63
|
+
if !f.exponent.is_number?
|
64
|
+
raise error
|
65
|
+
end
|
66
|
+
d = f.exponent.value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if !var.nil?
|
71
|
+
if @var.nil?
|
72
|
+
@var = var
|
73
|
+
elsif @var != var
|
74
|
+
raise error
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if terms.key?(d)
|
79
|
+
terms[d] += c
|
80
|
+
else
|
81
|
+
terms[d] = c
|
82
|
+
end
|
83
|
+
|
84
|
+
if d > max_degree
|
85
|
+
max_degree = d
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
@arr = (0..max_degree).to_a.reverse.map do |d|
|
90
|
+
if terms.key?(d)
|
91
|
+
terms[d]
|
92
|
+
else
|
93
|
+
0
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
strip!
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_galois(p)
|
101
|
+
return SyMath::Poly::Galois.new({ :dup => self, :p => p })
|
102
|
+
end
|
103
|
+
|
104
|
+
# Convenience method. Returns a a new instance from array, on the
|
105
|
+
# same variable as this instance.
|
106
|
+
def new_dup(arr)
|
107
|
+
return self.class.new({ :arr => arr, :var => @var })
|
108
|
+
end
|
109
|
+
|
110
|
+
# Convenience methods for creating some commonly used constant polynomials
|
111
|
+
def zero()
|
112
|
+
return new_dup([0])
|
113
|
+
end
|
114
|
+
|
115
|
+
def one()
|
116
|
+
return new_dup([1])
|
117
|
+
end
|
118
|
+
|
119
|
+
def minus_one()
|
120
|
+
return new_dup([-1])
|
121
|
+
end
|
122
|
+
|
123
|
+
def factor
|
124
|
+
# Transform to primitive form
|
125
|
+
(cont, g) = primitive
|
126
|
+
|
127
|
+
# Transform left coefficient to positive
|
128
|
+
if g.lc < 0
|
129
|
+
cont = -cont
|
130
|
+
g = -g
|
131
|
+
end
|
132
|
+
|
133
|
+
n = g.degree
|
134
|
+
|
135
|
+
# Handle some rivial cases
|
136
|
+
if n <= 0
|
137
|
+
# 0, cont
|
138
|
+
return [cont, []]
|
139
|
+
elsif n == 1
|
140
|
+
# cont*(a*x + b)
|
141
|
+
return [cont, [[g, 1]]]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Remove square factors
|
145
|
+
g = g.sqf_part
|
146
|
+
|
147
|
+
# Use the zassenhaus algorithm to compute candidate factors
|
148
|
+
h = g.zassenhaus
|
149
|
+
|
150
|
+
# Check each of the candidate factors by dividing the original
|
151
|
+
# polynomial with each of them until the quotient is 1.
|
152
|
+
factors = trial_division(h)
|
153
|
+
|
154
|
+
return [cont, factors]
|
155
|
+
end
|
156
|
+
|
157
|
+
def trial_division(factors)
|
158
|
+
result = []
|
159
|
+
f = self
|
160
|
+
|
161
|
+
factors.each do |factor|
|
162
|
+
k = 0
|
163
|
+
|
164
|
+
while true
|
165
|
+
(q, r) = f.div(factor)
|
166
|
+
|
167
|
+
if r.zero?
|
168
|
+
f = q
|
169
|
+
k += 1
|
170
|
+
else
|
171
|
+
break
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result << [factor, k]
|
176
|
+
end
|
177
|
+
|
178
|
+
return sort_factors_multiple(result)
|
179
|
+
end
|
180
|
+
|
181
|
+
def hensel_step(m, g, h, s, t)
|
182
|
+
mm = m**2
|
183
|
+
|
184
|
+
e = (self - g*h) % mm
|
185
|
+
|
186
|
+
(q, r) = (s*e).div(h)
|
187
|
+
|
188
|
+
q = q % mm
|
189
|
+
r = r % mm
|
190
|
+
|
191
|
+
u = t*e + q*g
|
192
|
+
|
193
|
+
gg = (g + u) % mm
|
194
|
+
hh = (h + r) % mm
|
195
|
+
|
196
|
+
u = s*gg + t*hh
|
197
|
+
b = (u - one) % mm
|
198
|
+
|
199
|
+
(c, d) = (s*b).div(hh)
|
200
|
+
|
201
|
+
c = c % mm
|
202
|
+
d = d % mm
|
203
|
+
|
204
|
+
u = t*b + c*gg
|
205
|
+
ss = (s - d) % mm
|
206
|
+
tt = (t - u) % mm
|
207
|
+
|
208
|
+
return [gg, hh, ss, tt]
|
209
|
+
end
|
210
|
+
|
211
|
+
def hensel_lift(p, f_list, l)
|
212
|
+
r = f_list.size
|
213
|
+
lcf = lc
|
214
|
+
|
215
|
+
if r == 1
|
216
|
+
ff = self * gcdext(lcf, p**l)[1]
|
217
|
+
return [ ff % (p**l) ]
|
218
|
+
end
|
219
|
+
|
220
|
+
m = p
|
221
|
+
k = r / 2
|
222
|
+
d = CMath.log(l, 2).ceil
|
223
|
+
|
224
|
+
g = new_dup([lcf]).to_galois(p)
|
225
|
+
|
226
|
+
f_list[0..k - 1].each do |f_i|
|
227
|
+
g = g*f_i.to_galois(p)
|
228
|
+
end
|
229
|
+
|
230
|
+
h = f_list[k].to_galois(p)
|
231
|
+
|
232
|
+
f_list[k + 1..-1].each do |f_i|
|
233
|
+
h = h*f_i.to_galois(p)
|
234
|
+
end
|
235
|
+
|
236
|
+
(s, t, x) = g.gcdex(h)
|
237
|
+
|
238
|
+
g = g.to_dup
|
239
|
+
h = h.to_dup
|
240
|
+
s = s.to_dup
|
241
|
+
t = t.to_dup
|
242
|
+
|
243
|
+
d.times do
|
244
|
+
(g, h, s, t) = hensel_step(m, g, h, s, t)
|
245
|
+
m = m**2
|
246
|
+
end
|
247
|
+
|
248
|
+
return g.hensel_lift(p, f_list[0..k - 1], l) +
|
249
|
+
h.hensel_lift(p, f_list[k..-1], l)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Zassenhaus algorithm for factorizing square free polynomial
|
253
|
+
def zassenhaus()
|
254
|
+
n = degree
|
255
|
+
|
256
|
+
# Trivial case, a*x + b
|
257
|
+
if n == 1
|
258
|
+
return [self.clone]
|
259
|
+
end
|
260
|
+
|
261
|
+
# Calculate bound of px:
|
262
|
+
# n = deg(f)
|
263
|
+
# B = sqrt(n + 1)*2^n*max_norm(f)*lc(f)
|
264
|
+
# C = (n + 1)^2n*A^(2n - 1)
|
265
|
+
# gm = 2log(cc,2)
|
266
|
+
# bound = 2*gm*ln(gm)
|
267
|
+
fc = self[-1]
|
268
|
+
aa = max_norm
|
269
|
+
b = lc
|
270
|
+
bb = (CMath.sqrt(n + 1).floor*2**n*aa*b).abs.to_i # Integer square root??
|
271
|
+
cc = ((n + 1)**(2*n)*aa**(2*n - 1)).to_i
|
272
|
+
gamma = (2*CMath.log(cc, 2)).ceil
|
273
|
+
bound = (2*gamma*CMath.log(gamma)).to_i
|
274
|
+
|
275
|
+
a = []
|
276
|
+
|
277
|
+
# Choose a prime number p such that f be square free in Z_p
|
278
|
+
# if there are many factors in Z_p, choose among a few different p
|
279
|
+
# the one with fewer factors
|
280
|
+
(3..bound).each do |px|
|
281
|
+
# Skip non prime px and px which do not divide lc(f)
|
282
|
+
if !Prime.prime?(px) or (b % px) == 0
|
283
|
+
next
|
284
|
+
end
|
285
|
+
|
286
|
+
# px = convert(px) ???
|
287
|
+
|
288
|
+
# Convert f to a galois field of order px
|
289
|
+
ff = self.to_galois(px)
|
290
|
+
|
291
|
+
# Skip if ff has square factors
|
292
|
+
if !ff.sqf_p
|
293
|
+
next
|
294
|
+
end
|
295
|
+
|
296
|
+
# Factorize ff and store all factors together with its order px
|
297
|
+
fsqfx = ff.factor_sqf[1]
|
298
|
+
a << [px, fsqfx]
|
299
|
+
|
300
|
+
if fsqfx.size < 15 or a.size > 4
|
301
|
+
break
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Select the factor list with the fewest factors.
|
306
|
+
(p, fsqf) = a.min { |x| x[1].size }
|
307
|
+
l = CMath.log(2*bb + 1, p).ceil
|
308
|
+
|
309
|
+
# Convert the factors back to integer polynomials
|
310
|
+
modular = fsqf.map { |ff| ff.to_dup }
|
311
|
+
|
312
|
+
# Hensel lift of modular -> g
|
313
|
+
g = hensel_lift(p, modular, l)
|
314
|
+
|
315
|
+
# Start with T as the set of factors in array g.
|
316
|
+
tt = (0..g.size - 1).to_a
|
317
|
+
factors = []
|
318
|
+
s = 1
|
319
|
+
pl = p**l # pl =~ 2*bb + 1
|
320
|
+
|
321
|
+
f = self
|
322
|
+
|
323
|
+
while 2*s <= tt.size
|
324
|
+
inc_s = true
|
325
|
+
|
326
|
+
tt.combination(s).each do |ss|
|
327
|
+
# Calculate G as the product of the subset S of factors. Lift
|
328
|
+
# the constant coefficient of G.
|
329
|
+
gg = new_dup([b])
|
330
|
+
ss.each { |i| gg = gg*g[i] }
|
331
|
+
gg = (gg % pl).primitive[1]
|
332
|
+
q = gg[-1]
|
333
|
+
|
334
|
+
# If it does not divide the input polynomial constant (fc), G
|
335
|
+
# does not divide the input polynomial.
|
336
|
+
if q != 0 and fc % q != 0
|
337
|
+
next
|
338
|
+
end
|
339
|
+
|
340
|
+
tt_new = tt - ss
|
341
|
+
|
342
|
+
# Calculate H as the product of the remaining factors in T.
|
343
|
+
hh = new_dup([b])
|
344
|
+
tt_new.each { |i| hh = hh*g[i] }
|
345
|
+
hh = hh % pl
|
346
|
+
|
347
|
+
# If the norm of the candidate G and the remaining H are bigger than
|
348
|
+
# the bound B, we have a valid candidate.
|
349
|
+
# - Store it in the factors list
|
350
|
+
# - Remove its corresponding selection from T
|
351
|
+
# - Continue with H as the remaining polynomial
|
352
|
+
if gg.l1_norm*hh.l1_norm <= bb
|
353
|
+
tt = tt_new
|
354
|
+
|
355
|
+
gg = gg.primitive[1]
|
356
|
+
f = hh.primitive[1]
|
357
|
+
|
358
|
+
factors << gg
|
359
|
+
b = f.lc
|
360
|
+
|
361
|
+
inc_s = false
|
362
|
+
break
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
s += 1 if inc_s
|
367
|
+
end
|
368
|
+
|
369
|
+
return factors + [f]
|
370
|
+
end
|
371
|
+
|
372
|
+
# Return square free part of f
|
373
|
+
def sqf_part()
|
374
|
+
# Trivial case
|
375
|
+
if zero?
|
376
|
+
return self.clone
|
377
|
+
end
|
378
|
+
|
379
|
+
if lc < 0
|
380
|
+
f = -self
|
381
|
+
else
|
382
|
+
f = self
|
383
|
+
end
|
384
|
+
|
385
|
+
sqf = f/f.gcd(f.diff)[0]
|
386
|
+
|
387
|
+
return sqf.primitive[1]
|
388
|
+
end
|
389
|
+
|
390
|
+
# Decompose a polynomial into square free components
|
391
|
+
def sqf_list()
|
392
|
+
(coeff, f) = primitive
|
393
|
+
|
394
|
+
if f.lc < 0
|
395
|
+
f = -f
|
396
|
+
coeff = -coeff
|
397
|
+
end
|
398
|
+
|
399
|
+
# Trivial case, constant polynomial
|
400
|
+
if f.degree <= 0
|
401
|
+
return coeff, []
|
402
|
+
end
|
403
|
+
|
404
|
+
res = []
|
405
|
+
i = 1
|
406
|
+
|
407
|
+
(g, p, q) = f.gcd(f.diff)
|
408
|
+
|
409
|
+
while true
|
410
|
+
h = q - p.diff
|
411
|
+
|
412
|
+
if h.zero?
|
413
|
+
res << [p, i]
|
414
|
+
break
|
415
|
+
end
|
416
|
+
|
417
|
+
(g, p, q) = p.gcd(h)
|
418
|
+
|
419
|
+
if g.degree > 0
|
420
|
+
res << [g, i]
|
421
|
+
end
|
422
|
+
|
423
|
+
i += 1
|
424
|
+
end
|
425
|
+
|
426
|
+
return [coeff, res]
|
427
|
+
end
|
428
|
+
|
429
|
+
# Fast differentiation of polynomial
|
430
|
+
def diff
|
431
|
+
d = degree
|
432
|
+
res = @arr.each_with_index.map { |e, i| e*(d - i) }
|
433
|
+
res.pop
|
434
|
+
|
435
|
+
return new_dup(res)
|
436
|
+
end
|
437
|
+
|
438
|
+
# Extended gcd for two integers
|
439
|
+
def gcdext(x, y)
|
440
|
+
if x < 0
|
441
|
+
g, a, b = gcdext(-x, y)
|
442
|
+
return [g, -a, b]
|
443
|
+
end
|
444
|
+
if y < 0
|
445
|
+
g, a, b = gcdext(x, -y)
|
446
|
+
return [g, a, -b]
|
447
|
+
end
|
448
|
+
r0, r1 = x, y
|
449
|
+
a0 = b1 = 1
|
450
|
+
a1 = b0 = 0
|
451
|
+
until r1.zero?
|
452
|
+
q = r0 / r1
|
453
|
+
r0, r1 = r1, r0 - q*r1
|
454
|
+
a0, a1 = a1, a0 - q*a1
|
455
|
+
b0, b1 = b1, b0 - q*b1
|
456
|
+
end
|
457
|
+
|
458
|
+
return [r0, a0, b0]
|
459
|
+
end
|
460
|
+
|
461
|
+
# FIXME: Implement heuristic gcd?
|
462
|
+
def gcd(g)
|
463
|
+
# Trivial cases
|
464
|
+
if zero? and g.zero?
|
465
|
+
return [zero, zero, zero]
|
466
|
+
end
|
467
|
+
if zero?
|
468
|
+
if g.lc >= 0
|
469
|
+
return [g, zero, one]
|
470
|
+
else
|
471
|
+
return [-g, zero, minus_one]
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
if g.zero?
|
476
|
+
if lc >= 0
|
477
|
+
return [f, one, zero]
|
478
|
+
else
|
479
|
+
return [-f, zero, one]
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
(fc, ff) = primitive
|
484
|
+
(gc, gg) = g.primitive
|
485
|
+
|
486
|
+
c = fc.gcd(gc)
|
487
|
+
|
488
|
+
h = subresultants(g)[-1]
|
489
|
+
h = h.primitive[1]
|
490
|
+
|
491
|
+
if (h.lc < 0)
|
492
|
+
c = -c
|
493
|
+
end
|
494
|
+
|
495
|
+
h = h*c
|
496
|
+
|
497
|
+
cff = self/h
|
498
|
+
cfg = g/h
|
499
|
+
|
500
|
+
return [h, cff, cfg]
|
501
|
+
end
|
502
|
+
|
503
|
+
# Calculate subresultants of polynomials self and g
|
504
|
+
def subresultants(g)
|
505
|
+
f = self
|
506
|
+
|
507
|
+
n = f.degree
|
508
|
+
m = g.degree
|
509
|
+
|
510
|
+
if n < m
|
511
|
+
f, g = g, f
|
512
|
+
n, m = m, n
|
513
|
+
end
|
514
|
+
|
515
|
+
if f.zero?
|
516
|
+
return []
|
517
|
+
end
|
518
|
+
|
519
|
+
if g.zero?
|
520
|
+
return [f.clone]
|
521
|
+
end
|
522
|
+
|
523
|
+
r = [f.clone, g.clone]
|
524
|
+
d = n - m
|
525
|
+
|
526
|
+
b = (-1)**(d + 1)
|
527
|
+
|
528
|
+
h = f.pseudo_rem(g)*b
|
529
|
+
|
530
|
+
lc = g.lc
|
531
|
+
c = -(lc**d)
|
532
|
+
|
533
|
+
while !h.zero?
|
534
|
+
k = h.degree
|
535
|
+
r << h
|
536
|
+
|
537
|
+
f, g, m, d = g, h, k, m - k
|
538
|
+
|
539
|
+
b = -lc * c**d
|
540
|
+
|
541
|
+
h = f.pseudo_rem(g)/b
|
542
|
+
|
543
|
+
lc = g.lc
|
544
|
+
|
545
|
+
if d > 1
|
546
|
+
q = c**(d - 1)
|
547
|
+
c = ((-lc)**d).to_i/q.to_i
|
548
|
+
else
|
549
|
+
c = -lc
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
return r
|
554
|
+
end
|
555
|
+
|
556
|
+
# Compute content and primitive form
|
557
|
+
def primitive()
|
558
|
+
if zero?
|
559
|
+
return [0, self.clone]
|
560
|
+
end
|
561
|
+
|
562
|
+
cont = content
|
563
|
+
|
564
|
+
if cont == 1
|
565
|
+
return [cont, self.clone]
|
566
|
+
else
|
567
|
+
return [cont, self/cont]
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
def content()
|
572
|
+
cont = 0
|
573
|
+
|
574
|
+
@arr.each do |c|
|
575
|
+
cont = cont.gcd(c)
|
576
|
+
|
577
|
+
break if cont == 1
|
578
|
+
end
|
579
|
+
|
580
|
+
return cont
|
581
|
+
end
|
582
|
+
|
583
|
+
def lshift(n)
|
584
|
+
if zero?
|
585
|
+
return zero
|
586
|
+
else
|
587
|
+
return new_dup(@arr + [0]*n)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def rshift(n)
|
592
|
+
return new_dup(@arr[0..-n - 1])
|
593
|
+
end
|
594
|
+
|
595
|
+
# Slice of polynomial between two degrees, >= a and < b
|
596
|
+
def slice(a, b)
|
597
|
+
s = @arr.size
|
598
|
+
aa = [0, s - a].max
|
599
|
+
bb = [0, s - b].max
|
600
|
+
|
601
|
+
if aa <= 0
|
602
|
+
return zero
|
603
|
+
end
|
604
|
+
|
605
|
+
ret = @arr[bb..aa-1]
|
606
|
+
|
607
|
+
if ret == []
|
608
|
+
return zero
|
609
|
+
else
|
610
|
+
return new_dup(ret + [0]*a)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def trunc(p)
|
615
|
+
ret = @arr.map do |e|
|
616
|
+
ep = e % p
|
617
|
+
if ep > p / 2
|
618
|
+
ep - p
|
619
|
+
else
|
620
|
+
ep
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
return new_dup(ret).strip!
|
625
|
+
end
|
626
|
+
|
627
|
+
def l1_norm()
|
628
|
+
return 0 if zero?
|
629
|
+
|
630
|
+
return @arr.map { |e| e.abs }.inject(:+)
|
631
|
+
end
|
632
|
+
|
633
|
+
# Sum two polynomials
|
634
|
+
def add(g)
|
635
|
+
ret = @arr.clone
|
636
|
+
|
637
|
+
if g.degree > degree
|
638
|
+
(g.degree - degree).times { ret.unshift(0) }
|
639
|
+
end
|
640
|
+
|
641
|
+
(0..g.degree).each do |i|
|
642
|
+
ret[ret.size - i - 1] += g[g.degree - i]
|
643
|
+
end
|
644
|
+
|
645
|
+
return new_dup(ret).strip!
|
646
|
+
end
|
647
|
+
|
648
|
+
# Subtract a polynomial from this one
|
649
|
+
def sub(g)
|
650
|
+
ret = @arr.clone
|
651
|
+
|
652
|
+
if g.degree > degree
|
653
|
+
(g.degree - degree).times { ret.unshift(0) }
|
654
|
+
end
|
655
|
+
|
656
|
+
(0..g.degree).each do |i|
|
657
|
+
ret[ret.size - i - 1] -= g[g.degree - i]
|
658
|
+
end
|
659
|
+
|
660
|
+
return new_dup(ret).strip!
|
661
|
+
end
|
662
|
+
|
663
|
+
# Return the negative of the polynomial
|
664
|
+
def neg()
|
665
|
+
return new_dup(@arr.map { |t| -t })
|
666
|
+
end
|
667
|
+
|
668
|
+
def mul(g)
|
669
|
+
if self == g
|
670
|
+
return self**2
|
671
|
+
end
|
672
|
+
|
673
|
+
if zero? and g.zero?
|
674
|
+
return zero
|
675
|
+
end
|
676
|
+
|
677
|
+
df = degree
|
678
|
+
dg = g.degree
|
679
|
+
|
680
|
+
n = [df, dg].max + 1
|
681
|
+
|
682
|
+
if n < 100
|
683
|
+
h = []
|
684
|
+
|
685
|
+
(0..df + dg).each do |i|
|
686
|
+
coeff = 0
|
687
|
+
|
688
|
+
a = [0, i - dg].max
|
689
|
+
b = [df, i].min
|
690
|
+
(a..b).each do |j|
|
691
|
+
coeff += self[j]*g[i - j]
|
692
|
+
end
|
693
|
+
h << coeff
|
694
|
+
end
|
695
|
+
|
696
|
+
return new_dup(h).strip!
|
697
|
+
else
|
698
|
+
# Use Karatsuba's algorithm for large polygons.
|
699
|
+
n2 = n/2
|
700
|
+
|
701
|
+
fl = slice(0, n2)
|
702
|
+
gl = g.slice(0, n2)
|
703
|
+
|
704
|
+
fh = slice(n2, n).rshift(n2)
|
705
|
+
gh = g.slice(n2, n).rshift(n2)
|
706
|
+
|
707
|
+
lo = fl*gl
|
708
|
+
hi = fh*gh
|
709
|
+
|
710
|
+
mid = (fl + fh)*(gl + gh)
|
711
|
+
mid -= lo + hi
|
712
|
+
|
713
|
+
return lo + mid.lshift(n2) + hi.lshift(2*n2)
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
def mul_ground(c)
|
718
|
+
ret = @arr.map { |t| t*c }
|
719
|
+
return new_dup(ret).strip!
|
720
|
+
end
|
721
|
+
|
722
|
+
# Multiply a polynomial with a single term.
|
723
|
+
def mul_term(c, j)
|
724
|
+
ret = @arr.map { |t| t*c }
|
725
|
+
j.times { ret.push(0) }
|
726
|
+
|
727
|
+
return new_dup(ret).strip!
|
728
|
+
end
|
729
|
+
|
730
|
+
# Compute pseudo remainder of self / g
|
731
|
+
def pseudo_rem(g)
|
732
|
+
df = self.degree
|
733
|
+
dg = g.degree
|
734
|
+
|
735
|
+
r = self
|
736
|
+
dr = df
|
737
|
+
|
738
|
+
if g.zero?
|
739
|
+
raise 'Division by zero'
|
740
|
+
elsif df < dg
|
741
|
+
return self.clone # self is remainder
|
742
|
+
end
|
743
|
+
|
744
|
+
n = df - dg + 1
|
745
|
+
|
746
|
+
while true
|
747
|
+
j = dr - dg
|
748
|
+
n -= 1
|
749
|
+
|
750
|
+
rr = r*g.lc
|
751
|
+
gg = g.mul_term(r.lc, j)
|
752
|
+
r = rr - gg
|
753
|
+
|
754
|
+
_dr = dr
|
755
|
+
dr = r.degree
|
756
|
+
|
757
|
+
if dr < dg
|
758
|
+
break
|
759
|
+
elsif !(dr < _dr)
|
760
|
+
raise 'Polynomial division failed'
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
return r*g.lc**n
|
765
|
+
end
|
766
|
+
|
767
|
+
def div(g)
|
768
|
+
# returns qv and r such that:
|
769
|
+
# f = fv*qv + r
|
770
|
+
df = degree
|
771
|
+
dg = g.degree
|
772
|
+
|
773
|
+
if g.zero?
|
774
|
+
raise 'Division by zero'
|
775
|
+
elsif df < dg
|
776
|
+
return [zero, self.clone] # no quotient, f is remainder
|
777
|
+
end
|
778
|
+
|
779
|
+
# Start with f as remainder, no quotient
|
780
|
+
q = zero
|
781
|
+
r = self
|
782
|
+
dr = df
|
783
|
+
|
784
|
+
lc_g = g.lc
|
785
|
+
|
786
|
+
while true
|
787
|
+
lc_r = r.lc
|
788
|
+
|
789
|
+
if (lc_r % lc_g) != 0
|
790
|
+
break
|
791
|
+
end
|
792
|
+
|
793
|
+
c = lc_r / lc_g
|
794
|
+
j = dr - dg
|
795
|
+
|
796
|
+
q = q + one.mul_term(c, j)
|
797
|
+
r = r - g.mul_term(c, j)
|
798
|
+
|
799
|
+
_dr = dr
|
800
|
+
dr = r.degree
|
801
|
+
|
802
|
+
if dr < dg
|
803
|
+
break
|
804
|
+
elsif dr >= _dr
|
805
|
+
raise 'Polynomial division failed'
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
return [q, r]
|
810
|
+
end
|
811
|
+
|
812
|
+
def quo(g)
|
813
|
+
return div(g)[0]
|
814
|
+
end
|
815
|
+
|
816
|
+
# Quotient by constant for each coefficient
|
817
|
+
def quo_ground(c)
|
818
|
+
ret = @arr.map { |t| t.to_i/c.to_i }
|
819
|
+
|
820
|
+
return new_dup(ret).strip!
|
821
|
+
end
|
822
|
+
|
823
|
+
def max_norm()
|
824
|
+
if zero?
|
825
|
+
return 0
|
826
|
+
else
|
827
|
+
return @arr.map { |e| e.abs }.max
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def to_s()
|
832
|
+
return @arr.to_s
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|