rlsm 1.1.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rlsm/helper.rb CHANGED
@@ -1,11 +1,17 @@
1
- class RLSMError < StandardError; end # :nodoc:
2
- class ParseError < RLSMError; end # :nodoc:
3
- class BinOpError < RLSMError; end # :nodoc:
4
- class MonoidError < RLSMError; end # :nodoc:
5
- class DFAError < RLSMError; end # :nodoc:
6
- class RegExpError < RLSMError; end # :nodoc:
7
1
 
8
2
  module RLSM
3
+ # The exception class which is raised by the code under the RLSM module.
4
+ # So somthing like
5
+ # begin
6
+ # ...
7
+ # rescue RLSM::Error => e
8
+ # ... #Error from RLSM code
9
+ # rescue
10
+ # ... #Some external error
11
+ # end
12
+ # works.
13
+ class Error < StandardError; end
14
+
9
15
  def self.require_extension(extension)
10
16
  require File.join(File.dirname(__FILE__), '..', '..', 'ext', extension, extension + '_cext')
11
17
  end
data/lib/rlsm/monoid.rb CHANGED
@@ -1,90 +1,237 @@
1
1
  require File.join(File.dirname(__FILE__), 'helper')
2
- require File.join(File.dirname(__FILE__), 'binary_operation')
3
2
  require File.join(File.dirname(__FILE__), 'dfa')
3
+
4
4
  RLSM::require_extension 'array'
5
5
  RLSM::require_extension 'monoid'
6
6
 
7
7
  module RLSM
8
+
9
+ # Implements the mathematical idea of a finite monoid.
8
10
  class Monoid
9
- private_class_method :new
10
11
 
11
- def self.each(order)
12
- raise ArgumentError, "Given order must be > 0" if order <= 0
12
+ class << self
13
+ # Iterates over each monoid of the given order.
14
+ #
15
+ # @param [Integer] order Specifies over which monoids the method iterates.
16
+ #
17
+ # @raise [RLSM::Error] The given parameter must be greater than zero.
18
+ #
19
+ # @yield [monoid] A monoid of the given order.
20
+ def each(order)
21
+ each_table(order) { |table| yield new(table,false) }
22
+ end
13
23
 
14
- if order == 1 #trivial case
15
- yield new(RLSM::BinaryOperation.original_new([0], ['0'], { '0' => 0}))
16
- return
24
+ # Iterates over each transition table of a monoid of the given order.
25
+ #
26
+ # @param [Integer] order Specifies over which monoids the method iterates.
27
+ #
28
+ # @raise [RLSM::Error] The given parameter must be greater than zero.
29
+ #
30
+ # @yield [table] An array describing the transition table of a monoid.
31
+ def each_table(order)
32
+ raise RLSM::Error, "Given order must be > 0" if order <= 0
33
+
34
+ if order == 1 #trivial case
35
+ yield [0]
36
+ return
37
+ end
38
+
39
+ #calculate the permutations once
40
+ permutations =
41
+ RLSM::ArrayExt::permutations((1...order).to_a).map { |p| p.unshift 0 }
42
+
43
+ each_diagonal(order,permutations) do |diagonal|
44
+ each_with_diagonal(diagonal,permutations) do |table|
45
+ yield table
46
+ end
47
+ end
17
48
  end
18
49
 
19
- elements = (0...order).to_a.map { |x| x.to_s }
20
- mapping = {}
21
- elements.each_with_index { |x,i| mapping[x] = i }
50
+ # Creates a new monoid.
51
+ # @see RLSM::Monoid#initialize
52
+ def [](table)
53
+ new(table)
54
+ end
22
55
 
23
- #calculate the permutations once
24
- permutations = (1...order).to_a.permutations.map { |p| p.unshift 0 }
56
+ # Creates the syntactic monoid to the given regular language.
57
+ #
58
+ # @param [String] regexp a regular expression.
59
+ #
60
+ # @raise [RLSM::Error] if given string isn't a valid regexp.
61
+ #
62
+ # @return [Monoid] the syntactic monoid of the given regular language.
63
+ def from_regexp(regexp)
64
+ RLSM::RegExp[regexp].to_monoid
65
+ end
25
66
 
26
- each_diagonal(order,permutations) do |diagonal|
27
- each_with_diagonal(diagonal,permutations) do |table|
28
- yield new(RLSM::BinaryOperation.original_new(table, elements, mapping))
29
- end
67
+ # Creates the syntactic monoid to the given regular language.
68
+ #
69
+ # @param [String] dfa a deterministic finite automaton
70
+ #
71
+ # @raise [RLSM::Error] if given string isn't a valid DFA description.
72
+ #
73
+ # @return [Monoid] the syntactic monoid of the given regular language.
74
+ def from_dfa(dfa)
75
+ RLSM::DFA[dfa].to_monoid
30
76
  end
77
+
31
78
  end
32
79
 
80
+ #The elements of the monoid.
81
+ attr_reader :elements
33
82
 
34
- attr_accessor :elements, :order, :binary_operation
83
+ #The order of the monoid.
84
+ attr_reader :order
35
85
 
36
- #Like new, but without validation.
37
- def self.new!(description)
38
- new RLSM::BinaryOperation.new!(description)
39
- end
86
+ #The transition table of the monoid.
87
+ attr_reader :table
40
88
 
