/*
   Copyright (C) 2012 Benjamin Redelings

This file is part of BAli-Phy.

BAli-Phy is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

BAli-Phy is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with BAli-Phy; see the file COPYING.  If not see
<http://www.gnu.org/licenses/>.  */

///
/// \file dp_hmm.H
///
/// \brief This file implements the generic Dynamic Programin specific HMM class.
///

#ifndef DP_HMM
#define DP_HMM

#include "hmm.H"

/// This is an HMM with a "generalized" transition matrix without silent cycles for using in dynamic programming.
class dp_HMM: public HMM
{
  /// Is each state part of the silent network?  (S: maybe.  E: no) If so, where is it in the order of such states?
  std::vector<int> silent_network_;

  /// Ordered list of states that are part of silent networks
  std::vector<int> silent_network_states;

  /// Ordered list of states that are not part of silent networks
  std::vector<int> non_silent_network;

  /// An ordered list of states, for DP
  std::vector<int> dp_order_;

  log_double_t generalize_P_one(std::vector<int>::const_iterator,int) const;

  void find_and_index_silent_network_states();
public:
  
  /// The 'generalized' transition matrix - silent loops removed
  Matrix GQ;

  void update_GQ();

  bool connected(int S1,int S2) const {return GQ(S1,S2) != 0.0;}

  /// Is state S part of a loop of silent states?  FIXME: s/network/cycle/...
  bool silent_network(int S) const;

  int n_dp_states() const {return dp_order_.size();}

  /// What is the n-th state, in the correct order for DP?
  int dp_order(int i) const {assert(i < dp_order_.size());  return dp_order_[i];}

  const std::vector<int>& dp_order() const {return dp_order_;}

  /// Convert a path to a generalized path
  std::vector<int> generalize(const std::vector<int>& path) const;

  /// Randomly sample a path from a generalized path g_path
  std::vector<int> ungeneralize(const std::vector<int>& g_path) const;

  /// Calculates the (log) probability that path would be generated by its generalized dual
  log_double_t generalize_P(const std::vector<int>& path) const;

  /// Calculates the (log) product of each move, according to the generalized HMM
  log_double_t path_GQ_path(const std::vector<int>& g_path) const;

  dp_HMM(const HMM&);
};

#endif
