rlsm 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +12 -4
- data/bin/smon +39 -2
- data/{lib/data → data}/monoids.db +0 -0
- data/lib/database.rb +95 -0
- data/lib/monkey_patching/array_ext.rb +50 -0
- data/lib/rlsm.rb +6 -49
- data/lib/{dfa.rb → rlsm/dfa.rb} +65 -4
- data/lib/{monoid.rb → rlsm/monoid.rb} +18 -6
- data/lib/{re.rb → rlsm/re.rb} +16 -4
- data/lib/smon/base.rb +284 -0
- data/lib/smon/db.rb +98 -0
- data/lib/smon/dot.rb +65 -0
- data/lib/smon/latex.rb +313 -0
- data/lib/smon/smon.rb +183 -0
- data/stdarb.tex +118 -0
- metadata +14 -6
data/Manifest.txt
CHANGED
@@ -3,8 +3,16 @@ Manifest.txt
|
|
3
3
|
README.txt
|
4
4
|
Rakefile
|
5
5
|
bin/smon
|
6
|
-
|
7
|
-
lib/
|
8
|
-
lib/
|
9
|
-
lib/re.rb
|
6
|
+
data/monoids.db
|
7
|
+
lib/database.rb
|
8
|
+
lib/monkey_patching/array_ext.rb
|
10
9
|
lib/rlsm.rb
|
10
|
+
lib/rlsm/dfa.rb
|
11
|
+
lib/rlsm/monoid.rb
|
12
|
+
lib/rlsm/re.rb
|
13
|
+
lib/smon/base.rb
|
14
|
+
lib/smon/db.rb
|
15
|
+
lib/smon/dot.rb
|
16
|
+
lib/smon/latex.rb
|
17
|
+
lib/smon/smon.rb
|
18
|
+
stdarb.tex
|
data/bin/smon
CHANGED
@@ -1,2 +1,39 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'smon', 'smon')
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
Version = RLSM::VERSION
|
6
|
+
out = STDOUT
|
7
|
+
path = ""
|
8
|
+
|
9
|
+
opts = OptionParser.new do |op|
|
10
|
+
op.banner = <<USEAGE
|
11
|
+
Usage: smon [-oh] [FILE1 FILE2 ...]
|
12
|
+
Executes each FILE as smon script or if no file is given
|
13
|
+
starts in interactive mode.
|
14
|
+
|
15
|
+
USEAGE
|
16
|
+
op.separator("Options:")
|
17
|
+
|
18
|
+
op.on('-p','--path PATH', "Adds PATH to the loadpath for librarys.") do |p|
|
19
|
+
path = p
|
20
|
+
end
|
21
|
+
|
22
|
+
op.on('-o', '--out FILE', "Output is redirected to FILE.") do |f|
|
23
|
+
out = File.open(f, 'w')
|
24
|
+
end
|
25
|
+
|
26
|
+
op.on_tail('-h', '--help', "This help.") { puts op; exit }
|
27
|
+
op.on_tail('--version', "Shows version.") { puts op.ver }
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
opts.parse! ARGV
|
32
|
+
|
33
|
+
SMON.new :files =>ARGV, :messenger => out, :path => path
|
34
|
+
rescue => e
|
35
|
+
STDERR.puts "Error: #{e.message}"
|
36
|
+
STDERR.puts
|
37
|
+
STDERR.puts opts
|
38
|
+
exit 1
|
39
|
+
end
|
File without changes
|
data/lib/database.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "sqlite3"
|
5
|
+
|
6
|
+
class RLSM::MonoidDB
|
7
|
+
@@db = SQLite3::Database.open(File.join(File.dirname(__FILE__), '..',
|
8
|
+
'data', 'monoids.db'))
|
9
|
+
|
10
|
+
Columns = [:binop, :m_order, :num_generators,
|
11
|
+
:num_idempotents, :num_left_nulls, :num_right_nulls,
|
12
|
+
:syntactic, :idempotent, :aperiodic,
|
13
|
+
:commutative, :is_group, :has_null,
|
14
|
+
:l_trivial, :r_trivial, :d_trivial]
|
15
|
+
|
16
|
+
def self.query(query, &block)
|
17
|
+
if block_given?
|
18
|
+
@@db.execute(query, &block)
|
19
|
+
else
|
20
|
+
@@db.execute(query)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find(params = {}, &block)
|
25
|
+
if block_given?
|
26
|
+
query construct_query(params), &block
|
27
|
+
else
|
28
|
+
query construct_query(params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.count(params = {})
|
33
|
+
q = construct_query(params).sub('T binop F',
|
34
|
+
"T count(*), total(syntactic) F")
|
35
|
+
query(q).first.map { |x| x.to_i }
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.statistic
|
39
|
+
res = @@db.execute2 <<SQL
|
40
|
+
SELECT
|
41
|
+
m_order AS 'Order',
|
42
|
+
count(*) AS 'Total',
|
43
|
+
total(syntactic) AS 'Syntactic',
|
44
|
+
total(is_group) AS 'Groups',
|
45
|
+
total(commutative) AS 'Commutative',
|
46
|
+
total(aperiodic) AS 'Aperiodic',
|
47
|
+
total(idempotent) AS 'Idempotent'
|
48
|
+
FROM monoids
|
49
|
+
GROUP BY m_order
|
50
|
+
ORDER BY 'Order' ASC;
|
51
|
+
SQL
|
52
|
+
|
53
|
+
desc = res.shift
|
54
|
+
res.map! { |row| row.map { |x| x.to_i } }
|
55
|
+
res.unshift desc
|
56
|
+
|
57
|
+
res
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def self.construct_query(params)
|
62
|
+
limit = ""
|
63
|
+
if params[:limit]
|
64
|
+
limit = "\nLIMIT #{params[:limit]}"
|
65
|
+
params.delete :limit
|
66
|
+
if params[:offset]
|
67
|
+
limit += " OFFSET #{params[:offset]}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
order_by = "\nORDER BY binop #{params[:ordering] || 'ASC'}"
|
72
|
+
|
73
|
+
params.delete :ordering
|
74
|
+
|
75
|
+
q = "SELECT binop FROM monoids"
|
76
|
+
|
77
|
+
where = Columns.inject([]) do |res,col|
|
78
|
+
if params[col]
|
79
|
+
if [:binop, :m_order].include?(col) or col.to_s =~ /^num_/
|
80
|
+
res << "#{col.to_s} = #{params[col]}"
|
81
|
+
else
|
82
|
+
res << "#{col.to_s} = 1"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
res
|
87
|
+
end.join(" AND ")
|
88
|
+
|
89
|
+
if where.length > 0
|
90
|
+
q += "\nWHERE " + where
|
91
|
+
end
|
92
|
+
|
93
|
+
q + order_by + limit + ";"
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RLSMArrayExt # :nodoc:
|
2
|
+
#Returns all permutations of the array.
|
3
|
+
def permutations
|
4
|
+
return [self] if size < 2
|
5
|
+
perm = []
|
6
|
+
each { |e| (self - [e]).permutations.each { |p| perm << ([e] + p) } }
|
7
|
+
perm
|
8
|
+
end
|
9
|
+
|
10
|
+
#Returns the powerset of the array (interpreted as set).
|
11
|
+
def powerset
|
12
|
+
ret = self.inject([[]]) do |acc, x|
|
13
|
+
res = []
|
14
|
+
acc.each { |s| res << s; res << ([x]+s).sort }
|
15
|
+
res
|
16
|
+
end
|
17
|
+
|
18
|
+
ret.sort_lex
|
19
|
+
end
|
20
|
+
|
21
|
+
#Sorts an array of arrays more or less lexicographical.
|
22
|
+
def sort_lex
|
23
|
+
sort { |s1, s2| s1.size == s2.size ? s1 <=> s2 : s1.size <=> s2.size }
|
24
|
+
end
|
25
|
+
|
26
|
+
#Returns the cartesian product of self and the given arrays
|
27
|
+
def product(*args)
|
28
|
+
args.inject(self.map { |x| [x] }) do |res,arr|
|
29
|
+
new = []
|
30
|
+
arr.each do |x|
|
31
|
+
new += res.map { |tup| tup += [x] }
|
32
|
+
end
|
33
|
+
new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#Returns all unordered pairs.
|
38
|
+
def unordered_pairs
|
39
|
+
pairs = []
|
40
|
+
(0...size-1).each do |i|
|
41
|
+
pairs |= self[i+1..-1].map { |x| [self[i],x] }
|
42
|
+
end
|
43
|
+
|
44
|
+
pairs
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Array # :nodoc:
|
49
|
+
include RLSMArrayExt
|
50
|
+
end
|
data/lib/rlsm.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
2
2
|
|
3
|
+
require 'monkey_patching/array_ext'
|
4
|
+
|
3
5
|
module RLSM
|
4
|
-
VERSION = '0.
|
6
|
+
VERSION = '1.0.0'
|
5
7
|
end
|
6
8
|
|
7
9
|
#Setting up the exception classes.
|
@@ -10,51 +12,6 @@ class MonoidException < RLSMException; end
|
|
10
12
|
class DFAException < RLSMException; end
|
11
13
|
class REException < RLSMException; end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#Returns all permutations of the array.
|
17
|
-
def permutations
|
18
|
-
return [self] if size < 2
|
19
|
-
perm = []
|
20
|
-
each { |e| (self - [e]).permutations.each { |p| perm << ([e] + p) } }
|
21
|
-
perm
|
22
|
-
end
|
23
|
-
|
24
|
-
#Returns the powerset of the array (interpreted as set).
|
25
|
-
def powerset
|
26
|
-
ret = self.inject([[]]) do |acc, x|
|
27
|
-
res = []
|
28
|
-
acc.each { |s| res << s; res << ([x]+s).sort }
|
29
|
-
res
|
30
|
-
end
|
31
|
-
|
32
|
-
ret.sort_lex
|
33
|
-
end
|
34
|
-
|
35
|
-
#Sorts an array of arrays more or less lexicographical.
|
36
|
-
def sort_lex
|
37
|
-
sort { |s1, s2| s1.size == s2.size ? s1 <=> s2 : s1.size <=> s2.size }
|
38
|
-
end
|
39
|
-
|
40
|
-
#Returns the cartesian product of self and the given arrays
|
41
|
-
def product(*args)
|
42
|
-
args.inject(self.map { |x| [x] }) do |res,arr|
|
43
|
-
new = []
|
44
|
-
arr.each do |x|
|
45
|
-
new += res.map { |tup| tup += [x] }
|
46
|
-
end
|
47
|
-
new
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
#Returns all unordered pairs.
|
52
|
-
def unordered_pairs
|
53
|
-
pairs = []
|
54
|
-
(0...size-1).each do |i|
|
55
|
-
pairs |= self[i+1..-1].map { |x| [self[i],x] }
|
56
|
-
end
|
57
|
-
|
58
|
-
pairs
|
59
|
-
end
|
60
|
-
end
|
15
|
+
require 'rlsm/monoid'
|
16
|
+
require 'rlsm/dfa'
|
17
|
+
require 'rlsm/re'
|
data/lib/{dfa.rb → rlsm/dfa.rb}
RENAMED
@@ -1,6 +1,4 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
|
2
|
-
require 'monoid'
|
3
|
-
require "re"
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rlsm'))
|
4
2
|
|
5
3
|
=begin rdoc
|
6
4
|
==Basic theory of deterministic finite automaton (short DFA)
|
@@ -160,6 +158,16 @@ It is required that all occuring states are in the states array and all transiti
|
|
160
158
|
|
161
159
|
attr_reader :alphabet, :states, :initial_state, :finals
|
162
160
|
|
161
|
+
#Returns an Array with 3-tuples as transitions.
|
162
|
+
def transitions
|
163
|
+
res = []
|
164
|
+
@alphabet.each do |letter|
|
165
|
+
res |= @transitions[letter].to_a.map { |x| [letter] + x }
|
166
|
+
end
|
167
|
+
|
168
|
+
res
|
169
|
+
end
|
170
|
+
|
163
171
|
#Returns the number of states.
|
164
172
|
def num_states
|
165
173
|
@states.size
|
@@ -338,7 +346,7 @@ Minimizes the DFA. Takes as optional parameter a hash with only processed key :+
|
|
338
346
|
|
339
347
|
#Returns the transition monoid of the DFA.
|
340
348
|
def transition_monoid
|
341
|
-
RLSM::Monoid.new get_binary_operation, :
|
349
|
+
RLSM::Monoid.new get_binary_operation, :rename => true
|
342
350
|
end
|
343
351
|
|
344
352
|
#Returns the transition monoid of the equivalent minimal DFA.
|
@@ -346,6 +354,16 @@ Minimizes the DFA. Takes as optional parameter a hash with only processed key :+
|
|
346
354
|
minimize.transition_monoid
|
347
355
|
end
|
348
356
|
|
357
|
+
#Synonym for syntactic_monoid
|
358
|
+
def to_monoid
|
359
|
+
syntactic_monoid
|
360
|
+
end
|
361
|
+
|
362
|
+
#Returns self.
|
363
|
+
def to_dfa
|
364
|
+
self
|
365
|
+
end
|
366
|
+
|
349
367
|
#Returns a RE which represents the same language.
|
350
368
|
def to_re
|
351
369
|
les = []
|
@@ -360,6 +378,49 @@ Minimizes the DFA. Takes as optional parameter a hash with only processed key :+
|
|
360
378
|
simplify_les_row(les.pop)[:final]
|
361
379
|
end
|
362
380
|
|
381
|
+
def inspect
|
382
|
+
"<#{self.class}: #{@states.join(',')}>"
|
383
|
+
end
|
384
|
+
|
385
|
+
def to_s
|
386
|
+
output = []
|
387
|
+
output << @states.inject(['']) do |res, state|
|
388
|
+
prefix = ''
|
389
|
+
prefix += '*' if @finals.include? state
|
390
|
+
prefix += '->' if initial_state == state
|
391
|
+
|
392
|
+
res + [prefix + ' ' + state + ' ']
|
393
|
+
end
|
394
|
+
|
395
|
+
@alphabet.each do |letter|
|
396
|
+
column = [' ' + letter + ' ']
|
397
|
+
@states.each do |state|
|
398
|
+
tmp = self[letter,state]
|
399
|
+
if tmp.nil?
|
400
|
+
column << ' nil '
|
401
|
+
else
|
402
|
+
column << ' ' + tmp + ' '
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
output << column.clone
|
407
|
+
end
|
408
|
+
|
409
|
+
#Align output
|
410
|
+
output.map! do |col|
|
411
|
+
max_length = col.map { |x| x.length }.max
|
412
|
+
col.map { |x| ' '*(max_length - x.length) + x }
|
413
|
+
end
|
414
|
+
|
415
|
+
rows = (0..@states.size).map { |i| output.map { |col| col[i] } }
|
416
|
+
rows.map! { |row| row.join('|') }
|
417
|
+
head = rows.shift
|
418
|
+
rows.unshift head.scan(/./).map { |c| c == '|' ? '+' : '-' }.join
|
419
|
+
rows.unshift head
|
420
|
+
|
421
|
+
rows.join("\n")
|
422
|
+
end
|
423
|
+
|
363
424
|
private
|
364
425
|
def initialize_row_for(state)
|
365
426
|
row = { :state => state.clone }
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
|
2
|
-
require 'dfa'
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rlsm'))
|
3
2
|
|
4
3
|
=begin rdoc
|
5
4
|
=The RLSM::Monoid class
|
@@ -548,7 +547,7 @@ Returns true if the monoid is aperiodic. (A synonym for h_trivial?)
|
|
548
547
|
end
|
549
548
|
|
550
549
|
def inspect # :nodoc:
|
551
|
-
"<#{self.class}
|
550
|
+
"<#{self.class}: #{to_s}>"
|
552
551
|
end
|
553
552
|
|
554
553
|
=begin rdoc
|
@@ -638,9 +637,22 @@ Also if the monoid is syntactic the set returned by disjunctive subset will be u
|
|
638
637
|
end
|
639
638
|
end
|
640
639
|
|
641
|
-
RLSM::DFA.create(:initial => neutral_element,
|
642
|
-
|
643
|
-
|
640
|
+
dfa = RLSM::DFA.create(:initial => neutral_element,
|
641
|
+
:finals => finals,
|
642
|
+
:transitions => get_transitions)
|
643
|
+
dfa.rename_states
|
644
|
+
|
645
|
+
dfa
|
646
|
+
end
|
647
|
+
|
648
|
+
#Returns itself.
|
649
|
+
def to_monoid
|
650
|
+
self
|
651
|
+
end
|
652
|
+
|
653
|
+
#Returns the regexp for the language.
|
654
|
+
def to_re
|
655
|
+
to_dfa.to_re
|
644
656
|
end
|
645
657
|
|
646
658
|
private
|
data/lib/{re.rb → rlsm/re.rb}
RENAMED
@@ -1,6 +1,4 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
|
2
|
-
require 'dfa'
|
3
|
-
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rlsm'))
|
4
2
|
require 'enumerator'
|
5
3
|
|
6
4
|
class RLSM::RE
|
@@ -11,6 +9,10 @@ class RLSM::RE
|
|
11
9
|
Lambda = '&'
|
12
10
|
Specials = [LeftBracket, RightBracket, Star, Union, Lambda]
|
13
11
|
|
12
|
+
def to_s
|
13
|
+
@pattern
|
14
|
+
end
|
15
|
+
|
14
16
|
def inspect
|
15
17
|
"<#{self.class}: #@pattern>"
|
16
18
|
end
|
@@ -86,6 +88,16 @@ class RLSM::RE
|
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
91
|
+
#Returns the syntactic monoid to this language.
|
92
|
+
def to_monoid
|
93
|
+
to_dfa.to_monoid
|
94
|
+
end
|
95
|
+
|
96
|
+
#Returns self.
|
97
|
+
def to_re
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
89
101
|
#Returns true if the res are equal
|
90
102
|
def ==(other)
|
91
103
|
return true if @pattern == other.pattern
|
@@ -158,7 +170,7 @@ class RLSM::RE
|
|
158
170
|
|
159
171
|
def recursive_split(child, type)
|
160
172
|
if type_of(child) == type
|
161
|
-
return self.send
|
173
|
+
return self.send("split_#{type}".to_sym, child)
|
162
174
|
else
|
163
175
|
return [child]
|
164
176
|
end
|