41
- #Creates a Monoid with the binary operation described in +description+.
42
- #Validates that the BinaryOperation is assoviative and the neutral element
43
- #is in the first row.
89
+ # Creates a new Monoid from the given table.
90
+ # The table is interpreted as follows:
91
+ #
92
+ # * Case 1: Table is an Array.
93
+ #
94
+ # It is assumed, that the array is flat and that every entry
95
+ # is an entry in the transition table. The neutral element must
96
+ # be described in the first row and column.
97
+ #
98
+ # * Case 2: Table is a String.
99
+ #
100
+ # The string will be parsed into a flat Array.
101
+ # If commas are present, the String will be splitted at these,
102
+ # otherwise it will be splitted at each character.
103
+ # Whitespaces will be ignored or threted as commas.
104
+ # After parsing, the resulting array will be treated as in case 1.
105
+ #
106
+ # @example Some different ways to create a Monoid
107
+ # RLSM::Monoid.new [0,1,1,1]
108
+ # RLSM::Monoid.new "0110"
109
+ # RLSM::Monoid.new "01 10"
110
+ # RLSM::Monoid.new "0,1,1,0"
111
+ # RLSM::Monoid.new "0,1 1,0"
44
112
  #
45
- #See also BinaryOperation::new.
46
- def self.[](description)
47
- binop = RLSM::BinaryOperation.new(description)
48
- binop.enforce_associativity
49
- enforce_identity_position(binop.table, binop.order)
113
+ # @param [Array,String] table The transition table of the monoid, either as
114
+ # a flat Array or a string.
115
+ #
116
+ # @param [Boolean] validate If true, the given table will be validated.
117
+ #
118
+ # @raise [RLSM::Error] If validate is true and the given table isn't
119
+ # associative or has no neutral element or the neutral
120
+ # element isn't in the first row and column.
121
+ def initialize(table, validate = true)
122
+ @table = parse(table)
123
+ @order = Math::sqrt(@table.size).to_i
124
+ @elements = @table[0,@order].clone
125
+ @internal = {}
126
+ @elements.each_with_index { |x,i| @internal[x] = i }
127
+ @table.map! { |x| @internal[x] }
128
+
129
+ if validate
130
+ if @order == 0
131
+ raise RLSM::Error, "No elements given."
132
+ end
133
+
134
+ unless @table.size == @order**2
135
+ raise RLSM::Error, "Binary operation must be quadratic."
136
+ end
50
137
 
51
- new(binop)
138
+ unless @table.uniq.size == @order
139
+ raise RLSM::Error, "Number of different elements is wrong."
140
+ end
141
+
142
+ enforce_identity_position(@table, @order)
143
+
144
+ nat = non_associative_triple
145
+ unless nat.nil?
146
+ err_str = "(#{nat[0]}#{nat[0]})#{nat[0]} != #{nat[0]}(#{nat[1]}#{nat[2]})"
147
+ raise RLSM::Error, "Associativity required, but #{err_str}."
148
+ end
149
+ end
52
150
  end
53
151
 
54
152
 
55
- #:notnew:
56
- #*Remark*: No validation is performed. Use it only when you're really know what to do.
57
- #Use Monoid::[] instead (or Monoid::new!).
58
- def initialize(binop)
59
- @binary_operation = binop
60
- @elements = @binary_operation.elements
61
- @order = @elements.size
153
+ # Parses a transition table description.
154
+ #
155
+ # @param [Array,String] table the transition table description.
156
+ # @return [Array] The parsed description.
157
+ def parse(table)
158
+ return table if Array === table
62
159
 
63
- instance_eval(&block) if block_given?
160
+ if table.include?(',')
161
+ table.gsub(/\W/,',').squeeze(',').split(',').reject { |x| x.empty? }
162
+ else
163
+ table.gsub(/\W/,'').scan(/./)
164
+ end
64
165
  end
65
-
166
+
66
167
  #Calculates the product of the given elements.
168
+ # @return The result of the operation
169
+ # @raise [RLSM::Error] If at least one of the given elements isn't a element
170
+ # of the monoid or too few elements are given.
67
171
  def [](*args)
68
172
  case args.size
69
173
  when 0,1
70
- raise ArgumentError, "At least two elements must be provided."
174
+ raise RLSM::Error, "At least two elements must be provided."
71
175
  when 2
72
- @binary_operation[*args]
176
+ begin
177
+ @elements[ @table[ @order*@internal[args[0]] + @internal[args[1]] ] ]
178
+ rescue
179
+ raise RLSM::Error, "Given arguments aren't monoid elements."
180
+ end
73
181
  else
74
- args[0,2] = @binary_operation[args[0],args[1]]
182
+ args[0,2] = self[ *args[0,2] ]
75
183
  self[*args]
76
184
  end
77
185
  end
78
186
 
79
- #Two monoids are equal if they have the same binary operation on the same set.
187
+
188
+ # Transforms monoid into a string.
189
+ #
190
+ # @return [String] The string representation of the monoid.
191
+ def to_s
192
+ result = ""
193
+ sep = @elements.any? { |x| x.to_s.length > 1 } ? ',' : ''
194
+ @table.each_with_index do |el,i|
195
+ result += @elements[el].to_s
196
+ if (i+1) % (@order) == 0
197
+ result += ' '
198
+ else
199
+ result += sep unless i == @order**2 - 1
200
+ end
201
+ end
202
+
203
+ result
204
+ end
205
+
206
+
207
+ # Transforms monoid into a string for debug purposes.
208
+ #
209
+ # @return [String] The string representation of the monoid.
210
+ def inspect
211
+ "<#{self.class}: #{to_s}>"
212
+ end
213
+
214
+ # Tests for monoid equality. Two monoids are equal if the have the same
215
+ # set of elements and the same transition table.
216
+ #
217
+ # @param [Monoid] other The righthand side of the equality test.
218
+ #
219
+ # @return [nil] if other isn't a monoid.
220
+ # @return [Boolean] the result of the equality check.
80
221
  def ==(other)
