/**
   * Criar duas classes de equivalencia iniciais para serem verificadas a fim de criar um conjunto
   * de classes de equivalencia que atenda a minimizacao do automato
   *
   * @param automato
   */
  private static void criarClassesEquivalencia(Automato automato) {
    ArrayListPersonalizado<ClasseEquivalencia> classesEquivalencia = new ArrayListPersonalizado<>();
    ClasseEquivalencia naoFinais = new ClasseEquivalencia();
    ClasseEquivalencia finais = new ClasseEquivalencia();

    for (Estado estado : automato.estados) {
      if (estado.tipo.equals(TipoEstado.FINAL) || estado.tipo.equals(TipoEstado.INICIALFINAL)) {
        finais.estadosClasse.add(estado);
      } else {
        naoFinais.estadosClasse.add(estado);
      }
    }
    if (!naoFinais.estadosClasse.isEmpty()) {
      naoFinais.label = String.valueOf(classesEquivalencia.size());
      classesEquivalencia.add(naoFinais);
    }
    if (!finais.estadosClasse.isEmpty()) {
      finais.label = String.valueOf(classesEquivalencia.size());
      classesEquivalencia.add(finais);
    }
    Equivalencia equivalencia = new Equivalencia(classesEquivalencia);
    equivalencia.verificaConjuntosModificados(automato.alfabeto, automato.funcaoMapeamento);

    criarNovoAutomato(automato, equivalencia);
  }
  /**
   * Retira os estados inalcançáveis do automato
   *
   * @param automato
   * @param alcancaveis
   */
  private static void retirarEstadosInalcancaveis(
      Automato automato, ArrayListPersonalizado<Estado> alcancaveis) {
    ArrayListPersonalizado<Estado> auxiliar = (ArrayListPersonalizado<Estado>) alcancaveis.clone();

    for (Estado estado : alcancaveis) {
      for (Mapeamento mapeamento : automato.funcaoMapeamento) {
        if (mapeamento.estadoOrigem.equals(estado)) {
          auxiliar.add(mapeamento.estadoDestino);
        }
      }
    }

    if (!alcancaveis.equals(auxiliar)) {
      retirarEstadosInalcancaveis(automato, auxiliar);
      return;
    }
    ArrayList<Estado> inalcancaveis = (ArrayList<Estado>) automato.estados.clone();
    inalcancaveis.removeAll(auxiliar);
    retirarEstadosEProducoes(automato, inalcancaveis);
  }
  /**
   * Minimiza o automato em questao
   *
   * @param automato Automato a ser minimizado
   * @param c
   * @return O automato minimizado
   */
  public static Automato minimizarAutomato(Automato automato, Component c) {
    automato = Transformacoes.determinizarAutomato(automato, c);

    // Retirar estados inalcancáveis
    ArrayListPersonalizado<Estado> alcancaveis = new ArrayListPersonalizado<>();
    alcancaveis.add(automato.inicial);
    retirarEstadosInalcancaveis(automato, alcancaveis);

    // Retirar estados mortos
    ArrayListPersonalizado<Estado> naoMortos =
        (ArrayListPersonalizado<Estado>) automato.estadosFinais.clone();
    // Excecao para nao computar o estado de erro do automato
    for (String letra : automato.alfabeto) {
      automato.funcaoMapeamento.add(new Mapeamento(automato.erro, automato.erro, letra));
    }
    retirarEstadosMortos(automato, naoMortos);
    // Criar classes de equivalencia
    criarClassesEquivalencia(automato);

    return automato;
  }
  /**
   * Retira os estados mortos do autômato
   *
   * @param automato
   * @param naoMortos
   */
  private static void retirarEstadosMortos(
      Automato automato, ArrayListPersonalizado<Estado> naoMortos) {

    ArrayListPersonalizado<Estado> auxiliar = new ArrayListPersonalizado<>();
    auxiliar.addAll(naoMortos);

    for (Mapeamento mapeamento : automato.funcaoMapeamento) {
      if (naoMortos.contains(mapeamento.estadoDestino)) {
        auxiliar.add(mapeamento.estadoOrigem);
      }
    }
    if (!auxiliar.equals(naoMortos)) {
      retirarEstadosMortos(automato, auxiliar);
      return;
    }
    ArrayListPersonalizado<Estado> mortos =
        (ArrayListPersonalizado<Estado>) automato.estados.clone();
    naoMortos.add(automato.erro);
    mortos.removeAll(naoMortos);

    retirarEstadosEProducoes(automato, mortos);
  }
 /**
  * Cria funcoes de mapeamento de cada classe para as suas respectivas classes de destino
  *
  * @param automato
  * @param classeEstado
  * @param equivalencia
  * @param novosMapeamentos
  */
 private static void criarFuncoesMapeamento(
     Automato automato,
     ClasseEquivalencia classeEstado,
     Equivalencia equivalencia,
     ArrayListPersonalizado<Mapeamento> novosMapeamentos) {
   Estado aux = classeEstado.estadosClasse.get(0);
   ArrayList<Mapeamento> mapsAdicionais = new ArrayList<>();
   for (String letraAlfabeto : automato.alfabeto) {
     for (Mapeamento mapeamento : automato.funcaoMapeamento) {
       if (mapeamento.estadoOrigem.equals(aux)
           && mapeamento.terminalTransicao.equals(letraAlfabeto)) {
         mapsAdicionais.add(
             new Mapeamento(
                 criaOuRetornaEstadoAPartirDeClasse(automato, classeEstado),
                 criaOuRetornaEstadoAPartirDeClasse(
                     automato, equivalencia.retornarClassePeloEstado(mapeamento.estadoDestino)),
                 letraAlfabeto));
       }
     }
   }
   novosMapeamentos.addAll(mapsAdicionais);
 }