#include "ElementContainer.hh"
#include "AdvDomain.hh"
#include "AdvParamSet.hh"
#include "AdvMovingAverage.hh"

#include <typeinfo>

/** @author TANIMORI Souichirou, AdvanceSoft Corp.
 *  @version 0.0
 */

/** class name
 */
const std::string AdvMovingAverage::className=std::string("AdvMovingAverage");

/** keys for paramters
 *
 */
const std::string AdvMovingAverage::WINDOW_WIDTH=std::string("window width");
const std::string AdvMovingAverage::WINDOW_UPPER=std::string("window upper");
const std::string AdvMovingAverage::USE_STRICT_DEFINITION=std::string("use strict definition");

/**
 *  default values for paramters
 */
const Bool AdvMovingAverage::DEFAULT_USE_STRICT_DEFINITION=false;

AdvMovingAverage::AdvMovingAverage() : AdvMethod() {
    std::string memberName=std::string("AdvMovingAverage()");

    methodName="moving average";
    DebugMessage(className, memberName, "initialize methodName=%s\n", "moving average");

}

AdvMovingAverage::~AdvMovingAverage() {
}


AdvParamSet AdvMovingAverage::setDefaultParam(ElementContainer &src) {

    std::string memberName=std::string("setDefaultParam(ElementContainer&)");

    AdvParamSet *param = new AdvParamSet();

    UInt4 windowWidth = 0;

    UInt4 s=src.PutSize(src.PutYKey());
    if (  1 <= s && s <= 30 ) {
        windowWidth=3U;
    } else if ( 30 < s && s <= 100 ) {
        windowWidth=7U;
    } else if ( 100 < s && s <= 1000 ) {
        windowWidth=s/10;
    } else if ( 1000 < s ) {
        windowWidth=s/10;
    }
    if (windowWidth % 2 == 0) {
        windowWidth += 1;
    }

    param->add(WINDOW_WIDTH, windowWidth);
    param->add(WINDOW_UPPER, windowWidth/2);
    param->add(USE_STRICT_DEFINITION, DEFAULT_USE_STRICT_DEFINITION);

    return *param;
}