81
222
  return nil unless RLSM::Monoid === other
82
223
 
83
- @binary_operation.table == other.binary_operation.table and
84
- @binary_operation.elements == other.binary_operation.elements
224
+ @table == other.table and
225
+ @elements == other.elements
85
226
  end
86
227
 
87
- #Checks if +self+ is a proper submonoid of +other+.
228
+ # Checks if this monoid is a proper submonoid (i.e. not equal)
229
+ # of the other one.
230
+ #
231
+ # @param [Monoid] other Righthand side of the inequality test.
232
+ #
233
+ # @return [nil] if other isn't a monoid.
234
+ # @return [Boolean] the result of the inequality test.
88
235
  def <(other)
89
236
  return nil unless RLSM::Monoid === other
90
237
  return false if @order >= other.order
@@ -93,7 +240,7 @@ module RLSM
93
240
  @elements.each do |e2|
94
241
  begin
95
242
  return false if self[e1,e2] != other[e1,e2]
96
- rescue BinOpError
243
+ rescue RLSM::Error
97
244
  return false
98
245
  end
99
246
  end
@@ -102,29 +249,82 @@ module RLSM
102
249
  true
103
250
  end
104
251
 
105
- #Checks if +self+ is a submonoid of (or equal to) +other+.
252
+ # Checks if this monoid is a submonoid of the other one.
253
+ #
254
+ # @param [Monoid] other Righthand side of the inequality test.
255
+ #
256
+ # @return [nil] if other isn't a monoid.
257
+ # @return [Boolean] the result of the inequality test.
106
258
  def <=(other)
107
259
  (self == other) || (self < other)
108
260
  end
109
261
 
110
- def >(other) #:nodoc:
262
+ # Checks if the other monoid is a proper submonoid (i.e. not equal)
263
+ # of this one.
264
+ #
265
+ # @param [Monoid] other Righthand side of the inequality test.
266
+ #
267
+ # @return [nil] if other isn't a monoid.
268
+ # @return [Boolean] the result of the inequality test.
269
+ def >(other)
111
270
  other < self
112
271
  end
113
272
 
114
- def >=(other) #:nodoc:
273
+ # Checks if the other monoid is a submonoid of this one.
274
+ #
275
+ # @param [Monoid] other Righthand side of the inequality test.
276
+ #
277
+ # @return [nil] if other isn't a monoid.
278
+ # @return [Boolean] the result of the inequality test.
279
+ def >=(other)
115
280
  other <= self
281
+ endy
116
282
  end
117
283
 
118
- #Returns the submonoid generated by +set+.
284
+ # @see RLSM::Monoid#isomorph?
285
+ def =~(other)
286
+ isomorph?(other)
287
+ end
288
+
289
+ # Checks if this monoid is isomorph to the other one.
119
290
  #
120
- #*Remark*: The returned value is only an Array, no Monoid. Use get_submonoid for this.
121
- def generated_set(set)
122
- if set.include? @elements.first
123
- gen_set = set.map { |element| element.to_s }
124
- else
125
- gen_set = set.map { |element| element.to_s } | @elements[0,1]
126
- end
291
+ # @param [Monoid] other Righthand side of the isomorphism check.
292
+ #
293
+ # @return [nil] if other isn't a monoid.
294
+ # @return [Boolean] the result of the isomorphism check.
295
+ def isomorph?(other)
296
+ return nil unless RLSM::Monoid === other
297
+ isomorphisms(other) { return true }
298
+
299
+ false
300
+ end
301
+
302
+ # Checks if this monoid is antiisomorph to the other one.
303
+ #
304
+ # @param [Monoid] other Righthand side of the isomorphism check.
305
+ #
306
+ # @return [nil] if other isn't a monoid.
307
+ # @return [Boolean] the result of the antiisomorphism check.
308
+ def antiisomorph?(other)
309
+ return nil unless RLSM::Monoid === other
310
+
311
+ antiisomorphisms(other) { return true }
312
+
313
+ false
314
+ end
127
315
 
316
+ # Calculates the set of elements which will be generated by the given set.
317
+ #
318
+ # @param [Array] set The elements which act as generators
319
+ #
320
+ # @return [Array] the generated set.
321
+ #
322
+ # @raise [RLSM::Error] if one of the elements isn't a monoid element.
323
+ #
324
+ # @see RLSM::Monoid#get_submonoid.
325
+ def generated_set(set)
326
+ gen_set = set | @elements[0,1]
327
+
128
328
  unfinished = true
129
329
 
130
330
  while unfinished
@@ -144,20 +344,33 @@ module RLSM
144
344
  gen_set.sort(&element_sorter)
145
345
  end
146
346
 
