#include "AdvAdditionalData.hh"

const string AdvFuncEvalBase::className=string("AdvFuncEvalBase");
const string AdvAdditionalData::className=string("AdvAdditionalData");

/** constructor
 */
AdvFuncEvalBase::AdvFuncEvalBase() {
    this->f = NULL; 
    this->x = NULL;
    this->w = NULL;
}

/** constructor
 *
 *  @apram[in] useWight  the flag whether use data weigths or not
 *  @param[in] nSeties   the number of data series
 *  @param[in] funcList  the list of the function elements for fitting
 *  @param[in] src       a element container as the source
 *  @param[in] AdvDomain    the AdvDomain for fitting
 */
AdvFuncEvalBase::AdvFuncEvalBase(Bool useWeight, UInt4 nSeries, vector<AdvFuncBase*> &funcList, ElementContainer &src, AdvDomain &AdvDomain) {
    string memberName="AdvFuncEvalBase(Bool&, vector<AdvFuncBase*>&, ElementContainer&, AdvDomain&)";
    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");
    DebugMessage(className, memberName, __FILE__, __LINE__, "nSeries: %d\n", nSeries);

    this->f = new AdvFuncComb(funcList);

    //DebugMessage(className, memberName, __FILE__, __LINE__, "fitting function: %d\n", this->f->getNumberOfComponents());

    this->bounds = AdvDomain.getBinBounds();
    DebugMessage(className, memberName, __FILE__, __LINE__, "lower and upper bounds: (%f, %f)\n", this->bounds.front(), this->bounds.back());

    this->x = AdvDomain.createXC();
    //DebugMessage(className, memberName, __FILE__, __LINE__, "the number of data points: %u\n", this->x->size());

    this->w = new vector<Double>(AdvDomain.getNumberOfBin());
    string key=src.PutEKey();

    UInt4 AdvDomainBegin=AdvDomain.getLowerBinID();
    UInt4 AdvDomainEnd  =AdvDomain.getUpperBinID();
    DebugMessage(className, memberName, __FILE__, __LINE__, "lower=%d upper=%d\n", AdvDomainBegin, AdvDomainEnd);
    DebugMessage(className, memberName, __FILE__, __LINE__, "useWeigth=%u\n", useWeight);
    if (useWeight) {
        UInt4 dof=this->x->size() - this->f->getNumberOfRequiredParam(); // degree of freedom
        Double c=1.0/sqrt(nSeries*dof);
        DebugMessage(className, memberName, __FILE__, __LINE__, "c=%23.15e, 1.0/sqrt(dof)=%23.15e\n", c, 1.0/sqrt(dof));
        for (UInt4 i=AdvDomainBegin; i<=AdvDomainEnd; ++i) {
            this->w->at(i-AdvDomainBegin)=c/src.Put(key, i);
            //DebugMessage(className, memberName, "i=%u: e=%f w=%f\n", i, e.at(i), this->w->at(i-AdvDomainBegin));
        }
    } else {
        Double sum=0.0;
        for (UInt4 i=AdvDomainBegin; i<=AdvDomainEnd; ++i) {
            sum += pow(src.Put(key, i), 2.0);
        }
        Double c=1.0/sqrt(nSeries*sum);
        DebugMessage(className, memberName, __FILE__, __LINE__, "c=%23.15e\n", c);
        for (UInt4 i=0; i<AdvDomainEnd-AdvDomainBegin+1; ++i) {
            this->w->at(i)=c;
        }
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}

/** constructor w link ids
 *
 *  @apram[in] useWight  the flag whether use data weigths or not
 *  @param[in] nSeties   the number of data series
 *  @param[in] funcList  the list of the function elements for fitting
 *  @param[in] src       a element container as the source
 *  @param[in] AdvDomain    the AdvDomain for fitting
 */
AdvFuncEvalBase::AdvFuncEvalBase(Bool useWeight, UInt4 nSeries, vector<AdvFuncBase*> &funcList, ElementContainer &src, AdvDomain &AdvDomain, vector<Double> p) {
    string memberName="AdvFuncEvalBase(Bool&, vector<AdvFuncBase*>&, ElementContainer&, AdvDomain&)";
    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");
    DebugMessage(className, memberName, __FILE__, __LINE__, "nSeries: %d\n", nSeries);

    //cout << "koko-set-AddData-FEB" << endl;
    this->f = new AdvFuncComb(funcList,"wlink",p);


    //DebugMessage(className, memberName, __FILE__, __LINE__, "fitting function: %d\n", this->f->getNumberOfComponents());

    this->bounds = AdvDomain.getBinBounds();
    DebugMessage(className, memberName, __FILE__, __LINE__, "lower and upper bounds: (%f, %f)\n", this->bounds.front(), this->bounds.back());

    this->x = AdvDomain.createXC();
    //DebugMessage(className, memberName, __FILE__, __LINE__, "the number of data points: %u\n", this->x->size());

    this->w = new vector<Double>(AdvDomain.getNumberOfBin());
    string key=src.PutEKey();

    UInt4 AdvDomainBegin=AdvDomain.getLowerBinID();
    UInt4 AdvDomainEnd  =AdvDomain.getUpperBinID();
    DebugMessage(className, memberName, __FILE__, __LINE__, "lower=%d upper=%d\n", AdvDomainBegin, AdvDomainEnd);
    DebugMessage(className, memberName, __FILE__, __LINE__, "useWeigth=%u\n", useWeight);
    if (useWeight) {
        UInt4 dof=this->x->size() - this->f->getNumberOfRequiredParam(); // degree of freedom
        Double c=1.0/sqrt(nSeries*dof);
        DebugMessage(className, memberName, __FILE__, __LINE__, "c=%23.15e, 1.0/sqrt(dof)=%23.15e\n", c, 1.0/sqrt(dof));
        for (UInt4 i=AdvDomainBegin; i<=AdvDomainEnd; ++i) {
            this->w->at(i-AdvDomainBegin)=c/src.Put(key, i);
            //DebugMessage(className, memberName, "i=%u: e=%f w=%f\n", i, e.at(i), this->w->at(i-AdvDomainBegin));
        }
    } else {
        Double sum=0.0;
        for (UInt4 i=AdvDomainBegin; i<=AdvDomainEnd; ++i) {
            sum += pow(src.Put(key, i), 2.0);
        }
        Double c=1.0/sqrt(nSeries*sum);
        DebugMessage(className, memberName, __FILE__, __LINE__, "c=%23.15e\n", c);
        for (UInt4 i=0; i<AdvDomainEnd-AdvDomainBegin+1; ++i) {
            this->w->at(i)=c;
        }
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}



/** destructor
 */
AdvFuncEvalBase::~AdvFuncEvalBase() {
    if (this->w != NULL) { this->w->clear(); delete this->w; }
    if (this->x != NULL) { this->x->clear(); delete this->x; }
    if (this->f != NULL) { delete this->f;                   }
}

/** check whether valid or not
 *  @return true if valid
 */
Bool AdvFuncEvalBase::check() {
    string memberName=string("check()");

    DebugMessage(className, memberName, "f: %p x: %p w: %p\n", this->f, this->x, this->w);
    return (this->f != NULL) && (this->x != NULL) && (this->w != NULL) && (this->x->size() == this->w->size());
}

/** set the parameters for fitting
 *  @param[in] param the list of fitting parameters
 */
void AdvFuncEvalBase::set(vector<Double> &param) {
    this->f->set(param);
}

/** evaluates the values of fitting function
 *  @return the values of fitting function
 */
vector<Double> AdvFuncEvalBase::eval() {
    vector<Double> v=this->f->eval(*(this->x));
    for (UInt4 i=0; i<v.size(); ++i) {
        v.at(i) *= this->w->at(i);
    }
    return v;
}

void AdvFuncEvalBase::eval(Int4 n, Double y[]) {
    vector<Double> v=this->f->eval(*(this->x));
    for (UInt4 i=0; i<v.size(); ++i) {
        y[i]=v.at(i)*this->w->at(i);
    }
}

/** evaluates the gradient of the fitting function for fitting parameters
 *  @return the gradienet at each x cooridnates as the Jacobi matirx
 */
vector< vector<Double> > AdvFuncEvalBase::gradient() {
    string memberName=string("gradient()");

    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");

//    std::cerr << className << "::" << memberName << ", " << __FILE__ << ", " << __LINE__ << ", " << "enter" << endl;
    vector< vector<Double> > g;
//    std::cerr << className << "::" << memberName << ", " << __FILE__ << ", " << __LINE__ << ", " << "g.size()=" << g.size() << endl;
    g=this->f->gradient(*(this->x));
//    std::cerr << className << "::" << memberName << ", " << __FILE__ << ", " << __LINE__ << ", " << "g.size()=" << g.size() << endl;
//    std::cerr << className << "::" << memberName << ", " << __FILE__ << ", " << __LINE__ << ", " << "gradient" << endl;
    for (UInt4 i=0; i<g.size(); ++i) {
        Double cwi=this->w->at(i);
        for (UInt4 j=0; j<g.at(i).size(); ++j) {
            g.at(i).at(j) *= cwi;
        }
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
//    std::cerr << className << "::" << memberName << ", " << __FILE__ << ", " << __LINE__ << ", " << "exit" << endl;
    return g;
}

void AdvFuncEvalBase::evalJacobi(Int4 n, Double jac[]) {
    vector< vector<Double> > g=this->f->gradient(*(this->x));
    for (UInt4 i=0; i<g.size(); ++i) {
        Double cwi=this->w->at(i);
        for (UInt4 j=0; j<g.at(i).size(); ++j) {
            jac[i*g.at(i).size()+j]=g.at(i).at(j) * cwi;
        }
    }
}

/** get the both bounds of the AdvDomain
 *  @return the the both bounds of the AdvDomain
 */
vector<Double> *AdvFuncEvalBase::getDomainBounds() {
    return new vector<Double>(this->bounds);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/** constructor
 *  @param[in] control the control parameters for Levmar (@see AdvLevmarControl)
 *  @param[in] src     a element container as source data
 *  @param[in] AdvDomain  the AdvDomain for fitting
 *  @param[in] param   parameters for fitting
 */
AdvAdditionalData::AdvAdditionalData(AdvLevmarControl &control, ElementContainer &src, AdvDomain &AdvDomain, AdvParamSet &param) {
    string memberName="AdvAdditionalData(AdvLevmarControl&, ElementContainer&, AdvDomain& AdvParamSet&)";
    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");

    this->useWeight=control.useDataWeights;
    this->nSeries=1;
    vector<AdvFuncBase*> funcList=param.getFuncList(AdvLevmarConsts::FUNCTIONS);


    //cout << "koko-set-AddData-Add-Data" << endl;
    Int4 n = param.getVectorSize(AdvLevmarConsts::LINK_IDS);
    Int4 np = param.getVectorSize(AdvLevmarConsts::PARAMETER_VALUES);
    //cout << "koko-set-AddData-Add-Data, " <<n<<","<<np<< endl;
    if (n==np){
        vector<Double> p;;
        for (Int4 i=0; i<n; ++i) {
            p.push_back(param.getDouble(AdvLevmarConsts::LINK_IDS, i));
        //    cout << "koko-set-AddData-Add-Data-lk,"<<i<<","<<p.at(i) << endl;
          }
        this->funcEvalBaseList = new vector<AdvFuncEvalBase>(1, *(new AdvFuncEvalBase(this->useWeight, 1, funcList, src, AdvDomain,p)));
     }
    else{
        this->funcEvalBaseList = new vector<AdvFuncEvalBase>(1, *(new AdvFuncEvalBase(this->useWeight, 1, funcList, src, AdvDomain)));
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}


/** constructor
 *  @param[in] control     the control parameters for Levmar (@see AdvLevmarControl)
 *  @param[in] eca         a array of element containers as sources for multi data fitting
 *  @param[in] AdvDomainArray a array of AdvDomains for the given containers
 *  @param[in] param       the set of parameter for multi data fitting
 */
AdvAdditionalData::AdvAdditionalData(AdvLevmarControl &control, ElementContainerArray &eca, vector<AdvDomain> &AdvDomainArray,  AdvParamSet &param) {
    string memberName="AdvAdditionalData(AdvLevmarControl&, ElementContainerArry&, vector<AdvDomain>& AdvParamSet&)";

    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");
    UInt4 n=eca.PutSize();
    this->useWeight=control.useDataWeights;
    this->nSeries=n;
    DebugMessage(className, memberName, __FILE__, __LINE__, "the number of Data series %d\n", this->nSeries);
    this->funcEvalBaseList = new vector<AdvFuncEvalBase>(n);
    vector< vector<AdvFuncBase*> > funcMatrix = param.getFuncMatrix(AdvLevmarConsts::FUNCTIONS);
    if ( this->nSeries != funcMatrix.size() ) {
        errorMessage(className, memberName, __FILE__, __LINE__, "invalid argument\n");
    }
    for (UInt4 i=0; i<n; ++i) {
        ElementContainer ec=eca.Put(i);
        this->funcEvalBaseList->at(i) = *(new AdvFuncEvalBase(control.useDataWeights, this->nSeries, funcMatrix.at(i), ec, AdvDomainArray.at(i)));
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}

/** destructor
 */
AdvAdditionalData::~AdvAdditionalData() {
    if (this->funcEvalBaseList != NULL) { this->funcEvalBaseList->clear(); delete this->funcEvalBaseList; }
}

/** check whether no data or not
 *  @retval true if no data
 */
Bool AdvAdditionalData::empty() const {
    return ((this->funcEvalBaseList != NULL) && (this->funcEvalBaseList->empty()));
}

/** check whether all data are valid
 */
Bool AdvAdditionalData::check() {
    string memberName=string("check()");
    Bool retval=true;
    //for (std::vector<AdvFuncEvalBase>::const_iterator elem=this->funcEvalBaseList->iterator(); elem != this->funcEvalBaseList->end(); ++elem) {
    if (this->funcEvalBaseList != NULL) {
        for (UInt4 i=0; i<this->funcEvalBaseList->size(); ++i) {
            DebugMessage(className, memberName, __FILE__, __LINE__, "the %u-th. data\n", i );
            retval = retval && this->funcEvalBaseList->at(i).check();
            if (! retval) {
                errorMessage(className, memberName, __FILE__, __LINE__, "the %u-th. function data\n", i );
            }
        }
    }
    return retval;
}

/** get the number of data series
 */
UInt4 AdvAdditionalData::getNumberOfSeries() const {
    //return this->nSeries;
    return this->funcEvalBaseList->size();
}

/** get the function that i-th. data includes
 */
AdvFuncComb *AdvAdditionalData::getFunc(UInt4 i) {
    return this->funcEvalBaseList->at(i).f;
}

/** get the number of the data points (bins) for the i-th data series.
 * @param[in] i the i-th data series
 */
UInt4 AdvAdditionalData::getNumberOfData(UInt4 i) const {
    return this->funcEvalBaseList->at(i).x->size(); // OK, instead of w->size()}
}

/** get the bounds of the AdvDomain for the i-th data series.
 * @param[in] i the i-th data series
 */
vector<Double> *AdvAdditionalData::getDomainBounds(UInt4 i) const {
    return this->funcEvalBaseList->at(i).getDomainBounds();
}

/** get the array of x coordinates that the i-th data includes
 * @param[in] i the i-th data series
 */
vector<Double> *AdvAdditionalData::getX(UInt4 i) {
    return this->funcEvalBaseList->at(i).x;
}

/** get the array of data weights that the i-th data includes
 * @param[in] i the i-th data series
 */
vector<Double> *AdvAdditionalData::getWeight(UInt4 i) {
    return this->funcEvalBaseList->at(i).w;
}

/** set the list of the fitting parameters for all data series
 * @param[in] param the values of fitting parameters
 */
void AdvAdditionalData::set(vector<Double> &param) {
    for (UInt4 i=0; i<this->getNumberOfSeries(); ++i) {
        this->funcEvalBaseList->at(i).set(param);
    }
//cout << "koko-2-,"<<this->linklist[0] << endl;
}

/** evalate the values for all fitting functions related with each data series
 * @param[in]  ndata the size of the buffer area
 * @param[out] y     the evaluated values of fitting functions
 */
void AdvAdditionalData::eval(Int4 ndata, Double *y) {
    string memberName=string("eval(Int4, Double *)");

    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");
    Int4 offset=0;
    for (UInt4 i=0; i<this->getNumberOfSeries(); ++i) {
        vector<Double> v=this->funcEvalBaseList->at(i).eval();
        for (UInt4 j=0; j<v.size(); ++j) {
            y[offset+j]=v.at(j);
        }
        offset += v.size();
        DebugMessage(className, memberName, __FILE__, __LINE__, "offset=%d\n", offset);
    }
    if (offset != ndata) {
        errorMessage(className, memberName, __FILE__, __LINE__, "invalid array size: %d\n", offset);
    }
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}

/** evaluate Jacobian matrix for all fitting functions related with each data series
 * @param[in]  n      the row size
 * @param[in]  nParam the number of the fitting parameters
 * @param[out] jac    the evaluated Jacobi matrix (n*nParam)
 */
void AdvAdditionalData::evalJacobian(Int4 n, Int4 nParam, Double *jac) {
    string memberName=string("evalJacobian(Int4, Int4, Double *)");

    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");
//    std::cerr << "AdvAdditionalData::evalJacobian(Int4, Ing4, Double*): enter" << endl;
    UInt4 offset=0;
    for (UInt4 i=0; i<this->getNumberOfSeries(); ++i) {
//        std::cerr << "AdvAdditionalData::evalJacobian(Int4, Ing4, Double*): series=" << i << endl;
        vector< vector<Double> > g=this->funcEvalBaseList->at(i).gradient();
//        std::cerr << "AdvAdditionalData::evalJacobian(Int4, Ing4, Double*): gradient i=" << i << endl;
        for (UInt4 j=0; j<g.size(); ++j) {
            for (UInt4 k=0; k<g.at(j).size(); ++k) {
                jac[(offset+j)*nParam+k]=g.at(j).at(k);
            }
        }
        offset += g.size();
        DebugMessage(className, memberName, __FILE__, __LINE__, "offset=%d\n", offset);
//        std::cerr << "AdvAdditionalData::evalJacobian(Int4, Ing4, Double*): offset=" << offset << endl;
    }
//    std::cerr << "AdvAdditionalData::evalJacobian(Int4, Ing4, Double*): exit" << endl;
    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}

#include <iostream>
#include <ios>
#include <iomanip>

/** output
 *  @param[in] indentWidth the indent width
 */
void AdvAdditionalData::output(UInt4 indentWidth) {

    /*
    std::cout << "Source(s)" << endl;

    std::cout << setw(indentWidth) << " ";
    std::cout << setw(24) << "use weights";
    std::cout << " : ";
    std::cout << setw(5)  << setiosflags(std::ios::boolalpha) << this->useWeight;
    std::cout << endl;
    std::cout << endl;
    */

    std::cout << setw(indentWidth) << " ";
    std::cout << setw(5) << setiosflags(std::ios::right) << "No.";
    std::cout << " ";
    std::cout << setw(5) << setiosflags(std::ios::right) << "point";
    std::cout << " ";
    std::cout << setw(50) << setiosflags(std::ios::right) << "--------------------- AdvDomain ---------------------";
    std::cout << " ";
    std::cout << setw(46) << setiosflags(std::ios::right) << "------------------ weight ------------------";
    std::cout << " ";
    std::cout << setw(8) << setiosflags(std::ios::right) << "function";
    std::cout << endl;

    //for(UInt4 i=0; i<this->nSeries; ++i) {
    for(UInt4 i=0; i<this->getNumberOfSeries(); ++i) {
        std::cout << setw(indentWidth) << " ";
        std::cout << setw(5) << setiosflags(std::ios::right) << i;
        std::cout << " ";
        std::cout << setw(5) << setiosflags(std::ios::right) << this->getNumberOfData(i);
        std::cout << " ";
        std::cout << "[";
        std::cout << setw(23) << setprecision(15) << setiosflags(std::ios::scientific) << setiosflags(std::ios::right) << this->getDomainBounds(i)->front();
        std::cout << ", ";
        std::cout << setw(23) << setprecision(15) << setiosflags(std::ios::scientific) << setiosflags(std::ios::right) << this->getDomainBounds(i)->back();
        std::cout << "]" ;
        std::cout << " ";
        std::cout << setw(23) << setprecision(15) << setiosflags(std::ios::scientific) << setiosflags(std::ios::right) << this->getWeight(i)->front();
        std::cout << setw(23) << setprecision(15) << setiosflags(std::ios::scientific) << setiosflags(std::ios::right) << this->getWeight(i)->back();
        std::cout << " ";

        
        std::cout << this->getFunc(i)->getSymbol(0);
        UInt4 s=this->getFunc(i)->getNumberOfRequiredParam(0);
        for (UInt4 j=1; j<this->getFunc(i)->getNumberOfComponents(); ++j) {
            std::cout << " ";
            std::cout << this->getFunc(i)->getSymbol(j);
            s += this->funcEvalBaseList->at(i).f->getNumberOfRequiredParam(j);
        }
        std::cout << " (";
        std::cout << s;
        std::cout << ")";
        std::cout << endl;
    }
    std::cout << endl;
}