Bool AdvMovingAverage::checkParam(ElementContainer &src, AdvDomain &domain, AdvParamSet &param) {

    std::string memberName=std::string("checkParam(ElementContainer&, Domain&, ParamSer&)");

    DebugMessage(className, memberName, "enter\n");
    std::string xKey, yKey, eKey;

    UInt4 w;   // window width
    UInt4 wu;  // upper bound of window
    UInt4 wl;  // lower bound ot window

    UInt4 u; // uppber bound of domain
    UInt4 l; // lower bound of domain

    // the number of data
    xKey=src.PutXKey();
    yKey=src.PutYKey();
    eKey=src.PutEKey();

    if ( src.PutSize(xKey) < 2) {
        errorMessage(className, memberName, "no bin in the geiven element container as the source\n");
        return false;
    }
    if ( src.PutSize(yKey) < 1 ) {
        errorMessage(className, memberName, "no data in the geiven element container as the source\n");
        return false;
    }
    if ( src.PutSize(eKey) < 1 ) {
        errorMessage(className, memberName, "no error in the geiven element container as the source\n");
        return false;
    }
    if ( ! (src.PutSize(xKey) == src.PutSize(yKey)+1 && src.PutSize(xKey) == src.PutSize(eKey)+1 )) {
        errorMessage(className, memberName, "the number of data is not consistent\n");
        errorMessage(className, memberName, "size for %s: %u\n", xKey.c_str(), src.PutSize(xKey));
        errorMessage(className, memberName, "size for %s: %u\n", yKey.c_str(), src.PutSize(yKey));
        errorMessage(className, memberName, "size for %s: %u\n", eKey.c_str(), src.PutSize(eKey));
        return false;
    }

    l=domain.getLowerBoundID();
    u=domain.getUpperBoundID();
    //if ( ! ( 0 <= l && l <= u && u < src.PutSize(yKey) ) ) {
    if ( ! ( 0 <= l && l <= u && u <= src.PutSize(xKey) ) ) {
        errorMessage(className, memberName, "lowwe and upper bounds of domain are not consistent\n");
        errorMessage(className, memberName, "lower and upper bounds of domain: %u, %u\n", l, u);
        errorMessage(className, memberName, "number of data: %u\n", src.PutSize(yKey));
        return false;
    }

    // check window width
    if (! param.contain(WINDOW_WIDTH) ) {
        errorMessage(className, memberName, "no window width specified\n");
        return false;
    } else {
        w=param.getUInt4(WINDOW_WIDTH);
        if ( ! (w >= 1) ) {
            errorMessage(className, memberName, "window width must be equal to or lager than 1: user's width=%d\n", w);
            return false;
        }

        if ( src.PutSize(yKey) < w) {
            errorMessage(className, memberName, "window width %d is too wide for the element container that has %d data", w, src.PutSize(yKey));
            return false;
        }
    }

    // check upper bound of window
    if (! param.contain(WINDOW_UPPER) ) {
        wu = w/2;
        infoMessage(className, memberName, "set upper bound of window to %d (a half of window width) by default \n", wu);
    } else {
        wu=param.getUInt4(WINDOW_UPPER);
        if ( ! (0 <= wu && wu < w) ) {
            errorMessage(className, memberName, "offset of upper bound of window is out of range: %d\n", wu);
            errorMessage(className, memberName, "possibel value: 0 <= upper bound of window <= %d\n", w-1);
            return false;
        }
    }
    wl = w-wu-1;

    if ( param.contain(USE_STRICT_DEFINITION) ) {
        if ( param.getBool(USE_STRICT_DEFINITION) ) {
            infoMessage(className, memberName, "the functonality \"%s\"=%d is not implemented yet.\n", USE_STRICT_DEFINITION.c_str(), false);

            // check lower bound of domain
            if ( ! (0 <= l-wl && l-wl <= l) ) {
                errorMessage(className, memberName, "wrong lower bound of domain: %d\n", l);
                errorMessage(className, memberName, "the lower bound of domain must be larger  than %d\n", wl-1);
                return false;
            }

            // check upper bound of domain
            if ( ! ( u <= u+wu && u+wu < src.PutSize(xKey) ) ) {
                errorMessage(className, memberName, "wrong upper bound of domain: %d\n", u);
                errorMessage(className, memberName, "the upper bound of domain must be less than %d\n", src.PutSize(src.PutXKey())-wu);
                return false;
            }

            // chel lower and upper bound of domain
            //if ( ! (l <= u) ) {
            //    errorMessage(className, memberName, "lower bound of Domain: %u\n",  domain.getLowerBound());
            //    errorMessage(className, memberName, "upper bound of Domain: %u\n",  domain.getUpperBound());
            //    return false;
            //}
        }

    } else {
	;
    }
    return true;
}

void AdvMovingAverage::toInnerForm(ElementContainer &src, AdvDomain &domain, AdvParamSet &param) {

    std::string memberName=std::string("toInnerForm(ElementContainer &, Domain &, ParamSet &)");

    srcX  =src.Put(src.PutXKey());
    srcY  =src.Put(src.PutYKey());
    srcE  =src.Put(src.PutEKey());
    for (UInt4 i=0; i < srcX.size()-1; ++i) {
        srcXm.push_back( (srcX.at(i) + srcX.at(i+1))/2.0 );
    }

    lower=domain.getLowerBoundID();
    upper=domain.getUpperBoundID();

    windowWidth=param.getUInt4(AdvMovingAverage::WINDOW_WIDTH);
    windowUpper=param.getUInt4(AdvMovingAverage::WINDOW_UPPER);
    windowLower=windowWidth-windowUpper-1;

    //resultX=new std::vector<Double>();
    //for (Int4 i=lower; i<=upper; ++i) {
    //    resultX->push_back(srcX.at(i));
    //}
    resultX=domain.getBin();
    resultY=new std::vector<Double>();
    resultE=new std::vector<Double>();

    DebugMessage(className, memberName, "srcX.size()=%u\n", srcX.size());
    DebugMessage(className, memberName, "srcY.size()=%u\n", srcY.size());
    DebugMessage(className, memberName, "srcE.size()=%u\n", srcE.size());
    DebugMessage(className, memberName, "window width=%u\n", param.getUInt4(WINDOW_WIDTH));
    DebugMessage(className, memberName, "window upper=%u\n", param.getUInt4(WINDOW_UPPER));
    DebugMessage(className, memberName, "lower=%d\n", lower);
    DebugMessage(className, memberName, "upper=%d\n", upper);
    DebugMessage(className, memberName, "resultX->size()=%u\n", resultX->size());
    DebugMessage(className, memberName, "resultY->size()=%u\n", resultY->size());
    DebugMessage(className, memberName, "resultE->size()=%u\n", resultE->size());
}


