rlsm 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,39 +0,0 @@
1
- #
2
- # This file is part of the RLSM gem.
3
- #
4
- #(The MIT License)
5
- #
6
- #Copyright (c) 2008 Gunther Diemant <g.diemant@gmx.net>
7
- #
8
- #Permission is hereby granted, free of charge, to any person obtaining
9
- #a copy of this software and associated documentation files (the
10
- #'Software'), to deal in the Software without restriction, including
11
- #without limitation the rights to use, copy, modify, merge, publish,
12
- #distribute, sublicense, and/or sell copies of the Software, and to
13
- #permit persons to whom the Software is furnished to do so, subject to
14
- #the following conditions:
15
- #
16
- #The above copyright notice and this permission notice shall be
17
- #included in all copies or substantial portions of the Software.
18
- #
19
- #THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
- #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
- #IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
- #CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
- #TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
- #SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
- #
27
-
28
-
29
- class RLSMException < Exception
30
- end
31
-
32
- class MonoidException < RLSMException
33
- end
34
-
35
- class DFAException < RLSMException
36
- end
37
-
38
- class RegExpException < RLSMException
39
- end
@@ -1,138 +0,0 @@
1
- #
2
- # This file is part of the RLSM gem.
3
- #
4
- #(The MIT License)
5
- #
6
- #Copyright (c) 2008 Gunther Diemant <g.diemant@gmx.net>
7
- #
8
- #Permission is hereby granted, free of charge, to any person obtaining
9
- #a copy of this software and associated documentation files (the
10
- #'Software'), to deal in the Software without restriction, including
11
- #without limitation the rights to use, copy, modify, merge, publish,
12
- #distribute, sublicense, and/or sell copies of the Software, and to
13
- #permit persons to whom the Software is furnished to do so, subject to
14
- #the following conditions:
15
- #
16
- #The above copyright notice and this permission notice shall be
17
- #included in all copies or substantial portions of the Software.
18
- #
19
- #THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
- #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
- #IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
- #CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
- #TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
- #SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
- #
27
-
28
- require File.join(File.dirname(__FILE__), 'monoid')
29
-
30
- #Generates all monoids of a given order. May take some time if order is greater than 5.
31
- module RLSM
32
- class MonoidGenerator
33
-
34
- #Iterates over all monoids of order n.
35
- def self.each(n=2)
36
- #The pathological cases...
37
- if n == 1
38
- yield Monoid.new('0')
39
- return
40
- elsif n == 2
41
- yield Monoid.new('01 10')
42
- yield Monoid.new('01 11')
43
- return
44
- end
45
-
46
- @@n = n-1
47
- @@t = ([0]*((n-1)*(n-1)))/(n-1)
48
- @@t = table_adjoin_one
49
- @@i, @@j = n-1, n-1
50
-
51
- @@end_reached = false
52
- yield Monoid.new(tab_to_str, :normalize => false) if restrictions_satisfied?
53
- succ
54
-
55
- while not @@end_reached
56
- yield Monoid.new(tab_to_str, :normalize => false)
57
- succ
58
- end
59
- end
60
-
61
- private
62
- def self.succ
63
- loop do
64
- @@t[@@i][@@j] += 1
65
- if @@t[@@i][@@j] > @@n
66
- @@t[@@i][@@j] = -1
67
- if @@i == 1 and @@j == 1
68
- @@end_reached = true
69
- break
70
- elsif @@j == 1
71
- @@j = @@n; @@i -= 1
72
- else
73
- @@j -= 1
74
- end
75
- else
76
- if restrictions_satisfied? and first?
77
- if @@i == @@n and @@j == @@n
78
- break
79
- elsif @@j == @@n
80
- @@j = 1; @@i += 1
81
- else
82
- @@j += 1
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
89
- def self.first?
90
- (1..@@n).to_a.permutations.collect{|p| p.unshift 0}.all? do |p|
91
- #if a permutation changes some elements before the given position
92
- #and the replacement is undefined no answer can be given
93
- last = (@@i-1)*(@@n) + @@j -1
94
-
95
- index = (0..last).find do |ind|
96
- i,j = (ind / (@@n))+1, (ind % (@@n))+1
97
- bij = @@t[p.index(i)][p.index(j)]
98
-
99
- (bij == -1) or @@t[i][j] != p[bij]
100
- end
101
-
102
- if index
103
- i,j = (index / (@@n))+1, (index % (@@n))+1
104
- bij = @@t[p.index(i)][p.index(j)]
105
-
106
- (bij == -1) or (@@t[i][j] < p[@@t[p.index(i)][p.index(j)]])
107
- else
108
- true
109
- end
110
- end
111
- end
112
-
113
- def self.restrictions_satisfied?
114
- #Associativity
115
- return false unless (0..@@n).to_a.triples.all? do |a,b,c|
116
- if [@@t[a][b],
117
- @@t[b][c],
118
- @@t[a][@@t[b][c]],
119
- @@t[@@t[a][b]][c]].include? -1
120
- true
121
- else
122
- @@t[a][@@t[b][c]] == @@t[@@t[a][b]][c]
123
- end
124
- end
125
-
126
- true
127
- end
128
-
129
- def self.table_adjoin_one
130
- res = [(1..@@n).to_a] + @@t
131
- (0..@@n).to_a.zip(res).collect { |i,x| [i]+x }
132
- end
133
-
134
- def self.tab_to_str
135
- @@t.collect { |r| r.join(@@n >= 10 ? ',' : '') }.join(' ')
136
- end
137
- end
138
- end
@@ -1,126 +0,0 @@
1
- #
2
- # This file is part of the RLSM gem.
3
- #
4
- #(The MIT License)
5
- #
6
- #Copyright (c) 2008 Gunther Diemant <g.diemant@gmx.net>
7
- #
8
- #Permission is hereby granted, free of charge, to any person obtaining
9
- #a copy of this software and associated documentation files (the
10
- #'Software'), to deal in the Software without restriction, including
11
- #without limitation the rights to use, copy, modify, merge, publish,
12
- #distribute, sublicense, and/or sell copies of the Software, and to
13
- #permit persons to whom the Software is furnished to do so, subject to
14
- #the following conditions:
15
- #
16
- #The above copyright notice and this permission notice shall be
17
- #included in all copies or substantial portions of the Software.
18
- #
19
- #THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
- #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
- #IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
- #CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
- #TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
- #SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
- #
27
-
28
-
29
- class Object
30
- #Makes a deep copy. Usful for cloning complexer classes.
31
- def deep_copy
32
- Marshal.load(Marshal.dump(self))
33
- end
34
- end
35
-
36
-
37
- class Array
38
- def tuples
39
- res = []
40
- self.each { |a| self.each { |b| res << [a,b] } }
41
-
42
- res
43
- end
44
-
45
- def triples
46
- res = []
47
- tuples.each { |a,b| self.each { |c| res << [a,b,c] } }
48
-
49
- res
50
- end
51
-
52
- #Only syntactic sugar. Adds an element unless the element is already in the array.
53
- def add?(x)
54
- if x
55
- unless include? x
56
- push(x).sort
57
- end
58
- end
59
-
60
- self
61
- end
62
-
63
- #Returns the powerset of the array (interpreted as set).
64
- def powerset
65
- ret = self.inject([[]]) do |acc, x|
66
- res = []
67
- acc.each { |s| res << s; res << ([x]+s).sort }
68
- res
69
- end
70
-
71
- ret.sort do |s1,s2|
72
- if s1.size == s2.size
73
- s1 <=> s2
74
- else
75
- s1.size <=> s2.size
76
- end
77
- end
78
- end
79
-
80
- #Returns all proper subsets of the array (the array interpreted as a set).
81
- def proper_subsets
82
- powerset.select { |s| s.size > 0 and s.size < size }
83
- end
84
-
85
- #Returns all permutations of the array.
86
- def permutations
87
- return [self] if size < 2
88
- perm = []
89
- each { |e| (self - [e]).permutations.each { |p| perm << ([e] + p) } }
90
- perm
91
- end
92
-
93
- #Returns the Array divided into subarrays each of length +l+. If The size of the array isn't even divisible by +l+, then the last subarray isn't of size +l+.
94
- def /(l)
95
- res = []
96
-
97
- each_with_index do |x,i|
98
- res << [] if i % l == 0
99
- res.last << x
100
- end
101
-
102
- res
103
- end
104
-
105
- #This is some hack, that in my regexp code, it works for both Strings and Arrays...
106
- def each_char(&block)
107
- each &block
108
- end
109
- end
110
-
111
- class String
112
- #Iterates over every character in a string. (Compatibility with 1.9 and convinience)
113
- def each_char
114
- if block_given?
115
- scan(/./m) { |c| yield c }
116
- else
117
- scan(/./m)
118
- end
119
- end
120
-
121
- #This is some hack, that in my regexp code, it works for both Strings and Arrays...
122
- def reject(&block)
123
- tmp = scan(/./m).reject &block
124
- tmp.join
125
- end
126
- end
@@ -1,552 +0,0 @@
1
- #
2
- # This file is part of the RLSM gem.
3
- #
4
- #(The MIT License)
5
- #
6
- #Copyright (c) 2008 Gunther Diemant <g.diemant@gmx.net>
7
- #
8
- #Permission is hereby granted, free of charge, to any person obtaining
9
- #a copy of this software and associated documentation files (the
10
- #'Software'), to deal in the Software without restriction, including
11
- #without limitation the rights to use, copy, modify, merge, publish,
12
- #distribute, sublicense, and/or sell copies of the Software, and to
13
- #permit persons to whom the Software is furnished to do so, subject to
14
- #the following conditions:
15
- #
16
- #The above copyright notice and this permission notice shall be
17
- #included in all copies or substantial portions of the Software.
18
- #
19
- #THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
- #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
- #IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
- #CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
- #TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
- #SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
- #
27
-
28
-
29
- require File.join(File.dirname(__FILE__), 'monkey_patching')
30
- require File.join(File.dirname(__FILE__), 'dfa')
31
- require File.join(File.dirname(__FILE__), 'exceptions.rb')
32
-
33
- # A Monoid is a set of elements with an associative binary operation and a neutral element.
34
- module RLSM
35
- class Monoid
36
-
37
- def initialize(table, options = {})
38
- options[:normalize] = true if options[:normalize].nil?
39
-
40
- _create_binary_operation(table, options[:names])
41
- _check_form_of_binary_operation
42
- _check_associativity_of_binary_operation
43
-
44
- @elements = (0...@order).to_a
45
-
46
- #If no neutral element exists an exception is thrown, otherwise the
47
- #neutral element is moved to position 0
48
- _check_and_rearrange_neutral_element
49
- _normalize if options[:normalize]
50
- _create_element_names if options[:create_names]
51
- end
52
-
53
-
54
- #--
55
- #READER METHODS
56
- #++
57
- #Returns the order of the monoid.
58
- attr_reader :order
59
-
60
- #Returns a copy of the elements of the monoid.
61
- def elements
62
- @names.dup
63
- end
64
-
65
- #Returns a copy of the binary operation of the monoid.
66
- def binary_operation
67
- @bo.deep_copy
68
- end
69
-
70
- #Returns the product of the given elements
71
- def[](a,b)
72
- a,b = check_arguments(a,b)
73
- @bo[a][b]
74
- end
75
-
76
-
77
- #--
78
- #SOME PROPERTIES CHECKS
79
- #++
80
- #Returns true if the monoid is commutative.
81
- def commutative?
82
- return @commutative unless @commutative.nil?
83
-
84
- @commutative = @elements.tuples.all? { |a,b| self[a,b] == self[b,a] }
85
- end
86
-
87
- #Returns true if the given element is idempotent. With no arguments called, returns true if all elements are idempotent.
88
- def idempotent?(x=nil)
89
- x = *check_arguments(x) if x
90
-
91
- x ? self[x,x] == x : @elements.all? { |x| idempotent? x }
92
- end
93
-
94
- #Returns true if the monoid is syntactic.
95
- def syntactic?
96
- return @syntactic unless @syntactic.nil?
97
-
98
- #Order 1 is a bit special...
99
- return(@syntactic = true) if @order == 1
100
-
101
- disjunctive_subset ? @syntactic = true : @syntactic = false
102
- end
103
-
104
- #Returns true if the monoid is aperiodic.
105
- def aperiodic?
106
- return @aperiodic unless @aperiodic.nil?
107
-
108
- @aperiodic ||= h_classes.all? { |hc| hc.size == 1 }
109
- end
110
-
111
- #Returns true if the monoid is a group.
112
- def group?
113
- return @group unless @proup.nil?
114
-
115
- @group ||= (idempotents.size == 1)
116
- end
117
-
118
- #--
119
- #DISJUNCTIVE SET STUFF
120
- #++
121
- #Returns true if the given set s is a disjunctive subset.
122
- def subset_disjunctive?(s)
123
- @elements.tuples.all? do |a,b|
124
- (a == b) or @elements.tuples.any? do |x,y|
125
- s.include?(self[x,self[a,y]]) ^ s.include?(self[x,self[b,y]])
126
- end
127
- end
128
- end
129
-
130
- #Returns the first disjunctive subset if there is any. Otherwise nil is returned.
131
- def disjunctive_subset
132
- return [] if @order == 1
133
-
134
- @disjunctive_subset ||=
135
- @elements.proper_subsets.find { |s| subset_disjunctive?(s) }
136
- end
137
-
138
- #Returns all disjunctive subsets.
139
- def all_disjunctive_subsets
140
- @ads ||= @elements.proper_subsets.select { |s| subset_disjunctive?(s) }
141
- end
142
-
143
-
144
- #--
145
- #SUBMONOID STUFF
146
- #++
147
- #Returns (one of the) smallest sets of elements, which generates the monoid.
148
- def generating_subset
149
- @gs ||= @elements.powerset.find do |s|
150
- _get_closure_of(s).size == @order
151
- end
152
- end
153
-
154
- #Returns true if this monoid is a submonoid of :other:
155
- def submonoid_of?(other)
156
- other.have_submonoid?(self)
157
- end
158
-
159
- #Returns true if :other: is a submonoid of this monoid.
160
- def have_as_submonoid?(other)
161
- submonoids_of_order(other.order).any? { |m| m == other }
162
- end
163
-
164
- #Returns all proper submonoids.
165
- def proper_submonoids(up_to_iso=true)
166
- (2..@order-1).to_a.map { |i| submonoids_of_order(i, up_to_iso) }.flatten
167
- end
168
-
169
- #Returns all submonoids.
170
- def submonoids(up_to_iso=true)
171
- (1..@order).to_a.map { |i| submonoids_of_order(i, up_to_iso) }.flatten
172
- end
173
-
174
- #Returns all submonoids of the given order.
175
- def submonoids_of_order(order, up_to_iso = true)
176
- pos = @elements.powerset.map { |s|
177
- _get_closure_of(s) }.select { |s| s.size == order }.uniq.sort
178
-
179
- pos.inject([]) do |soo,s|
180
- sm = get_submonoid(s)
181
- unless up_to_iso and soo.any? { |m| m == sm }
182
- soo << sm
183
- end
184
- soo
185
- end
186
- end
187
-
188
- #Returns the submonoid which is generated by the given set of elements :s:
189
- def get_submonoid(s)
190
- s = _get_closure_of(s)
191
-
192
- table = @bo.values_at(*s).map { |r| r.values_at(*s) }
193
-
194
- table.map! do |row|
195
- row.map { |x| s.index(x) }
196
- end
197
-
198
- Monoid.new table, :names => @names.values_at(*s), :normalize => false
199
- end
200
-
201
- #--
202
- #ISOMORPHISM STUFF
203
- #++
204
- #Returns true, if this monoid is isomorph to :other:.
205
- def isomorph_to?(other)
206
- #First a trivial check
207
- return false if @order != other.order
208
-
209
- isomorphisms.any? do |i|
210
- @elements.tuples.all? { |a,b| i[self[a,b]] == other[i[a],i[b]] }
211
- end
212
- end
213
-
214
- #Synonym for isomorph_to?
215
- def ==(other)
216
- isomorph_to?(other)
217
- end
218
-
219
- #Synonym for isomorph_to?
220
- def eql?(other)
221
- isomorph_to?(other)
222
- end
223
-
224
- #--
225
- #GREEN RELATIONS AND IDEAL STUFF
226
- #++
227
- #Returns the left ideal of :a:.
228
- def left_ideal_of(a)
229
- a = *check_arguments(a)
230
- @elements.inject([]) { |li,x| li.add? self[x,a]; li }.sort
231
- end
232
-
233
- #Returns the right ideal of :a:.
234
- def right_ideal_of(a)
235
- a = *check_arguments(a)
236
- @elements.inject([]) { |ri,x| ri.add? self[a,x]; ri }.sort
237
- end
238
-
239
- #Returns the two-sided ideal of :a:.
240
- def ideal_of(a)
241
- a = *check_arguments(a)
242
- res = []
243
- @elements.tuples.each { |x,y| res.add? self[x,self[a,y]] }
244
- res.sort
245
- end
246
-
247
- #Returns the L-class of :a:.
248
- def l_class_of(a)
249
- l_a = left_ideal_of a
250
- (@elements.select { |x| left_ideal_of(x) == l_a }).sort
251
- end
252
-
253
- #Returns the R-class of :a:.
254
- def r_class_of(a)
255
- r_a = right_ideal_of a
256
- (@elements.select { |x| right_ideal_of(x) == r_a }).sort
257
- end
258
-
259
- #Returns the H-class of :a:.
260
- def h_class_of(a)
261
- (l_class_of(a) & r_class_of(a)).sort
262
- end
263
-
264
- #Returns the D-class of :a:.
265
- def d_class_of(a)
266
- rc_a = r_class_of a
267
- (@elements.select { |x| (l_class_of(x) & rc_a).size > 0}).sort
268
- end
269
-
270
- #Returns all L classes
271
- def l_classes
272
- @elements.map { |x| l_class_of(x) }.uniq
273
- end
274
-
275
- #Returns all R classes
276
- def r_classes
277
- @elements.map { |x| r_class_of(x) }.uniq
278
- end
279
-
280
- #Returns all H classes
281
- def h_classes
282
- @elements.map { |x| h_class_of(x) }.uniq
283
- end
284
-
285
- #Returns all D classes
286
- def d_classes
287
- @elements.map { |x| d_class_of(x) }.uniq
288
- end
289
-
290
- #Returns true if L relation is the identity
291
- def l_trivial?
292
- l_classes.all? { |lc| lc.size == 1 }
293
- end
294
-
295
- #Returns true if R relation is the identity
296
- def r_trivial?
297
- r_classes.all? { |rc| rc.size == 1 }
298
- end
299
-
300
- #Returns true if D relation is the identity
301
- def d_trivial?
302
- d_classes.all? { |dc| dc.size == 1 }
303
- end
304
-
305
- #Returns true if H relation is the identity. This is a synonym for aperiodic?
306
- def h_trivial?
307
- aperiodic?
308
- end
309
-
310
- #--
311
- #SPECIAL ELEMENTS
312
- #++
313
- #Returns the index of the null element if any exists, otherwise false is returned.
314
- def null_element
315
- return @null_element unless @null_element.nil?
316
-
317
- #for a null element, there must exist at least two elements
318
- return @null_element = false if @order == 1
319
-
320
- ne = @elements.find do |n|
321
- @elements.all? { |x| self[n,x] == n and self[x,n] == n }
322
- end
323
-
324
- @null_element = ne ? ne : false
325
- end
326
-
327
- #Returns true if the given element is a left null, i.e. ay = a for all y
328
- def left_null?(a)
329
- a = *check_arguments(a)
330
- return false if @order == 1
331
- @elements.all? { |y| self[a,y] == a }
332
- end
333
-
334
- #Returns true if the given element is a right null, i.e. ya = a for all y
335
- def right_null?(a)
336
- a = *check_arguments(a)
337
- return false if @order == 1
338
- @elements.all? { |y| self[y,a] == a }
339
- end
340
-
341
- #Returns an array containing all left nulls.
342
- def left_nulls
343
- @elements.select { |x| left_null? x }
344
- end
345
-
346
- #Returns an array containing all right nulls.
347
- def right_nulls
348
- @elements.select { |x| right_null? x }
349
- end
350
-
351
- #Returns an array with all idempotent elements. (Remark: the neutral element is always idempotent).
352
- def idempotents
353
- @idempotents ||= @elements.select { |x| idempotent? x }
354
- end
355
-
356
-
357
- #--
358
- #MISC
359
- #++
360
- #Returns a string representation of the binary operator.
361
- def to_s
362
- @bo.map { |r| r.join(@order > 10 ? "," : "") }.join(" ")
363
- end
364
-
365
- def inspect # :nodoc:
366
- "<Monoid #{self.object_id}: {" + @names.join(",") + "};#{to_s}>"
367
- end
368
-
369
- def hash # :nodoc:
370
- isomorphisms.map { |i| bo_str_after_iso(i) }.min
371
- end
372
-
373
- #Returns a DFA with the monoid elements as states, the neutral element as initial state and transitions given by the binary operation. The argument gives the final states. If the monoid is syntactic, the finals must be a disjunctive subset. If no argument is given in this case, the smallest disjunctive subset is used.
374
- def to_dfa(finals = [])
375
- alph = @names.values_at(*generating_subset)
376
- states = @names.clone
377
- initial = @names.first
378
-
379
- if syntactic?
380
- if finals.empty?
381
- finals = disjunctive_subset
382
- else
383
- unless all_disjunctive_subsets.include? check_arguments(*finals).sort
384
- raise MonoidException, "finals must be a disjunctive subset"
385
- end
386
- end
387
- end
388
-
389
- trans = []
390
- alph.each do |char|
391
- @names.each { |s1| trans << [char, s1, @names[self[s1,char]]] }
392
- end
393
-
394
- RLSM::DFA.new alph, states, initial, finals.map {|c| @names[c] }, trans
395
- end
396
-
397
- private
398
- def isomorphisms
399
- #In every monoid the neutral element is 0, so isomorphisms must
400
- #map 0 to 0
401
- @elements[1..-1].permutations.map { |p| p.unshift(0) }
402
- end
403
-
404
- def check_arguments(*args)
405
- #Get the internal representation of each argument
406
- args.map! { |x| if x.kind_of? Integer then x else @names.index(x) end }
407
-
408
- unless args.all? { |arg| @elements.include? arg }
409
- raise MonoidException, "Bad Argument: #{args.inspect}"
410
- end
411
-
412
- args
413
- end
414
-
415
- def transpose(a,b)
416
- #somthing to do?
417
- return if a == b
418
-
419
- a,b = check_arguments(a,b)
420
-
421
- #create the transposition
422
- t = (0...@order).to_a
423
- t[a], t[b] = b, a
424
-
425
- #Rename the elements
426
- @bo = @bo.flatten.map { |x| t[x] }/@order
427
-
428
- #swap the columns
429
- @bo.map! { |r| r[a], r[b] = r[t[a]], r[t[b]]; r }
430
-
431
- #swap the rows
432
- @bo[a], @bo[b] = @bo[t[a]], @bo[t[b]]
433
-
434
- #update names
435
- @names[a], @names[b] = @names[b], @names[a]
436
- end
437
-
438
- def bo_str_after_iso(iso)
439
- bo = @bo.deep_copy
440
- (0...@order).to_a.tuples.each do |i,j|
441
- bo[i][j] = iso[@bo[iso.index(i)][iso.index(j)]]
442
- end
443
-
444
- bo.map { |r| r.join(@order >10 ? ',' : '') }.join(' ')
445
- end
446
-
447
- def _get_closure_of(s)
448
- res = s.dup
449
- res = check_arguments(*res)
450
- res.add? 0
451
-
452
- order = 1
453
-
454
- loop do
455
- order = res.size
456
- res.tuples.each do |a,b|
457
- res.add? self[a,b]
458
- res.add? self[b,a]
459
- end
460
-
461
- break if order == res.size
462
- end
463
-
464
- res.sort
465
- end
466
-
467
- def _create_binary_operation(table, names)
468
- if table.instance_of? Array
469
- @order = table.size
470
- @bo = table
471
- elsif table.instance_of? String
472
- #Normalizing the string, i.e. removing double spaces, trailing newlines...
473
- table.chomp!
474
- table.squeeze!(' ')
475
- table.gsub!(", ", ",")
476
-
477
- #Take the number of rows as order of the monoid
478
- @order = table.split.size
479
-
480
- #Transform now the string in a matrix
481
- if table.include? ","
482
- @bo = table.gsub(" ", ",").split(",")/@order
483
- else
484
- @bo = table.gsub(" ", "").scan(/./)/@order
485
- end
486
- end
487
-
488
- #Convert to internal represenation
489
- #Names given?
490
- if names and names.class == Array and names.size == @order
491
- @names = names
492
- else
493
- #Make a guess, works if the convention that the first row belongs to the neutral element is followed.
494
- @names = @bo.flatten.uniq.clone
495
- end
496
-
497
- @bo = (@bo.flatten.map { |e| @names.include?(e) ? @names.index(e) : e })/@order
498
- end
499
-
500
- def _check_form_of_binary_operation
501
- #Is the matrix quadratic?
502
- unless @bo.all? { |r| r.size == @order }
503
- raise MonoidException, "InitError: A binary operation must be quadratic."
504
- end
505
-
506
- #Are the matrix elements in the right range?
507
- unless @bo.flatten.all? { |e| (0...@order).include? e }
508
- raise MonoidException, "InitError: Too big numbers in description."
509
- end
510
- end
511
-
512
- def _check_associativity_of_binary_operation
513
- unless (0...@order).to_a.triples.all? do |a,b,c|
514
- @bo[a][@bo[b][c]] == @bo[@bo[a][b]][c]
515
- end
516
- raise MonoidException, "InitError: Given binary operation isn't associative."
517
- end
518
- end
519
-
520
- def _check_and_rearrange_neutral_element
521
- one = (0...@order).find do |e|
522
- (0...@order).all? { |x| @bo[e][x] == @bo[x][e] and @bo[e][x] == x }
523
- end
524
-
525
- one ? transpose(0,one) : raise(MonoidException, "InitError: Given binary operation has no neutral element.")
526
- end
527
-
528
- def _create_element_names
529
- @names = []
530
- char = "a"
531
- @elements.each do |i|
532
- if i == 0
533
- @names << "1"
534
- else
535
- @names << char.clone
536
- char.succ!
537
- end
538
- end
539
- end
540
-
541
- def _normalize
542
- gs = generating_subset
543
-
544
- #rearrange such that the generators are the first elements
545
- #after the neutral element
546
- gs.each_with_index { |x,i| transpose(x,i+1) }
547
-
548
- #set the new generating subset
549
- @gs = (1..gs.size).to_a
550
- end
551
- end
552
- end