// $Id: mathx.h,v 1.34 2018/01/03 21:33:02 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


#ifndef MATHX_H
#define MATHX_H

#include <cmath>
#include <vector>

#include "vectorx.h"

// if we're on an alpha...  does this catch that?
#ifdef alphaev56
#define MYEXP UnderFlowExp
#else
#define MYEXP exp
#endif

//------------------------------------------------------------------------------------
// The proper version of isnan and the right way  to get access to
// it depends on the system and compiler. Here we try to get that
// all straightened out.

// Macosx doesn't currently include isnan properly without this
// G-d only knows why it needs iostream to compile properly
#ifdef LAMARC_COMPILE_MACOSX
#if (__GNUC__ < 4)
#include <iostream>
extern "C" int isnan(double);
#endif
#endif

// the default isnan
#define systemSpecificIsnan isnan

// Microsoft VC++ uses a different isnan function. Unfortunately,
// Metrowerks defines _MSC_VER sometimes, but we don't want to
// do this in that case
#ifdef __MWERKS__
#elif defined _MSC_VER
#include <float.h>
#define systemSpecificIsnan _isnan
#endif

const double LOG2 = 0.69314718055994530942; // log(2.0).
const double LOG10 = 2.30258509299404568401799145468; // log(10.0)
const double PI = 3.1415926535897932384626433832795028841972;
const double SQRT_PI = 1.7724538509055160272981674833411451827975; // sqrt(PI)
const double EULERS_CONSTANT = 0.5772156649015328606065120900824024310422;

using std::vector;
double log_gamma(double x);
double my_gamma(double x);
double erfc(double x); // the "complementary error function"
double incompletegamma(double alpha, double x);
double probchi(long df, double chi);
double find_chi(long df, double prob);
DoubleVec1d gamma_rates(double alpha, long ratenum);
double alnorm (double x, int up);
double SafeDivide(double num, double denom);
double logfac(long n);
//long   factorial(long n);
double factorial(double n);
double UnderFlowExp(double pow);
bool IsEven(long n);
DoubleVec2d Invert(const DoubleVec2d& src);  // invert square matrix
DoubleVec2d Transpose(const DoubleVec2d& src);  // transpose square matrix
bool CloseEnough(double a, double b);  // are values within epsilon?
DoubleVec1d SafeExp(const DoubleVec1d& src);
double SafeExpAndSum(const DoubleVec1d& src);
double SafeExp(double src);
double SafeProductWithExp(double a, double x);
double OverflowOfProductWithExp(double a, double x);
double UnderflowOfProductWithExp(double a, double x);
double SafeExpDiff(double x1, double x2);
double OverflowOfSafeExpDiff(double x1, double x2);
double UnderflowOfSafeExpDiff(double x1, double x2);
double LogZero(double x);
double SafeLog(double x);
DoubleVec1d SafeLog(DoubleVec1d src);
long ChooseRandomFromWeights(DoubleVec1d& weights);
double ExpE1(const double& x);
double BesselK(double v, double x, double& resultFor_vPlusOne); // == K(v,x)
double DvBesselK(double v, double x, double Kvx);
double psi(double x); // Euler's psi function == d(log_gamma(x))/dx.
void ScaleLargestToZero(DoubleVec1d& unscaled);
void ScaleToSumToOne(DoubleVec1d& vec);
DoubleVec1d AddValsOfLogs(DoubleVec1d vec1, DoubleVec1d vec2);

#if 0
class Gamma
{
    double alpha;
    double logAlpha;
    double logAlpha1;
    double invAlpha9;
    double sqrAlpha3;

  public:
    Gamma();
    Gamma(double);
    void           SetAlpha(double);
    DoubleVec1d    Calculate(long);         // number of divisions
    double         IncompleteGamma(double alpha, double x);
    double         LogGamma(double);
    double         Tail(double);
    double ProbChi2(long df , double chi);
    double FindChi2(long df, double prob);
};
#endif

class EigenCalculator
{
  private:

    static const double accuracy;

    // Cosine and sine
    std::pair<double, double> Coeffs(double x, double y);

    // Givens matrix reduction
    void Givens(DoubleVec2d& a, long x, long y, long size,
                const std::pair<double, double> cs, bool userow);

    // Matrix tridiagonalization
    void Tridiag(DoubleVec2d& a, DoubleVec2d& eigvecs);

    // Intermediate work of eigenvalues/vectors
    void Shiftqr(DoubleVec2d& a, DoubleVec2d& eigvecs);

    // Modified dot-product
    void DotProduct(const DoubleVec2d& first, const DoubleVec2d& second,
                    DoubleVec2d& answer);

  public:

    // Driver for eigenvalues/vectors
    std::pair<DoubleVec1d, DoubleVec2d> Eigen(DoubleVec2d a);

};

#endif // MATHX_H

//____________________________________________________________________________________
