abst 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module Abst
2
+ def show_gif_image(path)
3
+ require 'tk'
4
+ TkButton.new do
5
+ image TkPhotoImage.new(file: path)
6
+ command lambda {TkRoot.destroy }
7
+ pack
8
+ end
9
+ Tk.mainloop
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ module Abst
2
+ module Group
3
+ def self.included(base)
4
+ base.class_eval do
5
+ [:+, :-].each do |sym|
6
+ unless method_defined? sym
7
+ define_method(sym) do |other|
8
+ return add_sub(sym, other)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module_function
17
+
18
+ def element_order(g, group_order)
19
+ one = g.class.one
20
+ order = group_order
21
+ factor = factorize(group_order)
22
+
23
+ factor.each do |f, e|
24
+ order /= f ** e
25
+
26
+ t = g ** order
27
+ until one == t
28
+ t **= f
29
+ order *= f
30
+ end
31
+ end
32
+
33
+ return order
34
+ end
35
+ end
@@ -0,0 +1,385 @@
1
+ class Integer
2
+ class << self
3
+ def zero
4
+ return 0
5
+ end
6
+
7
+ def one
8
+ return 1
9
+ end
10
+
11
+ def *(other)
12
+ raise ArgumentError unless other.kind_of?(self)
13
+ Abst::IntegerIdeal.new(other)
14
+ end
15
+
16
+ def /(other)
17
+ if other.kind_of?(Integer)
18
+ other = Integer * other
19
+ else
20
+ raise ArgumentError unless other.kind_of?(Abst::IntegerIdeal)
21
+ end
22
+ return residue_class(other)
23
+ end
24
+
25
+ def coerce(other)
26
+ return [self, other]
27
+ end
28
+
29
+ def to_tex
30
+ return "\\mathbb{Z}"
31
+ end
32
+ end
33
+
34
+ def bit_size
35
+ return Abst.ilog2(self) + 1
36
+ end
37
+
38
+ # Return:: formatted string
39
+ def to_fs
40
+ return self.to_s.gsub(/(?<=\d)(?=(\d\d\d)+$)/, ' ')
41
+ end
42
+
43
+ # Param:: non-negative integer self
44
+ # Return:: factorial self!
45
+ def factorial
46
+ return 2.upto(self).inject(1, &:*)
47
+ end
48
+
49
+ def inverse
50
+ return 1 / Rational(self)
51
+ end
52
+
53
+ # Param:: prime
54
+ # Return:: self! mod prime
55
+ def pmod_factorial(prime)
56
+ return 2.upto(self).inject(1) {|r, i| r * i % prime}
57
+ end
58
+
59
+ # Param:: prime
60
+ # Return:: integer f > 0 and e >= 0 s.t.
61
+ # prime ** e || self! and
62
+ # f == self! / (prime ** e) % prime
63
+ def extended_pmod_factorial(prime)
64
+ q, r = self.divmod prime
65
+ fac = q.even? ? 1 : prime - 1
66
+ fac = fac * r.pmod_factorial(prime) % prime
67
+
68
+ a = nil
69
+ e = q
70
+ if prime <= q
71
+ a, b = q.extended_pmod_factorial(prime)
72
+ e += b
73
+ else
74
+ a = q.pmod_factorial(prime)
75
+ end
76
+ fac = fac * a % prime
77
+
78
+ return fac, e
79
+ end
80
+
81
+ def combination(r)
82
+ r = self - r if self - r < r
83
+
84
+ if r <= 0
85
+ return 1 if 0 == r
86
+ return 0
87
+ end
88
+
89
+ rslt = self
90
+ 2.upto(r) do |i|
91
+ rslt = rslt * (self - i + 1) / i
92
+ end
93
+
94
+ return rslt
95
+ end
96
+ alias :C :combination
97
+
98
+ def pmod_combination(r, prime)
99
+ f1, e1 = self.extended_pmod_factorial(prime)
100
+ f2, e2 = r.extended_pmod_factorial(prime)
101
+ f3, e3 = (self - r).extended_pmod_factorial(prime)
102
+
103
+ return 0 if e2 + e3 < e1
104
+ return f1 * Abst.inverse(f2 * f3, prime) % prime
105
+ end
106
+
107
+ def repeated_combination(r)
108
+ return (self + r - 1).combination(r)
109
+ end
110
+ alias :H :repeated_combination
111
+
112
+ # @param positive integer m > 1
113
+ # @return boolean whether self is power of m or not
114
+ def power_of?(m)
115
+ n = self
116
+ until 1 == n
117
+ n, r = n.divmod(m)
118
+ return false unless 0 == r
119
+ end
120
+
121
+ return true
122
+ end
123
+
124
+ # Triangle numbers are generated by the formula, T_n = n * (n + 1) / 2.
125
+ # The first ten triangle numbers are:
126
+ # 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
127
+ # Return:: integer n s.t. self == T_n if exist else false
128
+ def triangle?
129
+ return false unless r = ((self << 3) + 1).square?
130
+ return false unless (r - 1).even?
131
+
132
+ return (r - 1) >> 1
133
+ end
134
+
135
+ # Test whether self is a square number or not
136
+ # Param:: positive integer self
137
+ # Return:: square root of self if self is square else false
138
+ def square?
139
+ check = {
140
+ 11=>[true, true, false, true, true, true, false, false, false, true, false],
141
+ 63=>[true, true, false, false, true, false, false, true, false, true, false, false, false, false, false, false, true, false, true, false, false, false, true, false, false, true, false, false, true, false, false, false, false, false, false, false, true, true, false, false, false, false, false, true, false, false, true, false, false, true, false, false, false, false, false, false, false, false, true,false, false, false, false],
142
+ 64=>[true, true, false, false, true, false, false, false, false, true, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, true, false, false, false, false, true, false, false, false, false, false, false,false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false],
143
+ 65=>[true, true, false, false, true, false, false, false, false, true, true, false, false, false, true, false, true, false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, false, false, true, true, false, false, true, true, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, true, false, false, false, false, true, false, false, true]
144
+ }
145
+
146
+ # 64
147
+ t = self & 63
148
+ return false unless check[64][t]
149
+
150
+ r = self % 45045 # == 63 * 65 * 11
151
+ [63, 65, 11].each do |c|
152
+ return false unless check[c][r % c]
153
+ end
154
+
155
+ q = Abst.isqrt(self)
156
+ return false unless q ** 2 == self
157
+
158
+ return q
159
+ end
160
+
161
+ #@slow
162
+ # Param:: positive integer self
163
+ # Return:: true if self is squarefree, false otherwise
164
+ def squarefree?
165
+ n = self
166
+ if n.even?
167
+ return false if 0 == n[1]
168
+ n >>= 1
169
+ end
170
+
171
+ return true if self <= 3
172
+ return false if self.square?
173
+
174
+ pl = Abst.primes_list
175
+
176
+ # trial division
177
+ limit = Abst.isqrt(n)
178
+ 1.upto(pl.size - 1).each do |i|
179
+ d = pl[i]
180
+ return true if limit < d
181
+
182
+ if n % d == 0
183
+ n /= d
184
+ return false if n % d == 0
185
+ limit = Abst.isqrt(n)
186
+ end
187
+ end
188
+
189
+ d = pl.last + 2
190
+ loop do
191
+ return true if limit < d
192
+
193
+ if n % d == 0
194
+ n /= d
195
+ return false if n % d == 0
196
+ limit = Abst.isqrt(n)
197
+ end
198
+
199
+ d += 2
200
+ end
201
+ end
202
+
203
+ # Pentagonal numbers are generated by the formula, P_n = n * (3 * n - 1) / 2.
204
+ # The first ten pentagonal numbers are:
205
+ # 1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ...
206
+ # Return:: integer n s.t. self == P_n if exist else false
207
+ def pentagonal?
208
+ return false unless r = (24 * self + 1).square?
209
+
210
+ q, r = (1 + r).divmod(6)
211
+ return false unless 0 == r
212
+
213
+ return q
214
+ end
215
+
216
+ # Hexagonal numbers are generated by the formula, H_n = n * (2 * n - 1)
217
+ # The first ten hexagonal numbers are:
218
+ # 1, 6, 15, 28, 45, 66, 91, 120, 153, 190, ...
219
+ # Return:: integer n s.t. self == H_n if exist else false
220
+ def hexagonal?
221
+ return false unless r = ((self << 3) + 1).square?
222
+ return false unless 0 == (1 + r) & 3
223
+
224
+ return (1 + r) >> 2
225
+ end
226
+
227
+ # Heptagonal numbers are generated by the formula, P7_n = n * (5 * n - 3) / 2
228
+ # The first ten heptagonal numbers are:
229
+ # 1, 7, 18, 34, 55
230
+ # Return:: integer n s.t. self == P7_n if exist else false
231
+ def heptagonal?
232
+ return false unless r = (self * 40 + 9).square?
233
+
234
+ q, r = (3 + r).divmod(10)
235
+ return false unless 0 == r
236
+
237
+ return q
238
+ end
239
+
240
+ # Octagonal numbers are generated by the formula, P8_n = n * (3 * n - 2)
241
+ # The first ten octagonal numbers are:
242
+ # 1, 8, 21, 40, 65, ...
243
+ # Return:: integer n s.t. self == P7_n if exist else false
244
+ def octagonal?
245
+ return false unless r = (self * 3 + 1).square?
246
+
247
+ q, r = (1 + r).divmod(3)
248
+ return false unless 0 == r
249
+
250
+ return q
251
+ end
252
+ end
253
+
254
+ module Abst
255
+ Z = Integer
256
+
257
+ class IntegerIdeal
258
+ attr_reader :n
259
+
260
+ def initialize(n)
261
+ @n = n
262
+ end
263
+
264
+ def +(other)
265
+ self.class.new(Abst.gcd(@mod, other.mod))
266
+ end
267
+
268
+ def *(other)
269
+ self.class.new(@mod * other.mod)
270
+ end
271
+
272
+ def &(other)
273
+ self.class.new(Abst.lcm(@mod, other.mod))
274
+ end
275
+
276
+ def to_s
277
+ return "IntegerIdeal #{@n}Z"
278
+ end
279
+ alias inspect to_s
280
+
281
+ def to_tex
282
+ return "#{@n}#{Z.to_tex}"
283
+ end
284
+ end
285
+
286
+ class IntegerResidueRing
287
+ class << self
288
+ attr_reader :mod
289
+
290
+ def zero
291
+ return self.new(0)
292
+ end
293
+
294
+ def one
295
+ return self.new(1)
296
+ end
297
+
298
+ def order(op = :+)
299
+ case op
300
+ when :+
301
+ return @mod
302
+ when :*
303
+ @order = phi(@mod) unless defined?(@order)
304
+ return @order
305
+ end
306
+
307
+ raise ArgumentError, "unrecognized argument #{op} was specified"
308
+ end
309
+ alias cardinality order
310
+
311
+ def to_s
312
+ return "Z / #{@mod}Z"
313
+ end
314
+
315
+ def to_tex
316
+ z = Z.to_tex
317
+ return z + " / #{@mod}" + z
318
+ end
319
+ end
320
+
321
+ attr_reader :n
322
+
323
+ def initialize(n)
324
+ @n = n % self.class.mod
325
+ end
326
+
327
+ [:+, :-, :*].each do |op|
328
+ define_method(op) do |other|
329
+ return self.class.new(@n.__send__(op, other.to_i))
330
+ end
331
+ end
332
+
333
+ def ==(other)
334
+ return false unless self.class.superclass == other.class.superclass
335
+ return false unless self.class.mod == other.class.mod
336
+ return @n == other.n
337
+ end
338
+
339
+ def inverse
340
+ return self.class.new(Abst.inverse(@n, self.class.mod))
341
+ end
342
+
343
+ def order(op)
344
+ mod = self.class.mod
345
+
346
+ case op
347
+ when :+
348
+ return mod / gcd(@n, mod)
349
+ when :*
350
+ raise ArgumentError, "#{self} is not invertible" unless 1 == gcd(@n, mod)
351
+ return element_order(self, self.class.order(op))
352
+ end
353
+
354
+ raise ArgumentError, "unrecognized argument #{op} was specified"
355
+ end
356
+
357
+ def to_s
358
+ return "#{@n} (mod #{self.class.mod})"
359
+ end
360
+ alias inspect to_s
361
+
362
+ def to_i
363
+ return @n
364
+ end
365
+
366
+ include Abst::Ring
367
+ end
368
+
369
+ class IntegerResidueField < IntegerResidueRing
370
+ class << self
371
+ def order(op = :+)
372
+ return @mod - 1 if :* == op
373
+ return super
374
+ end
375
+ end
376
+
377
+ def inverse
378
+ return self.class.new(Abst.inverse(@n, self.class.mod))
379
+ end
380
+
381
+ def /(other)
382
+ return self.class.new(@n * other.inverse.n)
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,143 @@
1
+ module Abst
2
+ module_function
3
+
4
+ class Matrix
5
+ include Abst::Group
6
+
7
+ class << self
8
+ attr_reader :coef_class, :height, :width, :coef_vector
9
+
10
+ def to_s
11
+ return "#{height} * #{width} #{self.coef_class} Matrix"
12
+ end
13
+
14
+ def inspect
15
+ return to_s
16
+ end
17
+ end
18
+
19
+ attr_reader :coef, :height, :width
20
+ protected :coef
21
+
22
+ def initialize(m)
23
+ raise MatrixSizeError unless self.class.height == m.size
24
+ @coef = m.map{|row| self.class.coef_vector.new(row)}
25
+ @height = self.class.height
26
+ @width = self.class.width
27
+ rescue VectorSizeError
28
+ raise MatrixSizeError
29
+ end
30
+
31
+ def each
32
+ return Enumerator.new(self) unless block_given?
33
+
34
+ @coef.each do |row|
35
+ row.each do |i|
36
+ yield i
37
+ end
38
+ end
39
+ end
40
+
41
+ def add_sub(op, other)
42
+ return self.class.new(@coef.zip(other.coef).map{|a, b| a.__send__(op, b)})
43
+ end
44
+
45
+ def *(other)
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # Param:: self must be n * (n + 1) matrix s.t. (MB)
50
+ # M is invertible n * n matrix
51
+ # B is a column vector
52
+ # Return:: a column vector X s.t. MX == B
53
+ def solve
54
+ raise MatrixSizeError unless @height + 1 == @width
55
+
56
+ inverse = []
57
+ m = self.to_a
58
+ n = @height
59
+
60
+ n.times do |j|
61
+ # Find non-zero entry
62
+ row = nil
63
+ j.upto(n - 1) do |i|
64
+ unless m[i][j].zero?
65
+ row = i
66
+ break
67
+ end
68
+ end
69
+ return nil unless row
70
+
71
+ # Swap?
72
+ if j < row
73
+ m[row], m[j] = m[j], m[row]
74
+ end
75
+
76
+ # Eliminate
77
+ inverse[j] = m[j][j].inverse
78
+ (j + 1).upto(n - 1) do |i|
79
+ c = m[i][j] * inverse[j]
80
+
81
+ (j + 1).upto(n) do |j2|
82
+ m[i][j2] -= m[j][j2] * c
83
+ end
84
+ end
85
+ end
86
+
87
+ # Solve triangular system
88
+ x = []
89
+ (n - 1).downto(0) do |i|
90
+ temp = m[i][n]
91
+ (i + 1).upto(n - 1) do |j|
92
+ temp -= m[i][j] * x[j]
93
+ end
94
+ x[i] = temp * inverse[i]
95
+ end
96
+
97
+ return Abst::Vector(self.class.coef_class, x)
98
+ end
99
+
100
+ def to_a
101
+ return @coef.map{|row| row.to_a}
102
+ end
103
+
104
+ def to_s
105
+ return "[#{@coef.map(&:to_s).join(', ')}]"
106
+ end
107
+
108
+ def inspect
109
+ return "#{self.class}\n#{self}"
110
+ end
111
+ end
112
+
113
+ class SquareMatrix < Matrix
114
+ include Abst::Ring
115
+
116
+ def trace
117
+ return @coef.map.with_index{|row, i| row[i]}.inject(&:+)
118
+ end
119
+ end
120
+
121
+ def create_matrix(coef_class, height, width = nil)
122
+ if height.kind_of?(Array)
123
+ elems = height
124
+ width = height[0].size
125
+ height = height.size
126
+ end
127
+
128
+ super_class = height == width ? SquareMatrix : Matrix
129
+ matrix = Class.new(super_class) do
130
+ @coef_class = coef_class
131
+ @height = height
132
+ @width = width
133
+ @coef_vector = Abst::Vector(coef_class, width)
134
+ end
135
+
136
+ return matrix.new(elems) if elems
137
+ return matrix
138
+ end
139
+ alias Matrix create_matrix
140
+ module_function :Matrix
141
+
142
+ class MatrixSizeError < Exception; end
143
+ end