/**
 *  do nothing
 */
void AdvMovingAverage::fit() {
}

void AdvMovingAverage::eval() {

    std::string memberName=std::string("eval()");

    resultY = do_moving_average(srcY);
    resultE = do_moving_average(srcE);
    /*
    Double sumY=0.0;
    Double sumE=0.0;
    for (UInt4 i=lower-windowLower; i<=lower+windowUpper; ++i) {
        sumY += srcY.at(i);
        sumE += srcE.at(i);
    }
    resultY->push_back(sumY/windowWidth);
    resultE->push_back(sumE/windowWidth);

    for (UInt4 i=lower+1; i<upper; ++i) {
        sumY -= srcY.at(i-1-windowLower);
        sumY += srcY.at(i  +windowUpper);
        resultY->push_back(sumY/windowWidth);

        sumE -= srcE.at(i-1-windowLower);
        sumE += srcE.at(i  +windowUpper);
        resultE->push_back(sumE/windowWidth);
    }
    */
    /*
    */

    /*
    Int4 ct=0;
    for (Int4 i=lower, ct=0; i < upper; ++i, ++ct) {
        Double sumY=0.0;
        Double sumE=0.0;
        for (Int4 j=i-windowLower; j <= i+windowUpper; ++j) {
            sumY += srcY.at(j);
            sumE += srcE.at(j);
        }
        resultY->push_back(sumY/windowWidth);
        resultE->push_back(sumE/windowWidth);

    }
    */

    //for (Int4 i=lower, ct=0; i < upper; ++i, ++ct) {
    //    std::printf("%5d [%5d, %5d] %23.15e %23.15e %23.15e %23.15e %23.15e\n",
    //        i,
    //        i-windowLower , i+windowUpper,
    //        (srcX.at(i)+srcX.at(i+1))/2.0, srcY.at(i), srcE.at(i),
    //        resultY->at(ct) , resultE->at(ct)
    //    );
    //}

    //std::cerr << "Debug: AdvMovingAverage::eval: type name of valueOfLikehood=" << typeid(valueOfLikehood).name() << std::endl;
    //std::cerr << "Debug: AdvMovingAverage::eval: type name of valueOfLikehood=" << &valueOfLikehood << std::endl;

    valueOfLikehood=likehood();
    //DebugMessage(className, memberName, "type name of valueOfLikehood: %s\n", typeid(valueOfLikehood).name());

}


void AdvMovingAverage::toElementContainer(ElementContainer &src, ElementContainer &dest) const {
    std::string memberName = std::string("toElementContainer(ElementContainer &, ElementContainer &) const");

    //DebugMessage(className, memberName, "size of resultX %n\n", resultX->size());
    std::cerr << "Debug: " << className << ": " << memberName << ": " << "size of resultX = " << resultX->size() << std::endl;
    std::cerr << "Debug: " << className << ": " << memberName << ": " << "size of resultY = " << resultY->size() << std::endl;
    std::cerr << "Debug: " << className << ": " << memberName << ": " << "size of resultE = " << resultE->size() << std::endl;

    dest.Add(src.PutXKey(), *resultX, src.PutUnit(src.PutXKey()));
    dest.Add(src.PutYKey(), *resultY, src.PutUnit(src.PutYKey()));
    dest.Add(src.PutEKey(), *resultE, src.PutUnit(src.PutEKey()));
    dest.SetKeys(src.PutXKey(), src.PutYKey(), src.PutEKey());
}

AdvParamSet AdvMovingAverage::getFittedParam() const {

    AdvParamSet *new_param=new AdvParamSet();

    new_param->add(WINDOW_WIDTH, windowWidth);
    new_param->add(WINDOW_UPPER, windowUpper);
    new_param->add(USE_STRICT_DEFINITION, DEFAULT_USE_STRICT_DEFINITION);

    return *new_param;
}


void AdvMovingAverage::getParamDescription() const {
    char fmt[] = "%s %s %s %s\n";
    std::printf(fmt, WINDOW_WIDTH.c_str(), "UInt4", "mandatory", "windw width for moving average");
    std::printf(fmt, WINDOW_UPPER.c_str(), "Int4",  "mandatory", "offset of windoe upper bound for the position where moving average is calcurateed");
}

