
/*******************************************************************/
/*                                                                 */
/*  FILE     hopcroft.C                                            */
/*  MODULE   hopcroft                                              */
/*  PROGRAM  SFST                                                  */
/*  AUTHOR   Helmut Schmid, IMS, University of Stuttgart           */
/*                                                                 */
/*******************************************************************/

#include "fst.h"

// HFST
namespace SFST {

/*****************  class Minimiser  *****************************/

class Minimiser {

  /*****************  class Transition  **************************/

  class Transition {

  public:
    Index source;
    Index next_for_target;
    Index next_for_label;
    Label label;

    Transition(Index s, Label l, Index n) {
      source = s;
      label = l;
      next_for_target = n;
      next_for_label = undef;
    }
  };

  /*****************  class State  *******************************/

  class State {

  public:
    Index group;             // index of group to which this state belongs
    Index next_in_group;     // index of next state in group
    Index previous_in_group; // index of previous state in group
    Index first_transition;  // index of first transition with this
                             // state as target

    State() {
      group = next_in_group = previous_in_group = undef;
      first_transition = undef;
    }
  };

  /*****************  class StateGroup  **************************/

  class StateGroup {

  public:
    Index next; // index of next source group
    Index next_in_agenda;
    Index previous_in_agenda;

    Index size;        // number of states in this group
    Index first_state; // pointer to first state

    Index new_size;
    Index first_new_state; // pointer to the set of intersection states

    void init(Index i) {
      next_in_agenda = i;
      size = new_size = 0;
      next = first_state = first_new_state = undef;
    }
    bool is_empty() { return first_state == undef; }
  };

  /*****************  class Agenda  *****************************/

  class Agenda {

    static const Index bucket_count = (Index)(sizeof(Index) * 8);
    // the first "bucket_count" many groups are dummy groups
    // used as the agenda buckets

    vector<StateGroup> &group;

  public:
    Agenda(vector<StateGroup> &g) : group(g) {
      // allocate some dummy groups for the agenda
      g.resize(bucket_count);
      for (Index i = 0; i < bucket_count; i++)
        group[i].next_in_agenda = group[i].previous_in_agenda = i;
    }

    Index pop() {
      for (Index i = 0; i < bucket_count; i++) {
        if (group[i].next_in_agenda != i) {
          Index result = group[i].next_in_agenda;
          erase(result);
          return result;
        }
      }
      return undef;
    }

    void add(Index g, Index size) {

      // find the bucket
      Index i;
      for (i = 0; (size >>= 1); i++)
        ;

      // insert the new group
      Index next = group[i].next_in_agenda;
      group[i].next_in_agenda = g;
      group[g].next_in_agenda = next;
      group[g].previous_in_agenda = i;
      group[next].previous_in_agenda = g;
    }

    void erase(Index g) {
      // update the pointers
      Index next = group[g].next_in_agenda;
      Index previous = group[g].previous_in_agenda;
      group[previous].next_in_agenda = next;
      group[next].previous_in_agenda = previous;

      // unlink the result element
      group[g].previous_in_agenda = group[g].next_in_agenda = g;
    }

    bool contains(Index g) { return (group[g].next_in_agenda != g); }

    Index number_of_buckets() { return bucket_count; }
  };

  /***************************************************************/

  Transducer &transducer;       // pointer to original transducer
  size_t number_of_nodes;       // node count in original t.
  size_t number_of_transitions; // transition count in original t.
  vector<Node *> nodearray;     // maps indices to original transducer nodes

  // CAVEAT: Do not use references to elements of the group vector
  // because they become invalid when the group vector is resized.
  vector<StateGroup> group;
  vector<State> state;
  vector<Transition> transition;
  Agenda agenda;

  // data structure for the sets of incoming transitions
  typedef map<Label, Index> Label2TransSet;

  // "first_transition_for_label" maps a label to a list of transitions
  // to (states in) C that are labelled with the respective label
  Label2TransSet first_transition_for_label;

  Index first_source_group; // linked list of source groups

public:
  Minimiser(Transducer &t);
  Transducer &result();

private:
  // transform the transducer to the representation needed for minimisation
  void add_transition(Index s, Label l, Index t);
  void link_state_in(Index &first_state, Index s);
  void add_state(Index g, Index s);
  void link_state_out(Index &first_state, Index s);
  void remove_state(Index g, Index s);
  void move_state_to_new(Index g, Index s);
  void merge_state_lists(Index g);

  void compute_source_states(Index g);
  void process_source_groups(Label l);
  void split(Index g, Label l);

  Index first_group() { return agenda.number_of_buckets(); }

