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,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