Double AdvMovingAverage::likehood() {
    std::string memberName=std::string("likehood()");

    Double sum=0.0;
    for (Int4 i=lower, ct=0; i < upper; ++i, ++ct) {
        sum += pow( (srcY.at(i) - resultY->at(ct))/srcE.at(i), 2.0);
    }
    DebugMessage(className, memberName, "sum   =%23.15e\n", sum);
    DebugMessage(className, memberName, "chi_sq=%23.15e\n", sum/(resultY->size()));

    return sum/(resultY->size());
}

/*
std::vector< std::vector<Double> > AdvMovingAverage::getTrend() {

    std::string memberName=std::string("getTrend()");

    std::vector< std::vector<Double> > *trend = new std::vector< std::vector<Double> >();
    trend->clear();

    // moving average
    std::vector<Double> *v0=new std::vector<Double>();
    v0->clear();
    Double sumY=0.0;
    for (UInt4 i=lower-windowLower; i<=lower+windowUpper; ++i) {
        sumY += srcY.at(i);
    }
    v0->push_back(sumY/windowWidth);
    for (UInt4 i=lower+1; i<upper; ++i) {
        sumY += srcY.at(i  +windowUpper) - srcY.at(i-1-windowLower);
        v0->push_back(sumY/windowWidth);
    }
    trend->push_back(*v0);

    //for (UInt4 i=0; i<v0->size(); ++i) {
    //   DebugMessage(std::string(typeid(this).name()), memberName, "%5u, v0=%24.15e, resultY=%24.15e\n", i, v0->at(i), resultY->at(i));
    //}

    std::vector<Double> *dq=new std::vector<Double>(); // difference quotient
    dq->clear();

    // differnece quotient: deltaY(i)/deltaX(i)=[Y(i+1) - Y(i)[/[Xm(i+1)-Xm(i)]
    //DebugMessage(std::string(typeid(this).name()), memberName, "dq->size()=%5d\n", dq->size());
    //switch (diff_type) {
    //    case FORWARD_DIFFERENCE:
    //        for (UInt4 i=0; i < 1; ++i) {
    //            dq->push_back( 2.0*(v0->at(i+1) - v0->at(i)) / (srcX.at(lower+i+2) - srcX.at(lower+i)) );
    //            //DebugMessage(std::string(typeid(this).name()), memberName, "lower+i=%5u, dq(%5u)=%23.15e\n", lower+i, i, dq->at(i));
    //        }
    //        break;
    //    case CENTRAL_DIFFERENCE:
    UInt4 offset=lower;
    for (UInt4 i=1; i < (v0->size())-1; ++i) {
        dq->push_back( 2.0*(v0->at(i+1) - v0->at(i-1)) / ((srcX.at(offset+i+2) - srcX.at(offset+i)) + (srcX.at(offset+i+1) - srcX.at(offset+i-1))) );
        //DebugMessage(std::string(typeid(this).name()), memberName, "lower+i=%5u, dq(%5u) =%23.15e\n", lower+i, i-1, dq->at(i-1));
    }
    //        break;
    //    case BACKWARD_DIFFERENCE:
    //        for (UInt4 i=1; i < v0->size(); ++i) {
    //            dq->push_back( 2.0*(v0->at(i) - v0->at(i-1)) / (srcX.at(lower+i+1) - srcX.at(lower+i-1)) );
    //            DebugMessage(std::string(typeid(this).name()), memberName, "lower+i=%5u, dq(%5u)=%23.15e\n", lower+i, i, dq->at(i));
    //        }
    //        break;
    //}
    //DebugMessage(std::string(typeid(this).name()), memberName, "dq->size()=%5d\n", dq->size());


    std::vector<Double> *v1=new std::vector<Double>(); // moving average of difference quotient
    v1->clear();
    Double sumDq=0.0;
    for (UInt4 i=0; i<windowWidth; ++i) {
        sumDq += dq->at(i);
    }
    v1->push_back(sumDq/windowWidth);

    for (UInt4 i=1; i < dq->size() - (windowWidth-1); ++i) {
        sumDq += dq->at(i+windowWidth-1) - dq->at(i-1);
        v1->push_back(sumDq/windowWidth);
    }
    trend->push_back(*v1);

    //for (UInt4 i=0; i<v1->size(); ++i) {
    //    DebugMessage(std::string(typeid(this).name()), memberName, "%5u, v1=%24.15e\n", i, v1->at(i));
    //}
    //DebugMessage(std::string(typeid(this).name()), memberName, "v1->size()=%5d\n", v1->size());


    std::vector<Double> *dq2 = new std::vector<Double>(); // difference quotient for 2nd drivative
    dq2->clear();

    offset=lower+(windowWidth-windowUpper-1); // windowLower
    for (UInt4 i=1; i < (v1->size())-1; ++i) {
        dq2->push_back( 2.0*(v1->at(i+1) - v1->at(i-1)) / ((srcX.at(offset+i+2) - srcX.at(offset+i)) + (srcX.at(offset+i+1) - srcX.at(offset+i-1))) );
        //DebugMessage(std::string(typeid(this).name()), memberName, "offset+i=%5u, dq2(%5u)=%23.15e\n", offset+i, i-1, dq2->at(i-1));
    }
    //DebugMessage(std::string(typeid(this).name()), memberName, "dq2->size()=%5d\n", dq2->size());

    std::vector<Double> *v2 = new std::vector<Double>();
    v2->clear();
    Double sumDq2=0.0;
    for (UInt4 i=0; i<windowWidth; ++i) {
        sumDq2 += dq2->at(i);
        //DebugMessage(std::string(typeid(this).name()), memberName, "i=%5u, sumDq2=%23.15e\n", i, sumDq2);
    }
    v2->push_back(sumDq2/windowWidth);

    for (UInt4 i=1; i < (dq2->size()) - (windowWidth-1); ++i) {
        sumDq2 += dq2->at(i+windowWidth-1) - dq2->at(i-1);
        v2->push_back(sumDq2/windowWidth);
    }
    offset=lower+2*(windowWidth-windowUpper-1U)+1U; // windowLower
    for (UInt4 i=0; i < v2->size(); ++i) {
        //DebugMessage(std::string(typeid(this).name()), memberName, "offset+i=%5u, v2(%5u)=%23.15e\n", offset+i, i, v2->at(i));
    }
    trend->push_back(*v2);

    return *trend;
}
*/

