rlsm 0.4.0 → 1.0.0
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/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/lib/smon/base.rb
ADDED
@@ -0,0 +1,284 @@
|
|
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
ADDED
@@ -0,0 +1,98 @@
|
|
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
ADDED
@@ -0,0 +1,65 @@
|
|
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
ADDED
@@ -0,0 +1,313 @@
|
|
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
|