// collection.h

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright 2005-2010 Google, Inc.
// Author: riley@google.com (Michael Riley)
//
// \file
// Class to store a collection of sets with elements of type T.

#ifndef FST_EXTENSIONS_PDT_COLLECTION_H__
#define FST_EXTENSIONS_PDT_COLLECTION_H__

#include <algorithm>
#include <vector>
using std::vector;

#include <fst/bi-table.h>

namespace fst {

// Stores a collection of non-empty sets with elements of type T. A
// default constructor, equality ==, a total order <, and an STL-style
// hash class must be defined on the elements. Provides signed
// integer ID (of type I) of each unique set. The IDs are allocated
// starting from 0 in order.
template <class I, class T>
class Collection {
 public:
  struct Node {  // Trie node
    I node_id;   // Root is kNoNodeId;
    T element;

    Node() : node_id(kNoNodeId), element(T()) {}
    Node(I i, const T &t) : node_id(i), element(t) {}

    bool operator==(const Node& n) const {
      return n.node_id == node_id && n.element == element;
    }
  };

  struct NodeHash {
    size_t operator()(const Node &n) const {
      return n.node_id + hash_(n.element) * kPrime;
    }
  };

  typedef CompactHashBiTable<I, Node, NodeHash> NodeTable;

  class SetIterator {
   public:
    SetIterator(I id, Node node, NodeTable *node_table)
        :id_(id), node_(node), node_table_(node_table) {}

    bool Done() const { return id_ == kNoNodeId; }

    const T &Element() const { return node_.element; }

    void Next() {
      id_ = node_.node_id;
      if (id_ != kNoNodeId)
        node_ = node_table_->FindEntry(id_);
    }

   private:
    I id_;                     // Iterator set node id
    Node node_;                // Iterator set node
    NodeTable *node_table_;
  };

  Collection() {}

  // Lookups integer ID from set. If it doesn't exist, then adds it.
  // Set elements should be in strict order (and therefore unique).
  I FindId(const vector<T> &set) {
    I node_id = kNoNodeId;
    for (ssize_t i = set.size() - 1; i >= 0; --i) {
      Node node(node_id, set[i]);
      node_id = node_table_.FindId(node);
    }
    return node_id;
  }

  // Finds set given integer ID. Returns true if ID corresponds
  // to set. Use iterators below to traverse result.
  SetIterator FindSet(I id) {
    if (id < 0 && id >= node_table_.Size()) {
      return SetIterator(kNoNodeId, Node(kNoNodeId, T()), &node_table_);
    } else {
      return SetIterator(id, node_table_.FindEntry(id), &node_table_);
    }
  }

 private:
  static const I kNoNodeId;
  static const size_t kPrime;
  static std::tr1::hash<T> hash_;

  NodeTable node_table_;

  DISALLOW_COPY_AND_ASSIGN(Collection);
};

template<class I, class T> const I Collection<I, T>::kNoNodeId = -1;

template <class I, class T> const size_t Collection<I, T>::kPrime = 7853;

template <class I, class T> std::tr1::hash<T> Collection<I, T>::hash_;

}  // namespace fst

#endif  // FST_EXTENSIONS_PDT_COLLECTION_H__