symath 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|