rlsm 1.0.0 → 1.1.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/{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/test/helpers.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#Setting up the testing environment
|
2
|
+
require "rubygems"
|
3
|
+
require "minitest/unit"
|
4
|
+
require "minitest/autorun"
|
5
|
+
|
6
|
+
#Add the lib dir to the load path
|
7
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
8
|
+
|
9
|
+
#Defining some convinience methods for tests (doesn't want to load the minitest/spec)
|
10
|
+
#Basically copied from minitest/spec and removed nested describe feature and renamed things
|
11
|
+
module Kernel
|
12
|
+
def context desc, &block
|
13
|
+
name = desc.to_s.split(/\W+/).map { |s| s.capitalize }.join + "Spec"
|
14
|
+
cls = Object.class_eval "class #{name} < MiniTest::Spec; end; #{name}"
|
15
|
+
|
16
|
+
cls.class_eval(&block)
|
17
|
+
end
|
18
|
+
private :context
|
19
|
+
end
|
20
|
+
|
21
|
+
class MiniTest::Spec < MiniTest::Unit::TestCase
|
22
|
+
def initialize name
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.before(type = :each, &block)
|
27
|
+
raise "unsupported before type: #{type}" unless type == :each
|
28
|
+
define_method :setup, &block
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.after(type = :each, &block)
|
32
|
+
raise "unsupported after type: #{type}" unless type == :each
|
33
|
+
define_method :teardown, &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.test desc, &block
|
37
|
+
default_block = lambda { skip "Pending: #{desc}" }
|
38
|
+
block = default_block unless block_given?
|
39
|
+
|
40
|
+
define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#Adding two IMHO missing assertions.
|
45
|
+
module MiniTest::Assertions
|
46
|
+
def assert_nothing_raised msg = nil, &block
|
47
|
+
begin
|
48
|
+
yield
|
49
|
+
rescue Exception => exception
|
50
|
+
msg = message(msg) { exception_details exception, "Expected nothing raises, but" }
|
51
|
+
assert false, msg
|
52
|
+
else
|
53
|
+
pass
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def assert_same_elements expected, actual, msg = nil
|
58
|
+
msg = message(msg) { "Expected that #{p expected} and #{p actual} have same elements" }
|
59
|
+
assert_equal expected.size, actual.size, msg
|
60
|
+
refute expected.uniq!, "assert_same_elements expects unique elements."
|
61
|
+
refute actual.uniq!, "assert_same_elements expects unique elements."
|
62
|
+
expected.each do |element|
|
63
|
+
assert actual.include?(element), msg
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/test/test_binop.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'shoulda'
|
3
|
+
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
5
|
+
|
6
|
+
require 'rlsm/binary_operation'
|
7
|
+
|
8
|
+
class BinaryOperationTest < Test::Unit::TestCase
|
9
|
+
context "BinaryOperation::new" do
|
10
|
+
should "Require argument" do
|
11
|
+
assert_raises ArgumentError do
|
12
|
+
RLSM::BinaryOperation.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should "Parse a valid description with elements" do
|
17
|
+
binop = RLSM::BinaryOperation.new "012:000 000 000"
|
18
|
+
|
19
|
+
assert_equal Hash['0', 0, '1', 1, '2', 2], binop.mapping
|
20
|
+
assert_equal %w(0 1 2), binop.elements
|
21
|
+
assert_equal 3, binop.order
|
22
|
+
assert_equal [0]*9, binop.table
|
23
|
+
end
|
24
|
+
|
25
|
+
should "Ignore whitspaces between element seperators" do
|
26
|
+
binop = RLSM::BinaryOperation.new "0, 1,2:0,0,0 0,0, 0 0, 0,0"
|
27
|
+
|
28
|
+
assert_equal Hash['0', 0, '1', 1, '2', 2], binop.mapping
|
29
|
+
assert_equal %w(0 1 2), binop.elements
|
30
|
+
assert_equal 3, binop.order
|
31
|
+
assert_equal [0]*9, binop.table
|
32
|
+
end
|
33
|
+
|
34
|
+
should "Raise ParseError if some commas are missing" do
|
35
|
+
assert_raises ParseError do
|
36
|
+
RLSM::BinaryOperation.new "012:0,1,2 210 2,2,2"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
should "Parse commas in the elements part, no commas in table part" do
|
41
|
+
binop = RLSM::BinaryOperation.new "0,1,2:000 000 000"
|
42
|
+
|
43
|
+
assert_equal Hash['0', 0, '1', 1, '2', 2], binop.mapping
|
44
|
+
assert_equal %w(0 1 2), binop.elements
|
45
|
+
assert_equal 3, binop.order
|
46
|
+
assert_equal [0]*9, binop.table
|
47
|
+
end
|
48
|
+
|
49
|
+
should "Parse commas in the table part, no commas in elements part" do
|
50
|
+
binop = RLSM::BinaryOperation.new "012:0,0,0 0,0,0 0,0,0"
|
51
|
+
|
52
|
+
assert_equal Hash['0', 0, '1', 1, '2', 2], binop.mapping
|
53
|
+
assert_equal %w(0 1 2), binop.elements
|
54
|
+
assert_equal 3, binop.order
|
55
|
+
assert_equal [0]*9, binop.table
|
56
|
+
end
|
57
|
+
|
58
|
+
should "Raise ParseError if too many elements are given." do
|
59
|
+
assert_raises ParseError do
|
60
|
+
RLSM::BinaryOperation.new "012:0,1,3 210 2,2,2"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
should "Raise ParseError if too few elements are given." do
|
65
|
+
assert_raises ParseError do
|
66
|
+
RLSM::BinaryOperation.new "000 000 000"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
should "Raise ParseError if table format is wrong." do
|
71
|
+
assert_raises ParseError do
|
72
|
+
RLSM::BinaryOperation.new "012:0,1,2 2,1 2,2,2"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
should "Parse a monoid with neutral element in first row." do
|
77
|
+
binop = RLSM::BinaryOperation.new "0123 1230 2301 3012"
|
78
|
+
|
79
|
+
assert_equal Hash['0', 0, '1', 1, '2', 2, '3', 3], binop.mapping
|
80
|
+
assert_equal %w(0 1 2 3), binop.elements
|
81
|
+
assert_equal 4, binop.order
|
82
|
+
assert_equal [0,1,2,3,1,2,3,0,2,3,0,1,3,0,1,2], binop.table
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "BinaryOperation#associative?" do
|
87
|
+
should "Return true if binary operation is associative." do
|
88
|
+
assert_equal true, RLSM::BinaryOperation.new("01:00 00").associative?
|
89
|
+
end
|
90
|
+
|
91
|
+
should "Return false if binary operation is not associative." do
|
92
|
+
assert_equal false, RLSM::BinaryOperation.new("01 00").associative?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "BinaryOperation#commutative?" do
|
97
|
+
should "Return true if binary operation is commutative." do
|
98
|
+
assert_equal true, RLSM::BinaryOperation.new("012 111 211").commutative?
|
99
|
+
end
|
100
|
+
|
101
|
+
should "Return false if binary operation is not commutative." do
|
102
|
+
assert_equal false, RLSM::BinaryOperation.new("012 111 222").commutative?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "BinaryOperation#enforce_associativity" do
|
107
|
+
should "Raise BinOpError for nonassociative binary operation." do
|
108
|
+
assert_raises BinOpError do
|
109
|
+
RLSM::BinaryOperation.new("01 00").enforce_associativity
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
should "Do nothing for assovityive binary operation." do
|
114
|
+
assert_nothing_raised do
|
115
|
+
RLSM::BinaryOperation.new("01 10").enforce_associativity
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/test/test_dfa.rb
ADDED
@@ -0,0 +1,435 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helpers')
|
2
|
+
|
3
|
+
require "rlsm/dfa"
|
4
|
+
require "rlsm/monoid"
|
5
|
+
|
6
|
+
context "Parsing the description of a DFA." do
|
7
|
+
test "A description of the DFA is required." do
|
8
|
+
assert_raises ArgumentError do
|
9
|
+
RLSM::DFA.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
test "The empty string is not accepted" do
|
14
|
+
assert_raises DFAError do
|
15
|
+
RLSM::DFA.new ""
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
test "A valid description should be parsed" do
|
20
|
+
assert_nothing_raised do
|
21
|
+
RLSM::DFA.new "}s1-a->*s2 s2-b->s1"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test "The description must include an initial state indicator." do
|
26
|
+
assert_raises DFAError do
|
27
|
+
RLSM::DFA.new "s1"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "The description may not include more than one initial state." do
|
32
|
+
assert_raises DFAError do
|
33
|
+
RLSM::DFA.new "}s1-a->}s2"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
test "A DFA without transitions is allowed." do
|
38
|
+
assert_nothing_raised do
|
39
|
+
RLSM::DFA.new "}s1"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
test "A transition arrow must have a starting dash." do
|
44
|
+
assert_raises DFAError do
|
45
|
+
RLSM::DFA.new "}s1 a-> s2"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
test "A transition arrow must have a closing arrow." do
|
50
|
+
assert_raises DFAError do
|
51
|
+
RLSM::DFA.new "}s1 -a> s2"
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_raises DFAError do
|
55
|
+
RLSM::DFA.new "}s1 -a- s2"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
test "A transition must have at least one label." do
|
60
|
+
assert_raises DFAError do
|
61
|
+
RLSM::DFA.new "}s1 --> s2"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
test "Multiple labels must be seperated by a comma." do
|
66
|
+
assert_raises DFAError do
|
67
|
+
RLSM::DFA.new "}s1 -a;b-> s2"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
test "Trailing and leading whitspaces will be ignored." do
|
72
|
+
assert_nothing_raised do
|
73
|
+
RLSM::DFA.new " }s1 -a, b-> *s2 "
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
test "A transition must start with a state." do
|
78
|
+
assert_raises DFAError do
|
79
|
+
RLSM::DFA.new " -a-> }s2"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
test "A transition must end with a state." do
|
84
|
+
assert_raises DFAError do
|
85
|
+
RLSM::DFA.new "}s1 -a-> "
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "Creation of a DFA." do
|
91
|
+
test "A DFA must have an initial state." do
|
92
|
+
dfa = RLSM::DFA.new "}s1"
|
93
|
+
|
94
|
+
assert_equal 's1', dfa.initial_state
|
95
|
+
|
96
|
+
dfa = RLSM::DFA.new "}s2"
|
97
|
+
|
98
|
+
assert_equal 's2', dfa.initial_state
|
99
|
+
end
|
100
|
+
|
101
|
+
test "A DFA without final states is allowed." do
|
102
|
+
assert_equal [], RLSM::DFA.new("}s1").final_states
|
103
|
+
end
|
104
|
+
|
105
|
+
test "A DFA may have final states." do
|
106
|
+
assert_equal ['s2'], RLSM::DFA.new("}s1-a->*s2").final_states
|
107
|
+
end
|
108
|
+
|
109
|
+
test "The initial state may be a final state" do
|
110
|
+
assert_equal ['s1'], RLSM::DFA.new("}*s1-a->s2").final_states
|
111
|
+
assert_equal ['s1'], RLSM::DFA.new("*}s1-a->s2").final_states
|
112
|
+
end
|
113
|
+
|
114
|
+
test "A DFA may have one state" do
|
115
|
+
assert_equal ['s1'], RLSM::DFA.new("}*s1-a->s1").states
|
116
|
+
end
|
117
|
+
|
118
|
+
test "A DFA may have more than one state" do
|
119
|
+
assert_equal ['s1','s2'], RLSM::DFA.new("}s1-a->*s2").states
|
120
|
+
end
|
121
|
+
|
122
|
+
test "A DFA may have more than one state without transitions." do
|
123
|
+
assert_equal ['s1','s2'], RLSM::DFA.new("}s1 s2").states
|
124
|
+
end
|
125
|
+
|
126
|
+
test "A DFA must have an alphabet." do
|
127
|
+
assert_equal ['1','a'], RLSM::DFA.new("}s1-a,1->*s1").alphabet
|
128
|
+
assert_equal ['a','b'], RLSM::DFA.new("}s1-a,b->*s1").alphabet
|
129
|
+
end
|
130
|
+
|
131
|
+
test "A DFA may have none transitions." do
|
132
|
+
assert_equal [], RLSM::DFA.new("}s1").transitions
|
133
|
+
end
|
134
|
+
|
135
|
+
test "A DFA may have transitions." do
|
136
|
+
assert_equal [ %w(s1 s2 a) ], RLSM::DFA.new("}s1-a->s2").transitions
|
137
|
+
assert_equal( [ %w(s1 s2 a), %w(s1 s2 b) ],
|
138
|
+
RLSM::DFA.new("}s1-a,b->s2").transitions)
|
139
|
+
end
|
140
|
+
|
141
|
+
test "Transition labels out from one state must be unique" do
|
142
|
+
assert_raises DFAError do
|
143
|
+
RLSM::DFA.new "}s1 -a-> s2 s1-a->s3"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
test "Duplications of transitions are allowed." do
|
148
|
+
assert_nothing_raised do
|
149
|
+
RLSM::DFA.new "}s1 -a-> s1 s1 -a->s1 s2 -a,a,a->s2"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "Properties of a state" do
|
155
|
+
test "There may be unreachable states." do
|
156
|
+
dfa = RLSM::DFA.new "}s1 s2"
|
157
|
+
|
158
|
+
assert dfa.reachable?('s1')
|
159
|
+
refute dfa.reachable?('s2')
|
160
|
+
end
|
161
|
+
|
162
|
+
test "A DFA may be connected." do
|
163
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s2 s3 -a-> s2"
|
164
|
+
dfa2 = RLSM::DFA.new "}s1 -a-> *s2 s2 -a-> s3"
|
165
|
+
|
166
|
+
refute dfa1.connected?
|
167
|
+
assert dfa2.connected?
|
168
|
+
end
|
169
|
+
|
170
|
+
test "A DFA may be complete." do
|
171
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s1"
|
172
|
+
dfa2 = RLSM::DFA.new "}s1 s2"
|
173
|
+
|
174
|
+
assert dfa1.complete?
|
175
|
+
assert dfa2.complete?
|
176
|
+
end
|
177
|
+
|
178
|
+
test "A DFA may be not complete." do
|
179
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2"
|
180
|
+
dfa2 = RLSM::DFA.new "}s1 -a,b-> s2 s2 -a-> s1"
|
181
|
+
|
182
|
+
refute dfa1.complete?
|
183
|
+
refute dfa2.complete?
|
184
|
+
end
|
185
|
+
|
186
|
+
test "Dead States: Initial state is never dead." do
|
187
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
188
|
+
dfa2 = RLSM::DFA.new "}s1"
|
189
|
+
dfa3 = RLSM::DFA.new "}s1 s2 s3"
|
190
|
+
|
191
|
+
refute dfa1.dead?('s1')
|
192
|
+
refute dfa2.dead?('s1')
|
193
|
+
refute dfa3.dead?('s1')
|
194
|
+
end
|
195
|
+
|
196
|
+
test "Dead States: Final states are never dead." do
|
197
|
+
dfa = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
198
|
+
|
199
|
+
refute dfa.dead?('s3')
|
200
|
+
end
|
201
|
+
|
202
|
+
test "Dead States: A state may be dead." do
|
203
|
+
dfa = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
204
|
+
|
205
|
+
assert dfa.dead?('s2')
|
206
|
+
end
|
207
|
+
|
208
|
+
test "Arguments for state properties must be states of the DFA." do
|
209
|
+
dfa = RLSM::DFA.new "}s1"
|
210
|
+
assert_raises DFAError do
|
211
|
+
dfa.dead? "s3"
|
212
|
+
end
|
213
|
+
|
214
|
+
assert_raises DFAError do
|
215
|
+
dfa.reachable? "s3"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context "Transformations of a DFA" do
|
221
|
+
test "DFA#== : Equal DFAs must have same alphabet." do
|
222
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
223
|
+
dfa2 = RLSM::DFA.new "}s1 -c-> s2 s1 -b-> *s3 s2 -c,b-> s2 s3 -c,b-> s3"
|
224
|
+
|
225
|
+
refute_equal dfa1, dfa2
|
226
|
+
end
|
227
|
+
|
228
|
+
test "DFA#== : Equal DFAs must have same initial state." do
|
229
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
230
|
+
dfa2 = RLSM::DFA.new "}t1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
231
|
+
|
232
|
+
refute_equal dfa1, dfa2
|
233
|
+
end
|
234
|
+
|
235
|
+
test "DFA#== : Equal DFAs must have same final_states." do
|
236
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
237
|
+
dfa2 = RLSM::DFA.new "}*s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
238
|
+
|
239
|
+
refute_equal dfa1, dfa2
|
240
|
+
end
|
241
|
+
|
242
|
+
test "DFA#== : Equal DFAs must have same states." do
|
243
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
244
|
+
dfa2 = RLSM::DFA.new "}s1 -a-> t2 s1 -b-> *s3 t2 -a,b-> t2 s3 -a,b-> s3"
|
245
|
+
|
246
|
+
refute_equal dfa1, dfa2
|
247
|
+
end
|
248
|
+
|
249
|
+
test "DFA#== : Equal DFAs must have same transitions." do
|
250
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
251
|
+
dfa2 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a-> s3"
|
252
|
+
|
253
|
+
refute_equal dfa1, dfa2
|
254
|
+
end
|
255
|
+
|
256
|
+
test "DFA#== : Equal monoids must be equal." do
|
257
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
258
|
+
dfa2 = RLSM::DFA.new "}s1 -a-> s2 s1 -b-> *s3 s2 -a,b-> s2 s3 -a,b-> s3"
|
259
|
+
|
260
|
+
assert_equal dfa1, dfa2
|
261
|
+
end
|
262
|
+
|
263
|
+
test "DFA#minimize! : Minimizing a connected complete DFA." do
|
264
|
+
dfa = RLSM::DFA.new "}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3 3 -a,b-> trap trap -a,b-> trap"
|
265
|
+
|
266
|
+
dfa.minimize!
|
267
|
+
|
268
|
+
expected = RLSM::DFA.new "}i-a,b-> 1 1 -b-> 1 1-a->*3"
|
269
|
+
assert_equal expected, dfa
|
270
|
+
end
|
271
|
+
|
272
|
+
test "DFA#minimize! : Minimizing a connected DFA." do
|
273
|
+
dfa = RLSM::DFA.new "}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3"
|
274
|
+
|
275
|
+
dfa.minimize!
|
276
|
+
|
277
|
+
expected = RLSM::DFA.new "}i-a,b-> 1 1 -b-> 1 1-a->*3"
|
278
|
+
assert_equal expected, dfa
|
279
|
+
end
|
280
|
+
|
281
|
+
test "DFA#minimize! : Minimizing a unconnected DFA." do
|
282
|
+
dfa = RLSM::DFA.new "}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3 s4-a->s5 *s5-a,b-> 3"
|
283
|
+
|
284
|
+
dfa.minimize!
|
285
|
+
|
286
|
+
expected = RLSM::DFA.new "}i-a,b-> 1 1 -b-> 1 1-a->*3"
|
287
|
+
assert_equal expected, dfa
|
288
|
+
end
|
289
|
+
|
290
|
+
test "DFA#minimal? : Recognizing a minimal DFA." do
|
291
|
+
assert RLSM::DFA.new("}i-a,b-> 1 1 -b-> 1 1-a->*3").minimal?
|
292
|
+
end
|
293
|
+
|
294
|
+
test "DFA#minimal? : Recognizing a nonminimal DFA." do
|
295
|
+
refute RLSM::DFA.new("}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3").minimal?
|
296
|
+
end
|
297
|
+
|
298
|
+
test "DFA#=~ : A DFA is isomorph with itself" do
|
299
|
+
dfa = RLSM::DFA.new "}s1 -a-> *s1"
|
300
|
+
assert dfa =~ dfa
|
301
|
+
end
|
302
|
+
|
303
|
+
test "DFA#=~ : A DFA is not isomorph to other things than DFAs." do
|
304
|
+
dfa = RLSM::DFA.new "}s1 -a-> *s1"
|
305
|
+
refute dfa =~ :dfa
|
306
|
+
end
|
307
|
+
|
308
|
+
test "DFA#=~ : A DFA is isomorph with another if only state names differs" do
|
309
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1"
|
310
|
+
dfa2 = RLSM::DFA.new "}t1 -a-> *t1"
|
311
|
+
assert dfa1 =~ dfa2
|
312
|
+
end
|
313
|
+
|
314
|
+
test "DFA#=~ : A DFA is not isomorph with another if alphabet differs" do
|
315
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1"
|
316
|
+
dfa2 = RLSM::DFA.new "}s1 -b-> *s1"
|
317
|
+
refute dfa1 =~ dfa2
|
318
|
+
end
|
319
|
+
|
320
|
+
test "DFA#=~ : A DFA is isomorph with another if number of state differs" do
|
321
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1 s3"
|
322
|
+
dfa2 = RLSM::DFA.new "}t1 -a-> *t1"
|
323
|
+
refute dfa1 =~ dfa2
|
324
|
+
end
|
325
|
+
|
326
|
+
test "DFA#=~ : DFA not isomorph with other if number of final state differs" do
|
327
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1 *s3"
|
328
|
+
dfa2 = RLSM::DFA.new "}t1 -a-> *t1 s3"
|
329
|
+
refute dfa1 =~ dfa2
|
330
|
+
end
|
331
|
+
|
332
|
+
test "DFA#=~ : DFA not isomorph with other if transition number differs" do
|
333
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1 s1-b->*s3"
|
334
|
+
dfa2 = RLSM::DFA.new "}s1 -a,b-> *s1 *s3-a->s3"
|
335
|
+
refute dfa1 =~ dfa2
|
336
|
+
end
|
337
|
+
|
338
|
+
test "DFA#=~ : DFA not isomorph with other if transitions differs" do
|
339
|
+
dfa1 = RLSM::DFA.new "}s1 -a-> *s1 s1-b->*s3"
|
340
|
+
dfa2 = RLSM::DFA.new "}s1 -a,b-> *s1 *s3"
|
341
|
+
refute dfa1 =~ dfa2
|
342
|
+
end
|
343
|
+
|
344
|
+
|
345
|
+
test "DFA#equivalent_to : Recognising equivalent DFAs." do
|
346
|
+
dfa1 = RLSM::DFA.new "}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3"
|
347
|
+
dfa2 = RLSM::DFA.new "}i-a,b-> 1 1 -b-> 1 1-a->*3"
|
348
|
+
|
349
|
+
assert dfa1.equivalent_to(dfa2)
|
350
|
+
end
|
351
|
+
|
352
|
+
test "DFA#equivalent_to : Recognising nonequivalent DFAs." do
|
353
|
+
dfa1 = RLSM::DFA.new "}i -a-> 1 i -b-> 2 1 -b-> 2 2-b->1 1-a->3 2-a->*3"
|
354
|
+
dfa2 = RLSM::DFA.new "}i-a,b-> 1 1-a->*3"
|
355
|
+
|
356
|
+
refute dfa1.equivalent_to(dfa2)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
context "Accepting of words, transition monoid" do
|
361
|
+
test "Following the transitions from an arbitrary state." do
|
362
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2"
|
363
|
+
|
364
|
+
assert_equal "s2", dfa['s3', 'b']
|
365
|
+
assert_equal "s2", dfa['s1', 'a']
|
366
|
+
assert_equal "s2", dfa['s3', 'bab']
|
367
|
+
end
|
368
|
+
|
369
|
+
test "Following the transitions from the initial state." do
|
370
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2"
|
371
|
+
|
372
|
+
assert_equal "s2", dfa << "a"
|
373
|
+
assert_equal "s2", dfa << "aabab"
|
374
|
+
assert_equal "s3", dfa << "aa"
|
375
|
+
end
|
376
|
+
|
377
|
+
test "Requesting a nonexistent transition." do
|
378
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2 s4"
|
379
|
+
|
380
|
+
assert_nil dfa << "b"
|
381
|
+
assert_nil dfa['s4','a']
|
382
|
+
end
|
383
|
+
|
384
|
+
test "Requesting a transition from a nonexistant state." do
|
385
|
+
assert_raises DFAError do
|
386
|
+
RLSM::DFA.new("}s1")['s2', 'a']
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
test "Accepting words." do
|
391
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2"
|
392
|
+
|
393
|
+
assert dfa.accepts?("aa")
|
394
|
+
assert dfa.accepts?("aaba")
|
395
|
+
end
|
396
|
+
|
397
|
+
test "Rejecting words." do
|
398
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2"
|
399
|
+
|
400
|
+
refute dfa.accepts?("a")
|
401
|
+
refute dfa.accepts?("aab")
|
402
|
+
end
|
403
|
+
|
404
|
+
test "Calculating the transition monoid." do
|
405
|
+
dfa = RLSM::DFA.new "}s1-a->s2 s2 -a-> *s3 s3 -b-> s2"
|
406
|
+
expected_monoid = RLSM::Monoid[ <<MONOID
|
407
|
+
id, a, b, aa, ab, ba, bb,aab,aba
|
408
|
+
a, aa, ab, bb,aab,aba, bb, bb, aa
|
409
|
+
b, ba, bb, bb, b, bb, bb, bb, ba
|
410
|
+
aa, bb,aab, bb, bb, aa, bb, bb, bb
|
411
|
+
ab,aba, bb, bb, ab, bb, bb, bb,aba
|
412
|
+
ba, bb, b, bb, bb, ba, bb, bb, bb
|
413
|
+
bb, bb, bb, bb, bb, bb, bb, bb, bb
|
414
|
+
aab, aa, bb, bb,aab, bb, bb, bb, aa
|
415
|
+
aba, bb, ab, bb, bb,aba, bb, bb, bb
|
416
|
+
MONOID
|
417
|
+
]
|
418
|
+
assert_equal expected_monoid, dfa.transition_monoid
|
419
|
+
end
|
420
|
+
|
421
|
+
end
|
422
|
+
|
423
|
+
context "DFA#to_regexp" do
|
424
|
+
test "Should return emptyset for DFA['}s1']" do
|
425
|
+
assert_equal RLSM::RegExp.empty_set, RLSM::DFA['}s1'].to_regexp
|
426
|
+
end
|
427
|
+
|
428
|
+
test "Should return empty word for DFA['}*s1']" do
|
429
|
+
assert_equal RLSM::RegExp.empty_word, RLSM::DFA['}*s1'].to_regexp
|
430
|
+
end
|
431
|
+
|
432
|
+
test "Should return a* for DFA['}*s1-a->s1']" do
|
433
|
+
assert_equal RLSM::RegExp['a*'], RLSM::DFA['}*s1-a->s1'].to_regexp
|
434
|
+
end
|
435
|
+
end
|