/* energy (score) functions, aka -log(p).
   * O(M) if Sigma is up to date
   * O(M^3) if Sigma has changed (Cholesky decomp.) */
double MultivariateFNormalSufficient::evaluate() const
  {
      double e = get_norms()[1] + get_minus_log_jacobian();
      if (N_==1)
      {
          e += 0.5*get_mean_square_residuals()/SQUARE(factor_) ;
          /*std::cout << "mvn"
              << " " << e
              << " " << get_norms()[0]
              << " " << get_norms()[1]
              << " " << get_minus_log_jacobian()
              << " " << get_mean_square_residuals()/SQUARE(factor_)
              <<std::endl; */
      } else {
          e += 0.5*( trace_WP()
                + double(N_)*get_mean_square_residuals())/SQUARE(factor_) ;
      }
      //if log(norm) is -inf, make the score bad, e.g. +inf.
      //should check if epsilon==0 in which case we should return -inf
      //to mimic the Dirac distribution, but this case has undefined derivatives
      if (std::abs(e) > std::numeric_limits<double>::max())
          e=std::numeric_limits<double>::infinity();
      LOG( "MVN: evaluate() = " << e << std::endl);
      return e;
  }
  /* energy (score) functions, aka -log(p) */
double MultivariateFNormalSufficientSparse::evaluate() const
  {
      //std::cout << " mean " << double(N_)*mean_dist();
      //std::cout << " WP " << trace_WP();
      double e = lnorm_ + lJF_ + 0.5*( trace_WP() + double(N_)*mean_dist()) ;
      IMP_LOG(TERSE, "MVNsparse: evaluate() = " << e << std::endl);
      return e;
  }
double MultivariateFNormalSufficient::get_minus_exponent() const
{
    double e;
    if (N_ == 1)
    {
        e = 0.5*get_mean_square_residuals()/SQUARE(factor_) ;
    } else {
        e = 0.5*( trace_WP()
                + double(N_)*get_mean_square_residuals())/SQUARE(factor_) ;
    }
    return e;
}
  /* probability density function */
double MultivariateFNormalSufficient::density() const
  {
      double d;
      if (N_ == 1)
      {
          d=get_norms()[0]*get_jacobian()
              *exp(-0.5*get_mean_square_residuals()/SQUARE(factor_));
      } else {
          d = get_norms()[0]*get_jacobian()
          *exp(-0.5*(trace_WP()
                     + N_ * get_mean_square_residuals())/SQUARE(factor_));
      }
      LOG( "MVN: density() = " << d << std::endl);
      return d;
  }
 double MultivariateFNormalSufficient::evaluate_derivative_factor() const
 {
     //warning: untested!
     //d(-log(p))/dfactor = -N/f^3 trans(eps)P(eps) -1/f^3 tr(WP) + NM/f
     LOG( "MVN: evaluate_derivative_factor() = " << std::endl);
     double R;
     if (N_==1)
     {
         R = - get_mean_square_residuals()/CUBE(factor_)
             + double(M_)/factor_;
     } else {
         R = - (double(N_)*get_mean_square_residuals()
                 + trace_WP())/CUBE(factor_)
             + double(M_*N_)/factor_;
     }
     return R;
 }
  /* probability density function */
double MultivariateFNormalSufficient::density() const
  {
      timer_.start(EVAL);
      double d;
      if (N_ == 1)
      {
          d=get_norms()[0]*get_jacobian()
              *exp(-0.5*get_mean_square_residuals()/IMP::square(factor_));
      } else {
          d = get_norms()[0]*get_jacobian()
          *exp(-0.5*(trace_WP()
                     + N_ * get_mean_square_residuals())/IMP::square(factor_));
      }
      IMP_LOG(TERSE, "MVN: density() = " << d << std::endl);
      timer_.stop(EVAL);
      return d;
  }
  /* energy (score) functions, aka -log(p).
   * O(M) if Sigma is up to date
   * O(M^3) if Sigma has changed (Cholesky decomp.) */
double MultivariateFNormalSufficient::evaluate() const
  {
      timer_.start(EVAL);
      double e;
      if (N_==1)
      {
          e = get_norms()[1] + get_minus_log_jacobian()
          + 0.5*get_mean_square_residuals()/IMP::square(factor_) ;
          /*std::cout << "mvn"
              << " " << e
              << " " << get_norms()[0]
              << " " << get_norms()[1]
              << " " << get_minus_log_jacobian()
              << " " << get_mean_square_residuals()/IMP::square(factor_)
              <<std::endl; */
      } else {
          e = get_norms()[1] + get_minus_log_jacobian()
          + 0.5*( trace_WP()
                + double(N_)*get_mean_square_residuals())/IMP::square(factor_) ;
      }
      IMP_LOG(TERSE, "MVN: evaluate() = " << e << std::endl);
      timer_.stop(EVAL);
      return e;
  }
  /* probability density function */
double MultivariateFNormalSufficientSparse::density() const
  {
      double d = norm_*JF_*exp(-0.5*(trace_WP() + N_ * mean_dist()));
      IMP_LOG(TERSE, "MVNsparse: density() = " << d << std::endl);
      return d;
  }