std::vector< std::vector<Double> > AdvMovingAverage::getTrend() {

    std::string memberName=std::string("getTrend()");

    DebugMessage(className, memberName, "getTrend\n");
    std::vector< std::vector<Double> > *trend = new std::vector< std::vector<Double> >();
    trend->clear();

    // moving average

    if ( resultY->empty() ) {
	eval();
    }
    //std::vector<Double> *v0 = do_moving_average(srcY);
    std::vector<Double> *v0 = new std::vector<Double>(resultY->begin(), resultY->end());
    trend->push_back(*v0);
    DebugMessage(className, memberName, "0th. order diff\n");

    // moving average of 1st. difference quotient
    std::vector<Double> *dq = diff_quot(srcXm, *v0);
    std::vector<Double> *v1 = do_moving_average(*dq);
    DebugMessage(className, memberName, "1st. order diff\n");
    trend->push_back(*v1);
    DebugMessage(className, memberName, "averaged 1st. order diff\n");

    // moving average of 2nd. difference quotient
    std::vector<Double> *dq2 = diff_quot(srcXm, *v1);
    std::vector<Double> *v2 = do_moving_average(*dq2);
    trend->push_back(*v2);
    DebugMessage(className, memberName, "averaged 2nd. order diff\n");

    char tit[]="%5s %24s %10s %23s %14s %5s %23s %23s %23s\n";
    char fmt[]="%5u [%10.5f, %10.5f) %10.5f %23.15e [%5u, %5u] %5u %23.15e %23.15e %23.15e\n";
    DebugMessage(className, memberName, tit, \
        "No.", "bin", "mid", "original", \
        "window range", "width", "average", "1st. diff", "2nd. diff" \
    );
    //Double xm = 0.0;
    for (Int4 i=0; i<windowLower; ++i) {
        Double xm=(srcX.at(i)+srcX.at(i+1))/2.0;
        DebugMessage(className, memberName, fmt, \
            i, srcX.at(i), srcX.at(i+1), xm, srcY.at(i), \
            i-i, i+windowUpper, i+windowUpper+1, trend->at(0).at(i), trend->at(1).at(i), trend->at(2).at(i) \
        );
    }
    for (UInt4 i=windowLower; i<srcY.size()-windowUpper; ++i) {
        Double xm=(srcX.at(i)+srcX.at(i+1))/2.0;
        DebugMessage(className, memberName, fmt, \
            i, srcX.at(i), srcX.at(i+1), xm, srcY.at(i), \
            i-windowLower, i+windowUpper, windowLower+windowUpper+1, trend->at(0).at(i), trend->at(1).at(i), trend->at(2).at(i) \
        );
   }
   for (UInt4 i=srcY.size()-windowUpper; i<srcY.size(); ++i) {
        Double xm=(srcX.at(i)+srcX.at(i+1))/2.0;
        DebugMessage(className, memberName, fmt, \
            i, srcX.at(i), srcX.at(i+1), xm, srcY.at(i), \
            i-windowLower, srcY.size()-1, srcY.size()+windowLower-i, trend->at(0).at(i), trend->at(1).at(i), trend->at(2).at(i) \
        );
   }

    return *trend;
}

