Side effects on input! Completes the DFA assigning to
    each state a transition for each letter in the alphabet (if
    not already defined).

    We say that a DFA is complete if its transition function
    :math:`ρ:S×Σ→S` is a total function, that is,
    for all :math:`s ∈ S` and all :math:`a ∈ Σ` we have that
    exists a :math:`ρ(s,a)=s_x` for some :math:`s_x ∈ S`.
    Given an arbitrary DFA A, its completed version :math:`A_T`
    is obtained as follows:
    :math:`A_T = (Σ, S ∪ \{sink\}, s_0 , ρ_T , F )` with
    :math:`ρ_T(s,a)=sink`
    when :math:`ρ(s,a)` is not defined in A and :math:`ρ_T=ρ` in
    the other cases.

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

def dfa_completion(dfa: dict) -> dict:
    """ Side effects on input! Completes the DFA assigning to
    each state a transition for each letter in the alphabet (if
    not already defined).

    We say that a DFA is complete if its transition function
    :math:`ρ:S×Σ→S` is a total function, that is,
    for all :math:`s ∈ S` and all :math:`a ∈ Σ` we have that
    exists a :math:`ρ(s,a)=s_x` for some :math:`s_x ∈ S`.
    Given an arbitrary DFA A, its completed version :math:`A_T`
    is obtained as follows:
    :math:`A_T = (Σ, S ∪ \{sink\}, s_0 , ρ_T , F )` with
    :math:`ρ_T(s,a)=sink`
    when :math:`ρ(s,a)` is not defined in A and :math:`ρ_T=ρ` in
    the other cases.

    :param dict dfa: input DFA.
    :return: *(dict)* representing the completed DFA.
    """
    dfa['states'].add('sink')
    for state in dfa['states']:
        for action in dfa['alphabet']:
            if (state, action) not in dfa['transitions']:
                dfa['transitions'][state, action] = 'sink'
    return dfa