symath 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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