abst 0.2.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.
@@ -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