int main() {
  TreeTemplate<Node>* tree = TreeTemplateTools::parenthesisToTree("((A:0.001, B:0.002):0.003,C:0.01,D:0.1);");
  cout << tree->getNumberOfLeaves() << endl;
  vector<int> ids = tree->getNodesId();
  //-------------

  NucleicAlphabet* alphabet = new DNA();
  SubstitutionModel* model = new GTR(alphabet, 1, 0.2, 0.3, 0.4, 0.4, 0.1, 0.35, 0.35, 0.2);
  //DiscreteDistribution* rdist = new GammaDiscreteDistribution(4, 0.4, 0.4);
  DiscreteDistribution* rdist = new ConstantDistribution(1.0);
  HomogeneousSequenceSimulator simulator(model, rdist, tree);

  unsigned int n = 100000;
  map<int, RowMatrix<unsigned int> > counts;
  for (size_t j = 0; j < ids.size() - 1; ++j) //ignore root, the last id
    counts[ids[j]].resize(4, 4);
  for (unsigned int i = 0; i < n; ++i) {
    RASiteSimulationResult* result = simulator.dSimulate();
    for (size_t j = 0; j < ids.size() - 1; ++j) { //ignore root, the last id
      result->getMutationPath(ids[j]).getEventCounts(counts[ids[j]]);
    }
    delete result;
  }
  map<int, RowMatrix<double> >freqs;
  map<int, double> sums;
  for (size_t k = 0; k < ids.size() - 1; ++k) { //ignore root, the last id
    RowMatrix<double>* freqsP = &freqs[ids[k]];
    RowMatrix<unsigned int>* countsP = &counts[ids[k]];
    freqsP->resize(4, 4);
    for (unsigned int i = 0; i < 4; ++i)
      for (unsigned int j = 0; j < 4; ++j)
        (*freqsP)(i, j) = static_cast<double>((*countsP)(i, j)) / (static_cast<double>(n));
    
    //For now we simply compare the total number of substitutions:
    sums[ids[k]] = MatrixTools::sumElements(*freqsP);
  
    cout << "Br" << ids[k] << " BrLen = " << tree->getDistanceToFather(ids[k]) << " counts = " << sums[ids[k]] << endl;
    MatrixTools::print(*freqsP);
  }
  //We should compare this matrix with the expected one!

  for (size_t k = 0; k < ids.size() - 1; ++k) { //ignore root, the last id
    if (abs(sums[ids[k]] - tree->getDistanceToFather(ids[k])) > 0.01) {
      delete tree;
      delete alphabet;
      delete model;
      delete rdist;
      return 1;
    }
  }
  //-------------
  delete tree;
  delete alphabet;
  delete model;
  delete rdist;

  //return (abs(obs - 0.001) < 0.001 ? 0 : 1);
  return 0;
}
void DualityDiagram::compute_(const Matrix<double>& matrix,
                              double tol, bool verbose)
{
    size_t rowNb = matrix.getNumberOfRows();
    size_t colNb = matrix.getNumberOfColumns();

    // If there are less rows than columns, the variance-covariance or correlation matrix is obtain differently (see below)
    bool transpose = (rowNb < colNb);

    // The initial matrix is multiplied by the square root of the row weigths.
    vector<double> rW(rowWeights_);
    for (unsigned int i = 0; i < rowWeights_.size(); i++)
    {
        rW[i] = sqrt(rowWeights_[i]);
    }

    RowMatrix<double> M1(rowNb, colNb);
    MatrixTools::hadamardMult(matrix, rW, M1, true);

    // The resulting matrix is then multiplied by the square root of the column weigths.
    vector<double> cW(colWeights_);
    for (unsigned int i = 0; i < colWeights_.size(); i++)
    {
        cW[i] = sqrt(colWeights_[i]);
    }

    RowMatrix<double> M2;
    MatrixTools::hadamardMult(M1, cW, M2, false);

    // The variance-covariance (if the data is centered) or the correlation (if the data is centered and normalized) matrix is calculated
    RowMatrix<double> tM2;
    MatrixTools::transpose(M2, tM2);
    RowMatrix<double> M3;
    if (!transpose)
        MatrixTools::mult(tM2, M2, M3);
    else
        MatrixTools::mult(M2, tM2, M3);

    EigenValue<double> eigen(M3);
    if (!eigen.isSymmetric())
        throw Exception("DualityDiagram (constructor). The variance-covariance or correlation matrix should be symmetric...");

    eigenValues_ = eigen.getRealEigenValues();
    eigenVectors_ = eigen.getV();

    // How many significant axes have to be conserved?
    size_t rank = 0;
    for (size_t i = eigenValues_.size(); i > 0; i--)
    {
        if ((eigenValues_[i - 1] / eigenValues_[eigenValues_.size() - 1]) > tol)
            rank++;
    }

    if (nbAxes_ <=0)
    {
        throw Exception("DualityDiagram (constructor). The number of axes to keep must be positive.");
    }
    if (nbAxes_ > rank)
    {
        if (verbose)
            ApplicationTools::displayWarning("The number of axes to kept has been reduced to conserve only significant axes");
        nbAxes_ = rank;
    }

    /*The eigen values are initially sorted into ascending order by the 'eigen' function. Here the significant values are sorted
       in the other way around.*/
    vector<double> tmpEigenValues(nbAxes_);
    size_t cpt = 0;
    for (size_t i = eigenValues_.size(); i > (eigenValues_.size() - nbAxes_); i--)
    {
        tmpEigenValues[cpt] = eigenValues_[i-1];
        cpt++;
    }
    eigenValues_ = tmpEigenValues;

    for (vector<double>::iterator it = rowWeights_.begin(); it != rowWeights_.end(); it++)
    {
        if (*it == 0.)
            *it = 1.;
    }

    for (vector<double>::iterator it = colWeights_.begin(); it != colWeights_.end(); it++)
    {
        if (*it == 0.)
            *it = 1.;
    }

    vector<double> dval(nbAxes_);
    for (size_t i = 0; i < dval.size(); i++)
    {
        dval[i] = sqrt(eigenValues_[i]);
    }

    vector<double> invDval(nbAxes_);
    for (size_t i = 0; i < invDval.size(); i++)
    {
        invDval[i] = 1. / sqrt(eigenValues_[i]);
    }

    // Calculation of the row and column coordinates as well as the principal axes and components:
    if (!transpose)
    {
        vector<double> tmpColWeights(colNb);
        for (unsigned int i = 0; i < colWeights_.size(); i++)
        {
            tmpColWeights[i] = 1. / sqrt(colWeights_[i]);
        }

        // The eigen vectors are placed in the same order as their corresponding eigen value in eigenValues_.
        RowMatrix<double> tmpEigenVectors;
        tmpEigenVectors.resize(eigenVectors_.getNumberOfRows(), nbAxes_);
        size_t cpt2 = 0;
        for (size_t i = eigenVectors_.getNumberOfColumns(); i > (eigenVectors_.getNumberOfColumns() - nbAxes_); i--)
        {
            for (unsigned int j = 0; j < eigenVectors_.getNumberOfRows(); j++)
            {
                tmpEigenVectors(j, cpt2) = eigenVectors_(j, i-1);
            }
            cpt2++;
        }

        // matrix of principal axes
        MatrixTools::hadamardMult(tmpEigenVectors, tmpColWeights, ppalAxes_, true);
        // matrix of row coordinates
        RowMatrix<double> tmpRowCoord_;
        tmpRowCoord_.resize(rowNb, nbAxes_);
        MatrixTools::hadamardMult(matrix, colWeights_, tmpRowCoord_, false);
        MatrixTools::mult(tmpRowCoord_, ppalAxes_, rowCoord_);

        // matrix of column coordinates
        MatrixTools::hadamardMult(ppalAxes_, dval, colCoord_, false);
        // matrix of principal components
        MatrixTools::hadamardMult(rowCoord_, invDval, ppalComponents_, false);
    }
    else
    {
        vector<double> tmpRowWeights(rowNb);
        for (unsigned int i = 0; i < rowWeights_.size(); i++)
        {
            tmpRowWeights[i] = 1. / sqrt(rowWeights_[i]);
        }

        // The eigen vectors are placed in the same order as their corresponding eigen value in eigenValues_.
        RowMatrix<double> tmpEigenVectors;
        tmpEigenVectors.resize(eigenVectors_.getNumberOfRows(), nbAxes_);
        size_t cpt2 = 0;
        for (size_t i = eigenVectors_.getNumberOfColumns(); i > (eigenVectors_.getNumberOfColumns() - nbAxes_); i--)
        {
            for (size_t j = 0; j < eigenVectors_.getNumberOfRows(); j++)
            {
                tmpEigenVectors(j, cpt2) = eigenVectors_(j, i-1);
            }
            cpt2++;
        }

        // matrix of principal components
        MatrixTools::hadamardMult(tmpEigenVectors, tmpRowWeights, ppalComponents_, true);
        // matrix of column coordinates
        RowMatrix<double> tmpColCoord_;
        tmpColCoord_.resize(colNb, nbAxes_);
        MatrixTools::hadamardMult(matrix, rowWeights_, tmpColCoord_, true);
        RowMatrix<double> tTmpColCoord_;
        MatrixTools::transpose(tmpColCoord_, tTmpColCoord_);
        MatrixTools::mult(tTmpColCoord_, ppalComponents_, colCoord_);

        // matrix of row coordinates
        MatrixTools::hadamardMult(ppalComponents_, dval, rowCoord_, false);
        // matrix of principal axes
        MatrixTools::hadamardMult(colCoord_, invDval, ppalAxes_, false);
    }
}