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