147
- #Returns the submonoid generated by set.
347
+ # Calculates the set of elements which will be generated by the given set.
348
+ #
349
+ # @param [Array] set The elements which act as generators
350
+ #
351
+ # @return [Monoid] the monoid generated by the given set.
352
+ #
353
+ # @raise [RLSM::Error] if one of the elements isn't a monoid element.
354
+ #
355
+ # @see RLSM::Monoid#get_generated_set.
148
356
  def get_submonoid(set)
149
357
  elements = generated_set(set)
150
358
 
151
359
  set_to_monoid(elements)
152
360
  end
153
361
 
154
- #Returns an array of all submonoids (including the trivial monoid and the monoid itself).
362
+ # Calculates all submonoids.
363
+ #
364
+ # @return [Array] List with all submonoids in it.
155
365
  def submonoids
156
366
  candidates = get_submonoid_candidates
157
367
  candidates.map { |set| set_to_monoid(set) }
158
368
  end
159
369
 
160
- #Returns an array of all proper submonoids.
370
+ # Calculates all proper submonoids (i.e. all submonoids without the
371
+ # monoid itself and the trivial one).
372
+ #
373
+ # @return [Array] List with all proper submonoids in it.
161
374
  def proper_submonoids
162
375
  candidates = get_submonoid_candidates.select do |cand|
163
376
  cand.size > 1 and cand.size < @order
@@ -166,27 +379,21 @@ module RLSM
166
379
  candidates.map { |set| set_to_monoid(set) }
167
380
  end
168
381
 
169
- #Returns the smallest set (first in terms of cardinality, then lexicographically) which generates the monoid.
382
+ # Finds the smallest set which generates the whole monoid
383
+ # (smallest in the sense of cardinality of the set).
384
+ #
385
+ # @return [Array] A set of elements which generates the whole monoid.
170
386
  def generating_subset
171
387
  sorted_subsets.find { |set| generated_set(set).size == @order }
172
388
  end
173
389
 
174
- #Checks if +self+ is isomorph to +other+
175
- def =~(other)
176
- bijective_maps_to(other).any? { |map| isomorphism?(map,other) }
177
- end
178
-
179
- #Synonym for =~
180
- def isomorph?(other)
181
- self =~ other
182
- end
183
-
184
- #Checks if +self+ is antiisomorph to +other+.
185
- def antiisomorph?(other)
186
- bijective_maps_to(other).any? { |map| antiisomorphism?(map,other) }
187
- end
188
-
189
- #If an argument is given, checks if this element is idempotent. Otherwise checks if the monoid itself is idempotent.
390
+ # @overload idempotent?
391
+ # Checks if the monoid is idempotent (i.e. all elements are idempotent).
392
+ #
393
+ # @overload idempotent?(element)
394
+ # Checks if given element is idempotent.
395
+ #
396
+ # @return [Boolean] result of the check
190
397
  def idempotent?(element = nil)
191
398
  if element
192
399
  self[element,element] == element
@@ -195,22 +402,46 @@ module RLSM
195
402
  end
196
403
  end
197
404
 
198
- #Returns the order of an element.
405
+ # Calculates the order of the monoid.
406
+ #
407
+ # @param element An element of the monoid.
408
+ #
409
+ # @raise [RLSM::Error] if the element isn't a monoid element.
410
+ #
411
+ # @return [Integer] Order of the given element.
199
412
  def order_of(element)
200
413
  generated_set([element]).size
201
414
  end
202
415
 
203
- #Returns the principal right ideal of the element.
416
+ # Calculates the principal right ideal of the given element.
417
+ #
418
+ # @param element An element of the monoid.
419
+ #
420
+ # @raise [RLSM::Error] if the element isn't a monoid element.
421
+ #
422
+ # @return [Array] Principle right ideal of the given element.
204
423
  def right_ideal(element)
205
424
  @elements.map { |el| self[element,el] }.uniq.sort(&element_sorter)
206
425
  end
207
426
 
208
- #Returns the principal left ideal of the element.
427
+ # Calculates the principal left ideal of the given element.
428
+ #
429
+ # @param element An element of the monoid.
430
+ #
431
+ # @raise [RLSM::Error] if the element isn't a monoid element.
432
+ #
433
+ # @return [Array] Principle left ideal of the given element.
209
434
  def left_ideal(element)
210
435
  @elements.map { |el| self[el,element] }.uniq.sort(&element_sorter)
211
436
  end
212
437
 
213
- #Returns the principal (twosided) ideal of the element.
438
+ # Calculates the principal (twosided) ideal of the given element.
439
+ #
440
+ # @param element An element of the monoid.
441
+ #
442
+ # @raise [RLSM::Error] if the element isn't a monoid element.
443
+ #
444
+ # @return [Array] Principle ideal of the given element.
214
445
  def ideal(element)
215
446
  result = []
216
447
  @elements.each do |el1|
@@ -222,17 +453,33 @@ module RLSM
222
453
  result.uniq.sort(&element_sorter)
223
454
  end
224
455
 
225
- #Returns the neutral element of the monoid.
456
+ # The neutral element of the monoid.
457
+ #
458
+ # @return neutral element of the monoid.
226
459
  def identity
227
460
  @elements.first
228
461
  end
229
462
 
230
- #Checks if +element+ is the neutral element.
463
+ # Checks if given element is the neutral element.
464
+ #
465
+ # @param element An element of the monoid.
466
+ #
467
+ # @return [Boolean] Result of the check.
231
468
  def identity?(element)
