
/*******************************************************************/
/*                                                                 */
/*  FILE     determinise.cpp                                         */
/*  MODULE   determinise                                           */
/*  PROGRAM  SFST                                                  */
/*  AUTHOR   Helmut Schmid, IMS, University of Stuttgart           */
/*                                                                 */
/*******************************************************************/

#include "fst.h"

using std::pair;
using std::set;
using std::vector;

#include <unordered_map>
using std::unordered_map;
namespace SFST {

/*****************  class NodeSet  *********************************/

class NodeSet {
  // This class is used to store a set of nodes.
  // Whenever a new node is added, all nodes accessible
  // through epsilon transitions are added as well.

private:
  set<Node *> ht;

public:
  typedef set<Node *>::iterator iterator;
  NodeSet(){};
  void add(Node *);
  bool insert(Node *node) {
    pair<iterator, bool> result = ht.insert(node);
    return result.second;
  };
  iterator begin() const { return ht.begin(); }
  iterator end() const { return ht.end(); }
  size_t size() const { return ht.size(); }
  void clear() { ht.clear(); }
};

typedef map<const Label, NodeSet> Label2NodeSet;

/*****************  class NodeArray  *******************************/

class NodeArray {

private:
  size_t sizev;
  bool final;
  Node **node;

public:
  NodeArray(NodeSet &);
  ~NodeArray() { delete[] node; };
  size_t size() const { return sizev; }
  bool is_final() const { return final; };
  Node *&operator[](size_t i) const { return node[i]; }
};

/*****************  class DTransition  *****************************/

class DTransition {
public:
  Label label;
  NodeArray *nodes;
  DTransition(Label l, NodeArray *na) {
    label = l;
    nodes = na;
  };
};

/*****************  class NodeMapping  ****************************/

class NodeMapping {
  // This class is used to map a node set from one transducer
  // to a single node in another transducer

private:
  struct hashf {
    size_t operator()(const NodeArray *na) const {
      size_t key = na->size() ^ na->is_final();
      for (size_t i = 0; i < na->size(); i++)
        key = (key << 1) ^ (size_t)(*na)[i];
      return key;
    }
  };
  struct equalf {
    int operator()(const NodeArray *na1, const NodeArray *na2) const {
      if (na1->size() != na2->size() || na1->is_final() != na2->is_final())
        return 0;
      for (size_t i = 0; i < na1->size(); i++)
        if ((*na1)[i] != (*na2)[i])
          return 0;
      return 1;
    }
  };
  typedef unordered_map<NodeArray *, Node *, hashf, equalf> NodeMap;
  NodeMap hm;

public:
  typedef NodeMap::iterator iterator;
  ~NodeMapping();
  iterator begin() { return hm.begin(); };
  iterator end() { return hm.end(); };
  iterator find(NodeArray *na) { return hm.find(na); };
  Node *&operator[](NodeArray *na) { return hm.operator[](na); };
};

static void determinise_node(NodeArray &, Node *, Transducer *, NodeMapping &);

/*******************************************************************/
/*                                                                 */
/*  NodeSet::add                                                   */
/*                                                                 */
/*******************************************************************/

void NodeSet::add(Node *node)

{
  pair<iterator, bool> result = ht.insert(node);
  if (result.second) {
    // new node, add nodes reachable with epsilon transitions
    for (ArcsIter p(node->arcs(), ArcsIter::eps); p; p++) {
      Arc *arc = p;
      if (!arc->label().is_epsilon())
        break;
      add(arc->target_node());
    }
  }
}

/*******************************************************************/
/*                                                                 */
/*  NodeArray::NodeArray                                           */
/*                                                                 */
/*******************************************************************/

NodeArray::NodeArray(NodeSet &ns)

{
  sizev = 0;
  NodeSet::iterator it;

  final = false;
  node = new Node *[ns.size()];
  for (it = ns.begin(); it != ns.end(); it++) {
    Node *nn = *it;
    if (nn->arcs()->non_epsilon_transition_exists())
      node[sizev++] = nn;
    if (nn->is_final())
      final = true;
  }
}

/*******************************************************************/
/*                                                                 */
/*  NodeMapping::~NodeMapping                                      */
/*                                                                 */
/*******************************************************************/

NodeMapping::~NodeMapping()

{
  // if we delete NodeArrays without removing them from NodeMapping,
  // the system will crash when NodeMapping is deleted.
  for (iterator it = hm.begin(); it != hm.end();) {
    NodeArray *na = it->first;
    iterator old = it++;
    hm.erase(old);
    delete na;
  }
}

/*******************************************************************/
/*                                                                 */
/*  compute_transitions                                            */
/*                                                                 */
/*******************************************************************/

static void compute_transitions(NodeArray &na, vector<DTransition> &t)

{
  Label2NodeSet lmap;

  // for all nodes in the current set
  for (size_t i = 0; i < na.size(); i++) {
    Node *n = na[i]; // old node

    // For each non-epsilon transition, add the target node
    // to the respective node set.
    for (ArcsIter p(n->arcs(), ArcsIter::non_eps); p; p++) {
      Arc *arc = p;
      lmap[arc->label()].add(arc->target_node());
    }
  }

  t.reserve(lmap.size());
  for (Label2NodeSet::iterator it = lmap.begin(); it != lmap.end(); it++) {
    t.push_back(DTransition(it->first, new NodeArray(it->second)));
  }
}

/*******************************************************************/
/*                                                                 */
/*  determinise_node                                               */
/*                                                                 */
/*******************************************************************/

static void determinise_node(NodeArray &na, Node *node, Transducer *a,
                             NodeMapping &map) {
  node->set_final(na.is_final());

  vector<DTransition> t;
  compute_transitions(na, t);

  for (size_t i = 0; i < t.size(); i++) {
    NodeMapping::iterator it = map.find(t[i].nodes);
    if (it == map.end()) {
      // new node set
      Node *target_node = a->new_node();
      map[t[i].nodes] = target_node;
      node->add_arc(t[i].label, target_node, a);
      determinise_node(*t[i].nodes, target_node, a, map);
    } else {
      delete t[i].nodes;
      node->add_arc(t[i].label, it->second, a);
    }
  }
}

/*******************************************************************/
/*                                                                 */
/*  Transducer::determinise                                        */
/*                                                                 */
/*******************************************************************/

Transducer &Transducer::determinise(bool copy_alphabet)

{
  if (deterministic)
    return copy();

  Transducer *a = new Transducer();
  if (copy_alphabet)
    a->alphabet.copy(alphabet);

  // creation of the initial node set consisting of all nodes
  // reachable from the start node via epsilon transitions.
  NodeArray *na;
  {
    NodeSet ns;
    ns.add(root_node());
    na = new NodeArray(ns);
  }

  // map the node set to the new root node
  NodeMapping map;
  map[na] = a->root_node();

  // determinise the transducer recursively
  determinise_node(*na, a->root_node(), a, map);
  a->deterministic = 1;
  return *a;
}
} // namespace SFST