std::vector<Double> *AdvMovingAverage::do_moving_average(std::vector<Double> &srcY) {

    std::string memberName=std::string("do_moving_average(std::vector<Double>, Int4, Int4)");

    Int4 b0=0U;
    Int4 b1=  windowLower;
    //UInt4 b2=2*windowLower;
    //UInt4 b3=3*windowLower;
    //UInt4 b4=srcY.size() - 3*windowUpper;
    //UInt4 b5=srcY.size() - 2*windowUpper;
    Int4 b6=srcY.size() -   windowUpper;
    Int4 b7=srcY.size();

    Double sumY;
    Int4 i;


    //DebugMessage(className, memberName, "enter\n");
    std::vector<Double> *v=new std::vector<Double>();
    v->clear();
    //DebugMessage(className, memberName, "v->clear()\n");

    sumY=0.0;
    for (i=0U; i<=windowUpper; ++i) {
        sumY += srcY.at(i);
    }
    //DebugMessage(className, memberName, "initial summention\n");
    v->push_back( sumY/(windowUpper+1) );
    //DebugMessage(className, memberName, "loop0 initial width i=%5u, v=%23.15e\n", 0, v->at(0));
    for (i=b0+1U; i<=b1; ++i) {
        sumY += srcY.at(windowUpper+i);
        v->push_back( sumY/(windowUpper+i+1) );
        //DebugMessage(className, memberName, "loop1 variable width i=%5u, v=%23.15e\n", i, v->at(i));
    }
    for (i=b1+1; i<b6; ++i) {
        sumY += srcY.at(i+windowUpper) - srcY.at(i-windowLower-1);
        v->push_back( sumY/windowWidth );
        //DebugMessage(className, memberName, "loop2 strict width i=%5u, v=%23.15e\n", i, v->at(i));
    }
    for (i=b6; i<b7; ++i) {
        sumY -= srcY.at(i-windowLower-1U);
        v->push_back( sumY/(windowWidth - (i-b6)) );
        //DebugMessage(className, memberName, "loop3 variable width i=%u, v=%23.15e\n", i, v->at(i));
    }

    return v;
}

std::vector<Double> *AdvMovingAverage::diff_quot(std::vector<Double> &x, std::vector<Double> &y) {

    std::string memberName="diff_quot(std::vector<Double>, std::vector<Double>)";

    //DebugMessage(className, memberName, "enter\n");
    std::vector<Double> *dq=new std::vector<Double>();
    dq->clear();
    //DebugMessage(className, memberName, "initialize\n");

    UInt4 i;

    i=0U;
    dq->push_back( (y.at(i+1U) - y.at(i))/(x.at(i+1U) - x.at(i)) ); // forward difference quotient
    //DebugMessage(className, memberName, "forward diff i=%5u dq=%23.15e\n", i, dq->at(i));
    for (i=1; i<y.size()-1; ++i) {
        dq->push_back( (y.at(i+1) - y.at(i-1))/(x.at(i+1) - x.at(i-1)) ); // central difference quotient
        //DebugMessage(className, memberName, "central diff i=%5u dq=%23.15e\n", i, dq->at(i));
    }
    i=y.size()-1;
    dq->push_back( (y.at(i) - y.at(i-1))/(x.at(i) - x.at(i-1)) ); // backward difference quotient
    //DebugMessage(className, memberName, "backward diff i=%5u %23.15e\n", i, dq->at(i));

    return dq;
}
