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,621 @@
1
+ require 'symath/poly'
2
+
3
+ module SyMath
4
+ # Class representing a galois field, i.e finite field
5
+ class Poly::Galois < Poly
6
+ attr_reader :p
7
+
8
+ # Create gl instance from dup or a gl of the same order.
9
+ def initialize(args)
10
+ if args.key?(:dup)
11
+ init_from_dup(args[:dup], args[:p])
12
+ return
13
+ end
14
+
15
+ if args.key?(:arr)
16
+ init_from_array(args[:arr], args[:p], args[:var])
17
+ return
18
+ end
19
+
20
+ raise 'Bad arguments for Poly::Galois constructor'
21
+ end
22
+
23
+ def init_from_dup(dup, p)
24
+ @arr = dup.arr.map { |t| t % p }
25
+ @p = p
26
+ @var = dup.var
27
+ end
28
+
29
+ def init_from_array(arr, p, var)
30
+ @arr = arr
31
+ @p = p
32
+ @var = var
33
+ end
34
+
35
+ # Convenience method for creating a new gl of the same order
36
+ # and the same variable
37
+ def new_gl(arr)
38
+ return SyMath::Poly::Galois.new({ :arr => arr, :p => @p, :var => @var })
39
+ end
40
+
41
+ def zero()
42
+ return new_gl([])
43
+ end
44
+
45
+ def one()
46
+ return new_gl([1])
47
+ end
48
+
49
+ def gl_x()
50
+ new_gl([1, 0])
51
+ end
52
+
53
+ # Assert that two fields have the same order
54
+ def assert_order(g)
55
+ if @p != g.p
56
+ raise 'Fields do not have the same order'
57
+ end
58
+ end
59
+
60
+ def to_dup()
61
+ ret = @arr.map do |e|
62
+ if e <= @p / 2
63
+ e
64
+ else
65
+ e - @p
66
+ end
67
+ end
68
+
69
+ return SyMath::Poly::DUP.new({ :arr => ret, :var => @var })
70
+ end
71
+
72
+ def invert(a)
73
+ p = @p
74
+ raise "No inverse - #{a} and #{p} not coprime" unless a.gcd(p) == 1
75
+
76
+ return p if p == 1
77
+
78
+ m0, inv, x0 = p, 1, 0
79
+
80
+ while a > 1
81
+ inv -= (a / p) * x0
82
+ a, p = p, a % p
83
+ inv, x0 = x0, inv
84
+ end
85
+
86
+ inv += m0 if inv < 0
87
+
88
+ return inv
89
+ end
90
+
91
+ def factor_sqf()
92
+ (lc, f) = monic
93
+
94
+ if f.degree < 1
95
+ return [lc, zero]
96
+ end
97
+
98
+ factors = f.zassenhaus
99
+
100
+ return [lc, factors]
101
+ end
102
+
103
+ # Return true if polynomial is square free
104
+ def sqf_p()
105
+ (lc, f) = monic
106
+
107
+ if f.zero?
108
+ return true
109
+ else
110
+ return f.gcd(f.diff).arr == [1]
111
+ end
112
+ end
113
+
114
+ def sqf_list()
115
+ n = 1
116
+ sqf = false
117
+ factors = []
118
+ r = @p
119
+
120
+ (lc, f) = monic
121
+
122
+ if degree < 1
123
+ return [lc, []]
124
+ end
125
+
126
+ f = self
127
+
128
+ while true
129
+ ff = f.diff
130
+
131
+ if !ff.zero?
132
+ g = f.gcd(ff)
133
+ h = f / g
134
+
135
+ i = 1
136
+
137
+ while h.arr != [1]
138
+ gg = g.gcd(h)
139
+ hh = h / gg
140
+
141
+ if hh.degree > 0
142
+ factors << [hh, i*n]
143
+ end
144
+
145
+ g = g / gg
146
+ h = gg
147
+ i += 1
148
+ end
149
+
150
+ if g.arr == [1]
151
+ sqf = true
152
+ else
153
+ f = g
154
+ end
155
+ end
156
+
157
+ if !sqf
158
+ d = f.degree/r
159
+
160
+ f = new_gl((0..d).map { |i| f[i*r] })
161
+ n = n*r
162
+ else
163
+ break
164
+ end
165
+ end
166
+
167
+ return [lc, factors]
168
+ end
169
+
170
+ def factor()
171
+ (lc, f) = monic
172
+
173
+ if f.degree < 1
174
+ return [lc, []]
175
+ end
176
+
177
+ factors = []
178
+
179
+ f.sqf_list[1].each do |g, n|
180
+ g.factor_sqf[1].each do |h|
181
+ factors << [h, n]
182
+ end
183
+ end
184
+
185
+ return [lc, sort_factors_multiple(factors)]
186
+ end
187
+
188
+ # Compute f**(p**n - 1) / 2 in GF(p)[x]/(g)
189
+ # (Utility function for edf_zassenhaus)
190
+ def pow_pnm1d2(n, g, b)
191
+ f = rem(g)
192
+ h = f
193
+ r = f
194
+
195
+ (n - 1).times do
196
+ h = h.frobenius_map(g, b)
197
+ r = (r*h) % g
198
+ end
199
+
200
+ return r.pow_mod((@p - 1)/2, g)
201
+ end
202
+
203
+ def frobenius_monomial_base()
204
+ n = degree
205
+
206
+ if n == 0
207
+ return []
208
+ end
209
+
210
+ b = []
211
+ b << one
212
+
213
+ if @p < n
214
+ (1..n - 1).each do |i|
215
+ mon = b[i - 1].lshift(@p)
216
+ b << mon % self
217
+ end
218
+ elsif n > 1
219
+ b << new_gl([1, 0]).pow_mod(@p, self)
220
+ (2..n - 1).each do |i|
221
+ b << (b[i - 1]*b[1]) % self
222
+ end
223
+ end
224
+
225
+ return b
226
+ end
227
+
228
+ def frobenius_map(g, b)
229
+ f = self
230
+ m = g.degree
231
+
232
+ if f.degree >= m
233
+ f = rem(g)
234
+ end
235
+
236
+ if f.zero?
237
+ return zero
238
+ end
239
+
240
+ n = f.degree
241
+ sf = new_gl([f[-1]])
242
+
243
+ (1..n).each do |i|
244
+ sf += b[i]*f[n - i]
245
+ end
246
+
247
+ return sf
248
+ end
249
+
250
+ # Deterministic distinct degree factorization
251
+ def ddf_zassenhaus()
252
+ x = gl_x
253
+
254
+ i = 1
255
+ g = x
256
+ factors = []
257
+
258
+ f = self
259
+ b = f.frobenius_monomial_base
260
+
261
+ while 2*i <= f.degree
262
+ g = g.frobenius_map(f, b)
263
+ h = f.gcd(g - x)
264
+
265
+ if h.arr != [1]
266
+ factors << [h, i]
267
+
268
+ f = f / h
269
+ g = g % f
270
+ b = f.frobenius_monomial_base
271
+ end
272
+
273
+ i += 1
274
+ end
275
+
276
+ if f.arr != [1]
277
+ return factors + [[f, f.degree]]
278
+ else
279
+ return factors
280
+ end
281
+ end
282
+
283
+ # Generate random polynomial of degree n
284
+ def random_gl(n)
285
+ ret = [1]
286
+ (n).times { ret << rand(@p) }
287
+ return new_gl(ret)
288
+ end
289
+
290
+ # Equal degree factorization
291
+ def edf_zassenhaus(n)
292
+ factors = [self.clone]
293
+
294
+ if degree <= n
295
+ return factors
296
+ end
297
+
298
+ nn = degree / n
299
+
300
+ if @p != 2
301
+ b = frobenius_monomial_base
302
+ end
303
+
304
+ while factors.size < nn
305
+ r = random_gl(2*n - 1)
306
+
307
+ if @p == 2
308
+ h = r
309
+
310
+ (2**(n*nn - 1)).times do
311
+ r = r.pow_mod(2, self)
312
+ h += r
313
+ end
314
+
315
+ g = gcd(h)
316
+ else
317
+ h = r.pow_pnm1d2(n, self, b)
318
+ g = gcd(h - 1)
319
+ end
320
+
321
+ if g.arr != [1] and g != self
322
+ factors = g.edf_zassenhaus(n) + (self / g).edf_zassenhaus(n)
323
+ end
324
+ end
325
+
326
+ return sort_factors(factors)
327
+ end
328
+
329
+ def zassenhaus()
330
+ factors = []
331
+
332
+ ddf_zassenhaus.each do |f|
333
+ factors += f[0].edf_zassenhaus(f[1])
334
+ end
335
+
336
+ return sort_factors(factors)
337
+ end
338
+
339
+ # Extended gcd for two polynomials
340
+ def gcdex(g)
341
+ assert_order(g)
342
+
343
+ if self.zero? and g.zero?
344
+ return [one, zero, zero]
345
+ end
346
+
347
+ (p0, r0) = monic
348
+ (p1, r1) = g.monic
349
+
350
+ if zero?
351
+ return [zero, new_gl([invert(p1)]), r1]
352
+ end
353
+
354
+ if g.zero?
355
+ return [new_gl([invert(p0)]), zero, r0]
356
+ end
357
+
358
+ s0, s1 = new_gl([invert(p0)]), zero
359
+ t0, t1 = zero, new_gl([invert(p1)])
360
+
361
+ while true
362
+ (qq, rr) = r0.div(r1)
363
+
364
+ if rr.zero?
365
+ break
366
+ end
367
+
368
+ r0 = r1
369
+ (c, r1) = rr.monic
370
+
371
+ inv = invert(c)
372
+
373
+ s = s0 - s1*qq
374
+ t = t0 - t1*qq
375
+
376
+ s0 = s1
377
+ s1 = s*inv
378
+
379
+ t0 = t1
380
+ t1 = t*inv
381
+ end
382
+
383
+ return [s1, t1, r1]
384
+ end
385
+
386
+ # Divide coefficients by lc
387
+ def monic()
388
+ if zero?
389
+ return self.clone
390
+ end
391
+
392
+ c = lc
393
+ return [c, mul_ground(invert(c))]
394
+ end
395
+
396
+ def diff()
397
+ d = degree
398
+ res = @arr.each_with_index.map { |e, i| e*(d - i) % @p }
399
+ res.pop
400
+
401
+ return new_gl(res).strip!
402
+ end
403
+
404
+ def gcd(g)
405
+ assert_order(g)
406
+
407
+ f = self
408
+ # Euclidian algorithm for gcd
409
+ while !g.zero?
410
+ (f, g) = [g, f.rem(g)]
411
+ end
412
+
413
+ return f.monic[1]
414
+ end
415
+
416
+ def pow_mod(n, g)
417
+ if n == 0
418
+ return one
419
+ elsif n == 1
420
+ return self % g
421
+ elsif n == 2
422
+ return self**2 % g
423
+ end
424
+
425
+ f = self
426
+ h = one
427
+
428
+ while true
429
+ if n.odd?
430
+ h = (h*f) % g
431
+ n -= 1
432
+ end
433
+
434
+ n >>= 1
435
+
436
+ if n == 0
437
+ break
438
+ end
439
+
440
+ f = f**2 % g
441
+ end
442
+
443
+ return h
444
+ end
445
+
446
+ # Sum two polynomials
447
+ def add(g)
448
+ ret = @arr.clone
449
+
450
+ rd = degree
451
+ gd = g.degree
452
+
453
+ if gd > rd
454
+ (gd - rd).times { ret.unshift(0) }
455
+ rd += gd - rd
456
+ end
457
+
458
+ (0..gd).each do |i|
459
+ ret[rd - i] = (ret[rd - i] + g[gd - i]) % @p
460
+ end
461
+
462
+ return new_gl(ret).strip!
463
+ end
464
+
465
+ # Subtract a polynomial from this one
466
+ def sub(g)
467
+ ret = @arr.clone
468
+
469
+ rd = degree
470
+ gd = g.degree
471
+
472
+ if gd > rd
473
+ (gd - rd).times { ret.unshift(0) }
474
+ rd += gd - rd
475
+ end
476
+
477
+ (0..gd).each do |i|
478
+ ret[rd - i] = (ret[rd - i] - g[gd - i]) % @p
479
+ end
480
+
481
+ return new_gl(ret).strip!
482
+ end
483
+
484
+ def add_ground(a)
485
+ return add(new_gl([a]))
486
+ end
487
+
488
+ def sub_ground(a)
489
+ return sub(new_gl([a]))
490
+ end
491
+
492
+ # Return the negative of the polynomial
493
+ def neg()
494
+ return new_dup(@arr.map { |t| -t % @p })
495
+ end
496
+
497
+ def mul(g)
498
+ df = degree
499
+ dg = g.degree
500
+
501
+ dh = df + dg
502
+ if dh > 0
503
+ h = [0]*(dh + 1)
504
+ else
505
+ h = []
506
+ end
507
+
508
+ (0..dh).each do |i|
509
+ coeff = 0
510
+
511
+ a = [0, i - dg].max
512
+ b = [i, df].min
513
+ (a..b).each do |j|
514
+ coeff += self[j]*g[i - j]
515
+ end
516
+
517
+ h[i] = coeff % p
518
+ end
519
+
520
+ ret = new_gl(h)
521
+ ret.strip!
522
+ return ret
523
+ end
524
+
525
+ def mul_ground(a)
526
+ if a == 0
527
+ return zero
528
+ else
529
+ return new_gl(@arr.map { |e| a*e % @p})
530
+ end
531
+ end
532
+
533
+ def sqr()
534
+ d = degree
535
+ dh = 2*d
536
+ h = []
537
+
538
+ (0..dh).each do |i|
539
+ coeff = 0
540
+
541
+ jmin = [0, i - d].max
542
+ jmax = [i, d].min
543
+
544
+ n = jmax - jmin + 1
545
+ jmax = jmin + n/2 - 1
546
+
547
+ (jmin..jmax).each do |j|
548
+ coeff += self[j]*self[i - j]
549
+ end
550
+
551
+ coeff += coeff
552
+
553
+ if n.odd?
554
+ elem = self[jmax + 1]
555
+ coeff += elem**2
556
+ end
557
+
558
+ h << coeff % @p
559
+ end
560
+
561
+ return new_gl(h).strip!
562
+ end
563
+
564
+ def quo(g)
565
+ return div(g)[0]
566
+ end
567
+
568
+ def rem(f)
569
+ return div(f)[1]
570
+ end
571
+
572
+ def div(g)
573
+ assert_order(g)
574
+
575
+ df = degree
576
+ dg = g.degree
577
+
578
+ if g.zero?
579
+ raise 'Division by zero'
580
+ elsif df < dg
581
+ return [zero, self.clone]
582
+ end
583
+
584
+ inv = invert(g[0])
585
+
586
+ h = @arr.clone
587
+ dq = df - dg
588
+ dr = dg - 1
589
+
590
+ (0..df).each do |i|
591
+ coeff = h[i]
592
+
593
+ a = [0, dg - i].max
594
+ b = [df - i, dr].min
595
+ (a..b).each do |j|
596
+ coeff -= h[i + j - dg]*g[dg - j]
597
+ end
598
+
599
+ if i <= dq
600
+ coeff *= inv
601
+ end
602
+
603
+ h[i] = coeff % @p
604
+ end
605
+
606
+ return [new_gl(h[0..dq]), new_gl(h[dq + 1..-1]).strip!]
607
+ end
608
+
609
+ def lshift(n)
610
+ if zero?
611
+ return zero
612
+ else
613
+ return new_gl(@arr + [0]*n)
614
+ end
615
+ end
616
+
617
+ def to_s()
618
+ return @arr.to_s + '/' + @p.to_s
619
+ end
620
+ end
621
+ end