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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +616 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/symath/definition/abs.rb +48 -0
  13. data/lib/symath/definition/arccos.rb +25 -0
  14. data/lib/symath/definition/arccot.rb +23 -0
  15. data/lib/symath/definition/arccsc.rb +24 -0
  16. data/lib/symath/definition/arcsec.rb +24 -0
  17. data/lib/symath/definition/arcsin.rb +25 -0
  18. data/lib/symath/definition/arctan.rb +23 -0
  19. data/lib/symath/definition/bounds.rb +39 -0
  20. data/lib/symath/definition/codiff.rb +31 -0
  21. data/lib/symath/definition/constant.rb +111 -0
  22. data/lib/symath/definition/cos.rb +17 -0
  23. data/lib/symath/definition/cot.rb +17 -0
  24. data/lib/symath/definition/csc.rb +17 -0
  25. data/lib/symath/definition/curl.rb +27 -0
  26. data/lib/symath/definition/d.rb +62 -0
  27. data/lib/symath/definition/div.rb +27 -0
  28. data/lib/symath/definition/exp.rb +112 -0
  29. data/lib/symath/definition/fact.rb +55 -0
  30. data/lib/symath/definition/flat.rb +31 -0
  31. data/lib/symath/definition/function.rb +197 -0
  32. data/lib/symath/definition/grad.rb +23 -0
  33. data/lib/symath/definition/hodge.rb +23 -0
  34. data/lib/symath/definition/int.rb +75 -0
  35. data/lib/symath/definition/laplacian.rb +23 -0
  36. data/lib/symath/definition/lmd.rb +97 -0
  37. data/lib/symath/definition/ln.rb +45 -0
  38. data/lib/symath/definition/number.rb +51 -0
  39. data/lib/symath/definition/operator.rb +228 -0
  40. data/lib/symath/definition/sec.rb +17 -0
  41. data/lib/symath/definition/sharp.rb +31 -0
  42. data/lib/symath/definition/sin.rb +17 -0
  43. data/lib/symath/definition/sqrt.rb +62 -0
  44. data/lib/symath/definition/tan.rb +17 -0
  45. data/lib/symath/definition/trig.rb +95 -0
  46. data/lib/symath/definition/variable.rb +284 -0
  47. data/lib/symath/definition/xd.rb +28 -0
  48. data/lib/symath/definition.rb +205 -0
  49. data/lib/symath/equation.rb +67 -0
  50. data/lib/symath/fraction.rb +177 -0
  51. data/lib/symath/matrix.rb +252 -0
  52. data/lib/symath/minus.rb +125 -0
  53. data/lib/symath/operation/differential.rb +167 -0
  54. data/lib/symath/operation/distributivelaw.rb +367 -0
  55. data/lib/symath/operation/exterior.rb +64 -0
  56. data/lib/symath/operation/integration.rb +329 -0
  57. data/lib/symath/operation/match.rb +166 -0
  58. data/lib/symath/operation/normalization.rb +458 -0
  59. data/lib/symath/operation.rb +36 -0
  60. data/lib/symath/operator.rb +163 -0
  61. data/lib/symath/parser.rb +473 -0
  62. data/lib/symath/parser.y +129 -0
  63. data/lib/symath/poly/dup.rb +835 -0
  64. data/lib/symath/poly/galois.rb +621 -0
  65. data/lib/symath/poly.rb +142 -0
  66. data/lib/symath/power.rb +224 -0
  67. data/lib/symath/product.rb +183 -0
  68. data/lib/symath/sum.rb +174 -0
  69. data/lib/symath/type.rb +282 -0
  70. data/lib/symath/value.rb +372 -0
  71. data/lib/symath/version.rb +3 -0
  72. data/lib/symath/wedge.rb +48 -0
  73. data/lib/symath.rb +157 -0
  74. data/symath.gemspec +39 -0
  75. 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