asmodis-rlsm 0.2.0

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