232
469
  element == identity
233
470
  end
234
471
 
235
- #If a argument is given, checks if +element+ is the zero element. If no arguement is given, checks if a zero element exists.
472
+ # @overload zero?
473
+ # Checks if the monoid has a zero element.
474
+ #
475
+ # @overload zero?(element)
476
+ # Checks if the given element is the zero element.
477
+ #
478
+ # @param element An element of the monoid.
479
+ #
480
+ # @raise [RLSM::Error] if element isn't a monoid element
481
+ #
482
+ # @return [Boolean] Result of the check.
236
483
  def zero?(element = nil)
237
484
  if element
238
485
  return false if @order == 1
@@ -244,140 +491,257 @@ module RLSM
244
491
  end
245
492
  end
246
493
 
247
- #Returns the zero element if it exists. Return +nil+ if no zero element exists.
494
+ # Calculates the zero element of the monoid (if it exists).
495
+ #
496
+ # @return [nil] if the monoid has no zero element
497
+ # @return [Object] the zero element
248
498
  def zero
249
499
  @elements.find { |el| zero?(el) }
250
500
  end
251
501
 
252
- #Checks if +element+ is a left zero element.
502
+ # Checks if the given element is a left zero element.
503
+ #
504
+ # @param element An element of the monoid.
505
+ #
506
+ # @raise [RLSM::Error] if element isn't a monoid element
507
+ #
508
+ # @return [Boolean] Result of the check.
253
509
  def left_zero?(element)
254
510
  return false if @order == 1
255
511
  @elements.all? { |x| self[element,x] == element }
256
512
  end
257
513
 
258
- #Checks if +element+ is a right zero element.
514
+ # Checks if the given element is a right zero element.
515
+ #
516
+ # @param element An element of the monoid.
517
+ #
518
+ # @raise [RLSM::Error] if element isn't a monoid element
519
+ #
520
+ # @return [Boolean] Result of the check.
259
521
  def right_zero?(element)
260
522
  return false if @order == 1
261
523
  @elements.all? { |x| self[x,element] == element }
262
524
  end
263
525
 
264
- #Returns an array with all right zero elements.
526
+ # Calculates all right zero elements of the monoid.
527
+ #
528
+ # @return [Array] the right zero elements of the monoid.
265
529
  def right_zeros
266
530
  @elements.select { |el| right_zero?(el) }
267
531
  end
268
532
 
269
- #Returns an array with all left zero elements.
533
+ # Calculates all left zero elements of the monoid.
534
+ #
535
+ # @return [Array] the left zero elements of the monoid.
270
536
  def left_zeros
271
537
  @elements.select { |el| left_zero?(el) }
272
538
  end
273
539
 
274
- #Returns an array with all idempotent elements.
540
+ # Calculates all idempotent elements of the monoid.
541
+ #
542
+ # @return [Array] the idempotent elements of the monoid.
275
543
  def idempotents
276
544
  @elements.select { |el| idempotent?(el) }
277
545
  end
278
546
 
279
- #Checks if the monoid is a group.
547
+ # Checks if the monoid is a group.
548
+ #
549
+ # @return [Boolean] Result of the check.
280
550
  def group?
281
551
  idempotents.size == 1
282
552
  end
283
553
 
284
- #Checks if the monoid is commutative.
554
+ # Checks if the monoid is commutative.
555
+ #
556
+ # @return [Boolean] Result of the check.
285
557
  def commutative?
286
- @binary_operation.commutative?
558
+ is_commutative
287
559
  end
288
560
 
289
- #Checks if the monoid is monogenic, i.e it is generated by a single element.
561
+ # Checks if the monoid is monogenic (i.e. generated by a single element).
562
+ #
563
+ # @return [Boolean] Result of the check.
290
564
  def monogenic?
291
565
  generating_subset.size == 1
292
566
  end
293
567
 
294
- #Calculates the L-class of an element.
568
+ # Calculates the L-class of an element.
569
+ #
570
+ # @param element An element of the monoid.
571
+ #
572
+ # @raise [RLSM::Error] if the element isn't a monoid element.
573
+ #
574
+ # @return [Array] L-class of the given element.
295
575
  def l_class(element)
296
576
  li = left_ideal(element)
297
577
  @elements.select { |el| left_ideal(el) == li }
298
578
  end
299
579
 
300
- #Calculates the R-class of an element.
580
+ # Calculates the R-class of an element.
581
+ #
582
+ # @param element An element of the monoid.
583
+ #
584
+ # @raise [RLSM::Error] if the element isn't a monoid element.
585
+ #
586
+ # @return [Array] R-class of the given element.
301
587
  def r_class(element)
302
588
  r = right_ideal(element)
303
589
  @elements.select { |el| right_ideal(el) == r }
304
590
  end
305
591
 
306
- #Calculates the J-class of an element.
592
+ # Calculates the J-class of an element.
593
+ #
594
+ # @param element An element of the monoid.
595
+ #
596
+ # @raise [RLSM::Error] if the element isn't a monoid element.
597
+ #
598
+ # @return [Array] J-class of the given element.
307
599
  def j_class(element)
308
600
  d = ideal(element)
309
601
  @elements.select { |el| ideal(el) == d }
310
602
  end
311
603
 
