#include "AdvTriangle.hh"
#include <cfloat>

#include <iostream>
#include <ostream>

#undef USE_POINTER

/** default constructor */
AdvTriangle::AdvTriangle() : AdvFuncBase(*(new string("triangle")), *(new string("tri")), NumberOfParamForTriangle) {
}

/** copy constructor */
AdvTriangle::AdvTriangle(const AdvTriangle &obj) {
}

/** destructor */
AdvTriangle::~AdvTriangle() {
}

/**
 *  evaluate the value of the functiosn
 */
Double AdvTriangle::eval(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (            x < t0 ) { retval = 0.0;                   }
    else if ( t0 <= x && x < t1 ) { retval = h*(x  - t0)/(t1 - t0); }
    else if ( t1 <= x && x < t2 ) { retval = h*(t2 - x )/(t2 - t1); }
    else   /* t2 <= x          */ { retval = 0.0;                   }
    return retval;
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvTriangle::der1st(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    //std::cout << "x=" << x << " h=" << h << " t0=" << t0 << " t1=" << t1 << " t2=" << t2 << endl;

    Double retval;
    if      (           x < t0 ) { retval =  0.0; }
    else if ( x == t0          ) { retval =  0.5*h/(t1 - t0); }
    else if ( t0 < x && x < t1 ) { retval =      h/(t1 - t0); }
    else if ( x == t1          ) { retval =  0.5*h*(1.0/(t1 - t0)-1.0/(t2 - t1)); }
    else if ( t1 < x && x < t2 ) { retval = -1.0*h/(t2 - t1); }
    else if ( x == t2          ) { retval = -0.5*h/(t2 - t1); }
    else   /* t2 < x       */    { retval =  0.0; }
    return retval;
}

Double AdvTriangle::der2nd(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (           x < t0 ) { retval =      0.0;     }
    else if ( x == t0          ) { retval =      DBL_MAX; }
    else if ( t0 < x && x < t1 ) { retval =      0.0;     }
    else if ( x == t1          ) { retval = -1.0*DBL_MAX; }
    else if ( t1 < x && x < t2 ) { retval =      0.0;     }
    else if ( x == t2          ) { retval =      DBL_MAX; }
    else   /* t2 < x       */    { retval =      0.0;     }
    return retval;
}

Double AdvTriangle::derH(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (            x < t0 ) { retval = 0.0; }
    else if ( t0 <= x && x < t1 ) { retval = (x   -t0)/(t1-t0); }
    else if ( t1 <= x && x < t2 ) { retval = (t2 - x )/(t2-t1); }
    else   /* t2 <= x       */    { retval = 0.0; }
    return retval;
}

Double AdvTriangle::derT0(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (            x < t0 ) { retval = 0.0; }
    else if ( t0 <= x && x < t1 ) { retval = -1.0*h*(t1-x)/pow(t1-t0, 2.0); }
    else if ( t1 <= x && x < t2 ) { retval = 0.0; }
    else   /* t2 <= x       */    { retval = 0.0; }
    return retval;
}

Double AdvTriangle::derT1(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (            x < t0 ) { retval = 0.0; }
    else if ( t0 <= x && x < t1 ) { retval = -1.0*h*(x   -t0)/pow(t1-t0, 2.0); }
    else if ( t1 <= x && x < t2 ) { retval =      h*(t2 - x )/pow(t2-t1, 2.0); }
    else   /* t2 <= x       */    { retval = 0.0; }
    return retval;
}

Double AdvTriangle::derT2(const Double x, const Double h, const Double t0, const Double t1, const Double t2) {

    assert ( t0 < t1);
    assert ( t1 < t2);

    Double retval;
    if      (            x < t0 ) { retval = 0.0; }
    else if ( t0 <= x && x < t1 ) { retval = 0.0; }
    else if ( t1 <= x && x < t2 ) { retval = h*(x - t1)/pow(t2-t1, 2.0); }
    else   /* t2 <= x       */    { retval = 0.0; }
    return retval;
}
#endif // HAVE_DIFFERENTIAL_MEMBER

#ifdef USE_POINTER
/**
 *  evaluate the valeu of the function
 *
 * \param[in] x argument
 * \param[in] p parameters
 *     p[3] : height
 *     p[0] < p[1] < p[2],
 */
Double AdvTriangle::eval(const Double x, const Double *p) {
    return this->eval(x, p[0], p[1], p[2], p[3]);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1si. defferential coefficient
 *
 * \param[in] x argument
 * \param[in] p parameters
 *     p[3] : height
 *     p[0] < p[1] < p[2],
 */
Double AdvTriangle::der1st(const Double x, const Double *p) {
    return this->der1st(x, p[0], p[1], p[2], p[3]);
}

/**
 *  evaluate the value of the 2nd. defferential coefficient
 *
 * \param[in] x argument
 * \param[in] p parameters
 *     p[3] : height
 *     p[0] < p[1] < p[2],
 */
Double AdvTriangle::der2nd(const Double x, const Double *p) {
    return this->der2nd(x, p[0], p[1], p[2], p[3]);
}

/**
 *  evaluate the gradient of the function for parameters
 *
 * \param[in] x argument
 * \param[in] p parameters
 *     p[3] : height
 *     p[0] < p[1] < p[2],
 */
Double *AdvTriangle::gradient(const Double x, const Double *p) {
    Double *g = new Double [NumberOfParamForTriangle];

    //g[0] = this->derH( x, p[0], p[1], p[2], p[3]);
    g[0] = this->eval( x, 1.0,  p[1], p[2], p[3]);
    g[1] = this->derT0(x, p[0], p[1], p[2], p[3]);
    g[2] = this->derT1(x, p[0], p[1], p[2], p[3]);
    g[3] = this->derT2(x, p[0], p[1], p[2], p[3]);

    return g;
}

#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_POINTER

#ifdef USE_VECTOR
Double AdvTriangle::eval(const Double x, const vector<Double> &p) {
    return this->eval(x, p[0], p[1], p[2], p[3]);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvTriangle::der1st(const Double x, const vector<Double> &p) {
    return this->der1st(x, p[0], p[1], p[2], p[3]);
}

Double AdvTriangle::der2nd(const Double x, const vector<Double> &p) {
    return this->der2nd(x, p[0], p[1], p[2], p[3]);
}

vector<Double> AdvTriangle::gradient(const Double x, const vector<Double> &p) {
    vector<Double> *g = new vector<Double>();

    //g->push_back( this->derH( x, p[0], p[1], p[2], p[3]) );
    g->push_back( this->eval( x, 1.0, p[1], p[2], p[3]) );
    g->push_back( this->derT0(x, p[0], p[1], p[2], p[3]) );
    g->push_back( this->derT1(x, p[0], p[1], p[2], p[3]) );
    g->push_back( this->derT2(x, p[0], p[1], p[2], p[3]) );

    return *g;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_VECTOR

