rlsm 1.1.0 → 1.8.1

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.
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