  Transducer &build_transducer();

#ifndef NDEBUG
  void print_groups() {
    fputs("--------------\n", stderr);
    for (size_t g = first_group(); g < group.size(); g++) {
      fprintf(stderr, "group %lu: ", (unsigned long)g - first_group());
      if (group[g].first_state != undef) {
        Index s = group[g].first_state;
        do {
          fprintf(stderr, "%lu ", (unsigned long)s);
          s = state[s].next_in_group;
        } while (s != group[g].first_state);
      }
      if (group[g].first_new_state != undef) {
        fputs("| ", stderr);
        Index s = group[g].first_new_state;
        do {
          fprintf(stderr, "%lu ", (unsigned long)s);
          s = state[s].next_in_group;
        } while (s != group[g].first_new_state);
      }
      fputc('\n', stderr);
    }
  }
#endif
};

/*******************************************************************/
/*                                                                 */
/*  Transducer::hopcroft_minimise                                  */
/*                                                                 */
/*******************************************************************/

Transducer &Transducer::hopcroft_minimise(bool verbose)

{
  if (minimised)
    return copy();

  Transducer *a1 = &reverse(false);
  Transducer *a2 = &a1->reverse(false);
  delete a1;
  a1 = &a2->determinise(false);
  delete a2;

  Transducer *result = &Minimiser(*a1).result();
  delete a1;

  result->minimised = true;
  result->alphabet.copy(alphabet);
  result->minimise_alphabet();

  return *result;
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::Minimiser                                           */
/*                                                                 */
/*******************************************************************/

Minimiser::Minimiser(Transducer &t)
    : transducer(t), agenda(group)

{
  std::pair<size_t, size_t> NC_TC = t.nodeindexing(&nodearray);
  number_of_nodes = NC_TC.first;
  number_of_transitions = NC_TC.second;

  state.resize(number_of_nodes);
  transition.reserve(number_of_transitions);

  group.reserve(number_of_nodes + first_group());

  // one group for final and non-final transducers resp.
  Index final = (Index)group.size();
  group.push_back(StateGroup());
  group.back().init(final);

  Index nonfinal = (Index)group.size();
  group.push_back(StateGroup());
  group.back().init(nonfinal);

  // build the transition table
  for (Index sourceID = 0; sourceID < (Index)nodearray.size(); sourceID++) {
    Node *node = nodearray[sourceID];

    if (node->is_final())
      add_state(final, sourceID);
    else
      add_state(nonfinal, sourceID);

    for (ArcsIter p(node->arcs()); p; p++) {
      Arc *arc = p;
      add_transition(sourceID, arc->label(), arc->target_node()->index);
    }
  }
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::link_state_in                                       */
/*                                                                 */
/*******************************************************************/

void Minimiser::link_state_in(Index &first_state, Index s)

{
  if (first_state == undef) {
    first_state = s;
    state[s].next_in_group = state[s].previous_in_group = s;
  } else {
    Index n = state[first_state].next_in_group;
    state[first_state].next_in_group = s;
    state[s].next_in_group = n;
    state[n].previous_in_group = s;
    state[s].previous_in_group = first_state;
  }
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::add_state                                           */
/*                                                                 */
/*******************************************************************/

void Minimiser::add_state(Index g, Index s)

{
  group[g].size++;
  state[s].group = g;
  link_state_in(group[g].first_state, s);
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::link_state_out                                      */
/*                                                                 */
/*******************************************************************/

void Minimiser::link_state_out(Index &first_state, Index s)

{
  State &S = state[s];
  // only state in group ?
  if (S.next_in_group == s)
    first_state = undef;
  else {
    Index p = S.previous_in_group;
    Index n = S.next_in_group;
    state[p].next_in_group = n;
    state[n].previous_in_group = p;
    if (first_state == s)
      first_state = n;
  }
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::remove_state                                        */
/*                                                                 */
/*******************************************************************/

void Minimiser::remove_state(Index g, Index s)

{
  group[g].size--;
  link_state_out(group[g].first_state, s);
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::move_state_to_new                                   */
/*                                                                 */
/*******************************************************************/

void Minimiser::move_state_to_new(Index g, Index s)

{
  group[g].size--;
  group[g].new_size++;

  link_state_out(group[g].first_state, s);
  link_state_in(group[g].first_new_state, s);
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::merge_state_lists                                   */
/*                                                                 */
/*******************************************************************/

void Minimiser::merge_state_lists(Index g)

{
  Index first1 = group[g].first_state;
  if (first1 == undef)
    group[g].first_state = group[g].first_new_state;
  else {
    Index first2 = group[g].first_new_state;
    Index next1 = state[first1].next_in_group;
    Index next2 = state[first2].next_in_group;
    state[first1].next_in_group = next2;
    state[first2].next_in_group = next1;
    state[next1].previous_in_group = first2;
    state[next2].previous_in_group = first1;
  }
  group[g].first_new_state = undef;
  group[g].size += group[g].new_size;
  group[g].new_size = 0;
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::add_transition                                      */
/*                                                                 */
/*******************************************************************/

void Minimiser::add_transition(Index s, Label l, Index t)

{
  Transition T(s, l, state[t].first_transition);
  state[t].first_transition = (Index)transition.size();
  transition.push_back(T);
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::result                                              */
/*                                                                 */
/*******************************************************************/

Transducer &Minimiser::result()

{
  if (number_of_nodes == 1)
    return transducer.copy(); // no need for a minimisation

  Index final = first_group();
  Index nonfinal = final + 1;
  if (group[final].is_empty())
    // no final transducers
    return *new Transducer(true); // return an empty transducer

  if (group[nonfinal].is_empty()) {
    // no non-final transducers
    group.pop_back();
    agenda.add(final, group[final].size);
  } else {
    agenda.add(final, group[final].size);
    agenda.add(nonfinal, group[nonfinal].size);
  }

  Index g;
  while ((g = agenda.pop()) != undef) {

    compute_source_states(g);

    // for all labels appearing on incoming transitions
    for (Label2TransSet::iterator it = first_transition_for_label.begin();
         it != first_transition_for_label.end(); it++) {
      process_source_groups(it->first);
    }
    if (group.size() - first_group() == number_of_nodes)
      break;
  }
  Transducer &t = build_transducer();

  return t;
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::compute_source_states                               */
/*                                                                 */
/*******************************************************************/

void Minimiser::compute_source_states(Index g)

{
  first_transition_for_label.clear();

  // for all states S in C
  Index first = group[g].first_state;
  Index s = first;
  do {
    State &S = state[s];
    // for all transitions T into S
    for (Index t = S.first_transition; t != undef;
         t = transition[t].next_for_target) {
      Transition &T = transition[t];
      T.next_for_label = undef;
      // add the transition to the list of
      // incoming transitions with the same label
      Label2TransSet::iterator it = first_transition_for_label.find(T.label);
      if (it == first_transition_for_label.end())
        // add a new mapping
        first_transition_for_label[T.label] = t;
      else {
        // prepend the new element to the list
        T.next_for_label = it->second;
        it->second = t;
      }
    }
    s = S.next_in_group;
  } while (s != first);
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::process_source_groups                               */
/*                                                                 */
/*******************************************************************/

void Minimiser::process_source_groups(Label l)

{
  first_source_group = undef;

  // for all incoming transitions with label l
  for (Index t = first_transition_for_label[l]; t != undef;
       t = transition[t].next_for_label) {
    // get the transition, source state, and source state group
    Transition &T = transition[t];
    State &S = state[T.source];
    Index g = S.group;

    // If new, add this group to the list of source groups
    if (group[g].first_new_state == undef) {
      group[g].next = first_source_group;
      first_source_group = S.group;
    }

    move_state_to_new(g, T.source);
  }

  // for all source groups
  for (Index g = first_source_group; g != undef; g = group[g].next) {
    if (group[g].size > 0)
      split(g, l);
    else
      merge_state_lists(g);
  }
  return;
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::split                                               */
/*                                                                 */
/*******************************************************************/

void Minimiser::split(Index g, Label l)

{
  // create a new group
  Index newg = (Index)group.size();
  group.push_back(StateGroup());
  StateGroup &NewG = group.back();
  NewG.init(newg);
  NewG.first_state = group[g].first_new_state;
  NewG.size = group[g].new_size;
  group[g].first_new_state = undef;
  group[g].new_size = 0;
  Index s = NewG.first_state;

  do {
    state[s].group = newg;
    s = state[s].next_in_group;
  } while (s != NewG.first_state);

  // update the agenda

  if (agenda.contains(g)) {
    // G was on the agenda
    agenda.erase(g);
    agenda.add(g, group[g].size);
    agenda.add(newg, group[newg].size);
  }
  // Otherwise, put the smaller subgroup on the agenda
  else if (group[g].size < group[newg].size)
    agenda.add(g, group[g].size);
  else
    agenda.add(newg, group[newg].size);

  return;
}

/*******************************************************************/
/*                                                                 */
/*  Minimiser::build_transducer                                    */
/*                                                                 */
/*******************************************************************/

Transducer &Minimiser::build_transducer()

{
  Transducer *t = new Transducer(true);
  t->alphabet.copy(transducer.alphabet);

  // create the nodes of the new transducer
  vector<Node *> node(group.size(), NULL);

  // define the root node
  node[state[0].group] = t->root_node();

  for (size_t i = first_group(); i < node.size(); i++)
    if (node[i] == NULL)
      node[i] = t->new_node();

  // Add the transitions
  for (size_t g = first_group(); g < group.size(); g++) {
    Node *old_node = nodearray[group[g].first_state];
    Node *new_node = node[g];
    new_node->set_final(old_node->is_final());

    for (ArcsIter p(old_node->arcs()); p; p++) {
      Arc *arc = p;
      // Compute the ID of the target state
      Index ts = (Index)arc->target_node()->index;
      // Get the node for the corresponding state group
      Node *target = node[state[ts].group];
      // Insert the transition
      new_node->add_arc(arc->label(), target, t);
    }
  }

  return *t;
}

} // namespace SFST
