// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: numfuncs.cpp 326 2007-08-01 05:54:03Z wojdyr $

#include "common.h"
#include "numfuncs.h"

#include <algorithm>

using namespace std;

vector<PointQ>::iterator 
get_interpolation_segment(vector<PointQ> &bb,  fp x)
{
    //optimized for sequence of x = x1, x2, x3, x1 < x2 < x3...
    static vector<PointQ>::iterator pos = bb.begin();
    assert (size(bb) > 1);
    if (x <= bb.front().x)
        return bb.begin();
    if (x >= bb.back().x)
        return bb.end() - 2;
    if (pos < bb.begin() || pos >= bb.end()) 
        pos = bb.begin();
    // check if current pos is ok
    if (pos->x <= x) {
        //pos->x <= x and x < bb.back().x and bb is sorted  => pos < bb.end()-1
        if (x <= (pos+1)->x) 
            return pos;
        // try again
        pos++;
        if (pos->x <= x && (pos+1 == bb.end() || x <= (pos+1)->x)) 
            return pos;
    }
    pos = lower_bound(bb.begin(), bb.end(), PointQ(x, 0)) - 1;
    // pos >= bb.begin() because x > bb.front().x
    return pos;
}

void prepare_spline_interpolation (vector<PointQ> &bb)
{   
    //first wroten for bb interpolation, then generalized
    const int n = bb.size();
    if (n == 0)
        return;
        //find d2y/dx2 and put it in .q
    bb[0].q = 0; //natural spline
    vector<fp> u(n);
    for (int k = 1; k <= n-2; k++) {
        PointQ *b = &bb[k];
        fp sig = (b->x - (b-1)->x) / ((b+1)->x - (b-1)->x);
        fp t = sig * (b-1)->q + 2.;
        b->q = (sig - 1.) / t;
        u[k] = ((b+1)->y - b->y) / ((b+1)->x - b->x) - (b->y - (b-1)->y)
                            / (b->x - (b - 1)->x);
        u[k] = (6. * u[k] / ((b+1)->x - (b-1)->x) - sig * u[k-1]) / t;
    }
    bb.back().q = 0; 
    for (int k = n-2; k >= 0; k--) {
        PointQ *b = &bb[k];
        b->q = b->q * (b+1)->q + u[k];
    }
}

fp get_spline_interpolation(vector<PointQ> &bb, fp x)
{
    if (bb.empty())
        return 0.;
    if (bb.size() == 1)
        return bb[0].y;
    vector<PointQ>::iterator pos = get_interpolation_segment(bb, x);
    // based on Numerical Recipes www.nr.com
    fp h = (pos+1)->x - pos->x;
    fp a = ((pos+1)->x - x) / h;
    fp b = (x - pos->x) / h;
    fp t = a * pos->y + b * (pos+1)->y + ((a * a * a - a) * pos->q 
            + (b * b * b - b) * (pos+1)->q) * (h * h) / 6.;
    return t;
}

fp get_linear_interpolation(vector<PointQ> &bb, fp x)
{
    if (bb.empty())
        return 0.;
    if (bb.size() == 1)
        return bb[0].y;
    vector<PointQ>::iterator pos = get_interpolation_segment(bb, x);
    fp a = ((pos + 1)->y - pos->y) / ((pos + 1)->x - pos->x);
    return pos->y + a * (x  - pos->x);
}


fp LnGammaE (fp x) //log_e of Gamma function
    // x > 0
{
    const fp c[6] = { 76.18009172947146, -86.50532032941677,
        24.01409824083091, -1.231739572450155,
        0.1208650973866179e-2, -0.5395239384953e-5 };
    fp tmp = x + 5.5 - (x + 0.5) * log(x + 5.5);
    fp s = 1.000000000190015;
    for (int j = 0; j <= 5; j++)
        s += c[j] / (x + j + 1);
    return - tmp + log (2.5066282746310005 * s / x);
}

// random number utilities
static const fp TINY = 1e-12; //only for rand_gauss() and rand_cauchy()

/// normal distribution, mean=0, variance=1
fp rand_gauss()
{
    static bool is_saved = false;
    static fp saved;
    if (!is_saved) {
        fp rsq, x1, x2;
        while(1) {
            x1 = rand_1_1();
            x2 = rand_1_1();
            rsq = x1 * x1 + x2 * x2;
            if (rsq >= TINY && rsq < 1) 
                break;
        }
        fp f = sqrt(-2. * log(rsq) / rsq);
        saved = x1 * f;
        is_saved = true;
        return x2 * f;
    }
    else {
        is_saved = false;
        return saved;
    }
}

fp rand_cauchy()
{
    while (1) {
        fp x1 = rand_1_1(); 
        fp x2 = rand_1_1();
        fp rsq = x1 * x1 + x2 * x2;
        if (rsq >= TINY && rsq < 1 && fabs(x1) >= TINY)
            return (x2 / x1);
    }
}


void SimplePolylineConvex::push_point(PointQ const& p)
{
    if (vertices.size() < 2 
            || is_left(*(vertices.end() - 2), *(vertices.end() - 1), p)) 
        vertices.push_back(p);
    else { 
        // the middle point (the last one currently in vertices) is not convex
        // remove it and check again the last three points
        vertices.pop_back(); 
        push_point(p);
    }
}



syntax highlighted by Code2HTML, v. 0.9.1