#include "FunctionTestBase.hh"

/**
 *  default constructor
 */
FunctionTestBase::FunctionTestBase() {
    this->initIO();
    this->setFormat(7);
}

/**
 *  constructor
 *
 *  \param[in] func  a build-in functions to be tested
 *  \param[in] param parameters for the function
 */
FunctionTestBase::FunctionTestBase(FuncBase *func, vector<Double> *param) {
    this->setFunc(func);
    this->setParam(param);
    this->initIO();
    this->setFormat(7);
}

/**
 *  destructor
 */
FunctionTestBase::~FunctionTestBase() {
    this->setDefaultIO();
    if (this->param != NULL) {
        this->param->clear(); delete this->param; this->param=NULL;
    }
    if (this->func != NULL) {
        delete this->func; this->func=NULL;
    }
}

/**
 *
 */
void FunctionTestBase::initIO() {
    this->ofs=NULL;
    this->buf=NULL;
}

/**
 *  set a built-in function
 *
 *  \param[in] func a built-in function
 */
void FunctionTestBase::setFunc(FuncBase *func) {
    this->func=func;
}

/**
 *  set output file stream
 *
 *  \param[in] f output file stream
 */
void FunctionTestBase::setOutputFile(string fileName) {

    std::cerr << "Degug: FunctionTestBase::stOutpurFile(string): fileName=" << fileName << endl;
    if ( this->ofs != NULL ) {
        this->ofs->close();
        cout.rdbuf(this->buf);
    }
    this->buf=cout.rdbuf();
    this->ofs=new ofstream(fileName.c_str(), std::ios_base::out);
}

void FunctionTestBase::setDefaultIO() {
    if ( this->ofs != NULL ) {
        this->ofs->close();
        cout.rdbuf(this->buf);
        this->ofs=NULL;
        this->buf=NULL;
    }
}

/**
 *  set parameters of the built_in function as a Double vector
 *
 *  \param[in] param parameters of the build-in function
 */
void FunctionTestBase::setParam(vector<Double> *param) {
    this->param=param;
}

/**
 *  set precision for output format
 *
 *  \param[in] precision
 */
void FunctionTestBase::setFormat(UInt4 precision) {
    if (precision < 1) {
        this->precision=7;
    } else if ( 1 <= precision && precision <= 15) {
        this->precision=precision;
    } else {
        this->precision=7;
    }

    this->width=this->precision+1+1+1+1+4; // digits(precision) + 1 digits + '-' + '.' + 'E|e' + 4 digits
}

// definition of macros for output routines
#define formatForHeader(width)            setw(width) << setiosflags(std::ios::right)
#define formatForDouble(width, precision) setiosflags(std::ios::scientific) << setw(width) << setprecision(precision) << setiosflags(std::ios::right)
#define space()                           std::cout << setw(1) << " "
// end definition

/**
 *  output column headers
 */
void FunctionTestBase::outputHeader() {

    string gradientHeader=string("gradient");
    UInt4 l;
    l=(3*width+2-gradientHeader.size())/2 + (3*width+2-gradientHeader.size())%2;
    string leftDash=string(l, '-');
    l=(3*width+2-gradientHeader.size())/2;
    string rightDash=string(l, '-');

    std::cout << setw(width*4 + 4) << " ";
    std::cout << leftDash + gradientHeader + rightDash << endl;

    std::cout << formatForHeader(this->width) << "x";
    space();
    std::cout << formatForHeader(this->width) << "eval";
    space();
    std::cout << formatForHeader(this->width) << "der1st";
    space();
    std::cout << formatForHeader(this->width) << "der2nd";
    for (UInt4 i=0; i<this->func->getNumberOfParam(); ++i) {
        space();
        std::cout << formatForHeader(this->width) << i;
    }
    std::cout << endl;
}

/**
 *  output various values of the build-in function at the given point x
 *
 *  the value of the function, 1st. derivative, 2nd. derivative and gradient of parameters at given point x
 *
 *  \param[in] x
 */
void FunctionTestBase::outputValues(const Double x) {

    vector<Double> g=this->func->gradient(x, *(this->param));

    std::cout << formatForDouble(this->width, this->precision) << x;
    space();
    std::cout << formatForDouble(this->width, this->precision) << this->func->eval(x, *(this->param));
    space();
    std::cout << formatForDouble(this->width, this->precision) << this->func->der1st(x, *(this->param));
    space();
    std::cout << formatForDouble(this->width, this->precision) << this->func->der2nd(x, *(this->param));
    for (UInt4 i=0; i<this->func->getNumberOfParam(); ++i) {
        space();
        std::cout << formatForDouble(this->width, this->precision) << g[i];
    }
    std::cout << endl;
}

/**
 *  output variout values of the build-in function at geven points
 *
 *  \param[in] xv points to evaluate various quantities of the build-in function
 */
void FunctionTestBase::outputValues(const vector<Double> &xv) {
    if ( ! xv.empty() ) {
        this->outputHeader();
        for (std::vector<Double>::const_iterator x=xv.begin(); x != xv.end(); ++x) {
            this->outputValues(*x);
        }
    }
}