312
- #Calculates the H-class of an element.
604
+ # Calculates the H-class of an element.
605
+ #
606
+ # @param element An element of the monoid.
607
+ #
608
+ # @raise [RLSM::Error] if the element isn't a monoid element.
609
+ #
610
+ # @return [Array] H-class of the given element.
313
611
  def h_class(element)
314
612
  l_class(element) & r_class(element)
315
613
  end
316
-
317
- #Synonym for j_class (in a finite monoid the J and D relation are the same).
614
+
615
+ # Calculates the D-class of an element.
616
+ # Synonym for j_class (J and D relation are the same for a finite monoid).
617
+ #
618
+ # @param element An element of the monoid.
619
+ #
620
+ # @raise [RLSM::Error] if the element isn't a monoid element.
621
+ #
622
+ # @return [Array] D-class of the given element.
318
623
  def d_class(element)
319
624
  j_class(element)
320
625
  end
321
626
 
322
- #Synonym for h_trivial?.
323
- def aperiodic?
324
- h_trivial?
627
+ # Calculates all R-classes of the monoid.
628
+ #
629
+ # @return [Array] List of all R-classes.
630
+ def r_classes
631
+ not_tested = @elements.dup
632
+ classes = []
633
+
634
+ until not_tested.empty?
635
+ classes << r_class(not_tested.first)
636
+ not_tested = not_tested.reject { |el| classes.last.include? el }
637
+ end
638
+
639
+ classes.sort(&subset_sorter)
325
640
  end
326
641
 
327
- ##
328
- # :method: l_classes
329
- # Returns all L-classes of the monoid.
330
-
331
- ##
332
- # :method: r_classes
333
- # Returns all R-classes of the monoid.
334
-
335
- ##
336
- # :method: j_classes
337
- # Returns all J-classes of the monoid.
338
-
339
- ##
340
- # :method: h_classes
341
- # Returns all H-classes of the monoid.
342
-
343
- ##
344
- # :method: d_classes
345
- # Returns all D-classes of the monoid.
642
+ # Calculates all L-classes of the monoid.
643
+ #
644
+ # @return [Array] List of all L-classes.
645
+ def l_classes
646
+ not_tested = @elements.dup
647
+ classes = []
648
+
649
+ until not_tested.empty?
650
+ classes << l_class(not_tested.first)
651
+ not_tested = not_tested.reject { |el| classes.last.include? el }
652
+ end
653
+
654
+ classes.sort(&subset_sorter)
655
+ end
346
656
 
347
- ##
348
- # :method: l_trivial?
349
- # Checks if all L-classes consist of one element.
657
+ # Calculates all J-classes of the monoid.
658
+ #
659
+ # @return [Array] List of all J-classes.
660
+ def j_classes
661
+ not_tested = @elements.dup
662
+ classes = []
663
+
664
+ until not_tested.empty?
665
+ classes << j_class(not_tested.first)
666
+ not_tested = not_tested.reject { |el| classes.last.include? el }
667
+ end
668
+
669
+ classes.sort(&subset_sorter)
670
+ end
350
671
 
351
- ##
352
- # :method: r_trivial?
353
- # Checks if all R-classes consist of one element.
672
+ # Calculates all H-classes of the monoid.
673
+ #
674
+ # @return [Array] List of all H-classes.
675
+ def h_classes
676
+ not_tested = @elements.dup
677
+ classes = []
678
+
679
+ until not_tested.empty?
680
+ classes << h_class(not_tested.first)
681
+ not_tested = not_tested.reject { |el| classes.last.include? el }
682
+ end
683
+
684
+ classes.sort(&subset_sorter)
685
+ end
354
686
 
355
- ##
356
- # :method: j_trivial?
357
- # Checks if all J-classes consist of one element.
687
+ # Calculates all D-classes of the monoid.
688
+ #
689
+ # @return [Array] List of all D-classes.
690
+ def d_classes
691
+ j_classes
692
+ end
693
+
694
+ # Checks if all R-classes of the monoid are singleton sets.
695
+ #
696
+ # @return [Boolean] Result of the check.
697
+ def r_trivial?
698
+ @elements.all? { |el| r_class(el).size == 1 }
699
+ end
700
+
701
+ # Checks if all L-classes of the monoid are singleton sets.
702
+ #
703
+ # @return [Boolean] Result of the check.
704
+ def l_trivial?
705
+ @elements.all? { |el| l_class(el).size == 1 }
706
+ end
358
707
 
359
- ##
360
- # :method: h_trivial?
361
- # Checks if all H-classes consist of one element.
708
+ # Checks if all J-classes of the monoid are singleton sets.
709
+ #
710
+ # @return [Boolean] Result of the check.
711
+ def j_trivial?
712
+ @elements.all? { |el| j_class(el).size == 1 }
713
+ end
362
714
 
363
- ##
364
- # :method: d_trivial?
365
- # Checks if all D-classes consist of one element.
715
+ # Checks if all H-classes of the monoid are singleton sets.
716
+ #
717
+ # @return [Boolean] Result of the check.
718
+ def h_trivial?
719
+ @elements.all? { |el| h_class(el).size == 1 }
720
+ end
366
721
 
