Transforms a Non Deterministic DFA into a Deterministic
        Args:
            None
        Returns:
            DFA: The resulting DFA

        Creating an equivalent DFA is done using the standard algorithm.
        A nice description can be found in the book:
        Harry R. Lewis and Christos H. Papadimitriou. 1998.
        E
        print target_dfa_statelements of the Theory of Computation.

def determinize(self):
        """
        Transforms a Non Deterministic DFA into a Deterministic
        Args:
            None
        Returns:
            DFA: The resulting DFA

        Creating an equivalent DFA is done using the standard algorithm.
        A nice description can be found in the book:
        Harry R. Lewis and Christos H. Papadimitriou. 1998.
        E
        print target_dfa_statelements of the Theory of Computation.
        """

        # Compute the \epsilon-closure for all states and save it in a diagram
        epsilon_closure = {}
        for state in self.states:
            sid = state.stateid
            epsilon_closure[sid] = self._epsilon_closure(state)

        # Get a transition diagram to speed up computations
        trans_table = {}
        for state in self.states:
            trans_table[state.stateid] = defaultdict(set)
            for arc in state:
                char = self.isyms.find(arc.ilabel)
                trans_table[state.stateid][char].add(arc.nextstate)

        # is_final function:
        # Given a set of nfa states representing a dfa_state return 1 if the
        # corresponding DFA state is a final state, i.e. if any of the
        # corresponding NFA states are final.
        is_final = lambda nfa_states, dfa_state: True \
            if sum([ int(nfa_states[x].final) for x in dfa_state ]) >= 1 \
            else False

        # Precomputation is over, start executing the conversion algorithm
        state_idx = 1
        nfa_states = copy.deepcopy(self.states)
        self.states = []
        # Initialize the new DFA state list
        self.add_state()
        new_initial = epsilon_closure[nfa_states[0].stateid]
        self.states[0].final = is_final(nfa_states, new_initial)

        dfa_state_idx_map = { frozenset(new_initial) : 0 }
        stack = [new_initial]
        while True:
            # Iterate until all added DFA states are processed.
            if not stack:
                break
            # This is a set of states from the NFA
            src_dfa_state = stack.pop()
            src_dfa_state_idx = dfa_state_idx_map[frozenset(src_dfa_state)]
            for char in self.alphabet:
                # Compute the set of target states
                target_dfa_state = set([])
                for nfa_state in src_dfa_state:
                    next_states = \
                        set([y for x in trans_table[nfa_state][char] \
                             for y in epsilon_closure[x] ])
                    target_dfa_state.update(next_states)
                # If the computed state set is not part of our new DFA add it,
                # along with the transition for the current character.
                if frozenset(target_dfa_state) not in dfa_state_idx_map:
                    self.add_state()
                    dfa_state_idx_map[frozenset(target_dfa_state)] = state_idx
                    self.states[state_idx].final = is_final(nfa_states,
                                                            target_dfa_state)
                    state_idx += 1
                    stack.append(target_dfa_state)

                dst_state_idx = dfa_state_idx_map[frozenset(target_dfa_state)]
                self.add_arc(src_dfa_state_idx, dst_state_idx, char)
        return self