// lat/lattice-functions.cc

// Copyright 2009-2011  Saarland University (Author: Arnab Ghoshal)
//           2012-2013  Johns Hopkins University (Author: Daniel Povey);  Chao Weng;
//                      Bagher BabaAli
//                2013  Cisco Systems (author: Neha Agrawal) [code modified
//                      from original code in ../gmmbin/gmm-rescore-lattice.cc]
//                2014  Guoguo Chen

// See ../../COPYING for clarification regarding multiple authors
//
// 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
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.


#include "lat/lattice-functions.h"
// #include "hmm/transition-model.h"
// #include "util/stl-utils.h"
#include "base/kaldi-math.h"
// #include "hmm/hmm-utils.h"

namespace kaldi {
using std::map;
using std::vector;

// void GetPerFrameAcousticCosts(const Lattice &nbest,
//                               Vector<BaseFloat> *per_frame_loglikes) {
//   using namespace fst;
//   typedef Lattice::Arc::Weight Weight;
//   vector<BaseFloat> loglikes;
//
//   int32 cur_state = nbest.Start();
//   int32 prev_frame = -1;
//   BaseFloat eps_acwt = 0.0;
//   while(1) {
//     Weight w = nbest.Final(cur_state);
//     if (w != Weight::Zero()) {
//       KALDI_ASSERT(nbest.NumArcs(cur_state) == 0);
//       if (per_frame_loglikes != NULL)  {
//         SubVector<BaseFloat> subvec(&(loglikes[0]), loglikes.size());
//         Vector<BaseFloat> vec(subvec);
//         *per_frame_loglikes = vec;
//       }
//       break;
//     } else {
//       KALDI_ASSERT(nbest.NumArcs(cur_state) == 1);
//       fst::ArcIterator<Lattice> iter(nbest, cur_state);
//       const Lattice::Arc &arc = iter.Value();
//       BaseFloat acwt = arc.weight.Value2();
//       if (arc.ilabel != 0) {
//         if (eps_acwt > 0) {
//           acwt += eps_acwt;
//           eps_acwt = 0.0;
//         }
//         loglikes.push_back(acwt);
//         prev_frame++;
//       } else if (acwt == acwt){
//         if (prev_frame > -1) {
//           loglikes[prev_frame] += acwt;
//         } else {
//           eps_acwt += acwt;
//         }
//       }
//       cur_state = arc.nextstate;
//     }
//   }
// }
//
// int32 LatticeStateTimes(const Lattice &lat, vector<int32> *times) {
//   if (!lat.Properties(fst::kTopSorted, true))
//     KALDI_ERR << "Input lattice must be topologically sorted.";
//   KALDI_ASSERT(lat.Start() == 0);
//   int32 num_states = lat.NumStates();
//   times->clear();
//   times->resize(num_states, -1);
//   (*times)[0] = 0;
//   for (int32 state = 0; state < num_states; state++) {
//     int32 cur_time = (*times)[state];
//     for (fst::ArcIterator<Lattice> aiter(lat, state); !aiter.Done();
//         aiter.Next()) {
//       const LatticeArc &arc = aiter.Value();
//
//       if (arc.ilabel != 0) {  // Non-epsilon input label on arc
//         // next time instance
//         if ((*times)[arc.nextstate] == -1) {
//           (*times)[arc.nextstate] = cur_time + 1;
//         } else {
//           KALDI_ASSERT((*times)[arc.nextstate] == cur_time + 1);
//         }
//       } else {  // epsilon input label on arc
//         // Same time instance
//         if ((*times)[arc.nextstate] == -1)
//           (*times)[arc.nextstate] = cur_time;
//         else
//           KALDI_ASSERT((*times)[arc.nextstate] == cur_time);
//       }
//     }
//   }
//   return (*std::max_element(times->begin(), times->end()));
// }
//
// int32 CompactLatticeStateTimes(const CompactLattice &lat,
//                                vector<int32> *times) {
//   if (!lat.Properties(fst::kTopSorted, true))
//     KALDI_ERR << "Input lattice must be topologically sorted.";
//   KALDI_ASSERT(lat.Start() == 0);
//   int32 num_states = lat.NumStates();
//   times->clear();
//   times->resize(num_states, -1);
//   (*times)[0] = 0;
//   int32 utt_len = -1;
//   for (int32 state = 0; state < num_states; state++) {
//     int32 cur_time = (*times)[state];
//     for (fst::ArcIterator<CompactLattice> aiter(lat, state); !aiter.Done();
//         aiter.Next()) {
//       const CompactLatticeArc &arc = aiter.Value();
//       int32 arc_len = static_cast<int32>(arc.weight.String().size());
//       if ((*times)[arc.nextstate] == -1)
//         (*times)[arc.nextstate] = cur_time + arc_len;
//       else
//         KALDI_ASSERT((*times)[arc.nextstate] == cur_time + arc_len);
//     }
//     if (lat.Final(state) != CompactLatticeWeight::Zero()) {
//       int32 this_utt_len = (*times)[state] + lat.Final(state).String().size();
//       if (utt_len == -1) utt_len = this_utt_len;
//       else {
//         if (this_utt_len != utt_len) {
//           KALDI_WARN << "Utterance does not "
//               "seem to have a consistent length.";
//           utt_len = std::max(utt_len, this_utt_len);
//         }
//       }
//     }
//   }
//   if (utt_len == -1) {
//     KALDI_WARN << "Utterance does not have a final-state.";
//     return 0;
//   }
//   return utt_len;
// }
//
// bool ComputeCompactLatticeAlphas(const CompactLattice &clat,
//                                  vector<double> *alpha) {
//   using namespace fst;
//
//   // typedef the arc, weight types
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   //Make sure the lattice is topologically sorted.
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     KALDI_WARN << "Input lattice must be topologically sorted.";
//     return false;
//   }
//   if (clat.Start() != 0) {
//     KALDI_WARN << "Input lattice must start from state 0.";
//     return false;
//   }
//
//   int32 num_states = clat.NumStates();
//   (*alpha).resize(0);
//   (*alpha).resize(num_states, kLogZeroDouble);
//
//   // Now propagate alphas forward. Note that we don't acount the weight of the
//   // final state to alpha[final_state] -- we acount it to beta[final_state];
//   (*alpha)[0] = 0.0;
//   for (StateId s = 0; s < num_states; s++) {
//     double this_alpha = (*alpha)[s];
//     for (ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -(arc.weight.Weight().Value1() +
//                           arc.weight.Weight().Value2());
//       (*alpha)[arc.nextstate] = LogAdd((*alpha)[arc.nextstate],
//                                        this_alpha + arc_like);
//     }
//   }
//
//   return true;
// }
//
// bool ComputeCompactLatticeBetas(const CompactLattice &clat,
//                                 vector<double> *beta) {
//   using namespace fst;
//
//   // typedef the arc, weight types
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   // Make sure the lattice is topologically sorted.
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     KALDI_WARN << "Input lattice must be topologically sorted.";
//     return false;
//   }
//   if (clat.Start() != 0) {
//     KALDI_WARN << "Input lattice must start from state 0.";
//     return false;
//   }
//
//   int32 num_states = clat.NumStates();
//   (*beta).resize(0);
//   (*beta).resize(num_states, kLogZeroDouble);
//
//   // Now propagate betas backward. Note that beta[final_state] contains the
//   // weight of the final state in the lattice -- compare that with alpha.
//   for (StateId s = num_states-1; s >= 0; s--) {
//     Weight f = clat.Final(s);
//     double this_beta = -(f.Weight().Value1()+f.Weight().Value2());
//     for (ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -(arc.weight.Weight().Value1() +
//                           arc.weight.Weight().Value2());
//       double arc_beta = (*beta)[arc.nextstate] + arc_like;
//       this_beta = LogAdd(this_beta, arc_beta);
//     }
//     (*beta)[s] = this_beta;
//   }
//
//   return true;
// }

template<class LatType>  // could be Lattice or CompactLattice
bool PruneLattice(BaseFloat beam, LatType *lat) {
  typedef typename LatType::Arc Arc;
  typedef typename Arc::Weight Weight;
  typedef typename Arc::StateId StateId;

  KALDI_ASSERT(beam > 0.0);
  if (!lat->Properties(fst::kTopSorted, true)) {
    if (fst::TopSort(lat) == false) {
      KALDI_WARN << "Cycles detected in lattice";
      return false;
    }
  }
  // We assume states before "start" are not reachable, since
  // the lattice is topologically sorted.
  int32 start = lat->Start();
  int32 num_states = lat->NumStates();
  if (num_states == 0) return false;
  std::vector<double> forward_cost(num_states,
                                   std::numeric_limits<double>::infinity());  // viterbi forward.
  forward_cost[start] = 0.0; // lattice can't have cycles so couldn't be
  // less than this.
  double best_final_cost = std::numeric_limits<double>::infinity();
  // Update the forward probs.
  // Thanks to Jing Zheng for finding a bug here.
  for (int32 state = 0; state < num_states; state++) {
    double this_forward_cost = forward_cost[state];
    for (fst::ArcIterator<LatType> aiter(*lat, state);
         !aiter.Done();
         aiter.Next()) {
      const Arc &arc(aiter.Value());
      StateId nextstate = arc.nextstate;
      KALDI_ASSERT(nextstate > state && nextstate < num_states);
      double next_forward_cost = this_forward_cost +
          ConvertToCost(arc.weight);
      if (forward_cost[nextstate] > next_forward_cost)
        forward_cost[nextstate] = next_forward_cost;
    }
    Weight final_weight = lat->Final(state);
    double this_final_cost = this_forward_cost +
        ConvertToCost(final_weight);
    if (this_final_cost < best_final_cost)
      best_final_cost = this_final_cost;
  }
  int32 bad_state = lat->AddState(); // this state is not final.
  double cutoff = best_final_cost + beam;

  // Go backwards updating the backward probs (which share memory with the
  // forward probs), and pruning arcs and deleting final-probs.  We prune arcs
  // by making them point to the non-final state "bad_state".  We'll then use
  // Trim() to remove unnecessary arcs and states.  [this is just easier than
  // doing it ourselves.]
  std::vector<double> &backward_cost(forward_cost);
  for (int32 state = num_states - 1; state >= 0; state--) {
    double this_forward_cost = forward_cost[state];
    double this_backward_cost = ConvertToCost(lat->Final(state));
    if (this_backward_cost + this_forward_cost > cutoff
        && this_backward_cost != std::numeric_limits<double>::infinity())
      lat->SetFinal(state, Weight::Zero());
    for (fst::MutableArcIterator<LatType> aiter(lat, state);
         !aiter.Done();
         aiter.Next()) {
      Arc arc(aiter.Value());
      StateId nextstate = arc.nextstate;
      KALDI_ASSERT(nextstate > state && nextstate < num_states);
      double arc_cost = ConvertToCost(arc.weight),
          arc_backward_cost = arc_cost + backward_cost[nextstate],
          this_fb_cost = this_forward_cost + arc_backward_cost;
      if (arc_backward_cost < this_backward_cost)
        this_backward_cost = arc_backward_cost;
      if (this_fb_cost > cutoff) { // Prune the arc.
        arc.nextstate = bad_state;
        aiter.SetValue(arc);
      }
    }
    backward_cost[state] = this_backward_cost;
  }
  fst::Connect(lat);
  return (lat->NumStates() > 0);
}

// instantiate the template for lattice and CompactLattice.
template bool PruneLattice(BaseFloat beam, Lattice *lat);
template bool PruneLattice(BaseFloat beam, CompactLattice *lat);


// BaseFloat LatticeForwardBackward(const Lattice &lat, Posterior *post,
//                                  double *acoustic_like_sum) {
//   // Note, Posterior is defined as follows:  Indexed [frame], then a list
//   // of (transition-id, posterior-probability) pairs.
//   // typedef std::vector<std::vector<std::pair<int32, BaseFloat> > > Posterior;
//   using namespace fst;
//   typedef Lattice::Arc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   if (acoustic_like_sum) *acoustic_like_sum = 0.0;
//
//   // Make sure the lattice is topologically sorted.
//   if (lat.Properties(fst::kTopSorted, true) == 0)
//     KALDI_ERR << "Input lattice must be topologically sorted.";
//   KALDI_ASSERT(lat.Start() == 0);
//
//   int32 num_states = lat.NumStates();
//   vector<int32> state_times;
//   int32 max_time = LatticeStateTimes(lat, &state_times);
//   std::vector<double> alpha(num_states, kLogZeroDouble);
//   std::vector<double> &beta(alpha); // we re-use the same memory for
//   // this, but it's semantically distinct so we name it differently.
//   double tot_forward_prob = kLogZeroDouble;
//
//   post->clear();
//   post->resize(max_time);
//
//   alpha[0] = 0.0;
//   // Propagate alphas forward.
//   for (StateId s = 0; s < num_states; s++) {
//     double this_alpha = alpha[s];
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight);
//       alpha[arc.nextstate] = LogAdd(alpha[arc.nextstate], this_alpha + arc_like);
//     }
//     Weight f = lat.Final(s);
//     if (f != Weight::Zero()) {
//       double final_like = this_alpha - (f.Value1() + f.Value2());
//       tot_forward_prob = LogAdd(tot_forward_prob, final_like);
//       KALDI_ASSERT(state_times[s] == max_time &&
//                    "Lattice is inconsistent (final-prob not at max_time)");
//     }
//   }
//   for (StateId s = num_states-1; s >= 0; s--) {
//     Weight f = lat.Final(s);
//     double this_beta = -(f.Value1() + f.Value2());
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight),
//           arc_beta = beta[arc.nextstate] + arc_like;
//       this_beta = LogAdd(this_beta, arc_beta);
//       int32 transition_id = arc.ilabel;
//
//       // The following "if" is an optimization to avoid un-needed exp().
//       if (transition_id != 0 || acoustic_like_sum != NULL) {
//         double posterior = Exp(alpha[s] + arc_beta - tot_forward_prob);
//
//         if (transition_id != 0) // Arc has a transition-id on it [not epsilon]
//           (*post)[state_times[s]].push_back(std::make_pair(transition_id,
//                                                            static_cast<kaldi::BaseFloat>(posterior)));
//         if (acoustic_like_sum != NULL)
//           *acoustic_like_sum -= posterior * arc.weight.Value2();
//       }
//     }
//     if (acoustic_like_sum != NULL && f != Weight::Zero()) {
//       double final_logprob = - ConvertToCost(f),
//           posterior = Exp(alpha[s] + final_logprob - tot_forward_prob);
//       *acoustic_like_sum -= posterior * f.Value2();
//     }
//     beta[s] = this_beta;
//   }
//   double tot_backward_prob = beta[0];
//   if (!ApproxEqual(tot_forward_prob, tot_backward_prob, 1e-8)) {
//     KALDI_WARN << "Total forward probability over lattice = " << tot_forward_prob
//               << ", while total backward probability = " << tot_backward_prob;
//   }
//   // Now combine any posteriors with the same transition-id.
//   for (int32 t = 0; t < max_time; t++)
//     MergePairVectorSumming(&((*post)[t]));
//   return tot_backward_prob;
// }
//
//
// void LatticeActivePhones(const Lattice &lat, const TransitionModel &trans,
//                          const vector<int32> &silence_phones,
//                          vector< std::set<int32> > *active_phones) {
//   KALDI_ASSERT(IsSortedAndUniq(silence_phones));
//   vector<int32> state_times;
//   int32 num_states = lat.NumStates();
//   int32 max_time = LatticeStateTimes(lat, &state_times);
//   active_phones->clear();
//   active_phones->resize(max_time);
//   for (int32 state = 0; state < num_states; state++) {
//     int32 cur_time = state_times[state];
//     for (fst::ArcIterator<Lattice> aiter(lat, state); !aiter.Done();
//         aiter.Next()) {
//       const LatticeArc &arc = aiter.Value();
//       if (arc.ilabel != 0) {  // Non-epsilon arc
//         int32 phone = trans.TransitionIdToPhone(arc.ilabel);
//         if (!std::binary_search(silence_phones.begin(),
//                                 silence_phones.end(), phone))
//           (*active_phones)[cur_time].insert(phone);
//       }
//     }  // end looping over arcs
//   }  // end looping over states
// }
//
// void ConvertLatticeToPhones(const TransitionModel &trans,
//                             Lattice *lat) {
//   typedef LatticeArc Arc;
//   int32 num_states = lat->NumStates();
//   for (int32 state = 0; state < num_states; state++) {
//     for (fst::MutableArcIterator<Lattice> aiter(lat, state); !aiter.Done();
//         aiter.Next()) {
//       Arc arc(aiter.Value());
//       arc.olabel = 0; // remove any word.
//       if ((arc.ilabel != 0) // has a transition-id on input..
//           && (trans.TransitionIdToHmmState(arc.ilabel) == 0)
//           && (!trans.IsSelfLoop(arc.ilabel))) {
//          // && trans.IsFinal(arc.ilabel)) // there is one of these per phone...
//         arc.olabel = trans.TransitionIdToPhone(arc.ilabel);
//       }
//       aiter.SetValue(arc);
//     }  // end looping over arcs
//   }  // end looping over states
// }
//
//
// static inline double LogAddOrMax(bool viterbi, double a, double b) {
//   if (viterbi)
//     return std::max(a, b);
//   else
//     return LogAdd(a, b);
// }
//
// template<typename LatticeType>
// double ComputeLatticeAlphasAndBetas(const LatticeType &lat,
//                                     bool viterbi,
//                                     vector<double> *alpha,
//                                     vector<double> *beta) {
//   typedef typename LatticeType::Arc Arc;
//   typedef typename Arc::Weight Weight;
//   typedef typename Arc::StateId StateId;
//
//   StateId num_states = lat.NumStates();
//   KALDI_ASSERT(lat.Properties(fst::kTopSorted, true) == fst::kTopSorted);
//   KALDI_ASSERT(lat.Start() == 0);
//   alpha->clear();
//   beta->clear();
//   alpha->resize(num_states, kLogZeroDouble);
//   beta->resize(num_states, kLogZeroDouble);
//
//   double tot_forward_prob = kLogZeroDouble;
//   (*alpha)[0] = 0.0;
//   // Propagate alphas forward.
//   for (StateId s = 0; s < num_states; s++) {
//     double this_alpha = (*alpha)[s];
//     for (fst::ArcIterator<LatticeType> aiter(lat, s); !aiter.Done();
//          aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight);
//       (*alpha)[arc.nextstate] = LogAddOrMax(viterbi, (*alpha)[arc.nextstate],
//                                                 this_alpha + arc_like);
//     }
//     Weight f = lat.Final(s);
//     if (f != Weight::Zero()) {
//       double final_like = this_alpha - ConvertToCost(f);
//       tot_forward_prob = LogAddOrMax(viterbi, tot_forward_prob, final_like);
//     }
//   }
//   for (StateId s = num_states-1; s >= 0; s--) { // it's guaranteed signed.
//     double this_beta = -ConvertToCost(lat.Final(s));
//     for (fst::ArcIterator<LatticeType> aiter(lat, s); !aiter.Done();
//          aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight),
//           arc_beta = (*beta)[arc.nextstate] + arc_like;
//       this_beta = LogAddOrMax(viterbi, this_beta, arc_beta);
//     }
//     (*beta)[s] = this_beta;
//   }
//   double tot_backward_prob = (*beta)[lat.Start()];
//   if (!ApproxEqual(tot_forward_prob, tot_backward_prob, 1e-8)) {
//     KALDI_WARN << "Total forward probability over lattice = " << tot_forward_prob
//                << ", while total backward probability = " << tot_backward_prob;
//   }
//   // Split the difference when returning... they should be the same.
//   return 0.5 * (tot_backward_prob + tot_forward_prob);
// }
//
// // instantiate the template for Lattice and CompactLattice
// template
// double ComputeLatticeAlphasAndBetas(const Lattice &lat,
//                                     bool viterbi,
//                                     vector<double> *alpha,
//                                     vector<double> *beta);
//
// template
// double ComputeLatticeAlphasAndBetas(const CompactLattice &lat,
//                                     bool viterbi,
//                                     vector<double> *alpha,
//                                     vector<double> *beta);
//
//
//
// /// This is used in CompactLatticeLimitDepth.
// struct LatticeArcRecord {
//   BaseFloat logprob; // logprob <= 0 is the best Viterbi logprob of this arc,
//                      // minus the overall best-cost of the lattice.
//   CompactLatticeArc::StateId state; // state in the lattice.
//   size_t arc; // arc index within the state.
//   bool operator < (const LatticeArcRecord &other) const {
//     return logprob < other.logprob;
//   }
// };
//
// void CompactLatticeLimitDepth(int32 max_depth_per_frame,
//                               CompactLattice *clat) {
//   typedef CompactLatticeArc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   if (clat->Start() == fst::kNoStateId) {
//     KALDI_WARN << "Limiting depth of empty lattice.";
//     return;
//   }
//   if (clat->Properties(fst::kTopSorted, true) == 0) {
//     if (!TopSort(clat))
//       KALDI_ERR << "Topological sorting of lattice failed.";
//   }
//
//   vector<int32> state_times;
//   int32 T = CompactLatticeStateTimes(*clat, &state_times);
//
//   // The alpha and beta quantities here are "viterbi" alphas and beta.
//   std::vector<double> alpha;
//   std::vector<double> beta;
//   bool viterbi = true;
//   double best_prob = ComputeLatticeAlphasAndBetas(*clat, viterbi,
//                                                   &alpha, &beta);
//
//   std::vector<std::vector<LatticeArcRecord> > arc_records(T);
//
//   StateId num_states = clat->NumStates();
//   for (StateId s = 0; s < num_states; s++) {
//     for (fst::ArcIterator<CompactLattice> aiter(*clat, s); !aiter.Done();
//          aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       LatticeArcRecord arc_record;
//       arc_record.state = s;
//       arc_record.arc = aiter.Position();
//       arc_record.logprob =
//           (alpha[s] + beta[arc.nextstate] - ConvertToCost(arc.weight))
//            - best_prob;
//       KALDI_ASSERT(arc_record.logprob < 0.1); // Should be zero or negative.
//       int32 num_frames = arc.weight.String().size(), start_t = state_times[s];
//       for (int32 t = start_t; t < start_t + num_frames; t++) {
//         KALDI_ASSERT(t < T);
//         arc_records[t].push_back(arc_record);
//       }
//     }
//   }
//   StateId dead_state = clat->AddState(); // A non-coaccesible state which we use
//                                          // to remove arcs (make them end
//                                          // there).
//   size_t max_depth = max_depth_per_frame;
//   for (int32 t = 0; t < T; t++) {
//     size_t size = arc_records[t].size();
//     if (size > max_depth) {
//       // we sort from worst to best, so we keep the later-numbered ones,
//       // and delete the lower-numbered ones.
//       size_t cutoff = size - max_depth;
//       std::nth_element(arc_records[t].begin(),
//                        arc_records[t].begin() + cutoff,
//                        arc_records[t].end());
//       for (size_t index = 0; index < cutoff; index++) {
//         LatticeArcRecord record(arc_records[t][index]);
//         fst::MutableArcIterator<CompactLattice> aiter(clat, record.state);
//         aiter.Seek(record.arc);
//         Arc arc = aiter.Value();
//         if (arc.nextstate != dead_state) { // not already killed.
//           arc.nextstate = dead_state;
//           aiter.SetValue(arc);
//         }
//       }
//     }
//   }
//   Connect(clat);
//   TopSortCompactLatticeIfNeeded(clat);
// }
//
//
// void TopSortCompactLatticeIfNeeded(CompactLattice *clat) {
//   if (clat->Properties(fst::kTopSorted, true) == 0) {
//     if (fst::TopSort(clat) == false) {
//       KALDI_ERR << "Topological sorting failed";
//     }
//   }
// }
//
// void TopSortLatticeIfNeeded(Lattice *lat) {
//   if (lat->Properties(fst::kTopSorted, true) == 0) {
//     if (fst::TopSort(lat) == false) {
//       KALDI_ERR << "Topological sorting failed";
//     }
//   }
// }
//
//
// /// Returns the depth of the lattice, defined as the average number of
// /// arcs crossing any given frame.  Returns 1 for empty lattices.
// /// Requires that input is topologically sorted.
// BaseFloat CompactLatticeDepth(const CompactLattice &clat,
//                               int32 *num_frames) {
//   typedef CompactLattice::Arc::StateId StateId;
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     KALDI_ERR << "Lattice input to CompactLatticeDepth was not topologically "
//               << "sorted.";
//   }
//   if (clat.Start() == fst::kNoStateId) {
//     *num_frames = 0;
//     return 1.0;
//   }
//   size_t num_arc_frames = 0;
//   int32 t;
//   {
//     vector<int32> state_times;
//     t = CompactLatticeStateTimes(clat, &state_times);
//   }
//   if (num_frames != NULL)
//     *num_frames = t;
//   for (StateId s = 0; s < clat.NumStates(); s++) {
//     for (fst::ArcIterator<CompactLattice> aiter(clat, s); !aiter.Done();
//          aiter.Next()) {
//       const CompactLatticeArc &arc = aiter.Value();
//       num_arc_frames += arc.weight.String().size();
//     }
//     num_arc_frames += clat.Final(s).String().size();
//   }
//   return num_arc_frames / static_cast<BaseFloat>(t);
// }
//
//
// void CompactLatticeDepthPerFrame(const CompactLattice &clat,
//                                  std::vector<int32> *depth_per_frame) {
//   typedef CompactLattice::Arc::StateId StateId;
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     KALDI_ERR << "Lattice input to CompactLatticeDepthPerFrame was not "
//               << "topologically sorted.";
//   }
//   if (clat.Start() == fst::kNoStateId) {
//     depth_per_frame->clear();
//     return;
//   }
//   vector<int32> state_times;
//   int32 T = CompactLatticeStateTimes(clat, &state_times);
//
//   depth_per_frame->clear();
//   if (T <= 0) {
//     return;
//   } else {
//     depth_per_frame->resize(T, 0);
//     for (StateId s = 0; s < clat.NumStates(); s++) {
//       int32 start_time = state_times[s];
//       for (fst::ArcIterator<CompactLattice> aiter(clat, s); !aiter.Done();
//            aiter.Next()) {
//         const CompactLatticeArc &arc = aiter.Value();
//         int32 len = arc.weight.String().size();
//         for (int32 t = start_time; t < start_time + len; t++) {
//           KALDI_ASSERT(t < T);
//           (*depth_per_frame)[t]++;
//         }
//       }
//       int32 final_len = clat.Final(s).String().size();
//       for (int32 t = start_time; t < start_time + final_len; t++) {
//         KALDI_ASSERT(t < T);
//         (*depth_per_frame)[t]++;
//       }
//     }
//   }
// }
//
//
//
// void ConvertCompactLatticeToPhones(const TransitionModel &trans,
//                                    CompactLattice *clat) {
//   typedef CompactLatticeArc Arc;
//   typedef Arc::Weight Weight;
//   int32 num_states = clat->NumStates();
//   for (int32 state = 0; state < num_states; state++) {
//     for (fst::MutableArcIterator<CompactLattice> aiter(clat, state);
//          !aiter.Done();
//          aiter.Next()) {
//       Arc arc(aiter.Value());
//       std::vector<int32> phone_seq;
//       const std::vector<int32> &tid_seq = arc.weight.String();
//       for (std::vector<int32>::const_iterator iter = tid_seq.begin();
//            iter != tid_seq.end(); ++iter) {
//         if (trans.IsFinal(*iter))// note: there is one of these per phone...
//           phone_seq.push_back(trans.TransitionIdToPhone(*iter));
//       }
//       arc.weight.SetString(phone_seq);
//       aiter.SetValue(arc);
//     } // end looping over arcs
//     Weight f = clat->Final(state);
//     if (f != Weight::Zero()) {
//       std::vector<int32> phone_seq;
//       const std::vector<int32> &tid_seq = f.String();
//       for (std::vector<int32>::const_iterator iter = tid_seq.begin();
//            iter != tid_seq.end(); ++iter) {
//         if (trans.IsFinal(*iter))// note: there is one of these per phone...
//           phone_seq.push_back(trans.TransitionIdToPhone(*iter));
//       }
//       f.SetString(phone_seq);
//       clat->SetFinal(state, f);
//     }
//   }  // end looping over states
// }
//
// bool LatticeBoost(const TransitionModel &trans,
//                   const std::vector<int32> &alignment,
//                   const std::vector<int32> &silence_phones,
//                   BaseFloat b,
//                   BaseFloat max_silence_error,
//                   Lattice *lat) {
//   TopSortLatticeIfNeeded(lat);
//
//   // get all stored properties (test==false means don't test if not known).
//   uint64 props = lat->Properties(fst::kFstProperties,
//                                  false);
//
//   KALDI_ASSERT(IsSortedAndUniq(silence_phones));
//   KALDI_ASSERT(max_silence_error >= 0.0 && max_silence_error <= 1.0);
//   vector<int32> state_times;
//   int32 num_states = lat->NumStates();
//   int32 num_frames = LatticeStateTimes(*lat, &state_times);
//   KALDI_ASSERT(num_frames == static_cast<int32>(alignment.size()));
//   for (int32 state = 0; state < num_states; state++) {
//     int32 cur_time = state_times[state];
//     for (fst::MutableArcIterator<Lattice> aiter(lat, state); !aiter.Done();
//          aiter.Next()) {
//       LatticeArc arc = aiter.Value();
//       if (arc.ilabel != 0) {  // Non-epsilon arc
//         if (arc.ilabel < 0 || arc.ilabel > trans.NumTransitionIds()) {
//           KALDI_WARN << "Lattice has out-of-range transition-ids: "
//                      << "lattice/model mismatch?";
//           return false;
//         }
//         int32 phone = trans.TransitionIdToPhone(arc.ilabel),
//             ref_phone = trans.TransitionIdToPhone(alignment[cur_time]);
//         BaseFloat frame_error;
//         if (phone == ref_phone) {
//           frame_error = 0.0;
//         } else { // an error...
//           if (std::binary_search(silence_phones.begin(), silence_phones.end(), phone))
//             frame_error = max_silence_error;
//           else
//             frame_error = 1.0;
//         }
//         BaseFloat delta_cost = -b * frame_error; // negative cost if
//         // frame is wrong, to boost likelihood of arcs with errors on them.
//         // Add this cost to the graph part.
//         arc.weight.SetValue1(arc.weight.Value1() + delta_cost);
//         aiter.SetValue(arc);
//       }
//     }
//   }
//   // All we changed is the weights, so any properties that were
//   // known before, are still known, except for whether or not the
//   // lattice was weighted.
//   lat->SetProperties(props,
//                      ~(fst::kWeighted|fst::kUnweighted));
//
//   return true;
// }
//
//
//
// BaseFloat LatticeForwardBackwardMpeVariants(
//     const TransitionModel &trans,
//     const std::vector<int32> &silence_phones,
//     const Lattice &lat,
//     const std::vector<int32> &num_ali,
//     std::string criterion,
//     bool one_silence_class,
//     Posterior *post) {
//   using namespace fst;
//   typedef Lattice::Arc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   KALDI_ASSERT(criterion == "mpfe" || criterion == "smbr");
//   bool is_mpfe = (criterion == "mpfe");
//
//   if (lat.Properties(fst::kTopSorted, true) == 0)
//     KALDI_ERR << "Input lattice must be topologically sorted.";
//   KALDI_ASSERT(lat.Start() == 0);
//
//   int32 num_states = lat.NumStates();
//   vector<int32> state_times;
//   int32 max_time = LatticeStateTimes(lat, &state_times);
//   KALDI_ASSERT(max_time == static_cast<int32>(num_ali.size()));
//   std::vector<double> alpha(num_states, kLogZeroDouble),
//       alpha_smbr(num_states, 0), //forward variable for sMBR
//       beta(num_states, kLogZeroDouble),
//       beta_smbr(num_states, 0); //backward variable for sMBR
//
//   double tot_forward_prob = kLogZeroDouble;
//   double tot_forward_score = 0;
//
//   post->clear();
//   post->resize(max_time);
//
//   alpha[0] = 0.0;
//   // First Pass Forward,
//   for (StateId s = 0; s < num_states; s++) {
//     double this_alpha = alpha[s];
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight);
//       alpha[arc.nextstate] = LogAdd(alpha[arc.nextstate], this_alpha + arc_like);
//     }
//     Weight f = lat.Final(s);
//     if (f != Weight::Zero()) {
//       double final_like = this_alpha - (f.Value1() + f.Value2());
//       tot_forward_prob = LogAdd(tot_forward_prob, final_like);
//       KALDI_ASSERT(state_times[s] == max_time &&
//                    "Lattice is inconsistent (final-prob not at max_time)");
//     }
//   }
//   // First Pass Backward,
//   for (StateId s = num_states-1; s >= 0; s--) {
//     Weight f = lat.Final(s);
//     double this_beta = -(f.Value1() + f.Value2());
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight),
//           arc_beta = beta[arc.nextstate] + arc_like;
//       this_beta = LogAdd(this_beta, arc_beta);
//     }
//     beta[s] = this_beta;
//   }
//   // First Pass Forward-Backward Check
//   double tot_backward_prob = beta[0];
//   // may loose the condition somehow here 1e-6 (was 1e-8)
//   if (!ApproxEqual(tot_forward_prob, tot_backward_prob, 1e-6)) {
//     KALDI_ERR << "Total forward probability over lattice = " << tot_forward_prob
//               << ", while total backward probability = " << tot_backward_prob;
//   }
//
//   alpha_smbr[0] = 0.0;
//   // Second Pass Forward, calculate forward for MPFE/SMBR
//   for (StateId s = 0; s < num_states; s++) {
//     double this_alpha = alpha[s];
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight);
//       double frame_acc = 0.0;
//       if (arc.ilabel != 0) {
//         int32 cur_time = state_times[s];
//         int32 phone = trans.TransitionIdToPhone(arc.ilabel),
//             ref_phone = trans.TransitionIdToPhone(num_ali[cur_time]);
//         bool phone_is_sil = std::binary_search(silence_phones.begin(),
//                                                silence_phones.end(),
//                                                phone),
//             ref_phone_is_sil = std::binary_search(silence_phones.begin(),
//                                                   silence_phones.end(),
//                                                   ref_phone),
//             both_sil = phone_is_sil && ref_phone_is_sil;
//         if (!is_mpfe) { // smbr.
//           int32 pdf = trans.TransitionIdToPdf(arc.ilabel),
//               ref_pdf = trans.TransitionIdToPdf(num_ali[cur_time]);
//           if (!one_silence_class)  // old behavior
//             frame_acc = (pdf == ref_pdf && !phone_is_sil) ? 1.0 : 0.0;
//           else
//             frame_acc = (pdf == ref_pdf || both_sil) ? 1.0 : 0.0;
//         } else {
//           if (!one_silence_class)  // old behavior
//             frame_acc = (phone == ref_phone && !phone_is_sil) ? 1.0 : 0.0;
//           else
//             frame_acc = (phone == ref_phone || both_sil) ? 1.0 : 0.0;
//         }
//       }
//       double arc_scale = Exp(alpha[s] + arc_like - alpha[arc.nextstate]);
//       alpha_smbr[arc.nextstate] += arc_scale * (alpha_smbr[s] + frame_acc);
//     }
//     Weight f = lat.Final(s);
//     if (f != Weight::Zero()) {
//       double final_like = this_alpha - (f.Value1() + f.Value2());
//       double arc_scale = Exp(final_like - tot_forward_prob);
//       tot_forward_score += arc_scale * alpha_smbr[s];
//       KALDI_ASSERT(state_times[s] == max_time &&
//                    "Lattice is inconsistent (final-prob not at max_time)");
//     }
//   }
//   // Second Pass Backward, collect Mpe style posteriors
//   for (StateId s = num_states-1; s >= 0; s--) {
//     for (ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_like = -ConvertToCost(arc.weight),
//           arc_beta = beta[arc.nextstate] + arc_like;
//       double frame_acc = 0.0;
//       int32 transition_id = arc.ilabel;
//       if (arc.ilabel != 0) {
//         int32 cur_time = state_times[s];
//         int32 phone = trans.TransitionIdToPhone(arc.ilabel),
//             ref_phone = trans.TransitionIdToPhone(num_ali[cur_time]);
//         bool phone_is_sil = std::binary_search(silence_phones.begin(),
//                                                silence_phones.end(), phone),
//             ref_phone_is_sil = std::binary_search(silence_phones.begin(),
//                                                   silence_phones.end(),
//                                                   ref_phone),
//             both_sil = phone_is_sil && ref_phone_is_sil;
//         if (!is_mpfe) { // smbr.
//           int32 pdf = trans.TransitionIdToPdf(arc.ilabel),
//               ref_pdf = trans.TransitionIdToPdf(num_ali[cur_time]);
//           if (!one_silence_class)  // old behavior
//             frame_acc = (pdf == ref_pdf && !phone_is_sil) ? 1.0 : 0.0;
//           else
//             frame_acc = (pdf == ref_pdf || both_sil) ? 1.0 : 0.0;
//         } else {
//           if (!one_silence_class)  // old behavior
//             frame_acc = (phone == ref_phone && !phone_is_sil) ? 1.0 : 0.0;
//           else
//             frame_acc = (phone == ref_phone || both_sil) ? 1.0 : 0.0;
//         }
//       }
//       double arc_scale = Exp(beta[arc.nextstate] + arc_like - beta[s]);
//       // check arc_scale NAN,
//       // this is to prevent partial paths in Lattices
//       // i.e., paths don't survive to the final state
//       if (KALDI_ISNAN(arc_scale)) arc_scale = 0;
//       beta_smbr[s] += arc_scale * (beta_smbr[arc.nextstate] + frame_acc);
//
//       if (transition_id != 0) { // Arc has a transition-id on it [not epsilon]
//         double posterior = Exp(alpha[s] + arc_beta - tot_forward_prob);
//         double acc_diff = alpha_smbr[s] + frame_acc + beta_smbr[arc.nextstate]
//                                - tot_forward_score;
//         double posterior_smbr = posterior * acc_diff;
//         (*post)[state_times[s]].push_back(std::make_pair(transition_id,
//                                                          static_cast<BaseFloat>(posterior_smbr)));
//       }
//     }
//   }
//
//   //Second Pass Forward Backward check
//   double tot_backward_score = beta_smbr[0];  // Initial state id == 0
//   // may loose the condition somehow here 1e-5/1e-4
//   if (!ApproxEqual(tot_forward_score, tot_backward_score, 1e-4)) {
//     KALDI_ERR << "Total forward score over lattice = " << tot_forward_score
//               << ", while total backward score = " << tot_backward_score;
//   }
//
//   // Output the computed posteriors
//   for (int32 t = 0; t < max_time; t++)
//     MergePairVectorSumming(&((*post)[t]));
//   return tot_forward_score;
// }
//
// bool CompactLatticeToWordAlignment(const CompactLattice &clat,
//                                    std::vector<int32> *words,
//                                    std::vector<int32> *begin_times,
//                                    std::vector<int32> *lengths) {
//   words->clear();
//   begin_times->clear();
//   lengths->clear();
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Label Label;
//   typedef CompactLattice::StateId StateId;
//   typedef CompactLattice::Weight Weight;
//   using namespace fst;
//   StateId state = clat.Start();
//   int32 cur_time = 0;
//   if (state == kNoStateId) {
//     KALDI_WARN << "Empty lattice.";
//     return false;
//   }
//   while (1) {
//     Weight final = clat.Final(state);
//     size_t num_arcs = clat.NumArcs(state);
//     if (final != Weight::Zero()) {
//       if (num_arcs != 0) {
//         KALDI_WARN << "Lattice is not linear.";
//         return false;
//       }
//       if (! final.String().empty()) {
//         KALDI_WARN << "Lattice has alignments on final-weight: probably "
//             "was not word-aligned (alignments will be approximate)";
//       }
//       return true;
//     } else {
//       if (num_arcs != 1) {
//         KALDI_WARN << "Lattice is not linear: num-arcs = " << num_arcs;
//         return false;
//       }
//       fst::ArcIterator<CompactLattice> aiter(clat, state);
//       const Arc &arc = aiter.Value();
//       Label word_id = arc.ilabel; // Note: ilabel==olabel, since acceptor.
//       // Also note: word_id may be zero; we output it anyway.
//       int32 length = arc.weight.String().size();
//       words->push_back(word_id);
//       begin_times->push_back(cur_time);
//       lengths->push_back(length);
//       cur_time += length;
//       state = arc.nextstate;
//     }
//   }
// }
//
//
// bool CompactLatticeToWordProns(
//     const TransitionModel &tmodel,
//     const CompactLattice &clat,
//     std::vector<int32> *words,
//     std::vector<int32> *begin_times,
//     std::vector<int32> *lengths,
//     std::vector<std::vector<int32> > *prons,
//     std::vector<std::vector<int32> > *phone_lengths) {
//   words->clear();
//   begin_times->clear();
//   lengths->clear();
//   prons->clear();
//   phone_lengths->clear();
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Label Label;
//   typedef CompactLattice::StateId StateId;
//   typedef CompactLattice::Weight Weight;
//   using namespace fst;
//   StateId state = clat.Start();
//   int32 cur_time = 0;
//   if (state == kNoStateId) {
//     KALDI_WARN << "Empty lattice.";
//     return false;
//   }
//   while (1) {
//     Weight final = clat.Final(state);
//     size_t num_arcs = clat.NumArcs(state);
//     if (final != Weight::Zero()) {
//       if (num_arcs != 0) {
//         KALDI_WARN << "Lattice is not linear.";
//         return false;
//       }
//       if (! final.String().empty()) {
//         KALDI_WARN << "Lattice has alignments on final-weight: probably "
//             "was not word-aligned (alignments will be approximate)";
//       }
//       return true;
//     } else {
//       if (num_arcs != 1) {
//         KALDI_WARN << "Lattice is not linear: num-arcs = " << num_arcs;
//         return false;
//       }
//       fst::ArcIterator<CompactLattice> aiter(clat, state);
//       const Arc &arc = aiter.Value();
//       Label word_id = arc.ilabel; // Note: ilabel==olabel, since acceptor.
//       // Also note: word_id may be zero; we output it anyway.
//       int32 length = arc.weight.String().size();
//       words->push_back(word_id);
//       begin_times->push_back(cur_time);
//       lengths->push_back(length);
//       const std::vector<int32> &arc_alignment = arc.weight.String();
//       std::vector<std::vector<int32> > split_alignment;
//       SplitToPhones(tmodel, arc_alignment, &split_alignment);
//       std::vector<int32> phones(split_alignment.size());
//       std::vector<int32> plengths(split_alignment.size());
//       for (size_t i = 0; i < split_alignment.size(); i++) {
//         KALDI_ASSERT(!split_alignment[i].empty());
//         phones[i] = tmodel.TransitionIdToPhone(split_alignment[i][0]);
//         plengths[i] = split_alignment[i].size();
//       }
//       prons->push_back(phones);
//       phone_lengths->push_back(plengths);
//
//       cur_time += length;
//       state = arc.nextstate;
//     }
//   }
// }
//
//
//
// void CompactLatticeShortestPath(const CompactLattice &clat,
//                                 CompactLattice *shortest_path) {
//   using namespace fst;
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     CompactLattice clat_copy(clat);
//     if (!TopSort(&clat_copy))
//       KALDI_ERR << "Was not able to topologically sort lattice (cycles found?)";
//     CompactLatticeShortestPath(clat_copy, shortest_path);
//     return;
//   }
//   // Now we can assume it's topologically sorted.
//   shortest_path->DeleteStates();
//   if (clat.Start() == kNoStateId) return;
//   typedef CompactLatticeArc Arc;
//   typedef Arc::StateId StateId;
//   typedef CompactLatticeWeight Weight;
//   vector<std::pair<double, StateId> > best_cost_and_pred(clat.NumStates() + 1);
//   StateId superfinal = clat.NumStates();
//   for (StateId s = 0; s <= clat.NumStates(); s++) {
//     best_cost_and_pred[s].first = std::numeric_limits<double>::infinity();
//     best_cost_and_pred[s].second = fst::kNoStateId;
//   }
//   best_cost_and_pred[clat.Start()].first = 0;
//   for (StateId s = 0; s < clat.NumStates(); s++) {
//     double my_cost = best_cost_and_pred[s].first;
//     for (ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done();
//          aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double arc_cost = ConvertToCost(arc.weight),
//           next_cost = my_cost + arc_cost;
//       if (next_cost < best_cost_and_pred[arc.nextstate].first) {
//         best_cost_and_pred[arc.nextstate].first = next_cost;
//         best_cost_and_pred[arc.nextstate].second = s;
//       }
//     }
//     double final_cost = ConvertToCost(clat.Final(s)),
//         tot_final = my_cost + final_cost;
//     if (tot_final < best_cost_and_pred[superfinal].first) {
//       best_cost_and_pred[superfinal].first = tot_final;
//       best_cost_and_pred[superfinal].second = s;
//     }
//   }
//   std::vector<StateId> states; // states on best path.
//   StateId cur_state = superfinal, start_state = clat.Start();
//   while (cur_state != start_state) {
//     StateId prev_state = best_cost_and_pred[cur_state].second;
//     if (prev_state == kNoStateId) {
//       KALDI_WARN << "Failure in best-path algorithm for lattice (infinite costs?)";
//       return; // return empty best-path.
//     }
//     states.push_back(prev_state);
//     KALDI_ASSERT(cur_state != prev_state && "Lattice with cycles");
//     cur_state = prev_state;
//   }
//   std::reverse(states.begin(), states.end());
//   for (size_t i = 0; i < states.size(); i++)
//     shortest_path->AddState();
//   for (StateId s = 0; static_cast<size_t>(s) < states.size(); s++) {
//     if (s == 0) shortest_path->SetStart(s);
//     if (static_cast<size_t>(s + 1) < states.size()) { // transition to next state.
//       bool have_arc = false;
//       Arc cur_arc;
//       for (ArcIterator<CompactLattice> aiter(clat, states[s]);
//            !aiter.Done();
//            aiter.Next()) {
//         const Arc &arc = aiter.Value();
//         if (arc.nextstate == states[s+1]) {
//           if (!have_arc ||
//               ConvertToCost(arc.weight) < ConvertToCost(cur_arc.weight)) {
//             cur_arc = arc;
//             have_arc = true;
//           }
//         }
//       }
//       KALDI_ASSERT(have_arc && "Code error.");
//       shortest_path->AddArc(s, Arc(cur_arc.ilabel, cur_arc.olabel,
//                                    cur_arc.weight, s+1));
//     } else { // final-prob.
//       shortest_path->SetFinal(s, clat.Final(states[s]));
//     }
//   }
// }
//
//
// void ExpandCompactLattice(const CompactLattice &clat,
//                           double epsilon,
//                           CompactLattice *expand_clat) {
//   using namespace fst;
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//   typedef std::pair<StateId, StateId> StatePair;
//   typedef unordered_map<StatePair, StateId, PairHasher<StateId> > MapType;
//   typedef MapType::iterator IterType;
//
//   if (clat.Start() == kNoStateId) return;
//   // Make sure the input lattice is topologically sorted.
//   if (clat.Properties(kTopSorted, true) == 0) {
//     CompactLattice clat_copy(clat);
//     KALDI_LOG << "Topsort this lattice.";
//     if (!TopSort(&clat_copy))
//       KALDI_ERR << "Was not able to topologically sort lattice (cycles found?)";
//     ExpandCompactLattice(clat_copy, epsilon, expand_clat);
//     return;
//   }
//
//   // Compute backward logprobs betas for the expanded lattice.
//   // Note: the backward logprobs in the original lattice <clat> and the
//   // expanded lattice <expand_clat> are the same.
//   int32 num_states = clat.NumStates();
//   std::vector<double> beta(num_states, kLogZeroDouble);
//   ComputeCompactLatticeBetas(clat, &beta);
//   double tot_backward_logprob = beta[0];
//   std::vector<double> alpha;
//   alpha.push_back(0.0);
//   expand_clat->DeleteStates();
//   MapType state_map; // Map from state pair (orig_state, copy_state) to
//   // copy_state, where orig_state is a state in the original lattice, and
//   // copy_state is its corresponding one in the expanded lattice.
//   unordered_map<StateId, StateId> states; // Map from orig_state to its
//   // copy_state for states with incoming arcs' posteriors <= epsilon.
//   std::queue<StatePair> state_queue;
//
//   // Set start state in the expanded lattice.
//   StateId start_state = expand_clat->AddState();
//   expand_clat->SetStart(start_state);
//   StatePair start_pair(clat.Start(), start_state);
//   state_queue.push(start_pair);
//   std::pair<IterType, bool> result =
//     state_map.insert(std::make_pair(start_pair, start_state));
//   KALDI_ASSERT(result.second == true);
//
//   // Expand <clat> and update forward logprobs alphas in <expand_clat>.
//   while (!state_queue.empty()) {
//     StatePair s = state_queue.front();
//     StateId s1 = s.first,
//             s2 = s.second;
//     state_queue.pop();
//
//     Weight f = clat.Final(s1);
//     if (f != Weight::Zero()) {
//       KALDI_ASSERT(state_map.find(s) != state_map.end());
//       expand_clat->SetFinal(state_map[s], f);
//     }
//
//     for (ArcIterator<CompactLattice> aiter(clat, s1);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       StateId orig_state = arc.nextstate;
//       double arc_like = -ConvertToCost(arc.weight),
//              this_alpha = alpha[s2] + arc_like,
//              arc_post = Exp(this_alpha + beta[orig_state] -
//                             tot_backward_logprob);
//       // Generate the expanded lattice.
//       StateId copy_state;
//       if (arc_post > epsilon) {
//         copy_state = expand_clat->AddState();
//         StatePair next_pair(orig_state, copy_state);
//         std::pair<IterType, bool> result =
//           state_map.insert(std::make_pair(next_pair, copy_state));
//         KALDI_ASSERT(result.second == true);
//         state_queue.push(next_pair);
//       } else {
//         unordered_map<StateId, StateId>::iterator iter = states.find(orig_state);
//         if (iter == states.end() ) { // The counterpart state of orig_state
//                                    // has not been created in <expand_clat> yet.
//           copy_state = expand_clat->AddState();
//           StatePair next_pair(orig_state, copy_state);
//           std::pair<IterType, bool> result =
//             state_map.insert(std::make_pair(next_pair, copy_state));
//           KALDI_ASSERT(result.second == true);
//           state_queue.push(next_pair);
//           states[orig_state] = copy_state;
//         } else {
//           copy_state = iter->second;
//         }
//       }
//       // Create an arc from state_map[s] to copy_state in the expanded lattice.
//       expand_clat->AddArc(state_map[s], Arc(arc.ilabel, arc.olabel, arc.weight,
//                                             copy_state));
//       // Compute forward logprobs alpha for the expanded lattice.
//       if ((alpha.size() - 1) < copy_state) { // The first time to compute alpha
//                                              // for copy_state in <expand_clat>.
//         alpha.push_back(this_alpha);
//       } else { // Accumulate alpha.
//         alpha[copy_state] = LogAdd(alpha[copy_state], this_alpha);
//       }
//     }
//   } // end while
// }
//
//
// void CompactLatticeBestCostsAndTracebacks(
//     const CompactLattice &clat,
//     CostTraceType *forward_best_cost_and_pred,
//     CostTraceType *backward_best_cost_and_pred) {
//
//   // typedef the arc, weight types
//   typedef CompactLatticeArc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//
//   forward_best_cost_and_pred->clear();
//   backward_best_cost_and_pred->clear();
//   forward_best_cost_and_pred->resize(clat.NumStates());
//   backward_best_cost_and_pred->resize(clat.NumStates());
//   // Initialize the cost and predecessor state for each state.
//   for (StateId s = 0; s < clat.NumStates(); s++) {
//     (*forward_best_cost_and_pred)[s].first =
//                                         std::numeric_limits<double>::infinity();
//     (*backward_best_cost_and_pred)[s].first =
//                                         std::numeric_limits<double>::infinity();
//     (*forward_best_cost_and_pred)[s].second = fst::kNoStateId;
//     (*backward_best_cost_and_pred)[s].second = fst::kNoStateId;
//   }
//
//   StateId start_state = clat.Start();
//   (*forward_best_cost_and_pred)[start_state].first = 0;
//   // Transverse the lattice forwardly to compute the best cost from the start
//   // state to each state and the best predecessor state of each state.
//   for (StateId s = 0; s < clat.NumStates(); s++) {
//     double cur_cost = (*forward_best_cost_and_pred)[s].first;
//     for (fst::ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double next_cost = cur_cost + ConvertToCost(arc.weight);
//       if (next_cost < (*forward_best_cost_and_pred)[arc.nextstate].first) {
//         (*forward_best_cost_and_pred)[arc.nextstate].first = next_cost;
//         (*forward_best_cost_and_pred)[arc.nextstate].second = s;
//       }
//     }
//   }
//   // Transverse the lattice backwardly to compute the best cost from a final
//   // state to each state and the best predecessor state of each state.
//   for (StateId s = clat.NumStates() - 1; s >= 0; s--) {
//     double this_cost = ConvertToCost(clat.Final(s));
//     for (fst::ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       double next_cost = (*backward_best_cost_and_pred)[arc.nextstate].first +
//         ConvertToCost(arc.weight);
//       if (next_cost < this_cost) {
//         this_cost = next_cost;
//         (*backward_best_cost_and_pred)[s].second = arc.nextstate;
//       }
//     }
//     (*backward_best_cost_and_pred)[s].first = this_cost;
//   }
// }
//
//
// void AddNnlmScoreToCompactLattice(const MapT &nnlm_scores,
//                                   CompactLattice *clat) {
//   if (clat->Start() == fst::kNoStateId) return;
//   // Make sure the input lattice is topologically sorted.
//   if (clat->Properties(fst::kTopSorted, true) == 0) {
//     KALDI_LOG << "Topsort this lattice.";
//     if (!TopSort(clat))
//       KALDI_ERR << "Was not able to topologically sort lattice (cycles found?)";
//     AddNnlmScoreToCompactLattice(nnlm_scores, clat);
//     return;
//   }
//
//   // typedef the arc, weight types
//   typedef CompactLatticeArc Arc;
//   typedef Arc::Weight Weight;
//   typedef Arc::StateId StateId;
//   typedef std::pair<int32, int32> StatePair;
//
//   int32 num_states = clat->NumStates();
//   unordered_map<StatePair, bool, PairHasher<int32> > final_state_check;
//   for (StateId s = 0; s < num_states; s++) {
//     for (fst::MutableArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       Arc arc(aiter.Value());
//       StatePair arc_index = std::make_pair(static_cast<int32>(s),
//                                            static_cast<int32>(arc.nextstate));
//       MapT::const_iterator it = nnlm_scores.find(arc_index);
//       double nnlm_score;
//       if (it != nnlm_scores.end())
//         nnlm_score = it->second;
//       else
//         KALDI_ERR << "Some arc does not have neural language model score.";
//       if (arc.ilabel != 0) { // if there is a word on this arc
//         LatticeWeight weight = arc.weight.Weight();
//         // Add associated neural LM score to each arc.
//         weight.SetValue1(weight.Value1() + nnlm_score);
//         arc.weight.SetWeight(weight);
//         aiter.SetValue(arc);
//       }
//       Weight clat_final = clat->Final(arc.nextstate);
//       StatePair final_pair = std::make_pair(arc.nextstate, arc.nextstate);
//       // Add neural LM scores to each final state only once.
//       if (clat_final != CompactLatticeWeight::Zero() &&
//           final_state_check.find(final_pair) == final_state_check.end()) {
//         MapT::const_iterator final_it = nnlm_scores.find(final_pair);
//         double final_nnlm_score = 0.0;
//         if (final_it != nnlm_scores.end())
//           final_nnlm_score = final_it->second;
//         // Add neural LM scores to the final weight.
//         Weight final_weight(LatticeWeight(clat_final.Weight().Value1() +
//                                           final_nnlm_score,
//                                           clat_final.Weight().Value2()),
//                                           clat_final.String());
//         clat->SetFinal(arc.nextstate, final_weight);
//         final_state_check[final_pair] = true;
//       }
//     } // end looping over arcs
//   } // end looping over states
// }
//
// void AddWordInsPenToCompactLattice(BaseFloat word_ins_penalty,
//                                    CompactLattice *clat) {
//   typedef CompactLatticeArc Arc;
//   int32 num_states = clat->NumStates();
//
//   //scan the lattice
//   for (int32 state = 0; state < num_states; state++) {
//     for (fst::MutableArcIterator<CompactLattice> aiter(clat, state);
//          !aiter.Done(); aiter.Next()) {
//
//       Arc arc(aiter.Value());
//
//       if (arc.ilabel != 0) { // if there is a word on this arc
//         LatticeWeight weight = arc.weight.Weight();
//         // add word insertion penalty to lattice
//         weight.SetValue1( weight.Value1() + word_ins_penalty);
//         arc.weight.SetWeight(weight);
//         aiter.SetValue(arc);
//       }
//     } // end looping over arcs
//   }  // end looping over states
// }
//
// struct ClatRescoreTuple {
//   ClatRescoreTuple(int32 state, int32 arc, int32 tid):
//       state_id(state), arc_id(arc), tid(tid) { }
//   int32 state_id;
//   int32 arc_id;
//   int32 tid;
// };
//
// /** RescoreCompactLatticeInternal is the internal code for both
//     RescoreCompactLattice and RescoreCompatLatticeSpeedup.  For
//     RescoreCompactLattice, "tmodel" will be NULL and speedup_factor will be 1.0.
//  */
// bool RescoreCompactLatticeInternal(
//     const TransitionModel *tmodel,
//     BaseFloat speedup_factor,
//     DecodableInterface *decodable,
//     CompactLattice *clat) {
//   KALDI_ASSERT(speedup_factor >= 1.0);
//   if (clat->NumStates() == 0) {
//     KALDI_WARN << "Rescoring empty lattice";
//     return false;
//   }
//   if (!clat->Properties(fst::kTopSorted, true)) {
//     if (fst::TopSort(clat) == false) {
//       KALDI_WARN << "Cycles detected in lattice.";
//       return false;
//     }
//   }
//   std::vector<int32> state_times;
//   int32 utt_len = kaldi::CompactLatticeStateTimes(*clat, &state_times);
//
//   std::vector<std::vector<ClatRescoreTuple> > time_to_state(utt_len);
//
//   int32 num_states = clat->NumStates();
//   KALDI_ASSERT(num_states == state_times.size());
//   for (size_t state = 0; state < num_states; state++) {
//     KALDI_ASSERT(state_times[state] >= 0);
//     int32 t = state_times[state];
//     int32 arc_id = 0;
//     for (fst::MutableArcIterator<CompactLattice> aiter(clat, state);
//          !aiter.Done(); aiter.Next(), arc_id++) {
//       CompactLatticeArc arc = aiter.Value();
//       std::vector<int32> arc_string = arc.weight.String();
//
//       for (size_t offset = 0; offset < arc_string.size(); offset++) {
//         if (t < utt_len) { // end state may be past this..
//           int32 tid = arc_string[offset];
//           time_to_state[t+offset].push_back(ClatRescoreTuple(state, arc_id, tid));
//         } else {
//           if (t != utt_len) {
//             KALDI_WARN << "There appears to be lattice/feature mismatch, "
//                        << "aborting.";
//             return false;
//           }
//         }
//       }
//     }
//     if (clat->Final(state) != CompactLatticeWeight::Zero()) {
//       arc_id = -1;
//       std::vector<int32> arc_string = clat->Final(state).String();
//       for (size_t offset = 0; offset < arc_string.size(); offset++) {
//         KALDI_ASSERT(t + offset < utt_len); // already checked in
//         // CompactLatticeStateTimes, so would be code error.
//         time_to_state[t+offset].push_back(
//             ClatRescoreTuple(state, arc_id, arc_string[offset]));
//       }
//     }
//   }
//
//   for (int32 t = 0; t < utt_len; t++) {
//     if ((t < utt_len - 1) && decodable->IsLastFrame(t)) {
//       KALDI_WARN << "Features are too short for lattice: utt-len is "
//                  << utt_len << ", " << t << " is last frame";
//       return false;
//     }
//     // frame_scale is the scale we put on the computed acoustic probs for this
//     // frame.  It will always be 1.0 if tmodel == NULL (i.e. if we are not doing
//     // the "speedup" code).  For frames with multiple pdf-ids it will be one.
//     // For frames with only one pdf-id, it will equal speedup_factor (>=1.0)
//     // with probability 1.0 / speedup_factor, and zero otherwise.  If it is zero,
//     // we can avoid computing the probabilities.
//     BaseFloat frame_scale = 1.0;
//     KALDI_ASSERT(!time_to_state[t].empty());
//     if (tmodel != NULL) {
//       int32 pdf_id = tmodel->TransitionIdToPdf(time_to_state[t][0].tid);
//       bool frame_has_multiple_pdfs = false;
//       for (size_t i = 1; i < time_to_state[t].size(); i++) {
//         if (tmodel->TransitionIdToPdf(time_to_state[t][i].tid) != pdf_id) {
//           frame_has_multiple_pdfs = true;
//           break;
//         }
//       }
//       if (frame_has_multiple_pdfs) {
//         frame_scale = 1.0;
//       } else {
//         if (WithProb(1.0 / speedup_factor)) {
//           frame_scale = speedup_factor;
//         } else {
//           frame_scale = 0.0;
//         }
//       }
//       if (frame_scale == 0.0)
//         continue; // the code below would be pointless.
//     }
//
//     for (size_t i = 0; i < time_to_state[t].size(); i++) {
//       int32 state = time_to_state[t][i].state_id;
//       int32 arc_id = time_to_state[t][i].arc_id;
//       int32 tid = time_to_state[t][i].tid;
//
//       if (arc_id == -1) { // Final state
//         // Access the trans_id
//         CompactLatticeWeight curr_clat_weight = clat->Final(state);
//
//         // Calculate likelihood
//         BaseFloat log_like = decodable->LogLikelihood(t, tid) * frame_scale;
//         // update weight
//         CompactLatticeWeight new_clat_weight = curr_clat_weight;
//         LatticeWeight new_lat_weight = new_clat_weight.Weight();
//         new_lat_weight.SetValue2(-log_like + curr_clat_weight.Weight().Value2());
//         new_clat_weight.SetWeight(new_lat_weight);
//         clat->SetFinal(state, new_clat_weight);
//       } else {
//         fst::MutableArcIterator<CompactLattice> aiter(clat, state);
//
//         aiter.Seek(arc_id);
//         CompactLatticeArc arc = aiter.Value();
//
//         // Calculate likelihood
//         BaseFloat log_like = decodable->LogLikelihood(t, tid) * frame_scale;
//         // update weight
//         LatticeWeight new_weight = arc.weight.Weight();
//         new_weight.SetValue2(-log_like + arc.weight.Weight().Value2());
//         arc.weight.SetWeight(new_weight);
//         aiter.SetValue(arc);
//       }
//     }
//   }
//   return true;
// }
//
//
// bool RescoreCompactLatticeSpeedup(
//     const TransitionModel &tmodel,
//     BaseFloat speedup_factor,
//     DecodableInterface *decodable,
//     CompactLattice *clat) {
//   return RescoreCompactLatticeInternal(&tmodel, speedup_factor, decodable, clat);
// }
//
// bool RescoreCompactLattice(DecodableInterface *decodable,
//                            CompactLattice *clat) {
//   return RescoreCompactLatticeInternal(NULL, 1.0, decodable, clat);
// }
//
//
// bool RescoreLattice(DecodableInterface *decodable,
//                     Lattice *lat) {
//   if (lat->NumStates() == 0) {
//     KALDI_WARN << "Rescoring empty lattice";
//     return false;
//   }
//   if (!lat->Properties(fst::kTopSorted, true)) {
//     if (fst::TopSort(lat) == false) {
//       KALDI_WARN << "Cycles detected in lattice.";
//       return false;
//     }
//   }
//   std::vector<int32> state_times;
//   int32 utt_len = kaldi::LatticeStateTimes(*lat, &state_times);
//
//   std::vector<std::vector<int32> > time_to_state(utt_len );
//
//   int32 num_states = lat->NumStates();
//   KALDI_ASSERT(num_states == state_times.size());
//   for (size_t state = 0; state < num_states; state++) {
//     int32 t = state_times[state];
//     // Don't check t >= 0 because non-accessible states could have t = -1.
//     KALDI_ASSERT(t <= utt_len);
//     if (t >= 0 && t < utt_len)
//       time_to_state[t].push_back(state);
//   }
//
//   for (int32 t = 0; t < utt_len; t++) {
//     if ((t < utt_len - 1) && decodable->IsLastFrame(t)) {
//       KALDI_WARN << "Features are too short for lattice: utt-len is "
//                  << utt_len << ", " << t << " is last frame";
//       return false;
//     }
//     for (size_t i = 0; i < time_to_state[t].size(); i++) {
//       int32 state = time_to_state[t][i];
//       for (fst::MutableArcIterator<Lattice> aiter(lat, state);
//            !aiter.Done(); aiter.Next()) {
//         LatticeArc arc = aiter.Value();
//         if (arc.ilabel != 0) {
//           int32 trans_id = arc.ilabel; // Note: it doesn't necessarily
//           // have to be a transition-id, just whatever the Decodable
//           // object is expecting, but it's normally a transition-id.
//
//           BaseFloat log_like = decodable->LogLikelihood(t, trans_id);
//           arc.weight.SetValue2(-log_like + arc.weight.Value2());
//           aiter.SetValue(arc);
//         }
//       }
//     }
//   }
//   return true;
// }
//
//
// BaseFloat LatticeForwardBackwardMmi(
//     const TransitionModel &tmodel,
//     const Lattice &lat,
//     const std::vector<int32> &num_ali,
//     bool drop_frames,
//     bool convert_to_pdf_ids,
//     bool cancel,
//     Posterior *post) {
//   // First compute the MMI posteriors.
//
//   Posterior den_post;
//   BaseFloat ans = LatticeForwardBackward(lat,
//                                          &den_post,
//                                          NULL);
//
//   Posterior num_post;
//   AlignmentToPosterior(num_ali, &num_post);
//
//   // Now negate the MMI posteriors and add the numerator
//   // posteriors.
//   ScalePosterior(-1.0, &den_post);
//
//   if (convert_to_pdf_ids) {
//     Posterior num_tmp;
//     ConvertPosteriorToPdfs(tmodel, num_post, &num_tmp);
//     num_tmp.swap(num_post);
//     Posterior den_tmp;
//     ConvertPosteriorToPdfs(tmodel, den_post, &den_tmp);
//     den_tmp.swap(den_post);
//   }
//
//   MergePosteriors(num_post, den_post,
//                   cancel, drop_frames, post);
//
//   return ans;
// }
//
//
// int32 LongestSentenceLength(const Lattice &lat) {
//   typedef Lattice::Arc Arc;
//   typedef Arc::Label Label;
//   typedef Arc::StateId StateId;
//
//   if (lat.Properties(fst::kTopSorted, true) == 0) {
//     Lattice lat_copy(lat);
//     if (!TopSort(&lat_copy))
//       KALDI_ERR << "Was not able to topologically sort lattice (cycles found?)";
//     return LongestSentenceLength(lat_copy);
//   }
//   std::vector<int32> max_length(lat.NumStates(), 0);
//   int32 lattice_max_length = 0;
//   for (StateId s = 0; s < lat.NumStates(); s++) {
//     int32 this_max_length = max_length[s];
//     for (fst::ArcIterator<Lattice> aiter(lat, s); !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       bool arc_has_word = (arc.olabel != 0);
//       StateId nextstate = arc.nextstate;
//       KALDI_ASSERT(static_cast<size_t>(nextstate) < max_length.size());
//       if (arc_has_word) {
//         // A lattice should ideally not have cycles anyway; a cycle with a word
//         // on is something very bad.
//         KALDI_ASSERT(nextstate > s && "Lattice has cycles with words on.");
//         max_length[nextstate] = std::max(max_length[nextstate],
//                                          this_max_length + 1);
//       } else {
//         max_length[nextstate] = std::max(max_length[nextstate],
//                                          this_max_length);
//       }
//     }
//     if (lat.Final(s) != LatticeWeight::Zero())
//       lattice_max_length = std::max(lattice_max_length, max_length[s]);
//   }
//   return lattice_max_length;
// }
//
// int32 LongestSentenceLength(const CompactLattice &clat) {
//   typedef CompactLattice::Arc Arc;
//   typedef Arc::Label Label;
//   typedef Arc::StateId StateId;
//
//   if (clat.Properties(fst::kTopSorted, true) == 0) {
//     CompactLattice clat_copy(clat);
//     if (!TopSort(&clat_copy))
//       KALDI_ERR << "Was not able to topologically sort lattice (cycles found?)";
//     return LongestSentenceLength(clat_copy);
//   }
//   std::vector<int32> max_length(clat.NumStates(), 0);
//   int32 lattice_max_length = 0;
//   for (StateId s = 0; s < clat.NumStates(); s++) {
//     int32 this_max_length = max_length[s];
//     for (fst::ArcIterator<CompactLattice> aiter(clat, s);
//          !aiter.Done(); aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       bool arc_has_word = (arc.ilabel != 0); // note: olabel == ilabel.
//       // also note: for normal CompactLattice, e.g. as produced by
//       // determinization, all arcs will have nonzero labels, but the user might
//       // decide to remplace some of the labels with zero for some reason, and we
//       // want to support this.
//       StateId nextstate = arc.nextstate;
//       KALDI_ASSERT(static_cast<size_t>(nextstate) < max_length.size());
//       KALDI_ASSERT(nextstate > s && "CompactLattice has cycles");
//       if (arc_has_word)
//         max_length[nextstate] = std::max(max_length[nextstate],
//                                          this_max_length + 1);
//       else
//         max_length[nextstate] = std::max(max_length[nextstate],
//                                          this_max_length);
//     }
//     if (clat.Final(s) != CompactLatticeWeight::Zero())
//       lattice_max_length = std::max(lattice_max_length, max_length[s]);
//   }
//   return lattice_max_length;
// }
//
// void ComposeCompactLatticeDeterministic(
//     const CompactLattice& clat,
//     fst::DeterministicOnDemandFst<fst::StdArc>* det_fst,
//     CompactLattice* composed_clat) {
//   // StdFst::Arc and CompactLatticeArc has the same StateId type.
//   typedef fst::StdArc::StateId StateId;
//   typedef fst::StdArc::Weight Weight1;
//   typedef CompactLatticeArc::Weight Weight2;
//   typedef std::pair<StateId, StateId> StatePair;
//   typedef unordered_map<StatePair, StateId, PairHasher<StateId> > MapType;
//   typedef MapType::iterator IterType;
//
//   // Empties the output FST.
//   KALDI_ASSERT(composed_clat != NULL);
//   composed_clat->DeleteStates();
//
//   MapType state_map;
//   std::queue<StatePair> state_queue;
//
//   // Sets start state in <composed_clat>.
//   StateId start_state = composed_clat->AddState();
//   StatePair start_pair(clat.Start(), det_fst->Start());
//   composed_clat->SetStart(start_state);
//   state_queue.push(start_pair);
//   std::pair<IterType, bool> result =
//       state_map.insert(std::make_pair(start_pair, start_state));
//   KALDI_ASSERT(result.second == true);
//
//   // Starts composition here.
//   while (!state_queue.empty()) {
//     // Gets the first state in the queue.
//     StatePair s = state_queue.front();
//     StateId s1 = s.first;
//     StateId s2 = s.second;
//     state_queue.pop();
//
//
//     Weight2 clat_final = clat.Final(s1);
//     if (clat_final.Weight().Value1() !=
//         std::numeric_limits<BaseFloat>::infinity()) {
//       // Test for whether the final-prob of state s1 was zero.
//       Weight1 det_fst_final = det_fst->Final(s2);
//       if (det_fst_final.Value() !=
//           std::numeric_limits<BaseFloat>::infinity()) {
//         // Test for whether the final-prob of state s2 was zero.  If neither
//         // source-state final prob was zero, then we should create final state
//         // in fst_composed. We compute the product manually since this is more
//         // efficient.
//         Weight2 final_weight(LatticeWeight(clat_final.Weight().Value1() +
//                                            det_fst_final.Value(),
//                                            clat_final.Weight().Value2()),
//                              clat_final.String());
//         // we can assume final_weight is not Zero(), since neither of
//         // the sources was zero.
//         KALDI_ASSERT(state_map.find(s) != state_map.end());
//         composed_clat->SetFinal(state_map[s], final_weight);
//       }
//     }
//
//     // Loops over pair of edges at s1 and s2.
//     for (fst::ArcIterator<CompactLattice> aiter(clat, s1);
//          !aiter.Done(); aiter.Next()) {
//       const CompactLatticeArc& arc1 = aiter.Value();
//       fst::StdArc arc2;
//       StateId next_state1 = arc1.nextstate, next_state2;
//       bool matched = false;
//
//       if (arc1.olabel == 0) {
//         // If the symbol on <arc1> is <epsilon>, we transit to the next state
//         // for <clat>, but keep <det_fst> at the current state.
//         matched = true;
//         next_state2 = s2;
//       } else {
//         // Otherwise try to find the matched arc in <det_fst>.
//         matched = det_fst->GetArc(s2, arc1.olabel, &arc2);
//         if (matched) {
//           next_state2 = arc2.nextstate;
//         }
//       }
//
//       // If matched arc is found in <det_fst>, then we have to add new arcs to
//       // <composed_clat>.
//       if (matched) {
//         StatePair next_state_pair(next_state1, next_state2);
//         IterType siter = state_map.find(next_state_pair);
//         StateId next_state;
//
//         // Adds composed state to <state_map>.
//         if (siter == state_map.end()) {
//           // If the composed state has not been created yet, create it.
//           next_state = composed_clat->AddState();
//           std::pair<const StatePair, StateId> next_state_map(next_state_pair,
//                                                              next_state);
//           std::pair<IterType, bool> result = state_map.insert(next_state_map);
//           KALDI_ASSERT(result.second);
//           state_queue.push(next_state_pair);
//         } else {
//           // If the composed state is already in <state_map>, we can directly
//           // use that.
//           next_state = siter->second;
//         }
//
//         // Adds arc to <composed_clat>.
//         if (arc1.olabel == 0) {
//           composed_clat->AddArc(state_map[s],
//                                 CompactLatticeArc(arc1.ilabel, 0,
//                                                   arc1.weight, next_state));
//         } else {
//           Weight2 composed_weight(
//               LatticeWeight(arc1.weight.Weight().Value1() +
//                             arc2.weight.Value(),
//                             arc1.weight.Weight().Value2()),
//               arc1.weight.String());
//           composed_clat->AddArc(state_map[s],
//                                 CompactLatticeArc(arc1.ilabel, arc2.olabel,
//                                                   composed_weight, next_state));
//         }
//       }
//     }
//   }
//   fst::Connect(composed_clat);
// }
//
//
// void ComputeAcousticScoresMap(
//     const Lattice &lat,
//     unordered_map<std::pair<int32, int32>, std::pair<BaseFloat, int32>,
//                                         PairHasher<int32> > *acoustic_scores) {
//   // typedef the arc, weight types
//   typedef Lattice::Arc Arc;
//   typedef Arc::Weight LatticeWeight;
//   typedef Arc::StateId StateId;
//
//   acoustic_scores->clear();
//
//   std::vector<int32> state_times;
//   LatticeStateTimes(lat, &state_times);   // Assumes the input is top sorted
//
//   KALDI_ASSERT(lat.Start() == 0);
//
//   for (StateId s = 0; s < lat.NumStates(); s++) {
//     int32 t = state_times[s];
//     for (fst::ArcIterator<Lattice> aiter(lat, s); !aiter.Done();
//           aiter.Next()) {
//       const Arc &arc = aiter.Value();
//       const LatticeWeight &weight = arc.weight;
//
//       int32 tid = arc.ilabel;
//
//       if (tid != 0) {
//         unordered_map<std::pair<int32, int32>, std::pair<BaseFloat, int32>,
//           PairHasher<int32> >::iterator it = acoustic_scores->find(std::make_pair(t, tid));
//         if (it == acoustic_scores->end()) {
//           acoustic_scores->insert(std::make_pair(std::make_pair(t, tid),
//                                           std::make_pair(weight.Value2(), 1)));
//         } else {
//           if (it->second.second == 2
//                 && it->second.first / it->second.second != weight.Value2()) {
//             KALDI_VLOG(2) << "Transitions on the same frame have different "
//                           << "acoustic costs for tid " << tid << "; "
//                           << it->second.first / it->second.second
//                           << " vs " << weight.Value2();
//           }
//           it->second.first += weight.Value2();
//           it->second.second++;
//         }
//       } else {
//         // Arcs with epsilon input label (tid) must have 0 acoustic cost
//         KALDI_ASSERT(weight.Value2() == 0);
//       }
//     }
//
//     LatticeWeight f = lat.Final(s);
//     if (f != LatticeWeight::Zero()) {
//       // Final acoustic cost must be 0 as we are reading from
//       // non-determinized, non-compact lattice
//       KALDI_ASSERT(f.Value2() == 0.0);
//     }
//   }
// }
//
// void ReplaceAcousticScoresFromMap(
//     const unordered_map<std::pair<int32, int32>, std::pair<BaseFloat, int32>,
//                                         PairHasher<int32> > &acoustic_scores,
//     Lattice *lat) {
//   // typedef the arc, weight types
//   typedef Lattice::Arc Arc;
//   typedef Arc::Weight LatticeWeight;
//   typedef Arc::StateId StateId;
//
//   TopSortLatticeIfNeeded(lat);
//
//   std::vector<int32> state_times;
//   LatticeStateTimes(*lat, &state_times);
//
//   KALDI_ASSERT(lat->Start() == 0);
//
//   for (StateId s = 0; s < lat->NumStates(); s++) {
//     int32 t = state_times[s];
//     for (fst::MutableArcIterator<Lattice> aiter(lat, s);
//          !aiter.Done(); aiter.Next()) {
//       Arc arc(aiter.Value());
//
//       int32 tid = arc.ilabel;
//       if (tid != 0) {
//         unordered_map<std::pair<int32, int32>, std::pair<BaseFloat, int32>,
//           PairHasher<int32> >::const_iterator it = acoustic_scores.find(std::make_pair(t, tid));
//         if (it == acoustic_scores.end()) {
//           KALDI_ERR << "Could not find tid " << tid << " at time " << t
//                     << " in the acoustic scores map.";
//         } else {
//           arc.weight.SetValue2(it->second.first / it->second.second);
//         }
//       } else {
//         // For epsilon arcs, set acoustic cost to 0.0
//         arc.weight.SetValue2(0.0);
//       }
//       aiter.SetValue(arc);
//     }
//
//     LatticeWeight f = lat->Final(s);
//     if (f != LatticeWeight::Zero()) {
//       // Set final acoustic cost to 0.0
//       f.SetValue2(0.0);
//       lat->SetFinal(s, f);
//     }
//   }
// }

}  // namespace kaldi