367
- ##
368
- # Method missing magic...
369
- def method_missing(name) #:nodoc:
370
- case name.to_s
371
- when /([jlrhd])_classes/
372
- green_classes($1)
373
- when /([jlrhd])_trivial?/
374
- green_trivial?($1)
375
- else
376
- super
377
- end
722
+ # Checks if all D-classes of the monoid are singleton sets.
723
+ #
724
+ # @return [Boolean] Result of the check.
725
+ def d_trivial?
726
+ @elements.all? { |el| d_class(el).size == 1 }
727
+ end
728
+
729
+ # Checks if the monoid is aperiodic (i.e. H-trivial).
730
+ #
731
+ # @return [Boolean] Result of the check.
732
+ #
733
+ # @see RLSM::Monoid#h_trivial?
734
+ def aperiodic?
735
+ h_trivial?
378
736
  end
379
737
 
380
- #Checks if the given set is a disjunctive subset.
738
+ # Checks if the given set is a disjunctive subset.
739
+ #
740
+ # @param [Array] set A set of monoid elements
741
+ #
742
+ # @raise [RLSM::Error] If one of the given elements isn't a monoid element.
743
+ #
744
+ # @return [Boolean] Result of the check.
381
745
  def subset_disjunctive?(set)
382
746
  tupels = []
383
747
  @elements.each do |el1|
@@ -393,51 +757,59 @@ module RLSM
393
757
  end
394
758
  end
395
759
 
396
- #Returns a disjunctive subset if any exists. Returns +nil+ otherwise.
760
+ # Calculate a disjunctive subset if one exists.
761
+ #
762
+ # @return [nil] if no disjunctive subset exists.
763
+ # @return [Array] a disjunctive subset.
397
764
  def disjunctive_subset
398
- @elements.powerset.find { |s| subset_disjunctive? s }
765
+ RLSM::ArrayExt::powerset(@elements).find { |s| subset_disjunctive? s }
399
766
  end
400
767
 
401
- #Returns an array with all disjunctive subsets.
768
+ # Calculate all disjunctive subsets.
769
+ #
770
+ # @return [Array] all disjunctive subsets.
402
771
  def all_disjunctive_subsets
403
- @elements.powerset.select { |s| subset_disjunctive? s }
772
+ RLSM::ArrayExt::powerset(@elements).select { |s| subset_disjunctive? s }
404
773
  end
405
774
 
406
- #Checks if the monoid is syntactic, i.e. if it has a disjunctive subset.
775
+ # Checks if the monoid is syntactic, i.e. if it has a disjunctive subset.
776
+ #
777
+ # @return [Boolean] Result of the check.
407
778
  def syntactic?
408
779
  !!disjunctive_subset
409
780
  end
410
781
 
411
- def to_s # :nodoc:
412
- result = ""
413
- sep = @elements.any? { |x| x.length > 1 } ? ',' : ''
414
- @binary_operation.table.each_with_index do |el,i|
415
- result += @binary_operation.elements[el]
416
- if (i+1) % (@order) == 0
417
- result += ' '
418
- else
419
- result += sep unless i = @order**2 - 1
420
- end
782
+ # Transforms the monoid to a regexp which has this monoid as its syntactic
783
+ # monoid. Each disjunctive subset may yield a different language.
784
+ # If no argument is given, the smallest disjunctive subset is used.
785
+ #
786
+ # @raise [RLSM::Error] If given set isn't a disjunctive subset or the monoid
787
+ # isn't syntactic.
788
+ #
789
+ # @param [Array] set A disjunctive subset of the monoid.
790
+ #
791
+ # @return [RLSM::RegExp] A regular expression which has this monoi as the
792
+ # syntactic monoid.
793
+ def to_regexp(set = nil)
794
+ unless syntactic?
795
+ raise RLSM::Error, "Only syntactic monoids may be transformed to regexp"
421
796
  end
422
-
423
- result
424
- end
425
-
426
- def inspect # :nodoc:
427
- "<#{self.class}: #{to_s}>"
428
- end
429
-
430
- #Returns the monoid.
431
- def to_monoid
432
- self
433
- end
434
-
435
- #Returns a regular expression which represents a language with a syntactic monoid isomorph to +self+.
436
- def to_regexp
437
- to_dfa.to_regexp
797
+
798
+ to_dfa(set).to_regexp
438
799
  end
439
800
 
440
- #Returns a DFA which represents a language with a syntactic monoid isomorph to +self+.
801
+ # Transform this monoid into a minimal DFA, which has this monoid as
802
+ # transition monoid.
803
+ #
804
+ # @param [Array] finals The set of final states in the resulting DFA.
805
+ # Must be a disjunctive subset. If no subset is given,
806
+ # the smallest disjunctive subset is used.
807
+ #
808
+ # @raise [RLSM::Error] If given set isn't a disjunctive subset of the monoid
809
+ # or the monoid isn't syntactic.
810
+ #
811
+ # @return [RLSM::DFA] A minimal DFA which has this monoid as its transition
812
+ # monoid.
441
813
  def to_dfa(finals = nil)
442
814
  finals = finals || disjunctive_subset || []
443
815
 
@@ -462,7 +834,37 @@ module RLSM
462
834
  RLSM::DFA.new string
463
835
  end
464
836
 
