rlsm 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README.txt → README} +10 -23
- data/Rakefile +79 -7
- data/ext/array/array_c_ext.c +137 -0
- data/ext/array/extconf.rb +2 -0
- data/ext/binop/binop_c_ext.c +57 -0
- data/ext/binop/extconf.rb +2 -0
- data/ext/monoid/extconf.rb +2 -0
- data/ext/monoid/monoid_c_ext.c +330 -0
- data/lib/rlsm.rb +10 -14
- data/lib/rlsm/binary_operation.rb +151 -0
- data/lib/rlsm/dfa.rb +418 -602
- data/lib/rlsm/helper.rb +12 -0
- data/lib/rlsm/monoid.rb +454 -694
- data/lib/rlsm/regexp.rb +125 -0
- data/lib/rlsm/regexp_parser.rb +450 -0
- data/test/helpers.rb +66 -0
- data/test/test_binop.rb +119 -0
- data/test/test_dfa.rb +435 -0
- data/test/test_monoid.rb +552 -0
- data/test/test_regexp.rb +440 -0
- metadata +109 -37
- data/History.txt +0 -6
- data/Manifest.txt +0 -18
- data/bin/smon +0 -39
- data/data/monoids.db +0 -0
- data/lib/database.rb +0 -95
- data/lib/monkey_patching/array_ext.rb +0 -50
- data/lib/rlsm/re.rb +0 -504
- data/lib/smon/base.rb +0 -284
- data/lib/smon/db.rb +0 -98
- data/lib/smon/dot.rb +0 -65
- data/lib/smon/latex.rb +0 -313
- data/lib/smon/smon.rb +0 -183
- data/stdarb.tex +0 -118
data/lib/smon/base.rb
DELETED
@@ -1,284 +0,0 @@
|
|
1
|
-
module SMONLIBbase
|
2
|
-
def quit
|
3
|
-
exit
|
4
|
-
end
|
5
|
-
|
6
|
-
def monoid(mon, options = {})
|
7
|
-
@objects << RLSM::Monoid.new(mon, options)
|
8
|
-
puts "=> #{@objects.last.inspect}" if @interactive
|
9
|
-
@monoid = @objects.last
|
10
|
-
end
|
11
|
-
|
12
|
-
def dfa(dfa)
|
13
|
-
@objects << RLSM::DFA.create(dfa)
|
14
|
-
puts "=> #{@objects.last.inspect}" if @interactive
|
15
|
-
@dfa = @objects.last
|
16
|
-
end
|
17
|
-
|
18
|
-
def regexp(re)
|
19
|
-
@objects << RLSM::RE.new(re)
|
20
|
-
puts "=> #{@objects.last.inspect}" if @interactive
|
21
|
-
@re = @objects.last
|
22
|
-
end
|
23
|
-
|
24
|
-
def show(obj = nil)
|
25
|
-
obj ||= @objects.last
|
26
|
-
if obj
|
27
|
-
@out.puts obj.to_s
|
28
|
-
@out.puts
|
29
|
-
else
|
30
|
-
STDERR.puts "No object present."
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def describe(obj = nil)
|
35
|
-
obj ||= @objects.last
|
36
|
-
|
37
|
-
if obj
|
38
|
-
monoid, dfa = obj.to_monoid, obj.to_dfa
|
39
|
-
print_language(monoid, dfa)
|
40
|
-
print_monoid_properties(monoid)
|
41
|
-
print_submonoids(monoid)
|
42
|
-
print_syntactic_properties(monoid)
|
43
|
-
else
|
44
|
-
STDERR.puts "No object present."
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
def self.included(mod)
|
50
|
-
#Setting up the help system
|
51
|
-
mod.add_help :type => 'cmd',
|
52
|
-
:name => 'quit',
|
53
|
-
:usage => 'quit',
|
54
|
-
:summary => "Quits the program.",
|
55
|
-
:description => <<DESC
|
56
|
-
The 'quit' command is a synonym for the 'exit' command.
|
57
|
-
Only needed in interactive mode.
|
58
|
-
DESC
|
59
|
-
|
60
|
-
mod.add_help :type => 'cmd',
|
61
|
-
:name => 'monoid',
|
62
|
-
:usage => "monoid '<mon>' [<options>]",
|
63
|
-
:summary => "Creates a new monoid.",
|
64
|
-
:description => <<DESC
|
65
|
-
The <mon> parameter is a description of the binary operation
|
66
|
-
of the monoid as a table. The <mon> parameter is parsed as follows:
|
67
|
-
* Rows are seperated by spaces.
|
68
|
-
* Elements in a row are seperated by commas (',').
|
69
|
-
Commas are optional if each element consists of a single letter.
|
70
|
-
* IMPORTANT: The first row and column belongs to the neutral element!
|
71
|
-
* IMPORTANT: The <mon> parameter must be enclosed
|
72
|
-
in single or double quotes.
|
73
|
-
|
74
|
-
EXAMPLES:
|
75
|
-
monoid '1ab aab bbb'
|
76
|
-
monoid 'e,a1,a2 a1,a2,e a2,e,a1'
|
77
|
-
|
78
|
-
The optional <options> parameter is a Hash and can take the following keys:
|
79
|
-
:elements -> an array
|
80
|
-
The value of this key is used to rename
|
81
|
-
the elements of the monoid.
|
82
|
-
:normalize -> true|false
|
83
|
-
If true, the monoid will be normalized, i.e.
|
84
|
-
the generating elements will be moved to the beginning.
|
85
|
-
:rename -> true|false
|
86
|
-
If true, the elements ill be renamed to 1 a b c ...
|
87
|
-
|
88
|
-
Adds the created monoid to the @objects array
|
89
|
-
and sets the variable @monoid to the created monoid.
|
90
|
-
DESC
|
91
|
-
|
92
|
-
mod.add_help :type => 'cmd',
|
93
|
-
:name => 'dfa',
|
94
|
-
:usage => "dfa <hash>",
|
95
|
-
:summary => "Creates a new DFA.",
|
96
|
-
:description => <<DESC
|
97
|
-
The <hash> parameter must have the following keys:
|
98
|
-
:initial -> label
|
99
|
-
The label of the initial state.
|
100
|
-
:finals -> an Array
|
101
|
-
The array contains the labels of the final states.
|
102
|
-
:transitions -> an Array
|
103
|
-
The array contains the transitions of the monoid.
|
104
|
-
A transition is an array consisting of
|
105
|
-
the label of the transition
|
106
|
-
the starting state of the transition
|
107
|
-
the destination of the transition
|
108
|
-
|
109
|
-
EXAMPLE:
|
110
|
-
dfa :initial => '0', :finals => ['1'], :transitions => [['a','0','1']]
|
111
|
-
|
112
|
-
Adds the created DFA to the @objects array
|
113
|
-
and sets the variable @dfa to the created DFA.
|
114
|
-
DESC
|
115
|
-
|
116
|
-
mod.add_help :type => 'cmd',
|
117
|
-
:name => 'regexp',
|
118
|
-
:usage => "regexp '<re>'",
|
119
|
-
:summary => "Creates a new Regular Expression.",
|
120
|
-
:description => <<DESC
|
121
|
-
The <re> parameter is simply a regular expression. Special Symbols are
|
122
|
-
#{RLSM::RE::Lambda} -> the empty word
|
123
|
-
#{RLSM::RE::Star} -> the Kleene star
|
124
|
-
#{RLSM::RE::Union} -> the union of two regexps
|
125
|
-
#{RLSM::RE::LeftBracket}, #{RLSM::RE::RightBracket} -> Parenthesis to group a subregexp
|
126
|
-
|
127
|
-
IMPORTANT: The <re> parameter must be enclosed in single or double quotes.
|
128
|
-
Parentheses must be balanced.
|
129
|
-
|
130
|
-
EXAMPLES:
|
131
|
-
regexp 'a'
|
132
|
-
regexp
|
133
|
-
'#{RLSM::RE::Lambda}#{RLSM::RE::Union}#{RLSM::RE::LeftBracket}a#{RLSM::RE::Union}aab#{RLSM::RE::Star}#{RLSM::RE::RightBracket}'
|
134
|
-
|
135
|
-
Adds the created regular expression to the @objects array
|
136
|
-
and sets the variable @re to the created regular expression.
|
137
|
-
DESC
|
138
|
-
|
139
|
-
mod.add_help :type => 'cmd',
|
140
|
-
:name => 'show',
|
141
|
-
:usage => "show [<obj>]",
|
142
|
-
:summary => "Prints a very concise description of <obj>.",
|
143
|
-
:description => <<DESC
|
144
|
-
The optional <obj> parameter is either a monoid, a DFA or a RE.
|
145
|
-
If none is given the last created object will be used.
|
146
|
-
DESC
|
147
|
-
|
148
|
-
mod.add_help :type => 'cmd',
|
149
|
-
:name => 'describe',
|
150
|
-
:usage => "describe [<obj>]",
|
151
|
-
:summary => "Prints a verbose description of <obj>.",
|
152
|
-
:description => <<DESC
|
153
|
-
The optional <obj> parameter is either a monoid, a DFA or a RE.
|
154
|
-
If none is given the last created object will be used.
|
155
|
-
DESC
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
def print_syntactic_properties(m)
|
160
|
-
if m.syntactic?
|
161
|
-
@out.puts "SYNTACTIC PROPERTIES:"
|
162
|
-
|
163
|
-
m.all_disjunctive_subsets.each do |ds|
|
164
|
-
@out.puts "{#{ds.join(',')}} => #{m.to_dfa(ds).to_re}"
|
165
|
-
end
|
166
|
-
@out.puts
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def print_submonoids(m)
|
171
|
-
subs = m.proper_submonoids.map do |sm|
|
172
|
-
binary_operation(sm)
|
173
|
-
end
|
174
|
-
|
175
|
-
if subs.empty?
|
176
|
-
@out.puts "SUBMONOIDS: none"
|
177
|
-
@out.puts
|
178
|
-
else
|
179
|
-
@out.puts "SUBMONOIDS:"
|
180
|
-
rows = [[]]
|
181
|
-
width = 0
|
182
|
-
subs.each do |sm|
|
183
|
-
spl = sm.split("\n")
|
184
|
-
if (width + spl[0].length) <= 30
|
185
|
-
rows.last << spl
|
186
|
-
width += spl[0].length
|
187
|
-
else
|
188
|
-
rows << [spl]
|
189
|
-
width = spl[0].length
|
190
|
-
end
|
191
|
-
end
|
192
|
-
rows.each do |row|
|
193
|
-
max_lines = row.map { |sm| sm.size }.max
|
194
|
-
|
195
|
-
row.map! { |sm| sm + [' '*sm[0].length]*(max_lines-sm.size) }
|
196
|
-
(0...row[0].size).each do |i|
|
197
|
-
@out.puts row.map { |sm| sm[i] }.join(' ')
|
198
|
-
end
|
199
|
-
@out.puts
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
def print_monoid_properties(m)
|
205
|
-
block1 = []
|
206
|
-
block1 << "PROPERTIES OF THE MONOID:"
|
207
|
-
block1 << " Generator: {#{m.generating_subset.join(',')}}"
|
208
|
-
block1 << " Group: #{m.group?}"
|
209
|
-
block1 << " Commutative: #{m.commutative?}"
|
210
|
-
block1 << " Idempotent: #{m.idempotent?}"
|
211
|
-
block1 << " Syntactic: #{m.syntactic?}"
|
212
|
-
block1 << " Aperiodic: #{m.aperiodic?}"
|
213
|
-
block1 << " L-trivial: #{m.l_trivial?}"
|
214
|
-
block1 << " R-trivial: #{m.r_trivial?}"
|
215
|
-
block1 << " D-trivial: #{m.d_trivial?}"
|
216
|
-
block1 << "Zero element: #{!m.zero_element.nil?}"
|
217
|
-
|
218
|
-
max_length = block1.map { |row| row.length }.max
|
219
|
-
block1.map! { |row| row + ' '*(max_length - row.length) }
|
220
|
-
|
221
|
-
block2 = []
|
222
|
-
block2 << "SPECIAL ELEMENTS:"
|
223
|
-
block2 << " Idempotents: #{m.idempotents.join(',')}"
|
224
|
-
if m.zero_element
|
225
|
-
block2 << "Zero element: #{m.zero_element}"
|
226
|
-
else
|
227
|
-
lz = (m.left_zeros.empty? ? ['none'] : m.left_zeros).join(',')
|
228
|
-
block2 << " Left-Zeros: #{lz}"
|
229
|
-
rz = (m.right_zeros.empty? ? ['none'] : m.right_zeros).join(',')
|
230
|
-
block2 << " Right-Zeros: #{rz}"
|
231
|
-
end
|
232
|
-
block2 << ''
|
233
|
-
block2 << ''
|
234
|
-
block2 << "GREEN RELATIONS:"
|
235
|
-
lc = m.l_classes.map { |cl| '{' + cl.join(',') + '}' }.join(' ')
|
236
|
-
rc = m.r_classes.map { |cl| '{' + cl.join(',') + '}' }.join(' ')
|
237
|
-
hc = m.h_classes.map { |cl| '{' + cl.join(',') + '}' }.join(' ')
|
238
|
-
dc = m.d_classes.map { |cl| '{' + cl.join(',') + '}' }.join(' ')
|
239
|
-
block2 << "L-Classes: #{lc}"
|
240
|
-
block2 << "R-Classes: #{rc}"
|
241
|
-
block2 << "H-Classes: #{hc}"
|
242
|
-
block2 << "D-Classes: #{dc}"
|
243
|
-
block2 << ''
|
244
|
-
|
245
|
-
@out.puts block1.zip(block2).map { |row| row.join(' ') }.join("\n")
|
246
|
-
@out.puts
|
247
|
-
end
|
248
|
-
|
249
|
-
def print_language(m,d)
|
250
|
-
@out.puts "\nDFA:\n#{d.to_s}\n"
|
251
|
-
@out.puts "\nMONOID:\n"
|
252
|
-
@out.puts binary_operation(m)
|
253
|
-
@out.puts
|
254
|
-
end
|
255
|
-
|
256
|
-
def binary_operation(monoid)
|
257
|
-
max_length = monoid.elements.map { |e| e.length }.max
|
258
|
-
rows = monoid.binary_operation.map do |row|
|
259
|
-
row.map do |e|
|
260
|
-
space = ' '*((max_length - e.length)/2)
|
261
|
-
extra_space = ' '*((max_length - e.length)%2)
|
262
|
-
space + extra_space + e + space
|
263
|
-
end.join(' | ')
|
264
|
-
end
|
265
|
-
|
266
|
-
first = ' '*(max_length + 2) + '| ' + rows[0].clone + ' |'
|
267
|
-
seperator = first.gsub(/[^|]/,'-').gsub('|', '+')
|
268
|
-
|
269
|
-
rows.map! do |row|
|
270
|
-
' ' + row[/^[^|]+\|/] + ' ' + row + " |"
|
271
|
-
end
|
272
|
-
|
273
|
-
result = [first]
|
274
|
-
result << seperator
|
275
|
-
result << rows.join("\n" + seperator + "\n")
|
276
|
-
result << seperator
|
277
|
-
|
278
|
-
result.join("\n")
|
279
|
-
end
|
280
|
-
|
281
|
-
def get_elements(obj)
|
282
|
-
[obj.to_monoid, obj.to_dfa, obj.to_re]
|
283
|
-
end
|
284
|
-
end
|
data/lib/smon/db.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
module SMONLIBdb
|
2
|
-
def db_stat
|
3
|
-
stat = RLSM::MonoidDB.statistic
|
4
|
-
result = stat.shift.join(' | ')
|
5
|
-
|
6
|
-
column_widths = result.scan(/./).inject([0]) do |res,char|
|
7
|
-
if char == '|'
|
8
|
-
res << 0
|
9
|
-
else
|
10
|
-
res << res.pop + 1
|
11
|
-
end
|
12
|
-
|
13
|
-
res
|
14
|
-
end
|
15
|
-
|
16
|
-
result += ("\n" + ' ' + result.gsub(/[^|]/, '-').gsub('|', '+'))
|
17
|
-
|
18
|
-
stat.each do |row|
|
19
|
-
justified = []
|
20
|
-
row.each_with_index do |col,i|
|
21
|
-
col = col.to_s
|
22
|
-
space = ' '*((column_widths[i] - col.length)/2)
|
23
|
-
extra_space = ' '*((column_widths[i] - col.length)%2)
|
24
|
-
justified << space + col + space + extra_space
|
25
|
-
end
|
26
|
-
|
27
|
-
result += ("\n" + ' ' + justified.join('|'))
|
28
|
-
end
|
29
|
-
|
30
|
-
@out.puts result
|
31
|
-
@out.puts
|
32
|
-
end
|
33
|
-
|
34
|
-
def db_find(args)
|
35
|
-
count = RLSM::MonoidDB.count(args)
|
36
|
-
STDOUT.puts "Found: #{count[0]} monoid(s) (#{count[1]} syntactic)"
|
37
|
-
STDOUT.puts "Saved result in variable '@search_result'"
|
38
|
-
|
39
|
-
@search_result = RLSM::MonoidDB.find(args).flatten
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.included(mon)
|
43
|
-
#Check if database is usable
|
44
|
-
begin
|
45
|
-
require 'database'
|
46
|
-
|
47
|
-
mon.add_help :type => 'cmd',
|
48
|
-
:name => 'db_stat',
|
49
|
-
:summary => "Shows an overview of the database.",
|
50
|
-
:usage => 'db_stat',
|
51
|
-
:description => <<DESC
|
52
|
-
Prints for each order of a monoid the number of monoids in the database
|
53
|
-
and the number of monoids with certain properties.
|
54
|
-
DESC
|
55
|
-
|
56
|
-
mon.add_help :type => 'cmd',
|
57
|
-
:name => 'db_find',
|
58
|
-
:summary => "Finds monoids in the database.",
|
59
|
-
:usage => 'db_find [<options>]',
|
60
|
-
:description => <<DESC
|
61
|
-
Finds monoids which matches the given options and saves the result
|
62
|
-
in the variable '@serach_result'.
|
63
|
-
|
64
|
-
The optional <options> parameter is a Hash with the following keys:
|
65
|
-
:binop -> a String
|
66
|
-
Searches in the database for a monoid with this binary operation.
|
67
|
-
The :binop-value must have the same format as for the monoid command
|
68
|
-
and in the database the element names are 0 1 2 3 4 5 ...
|
69
|
-
(Not very useful in practice.)
|
70
|
-
|
71
|
-
:m_order -> an Integer
|
72
|
-
Searches for monoids with the given order.
|
73
|
-
|
74
|
-
:num_generators
|
75
|
-
:num_idempotents
|
76
|
-
:num_left_nulls
|
77
|
-
:num_right_nulls -> an Integer
|
78
|
-
Seraches for monoids with the given number of special
|
79
|
-
elements like idempotents or left-zeros.
|
80
|
-
|
81
|
-
:syntactic
|
82
|
-
:idempotent
|
83
|
-
:aperiodic
|
84
|
-
:commutative
|
85
|
-
:is_group
|
86
|
-
:has_null
|
87
|
-
:l_trivial
|
88
|
-
:r_trivial
|
89
|
-
:d_trivial -> true|false
|
90
|
-
Searches for monoids with the given attribute.
|
91
|
-
DESC
|
92
|
-
rescue LoadError
|
93
|
-
STDERR.puts "W: Could not load 'db'."
|
94
|
-
remove_method :db_find
|
95
|
-
remove_method :db_stat
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
data/lib/smon/dot.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
module SMONLIBdot
|
2
|
-
#Creates a picture of a DFA using dot.
|
3
|
-
def dfa2pic(dfa, options = {:format => 'png'})
|
4
|
-
filename = options[:filename] || SMON.tmp_filename
|
5
|
-
File.open(filename + ".dot", "w") { |f| f.puts dfa2dot(dfa) }
|
6
|
-
system "dot -T#{options[:format]} -o #{filename}.#{options[:format]} #{filename}.dot"
|
7
|
-
File.delete(filename + ".dot")
|
8
|
-
end
|
9
|
-
|
10
|
-
#Creates string which is a dot representation of a DFA.
|
11
|
-
def dfa2dot(dfa)
|
12
|
-
str = "digraph {\n"
|
13
|
-
str += "size=\"2,2\"\nratio=1.0\n"
|
14
|
-
str += "node [shape=circle]\n"
|
15
|
-
str += "preinit [shape=plaintext, label=\"\"]\n"
|
16
|
-
(dfa.states - dfa.finals).each do |state|
|
17
|
-
str += state + "\n"
|
18
|
-
end
|
19
|
-
str += "node [shape=doublecircle]\n"
|
20
|
-
dfa.finals.each do |state|
|
21
|
-
str += state + "\n"
|
22
|
-
end
|
23
|
-
str += "preinit -> #{dfa.initial_state}\n"
|
24
|
-
dfa.states.product(dfa.states).each do |s1,s2|
|
25
|
-
res = dfa.transitions.find_all { |tr| tr[1] == s1 and tr[2] == s2 }
|
26
|
-
unless res.empty?
|
27
|
-
label = res.map { |tr| tr[0] }.join(',')
|
28
|
-
str += s1 + "->" + s2 + "[label=\"#{label}\"]\n"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
str + "}"
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.included(mod)
|
36
|
-
unless system("which dot > /dev/null")
|
37
|
-
remove_method :dfa2pic
|
38
|
-
STDERR.puts "W: No 'dot' command found."
|
39
|
-
else
|
40
|
-
mod.add_help :type => 'cmd',
|
41
|
-
:name => 'dfa2pic',
|
42
|
-
:summary => 'Creates a picture of a DFA using dot.',
|
43
|
-
:usage => 'dfa2pic <dfa> [<options>]',
|
44
|
-
:description => <<DESC
|
45
|
-
The parameter <dfa> is a DFA which will be transformed with the dot program.
|
46
|
-
Possible optional parameters are
|
47
|
-
:format -> a String.
|
48
|
-
The output format. Defaults to 'png'. Could be all what dot
|
49
|
-
accepts.
|
50
|
-
|
51
|
-
:filename -> a String.
|
52
|
-
The filename of the output.
|
53
|
-
DESC
|
54
|
-
end
|
55
|
-
|
56
|
-
mod.add_help :type => 'cmd',
|
57
|
-
:name => 'dfa2dot',
|
58
|
-
:summary => 'Creates a string with dot commands.',
|
59
|
-
:usage => 'dfa2pic <dfa>',
|
60
|
-
:description => <<DESC
|
61
|
-
The parameter <dfa> is a DFA which will be transformed to a string
|
62
|
-
which is a valid dot description of a digraph.
|
63
|
-
DESC
|
64
|
-
end
|
65
|
-
end
|
data/lib/smon/latex.rb
DELETED
@@ -1,313 +0,0 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
|
3
|
-
module SMONLIBlatex
|
4
|
-
def m2tex(monoid)
|
5
|
-
buffer = ['\begin{tabular}{' +
|
6
|
-
(['c']*(monoid.order+1)).join('|') + '|}']
|
7
|
-
|
8
|
-
buffer << ' & ' + monoid.binary_operation[0].map do |el|
|
9
|
-
"\\textbf{#{el}}"
|
10
|
-
end.join(' & ') + "\\\\ \\hline"
|
11
|
-
|
12
|
-
monoid.binary_operation.each do |row|
|
13
|
-
buffer << "\\textbf{#{row[0]}} & " + row.join(' & ') + "\\\\ \\hline"
|
14
|
-
end
|
15
|
-
|
16
|
-
buffer << ['\end{tabular}']
|
17
|
-
buffer.join("\n")
|
18
|
-
end
|
19
|
-
|
20
|
-
def d2tex(dfa, opts = {:dot => true})
|
21
|
-
#Have we dot support
|
22
|
-
if opts[:dot] #and @__cmds.include? "dfa2pic"
|
23
|
-
filename = SMON.tmp_filename
|
24
|
-
dfa2pic dfa, :filename => filename, :format => 'plain'
|
25
|
-
str = "\\begin{xy}\n0;<1.27cm,0cm>:\n"
|
26
|
-
|
27
|
-
edges = []
|
28
|
-
File.open(filename + ".plain", 'r').each_line do |line|
|
29
|
-
values = line.split
|
30
|
-
if ['edge','node'].include? values.first
|
31
|
-
str += tex_xy_node(values)
|
32
|
-
end
|
33
|
-
edges << tex_xy_edge(values) if values.first == 'edge'
|
34
|
-
end
|
35
|
-
|
36
|
-
str += edges.join("\n")
|
37
|
-
|
38
|
-
File.delete(filename + ".plain")
|
39
|
-
|
40
|
-
return str + "\n\\end{xy}\n"
|
41
|
-
else
|
42
|
-
str = "\\begin{tabular}{r|" +
|
43
|
-
(['c']*dfa.alphabet.size).join('|') + "}\n"
|
44
|
-
str += " & " + dfa.alphabet.map do |l|
|
45
|
-
"\\textbf{#{l}}"
|
46
|
-
end.join(' & ') + " \\\\ \\hline\n"
|
47
|
-
dfa.states.each do |state|
|
48
|
-
tmp_str = ''
|
49
|
-
tmp_str += "\\ensuremath{*}" if dfa.finals.include? state
|
50
|
-
tmp_str += "\\ensuremath{\\rightarrow}" if dfa.initial_state == state
|
51
|
-
tmp_str += state + " & "
|
52
|
-
tmp_str += dfa.alphabet.map do |letter|
|
53
|
-
tmp = dfa[letter,state]
|
54
|
-
tmp ? tmp : 'nil'
|
55
|
-
end.join(' & ')
|
56
|
-
str+= tmp_str + " \\\\\n"
|
57
|
-
end
|
58
|
-
|
59
|
-
return str + "\\end{tabular}\n"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def r2tex(re)
|
64
|
-
re_str = re.pattern.
|
65
|
-
gsub(RLSM::RE::Lambda, "\\lambda ").
|
66
|
-
gsub(RLSM::RE::Union, "\\mid ").
|
67
|
-
gsub(RLSM::RE::Star, "^{*}")
|
68
|
-
|
69
|
-
"\\ensuremath{#{re_str}}"
|
70
|
-
end
|
71
|
-
|
72
|
-
def tex_describe(obj)
|
73
|
-
monoid = obj.to_monoid
|
74
|
-
dfa = obj.to_dfa
|
75
|
-
|
76
|
-
str = <<LATEX
|
77
|
-
\\begin{tabular}[t]{c|c}
|
78
|
-
\\textbf{Binary Operation} & \\textbf{DFA} \\\\ \\hline
|
79
|
-
#{m2tex(monoid)} & #{d2tex(dfa)}
|
80
|
-
\\end{tabular}\\\\[2ex]
|
81
|
-
#{tex_properties(monoid)}\\\\[2ex]
|
82
|
-
#{tex_submonoids(monoid)}\\\\[2ex]
|
83
|
-
#{monoid.syntactic? ? tex_syntactic_properties(monoid) : ''}
|
84
|
-
|
85
|
-
LATEX
|
86
|
-
|
87
|
-
@out.puts str if @interactive
|
88
|
-
|
89
|
-
str
|
90
|
-
end
|
91
|
-
|
92
|
-
def tex_preamble
|
93
|
-
<<PREAMBLE
|
94
|
-
\\documentclass[a4paper,DIV15,halfparskip*]{scrartcl}
|
95
|
-
\\usepackage[all]{xy}
|
96
|
-
\\usepackage{amsmath}
|
97
|
-
|
98
|
-
\\begin{document}
|
99
|
-
PREAMBLE
|
100
|
-
end
|
101
|
-
|
102
|
-
def compile(opts = {:format => 'dvi'})
|
103
|
-
str = tex_preamble
|
104
|
-
if opts[:object]
|
105
|
-
str += tex_describe(opts[:object])
|
106
|
-
else
|
107
|
-
str += opts[:input]
|
108
|
-
end
|
109
|
-
str += "\n\\end{document}"
|
110
|
-
|
111
|
-
filename = opts[:filename] || SMON.tmp_filename
|
112
|
-
|
113
|
-
File.open(filename + ".tex", 'w') { |f| f.puts str }
|
114
|
-
|
115
|
-
cmd = "latex -interaction=nonstopmode "
|
116
|
-
cmd += "-output-format=#{opts[:format]} " + filename + ".tex"
|
117
|
-
|
118
|
-
system cmd
|
119
|
-
|
120
|
-
clean_up filename
|
121
|
-
end
|
122
|
-
|
123
|
-
def self.included(mod)
|
124
|
-
unless system("which latex >/dev/null")
|
125
|
-
remove_method :complile
|
126
|
-
STDERR.puts "W: compile command disabled."
|
127
|
-
else
|
128
|
-
mod.add_help :type => 'cmd',
|
129
|
-
:name => 'compile',
|
130
|
-
:summary => 'Creates a dvi or pdf from the given string or object.',
|
131
|
-
:usage => 'compile <options>',
|
132
|
-
:description => <<DESC
|
133
|
-
Possible options are
|
134
|
-
:input -> a String.
|
135
|
-
A string which contains a valid latex source file.
|
136
|
-
|
137
|
-
:object -> a DFA or Monoid or Regular Expression.
|
138
|
-
The object will be transformed using tex_describe
|
139
|
-
and then typsetted.
|
140
|
-
|
141
|
-
:format -> a String.
|
142
|
-
Either 'dvi' or 'pdf'. Determines the output format of the
|
143
|
-
latex command.
|
144
|
-
|
145
|
-
:filename -> a String.
|
146
|
-
The filename of the output.
|
147
|
-
DESC
|
148
|
-
end
|
149
|
-
|
150
|
-
mod.add_help :type => 'cmd',
|
151
|
-
:name => 'm2tex',
|
152
|
-
:summary => 'Transforms a monoid to tex code.',
|
153
|
-
:usage => 'm2tex <monoid>',
|
154
|
-
:description => <<DESC
|
155
|
-
Takes a monoid and returns a string containing tex code for
|
156
|
-
representing the monoid in tex.
|
157
|
-
DESC
|
158
|
-
|
159
|
-
mod.add_help :type => 'cmd',
|
160
|
-
:name => 'r2tex',
|
161
|
-
:summary => 'Transforms a regular expression to tex code.',
|
162
|
-
:usage => 'r2tex <re>',
|
163
|
-
:description => <<DESC
|
164
|
-
Takes a regular expression and returns a string containing tex code for
|
165
|
-
representing the regular expression in tex.
|
166
|
-
DESC
|
167
|
-
|
168
|
-
mod.add_help :type => 'cmd',
|
169
|
-
:name => 'd2tex',
|
170
|
-
:summary => 'Transforms a DFA to tex code.',
|
171
|
-
:usage => 'd2tex <dfa> [<options>]',
|
172
|
-
:description => <<DESC
|
173
|
-
Takes a DFA and returns a string containing tex code for
|
174
|
-
representing the DFA in tex.
|
175
|
-
|
176
|
-
Optional a parameter :dot can be given.
|
177
|
-
:dot -> true|false
|
178
|
-
If true and the dfa2pic command is found, uses dot and xy-pic
|
179
|
-
to genearte a graph for the DFA.
|
180
|
-
If false a simple table is used for representing the DFA.
|
181
|
-
DESC
|
182
|
-
|
183
|
-
mod.add_help :type => 'cmd',
|
184
|
-
:name => 'tex_describe',
|
185
|
-
:summary => 'Returns tex code equivalent to the describe command.',
|
186
|
-
:usage => 'tex_describe <object>',
|
187
|
-
:description => <<DESC
|
188
|
-
Takes an object and returns a string containing tex code for
|
189
|
-
representing this object in tex. Focuses on the monoid properties.
|
190
|
-
DESC
|
191
|
-
|
192
|
-
mod.add_help :type => 'cmd',
|
193
|
-
:name => 'tex_preamble',
|
194
|
-
:summary => 'Returns the tex preamble used by the compile command.',
|
195
|
-
:usage => 'tex_preamble',
|
196
|
-
:description => <<DESC
|
197
|
-
Not very useful in interactive mode.
|
198
|
-
DESC
|
199
|
-
end
|
200
|
-
|
201
|
-
private
|
202
|
-
def clean_up(file)
|
203
|
-
files = Dir.glob(file + ".*").reject { |f| f =~ /(dvi|pdf)$/ }
|
204
|
-
files.each { |f| system("rm " + f) }
|
205
|
-
end
|
206
|
-
|
207
|
-
def set(s)
|
208
|
-
"\\ensuremath{\\lbrace \\mbox{#{s.join(', ')}}\\rbrace}"
|
209
|
-
end
|
210
|
-
|
211
|
-
def list(s)
|
212
|
-
str = s.join(', ')
|
213
|
-
str.empty? ? 'none' : str
|
214
|
-
end
|
215
|
-
|
216
|
-
def tex_submonoids(m)
|
217
|
-
sm = m.proper_submonoids.map { |s| m2tex(s) }
|
218
|
-
buffer = nil
|
219
|
-
if sm.empty?
|
220
|
-
buffer = ["\\textbf{Submonoids:} none"]
|
221
|
-
else
|
222
|
-
buffer = ["\\textbf{Submonoids:}\\\\[.5\\baselineskip]"]
|
223
|
-
buffer << sm.join("\\quad\n")
|
224
|
-
end
|
225
|
-
|
226
|
-
buffer.join("\n")
|
227
|
-
end
|
228
|
-
|
229
|
-
def tex_syntactic_properties(m)
|
230
|
-
buffer = ["\\textbf{Syntactic Properties:}\\\\"]
|
231
|
-
buffer << "\\begin{tabular}{ll}"
|
232
|
-
buffer << "Disjunctive Subset & Possible Regular Expression \\\\"
|
233
|
-
m.all_disjunctive_subsets.each do |ds|
|
234
|
-
re = m.to_dfa(ds).to_re
|
235
|
-
buffer << set(ds) + " & " + r2tex(re) + "\\\\"
|
236
|
-
end
|
237
|
-
buffer << "\\end{tabular}"
|
238
|
-
|
239
|
-
buffer.join("\n")
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
|
-
def tex_properties(m)
|
244
|
-
buffer = ['\begin{tabular}{llcll}']
|
245
|
-
buffer << '\multicolumn{2}{l}{\textbf{Properties of the Monoid:}} &'
|
246
|
-
buffer << ' & \multicolumn{2}{l}{\textbf{Special Elements:}} \\\\'
|
247
|
-
buffer << "Generator: & #{set(m.generating_subset)} & \\quad &" +
|
248
|
-
"Idempotents: & #{list(m.idempotents)} \\\\"
|
249
|
-
|
250
|
-
buffer << "Group: & #{m.group?} & \\quad &"
|
251
|
-
if m.zero_element
|
252
|
-
buffer[-1] += " Zero Element: & #{m.zero_element} \\\\"
|
253
|
-
else
|
254
|
-
buffer[-1] += " Left-Zeros: & #{list(m.left_zeros)} \\\\"
|
255
|
-
end
|
256
|
-
|
257
|
-
buffer << "Commutative: & #{m.commutative?} & \\quad &"
|
258
|
-
if m.zero_element
|
259
|
-
buffer[-1] += " & \\\\"
|
260
|
-
else
|
261
|
-
buffer[-1] += " Rigth-Zeros: & #{list(m.right_zeros)} \\\\"
|
262
|
-
end
|
263
|
-
|
264
|
-
buffer << "Idempotent: & #{m.idempotent?} & \\quad & & \\\\"
|
265
|
-
buffer << "Syntactic: & #{m.syntactic?} & \\quad &" +
|
266
|
-
"\\multicolumn{2}{l}{\\textbf{Green Relations:}} \\\\"
|
267
|
-
buffer << "Aperiodic: & #{m.aperiodic?} & \\quad &" +
|
268
|
-
" L-Classes: & #{list(m.l_classes.map { |x| set(x) })} \\\\"
|
269
|
-
buffer << "L-trivial: & #{m.l_trivial?} & \\quad &" +
|
270
|
-
" R-Classes: & #{list(m.r_classes.map { |x| set(x) })} \\\\"
|
271
|
-
buffer << "R-trivial: & #{m.r_trivial?} & \\quad &" +
|
272
|
-
" H-Classes: & #{list(m.h_classes.map { |x| set(x) })} \\\\"
|
273
|
-
buffer << "D-trivial: & #{m.d_trivial?} & \\quad &" +
|
274
|
-
" D-Classes: & #{list(m.d_classes.map { |x| set(x) })} \\\\"
|
275
|
-
buffer << "Zero Element: & #{!m.zero_element.nil?} & \\quad & & \\\\"
|
276
|
-
buffer << '\end{tabular}'
|
277
|
-
|
278
|
-
buffer.join("\n")
|
279
|
-
end
|
280
|
-
|
281
|
-
def tex_xy_node(n)
|
282
|
-
if n.first == 'edge'
|
283
|
-
return "" if n[1] == 'preinit'
|
284
|
-
|
285
|
-
num_points = n[3].to_i
|
286
|
-
lable_pos = n[2*num_points + 5,2].join(',')
|
287
|
-
label = n[2*num_points + 4].gsub("\"",'')
|
288
|
-
|
289
|
-
return ";(#{lable_pos})*[r]\\txt{#{label}}\n"
|
290
|
-
else
|
291
|
-
id = n[1]
|
292
|
-
xypos = n[2,2].join(',')
|
293
|
-
return "(#{xypos})*{}=\"#{id}\"\n" if n[1] == 'preinit'
|
294
|
-
|
295
|
-
label = n[6]
|
296
|
-
type = n[8] == 'circle' ? '' : '='
|
297
|
-
|
298
|
-
return ";(#{xypos})*+=[o]++[F#{type}]{#{label}}=\"#{id}\"\n"
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def tex_xy_edge(e)
|
303
|
-
from, to = e[1,2]
|
304
|
-
num_points = e[3].to_i
|
305
|
-
points = points_to_str(e[4,2*num_points])
|
306
|
-
|
307
|
-
"\\ar @`{#{points}} \"#{from}\";\"#{to}\""
|
308
|
-
end
|
309
|
-
|
310
|
-
def points_to_str(a)
|
311
|
-
"(" + a.enum_slice(2).to_a.map { |x| x.join(',') }.join('),(') + ")"
|
312
|
-
end
|
313
|
-
end
|