#include <cstdlib>
#include <iostream>
#include <ostream>
#include <iomanip>

#include "Header.hh"

#include "FuncBase.hh"
#include "FuncComb.hh"
#include "Gaussian.hh"
#include "Lorentzian.hh"
#include "AugmentedLorentzian.hh"
#include "PseudoVoigt1.hh"
#include "PseudoVoigt2.hh"
#include "Triangle.hh"
#include "Trapezoid.hh"


Int4 main(Int4 argc, Char *argv[]) {

    vector<FuncBase*> funcList;
    vector<Double> param;
    vector< vector<Double> > covar;
    vector<Double> x;

    Double xmin=-5.0, xmax=5.0, delta;
    UInt4 nDiv=100;

    funcList.push_back(new Gaussian());
    funcList.push_back(new Gaussian());
    param.push_back(1.0);
    param.push_back(0.0);
    param.push_back(1.0);
    param.push_back(1.0);
    param.push_back(0.0);
    param.push_back(1.0);
    for (UInt4 i=0; i<param.size(); ++i) {
        covar.push_back(*(new vector<Double>(param.size(), 0.0001)));
    }
    FuncComb func = *(new FuncComb(funcList, param, covar));
    func.dump();
    /*
    */

    delta=(xmax-xmin)/nDiv;
    for (UInt4 i=0; i<=nDiv; ++i) {
        x.push_back(xmin+delta*i);
    }

    UInt4 id=0;
    for (UInt4 i=0; i<x.size(); ++i) {
    }

    vector<Double> y,  e,  d1,  d2;
    vector<Double> yv, ev, d1v, d2v;
    vector< vector<Double> > g;
    vector< vector<Double> > gv;

    for (UInt4 i=0; i<x.size(); ++i) {
        y.push_back(func.eval(x.at(i)));
        e.push_back(func.evalError(x.at(i)));
        d1.push_back(func.der1st(x.at(i)));
        d2.push_back(func.der2nd(x.at(i)));
        g.push_back(func.gradient(x.at(i)));
    }

    yv =func.eval(x);
    ev =func.evalError(x);
    d1v=func.der1st(x);
    d2v=func.der2nd(x);
    gv =func.gradient(x);

    std::cout << setw(5)  << "No." << setw(10) << "x" ;
    std::cout << setw(15) << "single y"  << setw(15) << "vector y"  << setw(8) << "y==yv"   ;
    std::cout << setw(15) << "single e"  << setw(15) << "vector e"  << setw(8) << "e==ev"   ;
    std::cout << setw(15) << "single d1" << setw(15) << "vector d1" << setw(8) << "d1==d1v" ;
    std::cout << setw(15) << "single d2" << setw(15) << "vector d2" << setw(8) << "d2==d2v" << endl;
    for (UInt4 i=0; i<x.size(); ++i) {
        std::cout << setw(5)  << i << setw(10) << x.at(i) ;
        std::cout << setw(15) << y.at(i)  << setw(15) << yv.at(i)  << setw(8) << setiosflags(std::ios::boolalpha) << (y.at(i)==yv.at(i)) ;
        std::cout << setw(15) << e.at(i)  << setw(15) << ev.at(i)  << setw(8) << setiosflags(std::ios::boolalpha) << (e.at(i)==ev.at(i)) ;
        std::cout << setw(15) << d1.at(i) << setw(15) << d1v.at(i) << setw(8) << setiosflags(std::ios::boolalpha) << (d1.at(i)==d1v.at(i)) ;
        std::cout << setw(15) << d2.at(i) << setw(15) << d2v.at(i) << setw(8) << setiosflags(std::ios::boolalpha) << (d2.at(i)==d2v.at(i)) << endl;
    }
    std::cout << endl;

    std::cout << "gradient" << endl;
    // line header for gradient check
    std::cout << setw(5)  << "No." << setw(10) << "x" ;
    for (UInt4 j=0; j<g.at(0).size(); ++j) {
        std::cout << setw(10) << "single"  << setw(5) << j << setw(10) <<"vector"  << setw(5) << j << setw(8) << "s==yv"   ;
    }
    std::cout << endl;

    for (UInt4 i=0; i<g.size(); ++i) {
        std::cout << setw(5)  << i << setw(10) << x.at(i) ;
        for (UInt4 j=0; j<g.at(i).size(); ++j) {
            std::cout << setw(15) << g.at(i).at(j)  << setw(15) << gv.at(i).at(j)  << setw(8) << setiosflags(std::ios::boolalpha) << (g.at(i).at(j)==gv.at(i).at(j)) ;

        }
        std::cout << endl;
    }
    std::cout << endl;
    
    y.clear();  yv.clear();
    e.clear();  ev.clear();
    d1.clear(); d1v.clear();
    d2.clear(); d2v.clear();
    x.clear();
    covar.clear();
    param.clear();

    return EXIT_SUCCESS;
}