465
- private
837
+ # @overload regular?
838
+ # Checks if the monoid is regular.
839
+ #
840
+ # @overload regular?(a)
841
+ # Checks if given element is regular.
842
+ #
843
+ # @param a an element of the monoid
844
+ # @raise [RLSM::Error] if given element isn't a monoid element.
845
+ # @return [Boolean] Result of the check.
846
+ def regular?(a=nil)
847
+ if a.nil?
848
+ @elements.all? { |x| regular?(x) }
849
+ else
850
+ @elements.any? { |x| self[a,x,a] == a}
851
+ end
852
+ end
853
+
854
+ # Checks if the monoid is inverse.
855
+ #
856
+ # @return [Boolean] Result of the check.
857
+ def inverse?
858
+ regular? and
859
+ idempotents.all? { |x| idempotents.all? { |y| self[x,y] == self[y,x] } }
860
+ end
861
+
862
+
863
+ # Transforms a given subset of the elements to a submonoid.
864
+ #
865
+ # @raise [RLSM::Error] if set isn't a subset of the monoid elements or the
866
+ # set isn't a submonoid.
867
+ # @return [Monoid] the submonoid formed by the given set.
466
868
  def set_to_monoid(set)
467
869
  description = set.map do |el1|
468
870
  set.map { |el2| self[el1,el2] }.join(",")
@@ -471,10 +873,19 @@ module RLSM
471
873
  RLSM::Monoid[ description.join(' ') ]
472
874
  end
473
875
 
876
+
877
+ # The order in the transition table is the natural order for the elements.
878
+ #
879
+ # @return [Proc] As argument for the sort method, elements will be compared
880
+ # by their indices rather than by their names.
474
881
  def element_sorter
475
882
  Proc.new { |el1,el2| @elements.index(el1) <=> @elements.index(el2)}
476
883
  end
477
884
 
885
+ # Subsets are first ordered by size and then lexicographically.
886
+ #
887
+ # @return [Proc] As argument for the sort method, subsets will be sorted
888
+ # first by size, then lexicographically.
478
889
  def subset_sorter
479
890
  Proc.new do |set1,set2|
480
891
  if set1.size == set2.size
@@ -486,16 +897,23 @@ module RLSM
486
897
  end
487
898
  end
488
899
 
900
+ # Calculates a list of all subsets and sorts them.
901
+ #
902
+ # @return [Array] List of subsets sorted by size and lexicographically
489
903
  def sorted_subsets
490
- subsets = @elements.powerset
904
+ subsets = RLSM::ArrayExt::powerset(@elements)
491
905
 
492
906
  subsets.sort(&subset_sorter)
493
907
  end
494
-
908
+
909
+ # Calculates a list of sets of elements which form a submonoid.
910
+ # Sorts the result.
911
+ #
912
+ # @return [Array] List of sets of elements which form a submonoid.
495
913
  def get_submonoid_candidates
496
914
  submons = []
497
915
 
498
- @elements.powerset.each do |set|
916
+ RLSM::ArrayExt::powerset(@elements).each do |set|
499
917
  candidate = generated_set(set)
500
918
  submons << candidate unless submons.include? candidate
501
919
  end
@@ -503,48 +921,40 @@ module RLSM
503
921
  submons.sort(&subset_sorter)
504
922
  end
505
923
 
506
- def bijective_maps_to(other)
924
+ # Iterates over all isomorphisms form this monoid to the other.
925
+ #
926
+ # @yield [isomorphism] An isomorphism from this monoid to the other.
927
+ def isomorphisms(other)
507
928
  return [] if @order != other.order
508
929
 
509
- other.elements.permutations.map do |perm|
510
- Hash[*@elements.zip(perm).flatten]
511
- end
512
- end
930
+ RLSM::ArrayExt::permutations(other.elements).map do |perm|
931
+ map = Hash[*@elements.zip(perm).flatten]
513
932
 
514
- def isomorphism?(map,other)
515
- @elements.each do |el1|
516
- @elements.each do |el2|
517
- return false if map[self[el1,el2]] != other[map[el1],map[el2]]
933
+ if @elements.all? { |x| @elements.all? { |y|
934
+ map[self[x,y]] == other[map[x],map[y]]
935
+ } }
936
+ yield map
518
937
  end
519
938
  end
520
-
521
- true
522
939
  end
523
940
 
524
- def antiisomorphism?(map,other)
525
- @elements.each do |el1|
526
- @elements.each do |el2|
527
- return false if map[self[el1,el2]] != other[map[el2],map[el1]]
528
- end
529
- end
941
+ # Iterates over all antiisomorphisms form this monoid to the other.
942
+ #
943
+ # @yield [isomorphism] An antiisomorphism from this monoid to the other.
944
+ def antiisomorphisms(other)
945
+ return [] if @order != other.order
530
946
 
531
- true
532
- end
947
+ RLSM::ArrayExt::permutations(other.elements).map do |perm|
948
+ map = Hash[*@elements.zip(perm).flatten]
533
949
 
534
- def green_classes(type)
535
- not_tested = @elements.dup
536
- classes = []
537
-
538
- until not_tested.empty?
539
- classes << self.send((type + '_class').to_sym, not_tested.first)
540
- not_tested = not_tested.reject { |el| classes.last.include? el }
950
+ if @elements.all? { |x| @elements.all? { |y|
951
+ map[self[x,y]] == other[map[y],map[x]]
952
+ } }
953
+ yield map
954
+ end
541
955
  end
542
-
543
- classes.sort(&subset_sorter)
544
956
  end
957
+
545
958
 
546
- def green_trivial?(type)
547
- @elements.all? { |el| self.send((type + '_class').to_sym, el).size == 1 }
548
- end
549
959
  end # of class Monoid
550
960
  end # of module RLSM