// Returns true if there isn't a word that takes this state into a final ones func (d *DFA) IsReverseUnreachable(state int) bool { if d.IsFinal(state) { return false } if len(d.Graph[state]) == 0 { return true } q := queue.New(d.NumStates) viz := make([]bool, d.NumStates+1) q.Push(state) for !q.Empty() { node, _ := q.Pop() for _, neighbours := range d.Graph[node] { if viz[neighbours[0]] { continue } if d.IsFinal(neighbours[0]) { return false } viz[neighbours[0]] = true q.Push(neighbours[0]) } } return true }
func TestQ(t *testing.T) { q := queue.New() fmt.Println(q.Empty()) fmt.Println(q.Size()) fmt.Println("~~~~~~~~~~~") for i := 0; i < 10; i++ { q.Push(i) fmt.Println(q.Empty()) fmt.Println(q.Size()) } fmt.Println("~~~~~~~~~~~~~~") for !q.Empty() { fmt.Println(q.Front()) q.Pop() } }
// Transforms an NFA into a DFA that accepts the same language; some // inaccessible states will be lost in the process func (original_nfa *NFA) ToDFA() dfa.DFA { nfa := Copy(*original_nfa) n := &nfa // first follow along λ-transitions to make them obsolete is_final := make([]bool, n.NumStates+1) for _, node := range n.FinalStates { is_final[node] = true } for i := 1; i <= n.NumStates; i++ { q := queue.New(n.NumStates) added := make([]bool, n.NumStates+1) for _, node := range n.Graph[i]['λ'] { q.Push(node) } for !q.Empty() { node, _ := q.Pop() if is_final[node] && !is_final[i] { is_final[i] = true n.FinalStates = append(n.FinalStates, i) } for character, neighbours := range n.Graph[node] { for _, neighbour := range neighbours { if character == 'λ' && !added[neighbour] { q.Push(neighbour) added[neighbour] = true } n.Graph[i][character] = append(n.Graph[i][character], neighbour) n.NumTransitions++ } } } } // then remove λ-transitions for i := 1; i <= n.NumStates; i++ { _, ok := n.Graph[i]['λ'] if ok { n.NumTransitions -= len(n.Graph[i]['λ']) delete(n.Graph[i], 'λ') } } // then turn that into a DFA visited := make([]bool, 1<<uint(n.NumStates+1)) dfa := make(map[int]map[rune]int) q := queue.New(1 << uint(n.NumStates)) q.Push(1 << uint(n.EntryState-1)) final_states := make([]int, 0) for !q.Empty() { node_code, _ := q.Pop() is_final_node := false for node := 1; node < (1 << uint(n.NumStates)); node++ { if 1<<uint(node-1) > node_code { break } if ((1 << uint(node-1)) & node_code) == 0 { continue } if is_final[node] { is_final_node = true } if _, ok := dfa[node_code]; !ok { dfa[node_code] = make(map[rune]int) } for character, neighbours := range n.Graph[node] { for _, neighbour := range neighbours { dfa[node_code][character] = dfa[node_code][character] | (1 << uint(neighbour-1)) } } for _, neighbour := range dfa[node_code] { if !visited[neighbour] { q.Push(neighbour) visited[neighbour] = true } } } if is_final_node { final_states = append(final_states, node_code) } } // encode the newly created DFA into our standard form for node, _ := range n.Graph { delete(n.Graph, node) } nr := n.NumStates n.NumStates = 0 n.NumTransitions = 0 n.EntryState = 1 << uint(n.EntryState-1) mapping := make(map[int]int) current_node := 0 for i := 1; i < (1 << uint(nr)); i++ { if _, ok := dfa[i]; !ok || len(dfa[i]) == 0 { continue } if _, ok := mapping[i]; !ok { current_node++ mapping[i] = current_node } n.Graph[mapping[i]] = make(map[rune][]int) n.NumTransitions += len(dfa[i]) for character, node := range dfa[i] { if _, ok := mapping[node]; !ok { current_node++ mapping[node] = current_node } n.Graph[mapping[i]][character] = []int{mapping[node]} } } if _, ok := mapping[n.EntryState]; ok { n.EntryState = mapping[n.EntryState] } else { current_node++ mapping[n.EntryState] = current_node n.EntryState = current_node } n.NumStates = current_node n.FinalStates = make([]int, 0) for _, node := range final_states { if _, ok := mapping[node]; ok { n.FinalStates = append(n.FinalStates, mapping[node]) } } return n.DFA }
// Minimize simplifies the DFA, by removing unnecessary states and merging // states that can be merged func (d *DFA) Minimize() { // find unreachable and reverse-unreachable states q := queue.New(d.NumStates) to_remove := make([]bool, d.NumStates+1) viz := make(map[int]bool) nodes_removed := 0 transitions_removed := 0 q.Push(d.EntryState) viz[d.EntryState] = true for !q.Empty() { node, _ := q.Pop() for _, neighbours := range d.Graph[node] { ok, _ := viz[neighbours[0]] if !ok { viz[neighbours[0]] = true q.Push(neighbours[0]) } } } for i := 1; i <= d.NumStates; i++ { ok, _ := viz[i] if !ok || d.IsReverseUnreachable(i) { to_remove[i] = true nodes_removed++ } } // Moore's Algorithm // See http://en.wikipedia.org/wiki/DFA_minimization#Moore.27s_algorithm is_final := make([]bool, d.NumStates+1) renames := make(map[int]int) for i := 1; i <= d.NumStates; i++ { renames[i] = i if d.IsFinal(i) { is_final[i] = true } } found_match := true for found_match { found_match = false old_graph := d.Graph for i := 1; i <= d.NumStates; i++ { if to_remove[renames[i]] { continue } for j := 1; j <= d.NumStates; j++ { if to_remove[renames[i]] || renames[i] >= renames[j] { continue } if is_final[renames[i]] && !is_final[renames[j]] || is_final[renames[j]] && !is_final[renames[i]] { continue } match := nodes_match(old_graph, is_final, i, j) if match { // join states i and j obsolete := 0 used := 0 if renames[i] > renames[j] { obsolete, used = renames[i], renames[j] renames[i] = renames[j] } else { obsolete, used = renames[j], renames[i] renames[j] = renames[i] } if !to_remove[obsolete] { to_remove[obsolete] = true nodes_removed++ } for character, nodes := range d.Graph[obsolete] { if _, ok := d.Graph[used]; !ok { d.Graph[used] = make(map[rune][]int) } if _, ok := d.Graph[used][character]; !ok { d.Graph[used][character] = make([]int, 1) } else { transitions_removed++ } d.Graph[used][character][0] = renames[nodes[0]] } renames[obsolete] = renames[used] found_match = true } } } } // Apply remaining renames // TODO: improve the main algorithm so that it doesn't need this for i := 1; i <= d.NumStates; i++ { for character, _ := range d.Graph[i] { for d.Graph[i][character][0] != renames[d.Graph[i][character][0]] { d.Graph[i][character][0] = renames[d.Graph[i][character][0]] } } } for d.EntryState != renames[d.EntryState] { d.EntryState = renames[d.EntryState] } // rename states so that there are no gaps between them mapping := make(map[int]int) next_available := 0 for node := 1; node <= d.NumStates; node++ { if !to_remove[node] { next_available++ mapping[node] = next_available } } d.EntryState = mapping[d.EntryState] final_states := make([]int, 0, len(d.FinalStates)) for id, _ := range d.FinalStates { if _, ok := mapping[d.FinalStates[id]]; ok { final_states = append(final_states, mapping[d.FinalStates[id]]) } } d.FinalStates = final_states // rename the states found for node, _ := range d.Graph { for character, _ := range d.Graph[node] { d.Graph[node][character][0] = mapping[d.Graph[node][character][0]] } } for id := 1; id <= d.NumStates; id++ { _, ok := mapping[id] if !ok { delete(d.Graph, id) continue } d.Graph[mapping[id]] = d.Graph[id] } d.NumStates -= nodes_removed d.NumTransitions -= transitions_removed }