/********************************************************************************************
Posterior of observing a certain state substitution along a branch:
P(Node=x,Father=y|D) = P(D,Node=x,Father=y)/P(D)
usage: posteriorPerNodePer2States[mynode->id()][fatherState][sonState]
*********************************************************************************************/
void computePosteriorExpectationOfSubstitutions_nonReversibleSp::computePosteriorOfChangeGivenTerminals(VVVdouble &posteriorPerNodePer2States, int pos){
	int numNodes = _tr.getNodesNum();
	int alphabetSize = _sp->alphabetSize();
	posteriorPerNodePer2States.resize(numNodes);
	for (int n=0;n<posteriorPerNodePer2States.size();++n)
		resizeMatrix(posteriorPerNodePer2States[n],alphabetSize,alphabetSize);
	suffStatGlobalHomPos sscUp;
	suffStatGlobalGamPos sscDownNonRev;	// The "Gam" is used for the letter at father - sscGivenRoot
	sscUp.allocatePlace(numNodes,alphabetSize);
	computePijHom pi;
	pi.fillPij(_tr,*_sp); 

	computeUpAlg comp_Up;
	computeDownAlg comp_Down;
	comp_Up.fillComputeUp(_tr,_sc,pos,pi,sscUp);
	comp_Down.fillComputeDownNonReversible(_tr,_sc,pos,pi,sscDownNonRev,sscUp);
	treeIterTopDownConst tIt(_tr);
	MDOUBLE ll = convert(likelihoodComputation::getLofPos(pos,_tr,_sc,pi,*_sp));
	for (tree::nodeP mynode = tIt.first(); mynode != tIt.end(); mynode = tIt.next()) {
		for (int sonState = 0; sonState<alphabetSize; ++sonState){
			for (int fatherState = 0; fatherState<alphabetSize; ++fatherState){
				posteriorPerNodePer2States[mynode->id()][fatherState][sonState]= computePosterioGivenTerminalsPerBranch(mynode->id(),sonState,fatherState,sscUp,sscDownNonRev, pi,ll,mynode->name());
			}
		}
	}
}
void AbstractDiscreteRatesAcrossSitesTreeLikelihood::resetLikelihoodArray(
    VVVdouble & likelihoodArray)
{
  unsigned int nbSites   = likelihoodArray.size();
  unsigned int nbClasses = likelihoodArray[0].size();
  unsigned int nbStates  = likelihoodArray[0][0].size();
  for(unsigned int i = 0; i < nbSites; i++)
  {
    for(unsigned int c = 0; c < nbClasses; c++)
    {
      for(unsigned int s = 0; s < nbStates; s++)
      {
        likelihoodArray[i][c][s] = 1.;
      }
    }
  }
}
void AbstractDiscreteRatesAcrossSitesTreeLikelihood::displayLikelihoodArray(
    const VVVdouble & likelihoodArray)
{
  unsigned int nbSites   = likelihoodArray.size();
  unsigned int nbClasses = likelihoodArray[0].size();
  unsigned int nbStates  = likelihoodArray[0][0].size();
  for(unsigned int i = 0; i < nbSites; i++)
  {
    cout << "Site " << i << ":" << endl;
    for(unsigned int c = 0; c < nbClasses; c++)
    {
      cout << "Rate class " << c;
      for(unsigned int s = 0; s < nbStates; s++)
      {
        cout << "\t" << likelihoodArray[i][c][s];
      }
      cout << endl;
    }
    cout << endl;
  }
}
void DRNonHomogeneousTreeLikelihood::computeLikelihoodAtNode(int nodeId, VVVdouble& likelihoodArray) const
{
  const Node * node = _tree->getNode(nodeId);

  likelihoodArray.resize(_nbDistinctSites);
  map<int, VVVdouble> * likelihoods_node = & _likelihoodData->getLikelihoodArrays(nodeId);
  
  //Initialize likelihood array:
  if(node->isLeaf())
  {
    VVdouble * leavesLikelihoods_node = & _likelihoodData->getLeafLikelihoods(nodeId);
    for(unsigned int i = 0; i < _nbDistinctSites; i++)
    {
      VVdouble * likelihoodArray_i = & likelihoodArray[i];
      Vdouble * leavesLikelihoods_node_i = & (* leavesLikelihoods_node)[i];
      likelihoodArray_i->resize(_nbClasses);
      for(unsigned int c = 0; c < _nbClasses; c++)
      {
        Vdouble * likelihoodArray_i_c = & (* likelihoodArray_i)[c];
        likelihoodArray_i_c->resize(_nbStates);
        for(unsigned int x = 0; x < _nbStates; x++)
        {
          (* likelihoodArray_i_c)[x] = (* leavesLikelihoods_node_i)[x];
        }
      }
    }
  }
  else
  {
    // Otherwise:
    // Set all likelihoods to 1 for a start:
    for(unsigned int i = 0; i < _nbDistinctSites; i++)
    {
      VVdouble * likelihoodArray_i = & likelihoodArray[i];
      likelihoodArray_i->resize(_nbClasses);
      for(unsigned int c = 0; c < _nbClasses; c++)
      {
        Vdouble * likelihoodArray_i_c = & (* likelihoodArray_i)[c];
        likelihoodArray_i_c->resize(_nbStates);
        for(unsigned int x = 0; x < _nbStates; x++)
        {
          (* likelihoodArray_i_c)[x] = 1.;
        }
      }
    }
  }
  
  unsigned int nbNodes = node->getNumberOfSons();
  
  vector<const VVVdouble *> iLik(nbNodes);
  vector<const VVVdouble *> tProb(nbNodes);
  for(unsigned int n = 0; n < nbNodes; n++)
  {
    const Node * son = node->getSon(n);
    tProb[n] = & _pxy[son->getId()];
    iLik[n] = & (* likelihoods_node)[son->getId()];
  }
  
  if(node->hasFather())
  {
    const Node * father = node->getFather();
    computeLikelihoodFromArrays(iLik, tProb, & (* likelihoods_node)[father->getId()], & _pxy[nodeId], likelihoodArray, nbNodes, _nbDistinctSites, _nbClasses, _nbStates, false);
  }
  else
  {
    computeLikelihoodFromArrays(iLik, tProb, likelihoodArray, nbNodes, _nbDistinctSites, _nbClasses, _nbStates, false);
    
    //We have to account for the root frequencies:
     for(unsigned int i = 0; i < _nbDistinctSites; i++)
    {
      VVdouble * likelihoodArray_i = & likelihoodArray[i];
      for(unsigned int c = 0; c < _nbClasses; c++)
      {
        Vdouble * likelihoodArray_i_c = & (* likelihoodArray_i)[c];
        for(unsigned int x = 0; x < _nbStates; x++)
        {
          (* likelihoodArray_i_c)[x] *= _rootFreqs[x];
        }
      }
    }
  }
}