Skip to content

Commit

Permalink
[automata] Refactoring & New API (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
moorara authored Nov 1, 2022
1 parent 96d345e commit 57dd7ae
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 15 deletions.
2 changes: 1 addition & 1 deletion automata/automata.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type States []State
// Contains determines whether or not a set of states contains a given state.
func (s States) Contains(t State) bool {
for _, state := range s {
if t == state {
if state == t {
return true
}
}
Expand Down
14 changes: 14 additions & 0 deletions automata/dfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ func (d *DFA) Accept(s String) bool {
return d.Final.Contains(curr)
}

// ToNFA constructs a new NFA accepting the same language as the DFA (every DFA is an NFA).
func (d *DFA) ToNFA() *NFA {
nfa := NewNFA(d.Start, d.Final)
for _, kv := range d.trans.KeyValues() {
S := kv.Key
for _, kv := range kv.Val.KeyValues() {
a, T := kv.Key, kv.Val
nfa.Add(S, a, States{T})
}
}

return nfa
}

// Equals determines whether or not two DFAs are the same.
//
// TODO: Implement isomorphic equality.
Expand Down
36 changes: 35 additions & 1 deletion automata/dfa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ func getTestDFAs() []*DFA {
d2.Add(8, 'a', 5)
d2.Add(8, 'b', 6)

return []*DFA{d0, d1, d2}
d3 := NewDFA(0, States{3})
d3.Add(0, 'a', 1)
d3.Add(0, 'b', 0)
d3.Add(1, 'a', 1)
d3.Add(1, 'b', 2)
d3.Add(2, 'a', 1)
d3.Add(2, 'b', 3)
d3.Add(3, 'a', 1)
d3.Add(3, 'b', 0)

return []*DFA{d0, d1, d2, d3}
}

func TestNewDFA(t *testing.T) {
Expand Down Expand Up @@ -296,6 +306,30 @@ func TestDFA_Accept(t *testing.T) {
}
}

func TestDFA_ToNFA(t *testing.T) {
dfas := getTestDFAs()
nfas := getTestNFAs()

tests := []struct {
name string
d *DFA
expectedNFA *NFA
}{
{
name: "OK",
d: dfas[3],
expectedNFA: nfas[3],
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
nfa := tc.d.ToNFA()
assert.True(t, nfa.Equals(tc.expectedNFA))
})
}
}

func TestDFA_Equals(t *testing.T) {
dfas := getTestDFAs()

Expand Down
10 changes: 6 additions & 4 deletions automata/nfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,14 @@ func (n *NFA) ToDFA() *DFA {
return s.Equals(t)
})

// Initially, ε-closure(s0) is the only state in Dstates
Dstates.Enqueue(n.εClosure(States{n.Start}))

for T, i := Dstates.Dequeue(); i >= 0; T, i = Dstates.Dequeue() {
for _, a := range symbols {
for _, a := range symbols { // for each input symbol c
U := n.εClosure(n.move(T, a))

// If U is not in Dstates, add U to Dstates
j := Dstates.Contains(U)
if j == -1 {
j = Dstates.Enqueue(U)
Expand All @@ -225,11 +227,11 @@ func (n *NFA) ToDFA() *DFA {
dfa.Start = State(0)
dfa.Final = States{}

for i, s := range Dstates.Values() {
for i, S := range Dstates.Values() {
for _, f := range n.Final {
if s.Contains(f) {
if S.Contains(f) {
dfa.Final = append(dfa.Final, State(i))
break
break // The accepting states of D are all those sets of N's states that include at least one accepting state of N
}
}
}
Expand Down
14 changes: 12 additions & 2 deletions automata/nfa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ func getTestNFAs() []*NFA {
n2.Add(13, 'b', States{14})
n2.Add(14, 'b', States{15})

return []*NFA{n0, n1, n2}
n3 := NewNFA(0, States{3})
n3.Add(0, 'a', States{1})
n3.Add(0, 'b', States{0})
n3.Add(1, 'a', States{1})
n3.Add(1, 'b', States{2})
n3.Add(2, 'a', States{1})
n3.Add(2, 'b', States{3})
n3.Add(3, 'a', States{1})
n3.Add(3, 'b', States{0})

return []*NFA{n0, n1, n2, n3}
}

func TestNewNFA(t *testing.T) {
Expand Down Expand Up @@ -293,8 +303,8 @@ func TestNFA_Accept(t *testing.T) {
}

func TestNFA_ToDFA(t *testing.T) {
dfas := getTestDFAs()
nfas := getTestNFAs()
dfas := getTestDFAs()

tests := []struct {
name string
Expand Down
4 changes: 2 additions & 2 deletions list/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

type containsTest[T any] struct {
val T
expectedResult bool
val T
expected bool
}

func TestNewArrayNode(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions list/queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ func TestQueue(t *testing.T) {
}

for _, tc := range tc.containsTests {
res := queue.Contains(tc.val)
assert.Equal(t, tc.expectedResult, res)
assert.Equal(t, tc.expected, queue.Contains(tc.val))
}

for _, val := range tc.expectedDequeueValues {
Expand Down
2 changes: 1 addition & 1 deletion list/soft_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestSoftQueue(t *testing.T) {

for _, tc := range tc.containsTests {
i := squeue.Contains(tc.val)
assert.Equal(t, tc.expectedResult, i >= 0)
assert.Equal(t, tc.expected, i >= 0)
}

for _, val := range tc.expectedDequeues {
Expand Down
3 changes: 1 addition & 2 deletions list/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ func TestStack(t *testing.T) {
}

for _, tc := range tc.containsTests {
res := stack.Contains(tc.val)
assert.Equal(t, tc.expectedResult, res)
assert.Equal(t, tc.expected, stack.Contains(tc.val))
}

for _, val := range tc.expectedPopValues {
Expand Down

0 comments on commit 57dd7ae

Please sign in to comment.