Side effects on input! Removes from the DFA all states that
    do not reach a final state and returns the pruned DFA.

    It is possible to remove from a DFA A all states that do not
    reach a final state without altering the language.
    The co-reachable dfa :math:`A_F` corresponding to A is
    defined as:

    :math:`A_F = (Σ, S_F , s_0 , ρ|S_F , F )`

    where

    • :math:`S_F` is the set of states that reach a final state
    • :math:`ρ|S_F` is the restriction on :math:`S_F × Σ` of ρ.

    :param dict dfa: input DFA.
    :return: *(dict)* representing the pruned DFA.

def dfa_co_reachable(dfa: dict) -> dict:
    """ Side effects on input! Removes from the DFA all states that
    do not reach a final state and returns the pruned DFA.

    It is possible to remove from a DFA A all states that do not
    reach a final state without altering the language.
    The co-reachable dfa :math:`A_F` corresponding to A is
    defined as:

    :math:`A_F = (Σ, S_F , s_0 , ρ|S_F , F )`

    where

    • :math:`S_F` is the set of states that reach a final state
    • :math:`ρ|S_F` is the restriction on :math:`S_F × Σ` of ρ.

    :param dict dfa: input DFA.
    :return: *(dict)* representing the pruned DFA.
    """

    co_reachable_states = dfa['accepting_states'].copy()
    boundary = co_reachable_states.copy()

    # inverse transition function
    inverse_transitions = dict()
    for key, value in dfa['transitions'].items():
        inverse_transitions.setdefault(value, set()).add(key)

    while boundary:
        s = boundary.pop()
        if s in inverse_transitions:
            for (state, action) in inverse_transitions[s]:
                if state not in co_reachable_states:
                    boundary.add(state)
                    co_reachable_states.add(state)

    dfa['states'] = co_reachable_states

    # If not s_0 ∈ S_F the resulting dfa is empty
    if dfa['initial_state'] not in dfa['states']:
        dfa = {
            'alphabet': set(),
            'states': set(),
            'initial_state': None,
            'accepting_states': set(),
            'transitions': dict()
        }
        return dfa

    transitions = dfa['transitions'].copy()
    for t in transitions:
        if t[0] not in dfa['states']:
            dfa['transitions'].pop(t)
        elif dfa['transitions'][t] not in dfa['states']:
            dfa['transitions'].pop(t)

    return dfa