2 // ********************************************************************
 
    3 // * License and Disclaimer                                           *
 
    5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
 
    6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
 
    7 // * conditions of the Geant4 Software License,  included in the file *
 
    8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
 
    9 // * include a list of copyright holders.                             *
 
   11 // * Neither the authors of this software system, nor their employing *
 
   12 // * institutes,nor the agencies providing financial support for this *
 
   13 // * work  make  any representation or  warranty, express or implied, *
 
   14 // * regarding  this  software system or assume any liability for its *
 
   15 // * use.  Please see the license in the file  LICENSE  and URL above *
 
   16 // * for the full disclaimer and the limitation of liability.         *
 
   18 // * This  code  implementation is the result of  the  scientific and *
 
   19 // * technical work of the GEANT4 collaboration.                      *
 
   20 // * By using,  copying,  modifying or  distributing the software (or *
 
   21 // * any work based  on the software)  you  agree  to acknowledge its *
 
   22 // * use  in  resulting  scientific  publications,  and indicate your *
 
   23 // * acceptance of all terms of the Geant4 Software license.          *
 
   24 // ********************************************************************
 
   28  *  \file electromagnetic/TestEm7/include/c2_function.icc
 
   29  *  \brief Provides code for the general c2_function algebra which supports 
 
   30  *  fast, flexible operations on piecewise-twice-differentiable functions
 
   32  *  \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05.
 
   33  *  \author Copyright 2005 __Vanderbilt University__. All rights reserved.
 
   35  * \version c2_function.cc 488 2012-04-04 11:30:21Z marcus 
 
   52 template <typename float_type> const std::string c2_function<float_type>::cvs_file_vers() const 
 
   53 { return "c2_function.cc 488 2012-04-04 11:30:21Z marcus "; }
 
   55 // find a pre-bracketed root of a c2_function, which is a MUCH easier job than general root finding
 
   56 // since the derivatives are known exactly, and smoothness is guaranteed.
 
   57 // this searches for f(x)=value, to make life a little easier than always searching for f(x)=0
 
   58 template <typename float_type> float_type c2_function<float_type>::find_root(float_type lower_bracket, float_type upper_bracket, 
 
   59    float_type start, float_type value, int *error,
 
   60    float_type *final_yprime, float_type *final_yprime2) const throw(c2_exception) 
 
   62    // find f(x)=value within the brackets, using the guarantees of smoothness associated with a c2_function
 
   63    // can use local f(x)=a*x**2 + b*x + c and solve quadratic to find root, then iterate
 
   65    float_type yp, yp2; // we will make unused pointers point here, to save null checks later
 
   66    if (!final_yprime) final_yprime=&yp;
 
   67    if (!final_yprime2) final_yprime2=&yp2;
 
   69    float_type ftol=5*(std::numeric_limits<float_type>::epsilon()*std::abs(value)+std::numeric_limits<float_type>::min());
 
   70    float_type xtol=5*(std::numeric_limits<float_type>::epsilon()*(std::abs(upper_bracket)+std::abs(lower_bracket))+std::numeric_limits<float_type>::min());
 
   72    float_type root=start; // start looking in the middle
 
   73    if(error) *error=0; // start out with error flag set to OK, if it is expected
 
   78            root_info=new struct c2_root_info;
 
   79            root_info->inited=false;
 
   81    // this new logic is to keep track of where we were before, and lower the number of 
 
   82    // function evaluations if we are searching inside the same bracket as before.
 
   83    // Since this root finder has, very often, the bracket of the entire domain of the function,
 
   84    // this makes a big difference, especially to c2_inverse_function
 
   85    if(!root_info->inited || upper_bracket != root_info->upper.x || lower_bracket != root_info->lower.x) {
 
   86            root_info->upper.x=upper_bracket;
 
   87            fill_fblock(root_info->upper);          
 
   88            root_info->lower.x=lower_bracket;
 
   89            fill_fblock(root_info->lower);
 
   90            root_info->inited=true;
 
   93    float_type clower=root_info->lower.y-value;
 
   95            *final_yprime=root_info->lower.yp;
 
   96            *final_yprime2=root_info->lower.ypp;
 
  100    float_type cupper=root_info->upper.y-value;
 
  102            *final_yprime=root_info->upper.yp;
 
  103            *final_yprime2=root_info->upper.ypp;
 
  104            return upper_bracket;
 
  106    const float_type lower_sign = (clower < 0) ? -1 : 1;
 
  108    if(lower_sign*cupper >0) { 
 
  109            // argh, no sign change in here!
 
  110            if(error) { *error=1; return 0.0; }
 
  112                    std::ostringstream outstr;
 
  113                    outstr << "unbracketed root in find_root at xlower= " << lower_bracket << ", xupper= " << upper_bracket;
 
  114                    outstr << ", value= " << value << ": bailing"; 
 
  115                    throw c2_exception(outstr.str().c_str());
 
  119    float_type delta=upper_bracket-lower_bracket; // first error step
 
  120    c=value_with_derivatives(root, final_yprime, final_yprime2)-value; // compute initial values
 
  121    b=*final_yprime; // make a local copy for readability
 
  122    increment_evaluations();
 
  125                     std::abs(delta) > xtol && // absolute x step check
 
  126                     std::abs(c) > ftol && // absolute y tolerance
 
  127                     std::abs(c) > xtol*std::abs(b) // comparison to smallest possible Y step from derivative
 
  130       float_type a=(*final_yprime2)/2; // second derivative is 2*a
 
  131       float_type disc=b*b-4*a*c;
 
  132       // std::cout << std::endl << "find_root_debug a,b,c,d " << a << " " << b << " " << c << " " << disc << std::endl; 
 
  135               float_type q=-0.5*((b>=0)?(b+std::sqrt(disc)):(b-std::sqrt(disc)));
 
  136               if(q*q > std::abs(a*c)) delta=c/q; // since x1=q/a, x2=c/q, x1/x2=q^2/ac, this picks smaller step
 
  141       if(disc < 0 || root<lower_bracket || root>upper_bracket ||
 
  142                  std::abs(delta) >= 0.5*(upper_bracket-lower_bracket)) { 
 
  143                  // if we jump out of the bracket, or aren't converging well, bisect
 
  144               root=0.5*(lower_bracket+upper_bracket);
 
  145               delta=upper_bracket-lower_bracket;
 
  147       c=value_with_derivatives(root, final_yprime, final_yprime2)-value; // compute initial values
 
  150               return c; // return the nan if a computation failed
 
  152       b=*final_yprime; // make a local copy for readability
 
  153       increment_evaluations();
 
  155       // now, close in bracket on whichever side this still brackets
 
  156       if(c*lower_sign < 0.0) {
 
  163       // std::cout << "find_root_debug x, y, dx " << root << " "  << c << " " << delta << std::endl;
 
  168 /* def partial_integrals(self, xgrid):
 
  169    Return the integrals of a function between the sampling points xgrid.  The sum is the definite integral.
 
  170    This method uses an exact integration of the polynomial which matches the values and derivatives at the 
 
  171    endpoints of a segment.  Its error scales as h**6, if the input functions really are smooth.  
 
  172    This could very well be used as a stepper for adaptive Romberg integration.
 
  173    For InterpolatingFunctions, it is likely that the Simpson's rule integrator is sufficient.
 
  174    #the weights come from an exact mathematica solution to the 5th order polynomial with the given values & derivatives
 
  175    #yint = (y0+y1)*dx/2 + dx^2*(yp0-yp1)/10 + dx^3 * (ypp0+ypp1)/120 )
 
  178 // the recursive part of the integrator is agressively designed to minimize copying of data... lots of pointers
 
  179 template <typename float_type> float_type c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const throw(c2_exception)
 
  181    std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion
 
  185    top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0;
 
  187    // push storage for our initial elements
 
  188    rb_stack.push_back(top);
 
  189    rb_stack.back().f1=*rb.f0;
 
  190    rb_stack.back().done=true; // this element will never be evaluated further
 
  192    rb_stack.push_back(top);
 
  193    rb_stack.back().f1=*rb.f1;
 
  194    rb_stack.back().done=true; // this element will never be evaluated further
 
  199                            rb.eps_scale=0.1; rb.extrap_coef=16; break;
 
  201                            rb.eps_scale=0.1; rb.extrap_coef=64; break;
 
  203                            rb.eps_scale=0.02; rb.extrap_coef=1024; break;
 
  205                            throw c2_exception("derivs must be 0, 1 or 2 in partial_integrals");
 
  208            rb.extrap2=1.0/(rb.extrap_coef-1.0);
 
  209            rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
 
  210            rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
 
  214    // now, push our first real element
 
  215    top.f0index=0; // left element is stack[0]
 
  216    top.f2index=1; // right element is stack[1]
 
  217    top.abs_tol=rb.abs_tol;
 
  218    rb_stack.push_back(top);
 
  220    while(rb_stack.size() > 2) {
 
  221            recur_item &back=rb_stack.back();
 
  223                    float_type sum=back.step_sum;
 
  225                    rb_stack.back().step_sum+=sum; // bump our sum up to the parent
 
  230            c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1; 
 
  231            c2_fblock<float_type> &f1=back.f1; // will hold new middle values
 
  232            size_t f1index=rb_stack.size()-1; // our current offset
 
  233            float_type abs_tol=back.abs_tol;
 
  235            f1.x=0.5*(f0.x + f2.x); // center of interval
 
  236            float_type dx2=0.5*(f2.x - f0.x);
 
  238            // check for underflow on step size, which prevents us from achieving specified accuracy. 
 
  239            if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) {
 
  240                    std::ostringstream outstr;
 
  241                    outstr << "Step size underflow in adaptive_partial_integrals at depth=" << back.depth << ", x= " << f1.x;
 
  242                    throw c2_exception(outstr.str().c_str());
 
  248                    return f1.y; // can't go any further if a nan has appeared
 
  251            bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad;
 
  252            bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad;
 
  254            // select the real derivative count based on whether we are at a point where derivatives exist
 
  255            int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2);
 
  257            if(!back.depth) { // top level, total has not been initialized yet
 
  258                    switch(derivs) { // create estimate of next lower order for first try
 
  260                                    back.previous_estimate=(f0.y+f2.y)*dx2; break;
 
  262                                    back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break;
 
  264                                    back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y) +  2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break;
 
  266                                    back.previous_estimate=0.0; // just to suppress missing default warnings
 
  270            float_type left, right;
 
  272            // pre-compute constants so all multiplies use a small dynamic range
 
  273            // note the forced csting of the denominators to assure full precision for (e.g.) long double
 
  274            // constants for 0 derivative integrator
 
  275            static const float_type c0c1=5./((float_type)12.), c0c2=8./((float_type)12.), c0c3=-1./((float_type)12.);
 
  276            // constants for 1 derivative integrator
 
  277            static const float_type c1c1=101./((float_type)240.), c1c2=128./((float_type)240.), c1c3=11./((float_type)240.),
 
  278                    c1c4=13./((float_type)240.), c1c5=-40./((float_type)240.), c1c6=-3./((float_type)240.);
 
  279            // constants for 2 derivative integrator
 
  280            static const float_type c2c1=169./((float_type)40320.), c2c2=1024./ ((float_type)40320.), c2c3=-41./((float_type)40320.),
 
  281                    c2c4=2727./((float_type)40320.), c2c5=-5040./((float_type)40320.), c2c6=423./((float_type)40320.),
 
  282                    c2c7=17007./((float_type)40320.), c2c8=24576./((float_type)40320.), c2c9=-1263./((float_type)40320.);
 
  286                            // use ninth-order estimates for each side, from full set of all values (!) (Thanks, Mathematica!)
 
  287                            left=   ( ( (c2c1*f0.ypp + c2c2*f1.ypp +  c2c3*f2.ypp)*dx2 + 
 
  288                                                    (c2c4*f0.yp + c2c5*f1.yp +  c2c6*f2.yp) )*dx2 + 
 
  289                                              (c2c7*f0.y + c2c8*f1.y +  c2c9*f2.y) )* dx2;
 
  290                            right=  ( ( (c2c1*f2.ypp + c2c2*f1.ypp +  c2c3*f0.ypp)*dx2 - 
 
  291                                                    (c2c4*f2.yp + c2c5*f1.yp +  c2c6*f0.yp) )*dx2 + 
 
  292                                              (c2c7*f2.y + c2c8*f1.y +  c2c9*f0.y) )* dx2;
 
  293                            // std::cout << f0.x << " " << f1.x << " " << f2.x <<  std::endl ;
 
  294                            // std::cout << f0.y << " " << f1.y << " " << f2.y << " " << left << " " << right << " " << total << std::endl ;
 
  297                            left=   ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y) + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ;
 
  298                            right=  ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y) - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ;
 
  301                            left=   (c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2;
 
  302                            right=  (c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2;
 
  305                            left=right=0.0; // suppress warnings about missing default
 
  309            float_type lrsum=left+right;
 
  311            bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs); // only extrapolate if no trouble with derivs
 
  312            float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale; 
 
  313            if(extrapolate) eps*=rb.eps_scale; 
 
  315            if(rb.adapt && eps > abs_tol &&  eps > std::abs(lrsum)*rb.rel_tol) {
 
  316                    // tolerance not met, subdivide & recur
 
  317                    if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5; // each half has half the error budget
 
  319                    top.depth=back.depth+1;
 
  321                    // save the last things we need from back before a push happens, in case 
 
  322                    // the push causes a reallocation and moves the whole stack.
 
  323                    size_t f0index=back.f0index, f2index=back.f2index;
 
  325                    top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block
 
  326                    top.previous_estimate=right;
 
  327                    rb_stack.push_back(top);
 
  329                    top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block
 
  330                    top.previous_estimate=left;
 
  331                    rb_stack.push_back(top);
 
  333            } else if(extrapolate) {
 
  334                    // extrapolation only happens on leaf nodes, where the tolerance was met.
 
  335                    back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2; 
 
  337                    back.step_sum+=lrsum; 
 
  340    return rb_stack.back().step_sum; // last element on the stack holds the sum
 
  343 template <typename float_type> bool c2_function<float_type>::check_monotonicity(
 
  344    const std::vector<float_type> &data, const char message[]) const throw(c2_exception)
 
  346    size_t np=data.size();
 
  347    if(np < 2) return false;  // one point has no direction!
 
  349    bool rev=(data[1] < data[0]); // which way do data point?
 
  352    if(!rev) for(i = 2; i < np && (data[i-1] < data[i]) ; i++) { }
 
  353    else for(i = 2; i < np &&(data[i-1] > data[i]) ; i++) { }
 
  355    if(i != np) throw c2_exception(message);
 
  360 template <typename float_type> void c2_function<float_type>::set_sampling_grid(const std::vector<float_type> &grid) throw(c2_exception)
 
  362    bool rev=this->check_monotonicity(grid, "set_sampling_grid: sampling grid not monotonic");
 
  364    if(!sampling_grid || no_overwrite_grid) sampling_grid=new std::vector<float_type>;
 
  365    (*sampling_grid)=grid; no_overwrite_grid=0;  
 
  367    if(rev) std::reverse(sampling_grid->begin(), sampling_grid->end()); // make it increasing
 
  370 template <typename float_type> void c2_function<float_type>::
 
  371    get_sampling_grid(float_type amin, float_type amax, std::vector<float_type> &grid) const 
 
  373    std::vector<float_type> *result=&grid;
 
  376    if( !(sampling_grid) || !(sampling_grid->size()) || (amax <= sampling_grid->front()) || (amin >= sampling_grid->back()) ) { 
 
  377            // nothing is known about the function in this region, return amin and amax             
 
  378            result->push_back(amin);
 
  379            result->push_back(amax);
 
  381            std::vector<float_type> &sg=*sampling_grid; // just a shortcut
 
  383            int klo=0, khi=np-1, firstindex=0, lastindex=np-1;
 
  385            result->push_back(amin);
 
  387            if(amin > sg.front() ) {
 
  388                    // hunt through table for position bracketing starting point
 
  391                            if(sg[kk] > amin) khi=kk;
 
  395                    // khi now points to first point definitively beyond our first point, or last point of array
 
  397                    khi=np-1; // restart upper end of search
 
  400            if(amax < sg.back()) {
 
  401                    // hunt through table for position bracketing starting point
 
  404                            if(sg[kk] > amax) khi=kk;
 
  408                    // khi now points to first point definitively beyond our last point, or last point of array
 
  412            int initsize=result->size();
 
  413            result->resize(initsize+(lastindex-firstindex+2));
 
  414            std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1, result->begin()+initsize);
 
  417            //  this is the unrefined sampling grid... now check for very close points on front & back and fix if needed.
 
  418            preen_sampling_grid(result);
 
  422 template <typename float_type> void c2_function<float_type>::preen_sampling_grid(std::vector<float_type> *result) const
 
  424    //  this is the unrefined sampling grid... now check for very close points on front & back and fix if needed.
 
  425    if(result->size() > 2) { // may be able to prune dangerously close points near the ends if there are at least 3 points
 
  427            float_type x0=(*result)[0], x1=(*result)[1];
 
  428            float_type dx1=x1-x0;
 
  430            float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()*(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
 
  431            if(dx1 < ftol) deleteit=true;
 
  432            float_type dx2=(*result)[2]-x0;
 
  433            if(dx1/dx2 < 0.1) deleteit=true; // endpoint is very close to internal interesting point
 
  435            if(deleteit) result->erase(result->begin()+1); // delete redundant interesting point
 
  438    if(result->size() > 2) { // may be able to prune dangerously close points near the ends if there are at least 3 points
 
  440            int pos=result->size()-3;
 
  441            float_type x0=(*result)[pos+1], x1=(*result)[pos+2];
 
  442            float_type dx1=x1-x0;
 
  444            float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()*(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
 
  445            if(dx1 < ftol) deleteit=true;
 
  446            float_type dx2=x1-(*result)[pos];
 
  447            if(dx1/dx2 < 0.1) deleteit=true; // endpoint is very close to internal interesting point
 
  449            if(deleteit) result->erase(result->end()-2); // delete redundant interesting point
 
  453 template <typename float_type> void c2_function<float_type>::
 
  454    refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const
 
  456    size_t np=grid.size();
 
  457    size_t count=(np-1)*refinement + 1;
 
  458    float_type dxscale=1.0/refinement;
 
  460    std::vector<float_type> result(count);
 
  462    for(size_t i=0; i<(np-1); i++) {
 
  463            float_type x=grid[i];
 
  464            float_type dx=(grid[i+1]-x)*dxscale;
 
  465            for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x;
 
  467    result.back()=grid.back();
 
  468    grid=result; // copy the expanded grid back to the input
 
  471 template <typename float_type> float_type c2_function<float_type>::integral(float_type amin, float_type amax, std::vector<float_type> *partials,
 
  472     float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) const throw(c2_exception)
 
  475            if(partials) partials->clear();
 
  478    std::vector<float_type> grid;
 
  479    get_sampling_grid(amin, amax, grid);
 
  480    float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol, derivs, adapt, extrapolate);
 
  484 template <typename float_type> c2_function<float_type> &c2_function<float_type>::normalized_function(float_type amin, float_type amax, float_type norm) 
 
  485    const throw(c2_exception)
 
  487    float_type intg=integral(amin, amax);
 
  488    return *new c2_scaled_function_p<float_type>(*this, norm/intg);
 
  491 template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(float_type amin, float_type amax, float_type norm)
 
  492    const throw(c2_exception)
 
  494    c2_ptr<float_type> mesquared((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this));
 
  496    std::vector<float_type> grid;
 
  497    get_sampling_grid(amin, amax, grid);            
 
  498    float_type intg=mesquared->partial_integrals(grid);
 
  500    return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
 
  503 template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(
 
  504            float_type amin, float_type amax, const c2_function<float_type> &weight, float_type norm) 
 
  505    const throw(c2_exception)
 
  507    c2_ptr<float_type> weighted((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight);
 
  509    std::vector<float_type> grid;
 
  510    get_sampling_grid(amin, amax, grid);    
 
  511    float_type intg=weighted->partial_integrals(grid);
 
  513    return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
 
  516 template <typename float_type> float_type c2_function<float_type>::partial_integrals(
 
  517    std::vector<float_type> xgrid, std::vector<float_type> *partials,
 
  518    float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) 
 
  519    const throw(c2_exception)
 
  523    c2_fblock<float_type> f0, f2;
 
  524    struct c2_integrate_recur rb;
 
  526    rb.extrapolate=extrapolate;
 
  529    std::vector< recur_item > rb_stack;
 
  530    rb_stack.reserve(20); // enough for most operations
 
  531    rb.rb_stack=&rb_stack;
 
  533    float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front());
 
  535    if(partials) partials->resize(np-1);
 
  543            return f2.y; // can't go any further if a nan has appeared
 
  546    for(int i=0; i<np-1; i++) {
 
  547            f0=f2; // copy upper bound to lower before computing new upper bound
 
  553                    return f2.y; // can't go any further if a nan has appeared
 
  556            rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv; // distribute error tolerance over whole domain
 
  557            rb.f0=&f0; rb.f1=&f2; 
 
  558            float_type ps=integrate_step(rb);
 
  560            if(partials) (*partials)[i]=ps;
 
  561            if(c2_isnan(ps)) break; // NaN stops integration
 
  566 // generate a sampling grid at points separated by dx=5, which is intentionally
 
  567 // incommensurate with pi and 2*pi so grid errors are somewhat randomized
 
  568 template <typename float_type> void c2_sin_p<float_type>::
 
  569    get_sampling_grid(float_type amin, float_type amax,  std::vector<float_type> &grid) const
 
  572    for(; amin < amax; amin+=5.0) grid.push_back(amin);
 
  573    grid.push_back(amax);
 
  574    this->preen_sampling_grid(&grid);
 
  577 template <typename float_type> float_type c2_function_transformation<float_type>::evaluate(
 
  579            float_type y, float_type yp0, float_type ypp0,
 
  580            float_type *yprime, float_type *yprime2) const
 
  582    y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y); 
 
  584    if(yprime || yprime2) {
 
  587            if(X.fHasStaticTransforms && Y.fHasStaticTransforms) {
 
  588                    float_type fpi=1.0/Y.pInPrime(y);
 
  589                    float_type gp=X.pInPrime(xraw);
 
  590                    // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
 
  591                    yp=gp*yp0*fpi; // transformed derivative
 
  592                    yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0  - Y.pInDPrime(y)*yp*yp )*fpi; 
 
  594                    float_type fpi=1.0/Y.fInPrime(y);
 
  595                    float_type gp=X.fInPrime(xraw);
 
  596                    // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
 
  597                    yp=gp*yp0*fpi; // transformed derivative
 
  598                    yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0  - Y.fInDPrime(y)*yp*yp )*fpi; 
 
  600            if(yprime) *yprime=yp;
 
  601            if(yprime2) *yprime2=yp2;
 
  607 template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load(
 
  608                                                            const std::vector<float_type> &x, const std::vector<float_type> &f, 
 
  609                                                            bool lowerSlopeNatural, float_type lowerSlope, 
 
  610                                                            bool upperSlopeNatural, float_type upperSlope,
 
  612            ) throw(c2_exception) 
 
  614    c2_ptr<float_type> keepme(*this);
 
  618    // Xraw is useful in some of the arithmetic operations between interpolating functions
 
  621    this->set_domain(std::min(Xraw.front(), Xraw.back()),std::max(Xraw.front(), Xraw.back()));
 
  623    if(x.size() != f.size()) {
 
  624            throw c2_exception("interpolating_function::init() -- x & y inputs are of different size");
 
  627    size_t np=X.size(); // they are the same now, so lets take a short cut
 
  630            throw c2_exception("interpolating_function::init() -- input < 2 elements ");
 
  633    bool xraw_rev=this->check_monotonicity(Xraw, 
 
  634                    "interpolating_function::init() non-monotonic raw x input"); // which way does raw X point?  sampling grid MUST be increasing
 
  636    if(!xraw_rev) { // we can use pointer to raw X values if they are in the right order
 
  637            this->set_sampling_grid_pointer(Xraw); // our intial grid of x values is certainly a good guess for 'interesting' points
 
  639            this->set_sampling_grid(Xraw); // make a copy of it, and assure it is in right order
 
  642    if(fTransform.X.fTransformed) { // check if X scale is nonlinear, and if so, do transform
 
  643            if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]);
 
  644            if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]);
 
  645            for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]);
 
  647    if(fTransform.Y.fTransformed) {  // check if Y scale is nonlinear, and if so, do transform
 
  648            if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]);
 
  649            if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]);
 
  650            for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]);
 
  653    xInverted=this->check_monotonicity(X, 
 
  654            "interpolating_function::init() non-monotonic transformed x input"); 
 
  656    if(splined) spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
 
  657    else y2.assign(np,0.0);
 
  660    keepme.release_for_return();
 
  666 template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load_pairs(
 
  667                                                            std::vector<std::pair<float_type, float_type> > &data, 
 
  668                                                            bool lowerSlopeNatural, float_type lowerSlope, 
 
  669                                                            bool upperSlopeNatural, float_type upperSlope,
 
  671            ) throw(c2_exception) 
 
  673    c2_ptr<float_type> keepme(*this);
 
  675    size_t np=data.size();
 
  677            throw c2_exception("interpolating_function::init() -- input < 2 elements ");
 
  680    // sort into ascending order
 
  681    std::sort(data.begin(), data.end(), comp_pair); 
 
  683    std::vector<float_type> xtmp, ytmp;
 
  686    for (size_t i=0; i<np; i++) {
 
  687            xtmp.push_back(data[i].first);
 
  688            ytmp.push_back(data[i].second);
 
  690    this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, splined);
 
  692    keepme.release_for_return();
 
  696 template <typename float_type> interpolating_function_p<float_type> & 
 
  697    interpolating_function_p<float_type>::load_random_generator_function(
 
  698    const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights)
 
  701    c2_ptr<float_type> keepme(*this);
 
  703    std::vector<float_type> integral;
 
  704    c2_const_ptr<float_type> keepit(binheights); // manage function... not really needed here, but always safe.
 
  705    // integrate from first to last bin in original order, leaving results in integral
 
  706    // ask for relative error of 1e-6 on each bin, with absolute error set to 0 (since we don't know the data scale).
 
  707    float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6); 
 
  708    // the integral vector now has partial integrals... it must be accumulated by summing
 
  709    integral.insert(integral.begin(), 0.0); // integral from start to start is 0
 
  710    float_type scale=1.0/sum;
 
  711    for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale + integral[i-1];
 
  712    integral.back()=1.0; // force exact value on boundary
 
  714    this->load(integral, bincenters, 
 
  715                                       false, 1.0/(scale*binheights(bincenters.front() )), 
 
  716                                       false, 1.0/(scale*binheights(bincenters.back() ))
 
  717                                       ); // use integral as x axis in inverse function
 
  718    keepme.release_for_return();
 
  722 template <typename float_type> interpolating_function_p<float_type> & 
 
  723    interpolating_function_p<float_type>::load_random_generator_bins(
 
  724    const std::vector<float_type> &bins, const std::vector<float_type> &binheights, bool splined)
 
  727    c2_ptr<float_type> keepme(*this);
 
  729    size_t np=binheights.size();
 
  730    std::vector<float_type> integral(np+1), bin_edges(np+1);
 
  732    // compute the integral based on estimates of the bin edges from the given bin centers...
 
  733    // except for bin 0 & final bin, the edge of a bin is halfway between then center of the 
 
  734    // bin and the center of the previous/next bin.
 
  735    // This gives width[n] = (center[n+1]+center[n])/2 - (center[n]+center[n-1])/2 = (center[n+1]-center[n-1])/2
 
  736    // for the edges, assume a bin of width (center[1]-center[0]) or (center[np-1]-center[np-2])
 
  737    // be careful that absolute values are used in case data are reversed.
 
  739    if(bins.size() == binheights.size()+1) {
 
  740            bin_edges=bins; // edges array was passed in
 
  741    } else if (bins.size() == binheights.size()) {
 
  742            bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5; // edge bin
 
  743            for(size_t i=1; i<np; i++) {
 
  744                    bin_edges[i]=(bins[i]+bins[i-1])*0.5;
 
  746            bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5; // edge bin
 
  748            throw c2_exception("inconsistent bin vectors passed to load_random_generator_bins");
 
  751    float_type running_sum=0.0;
 
  752    for(size_t i=0; i<np; i++) {
 
  753            integral[i]=running_sum;
 
  754            if(!binheights[i]) throw c2_exception("empty bin passed to load_random_generator_bins");
 
  755            running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]);
 
  757    float_type scale=1.0/running_sum;
 
  758    for(size_t i=0; i<np; i++) integral[i]*=scale;
 
  759    integral.back()=1.0; // force exactly correct value on boundary
 
  760    this->load(integral, bin_edges, 
 
  761              false, 1.0/(scale*binheights.front()), 
 
  762              false, 1.0/(scale*binheights.back()),
 
  763              splined); // use integral as x axis in inverse function
 
  764    keepme.release_for_return();
 
  769 //  The spline table generator
 
  770 template <typename float_type> void interpolating_function_p<float_type>::spline(
 
  771     bool lowerSlopeNatural, float_type lowerSlope, 
 
  772     bool upperSlopeNatural, float_type upperSlope
 
  773     ) throw(c2_exception) 
 
  775 // construct spline tables here.  
 
  776    // this code is a re-translation of the pythonlabtools spline algorithm from pythonlabtools.sourceforge.net
 
  778    std::vector<float_type> u(np),  dy(np-1), dx(np-1), dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1);
 
  780    std::transform(X.begin()+1, X.end(), X.begin(), dx.begin(), std::minus<float_type>() ); // dx=X[1:] - X [:-1]
 
  781    for(size_t i=0; i<dxi.size(); i++) dxi[i]=1.0/dx[i]; // dxi = 1/dx
 
  782    for(size_t i=0; i<dx2i.size(); i++) dx2i[i]=1.0/(X[i+2]-X[i]); 
 
  784    std::transform(F.begin()+1, F.end(), F.begin(), dy.begin(), std::minus<float_type>() ); // dy = F[i+1]-F[i]
 
  785    std::transform(dx2i.begin(), dx2i.end(), dx.begin(), siga.begin(), std::multiplies<float_type>()); // siga = dx[:-1]*dx2i
 
  786    std::transform(dxi.begin(), dxi.end(), dy.begin(), dydx.begin(), std::multiplies<float_type>()); // dydx=dy/dx
 
  788    // u[i]=(y[i+1]-y[i])/float(x[i+1]-x[i]) - (y[i]-y[i-1])/float(x[i]-x[i-1])
 
  789    std::transform(dydx.begin()+1, dydx.end(), dydx.begin(), u.begin()+1, std::minus<float_type>() ); // incomplete rendering of u = dydx[1:]-dydx[:-1]
 
  793    if(lowerSlopeNatural) {
 
  797            u[0]=(3.0*dxi[0])*(dy[0]*dxi[0] -lowerSlope);
 
  800    for(size_t i=1; i < np -1; i++) { // the inner loop
 
  801            float_type sig=siga[i-1];
 
  802            float_type p=sig*y2[i-1]+2.0;
 
  804            u[i]=(6.0*u[i]*dx2i[i-1] - sig*u[i-1])/p;
 
  809    if(upperSlopeNatural) {
 
  813            un=(3.0*dxi[dxi.size()-1])*(upperSlope- dy[dy.size()-1]*dxi[dxi.size()-1] );
 
  816    y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0);
 
  817    for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1];
 
  820 template <typename float_type> interpolating_function_p<float_type> &interpolating_function_p<float_type>::sample_function(
 
  821                    const c2_function<float_type> &func,
 
  822                    float_type amin, float_type amax, float_type abs_tol, float_type rel_tol, 
 
  823                    bool lowerSlopeNatural, float_type lowerSlope, 
 
  824                    bool upperSlopeNatural, float_type upperSlope
 
  825  ) throw(c2_exception) 
 
  827    c2_ptr<float_type> keepme(*this);
 
  829    const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y; // shortcuts
 
  831     // set up our params to look like the samplng function for now
 
  832     sampler_function=func;
 
  833     std::vector<float_type> grid;
 
  834     func.get_sampling_grid(amin, amax, grid);
 
  835     size_t gsize=grid.size();
 
  836     if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]);
 
  837     this->set_sampling_grid_pointer(grid);
 
  839    // float_type amin1=fXin(amin), amax1=fXin(amax); // bounds in transformed space
 
  840     // get a list of points needed in transformed space, directly into our tables
 
  841     this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol, 8*rel_tol, 0, &X, &F);
 
  842     // clear the sampler function now, since otherwise our value_with_derivatives is broken
 
  843     sampler_function.unset_function();
 
  845     xInverted=this->check_monotonicity(X, 
 
  846              "interpolating_function::init() non-monotonic transformed x input"); 
 
  850     // Xraw is useful in some of the arithmetic operations between interpolating functions
 
  851     if(!XX.fTransformed) Xraw=X;
 
  854             for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]);
 
  859     bool xraw_rev=this->check_monotonicity(Xraw, 
 
  860              "interpolating_function::init() non-monotonic raw x input"); 
 
  861              // which way does raw X point?  sampling grid MUST be increasing
 
  863     if(!xraw_rev) { // we can use pointer to raw X values if they are in the right order
 
  864             this->set_sampling_grid_pointer(Xraw); 
 
  865             // our intial grid of x values is certainly a good guess for 'interesting' points
 
  867             this->set_sampling_grid(Xraw); // make a copy of it, and assure it is in right order
 
  870     if(XX.fTransformed) { // check if X scale is nonlinear, and if so, do transform
 
  871             if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(amin);
 
  872             if(!upperSlopeNatural) upperSlope /= XX.fInPrime(amax);
 
  874     if(YY.fTransformed) {  // check if Y scale is nonlinear, and if so, do transform
 
  875             if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(amin));
 
  876             if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(amax));
 
  878     // note that each of ends has 3 points with two equal gaps, since they were obtained by bisection
 
  879     // so the step sizes are easy to get
 
  880     // the 'natural slope' option for sampled functions has a different meaning than 
 
  881     // for normal splines.  In this case, the derivative is adjusted to make the
 
  882     // second derivative constant on the last two points at each end
 
  883     // which is consistent with the error sampling technique we used to get here
 
  884     if(lowerSlopeNatural) {
 
  885             float_type hlower=X[1]-X[0];
 
  886             lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower;
 
  887             lowerSlopeNatural=false; // it's not the usual meaning of natural any more 
 
  889     if(upperSlopeNatural) {
 
  890             float_type hupper=X[np-1]-X[np-2];
 
  891             upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper;
 
  892             upperSlopeNatural=false; // it's not the usual meaning of natural any more 
 
  894     this->set_domain(amin, amax);
 
  896     spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
 
  898     keepme.release_for_return();
 
  902 //  This function is the reason for this class to exist
 
  903 // it computes the interpolated function, and (if requested) its proper first and second derivatives including all coordinate transforms
 
  904 template <typename float_type> float_type interpolating_function_p<float_type>::value_with_derivatives(
 
  905                            float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
 
  907    if(sampler_function.valid()) { 
 
  908            // if this is non-null, we are sampling data for later, so just return raw function
 
  909            // however, transform it into our sampling space, first.
 
  910            if(yprime) *yprime=0;
 
  911            if(yprime2) *yprime2=0;
 
  912            sampler_function->increment_evaluations();
 
  913            return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x))); // derivatives are completely undefined
 
  916    if(x < this->xmin() || x > this->xmax()) {
 
  917            std::ostringstream outstr;
 
  918            outstr << "Interpolating function argument " << x << " out of range " << this->xmin() << " -- " << this ->xmax() << ": bailing";
 
  919            throw c2_exception(outstr.str().c_str());
 
  924    if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms? 
 
  925            fTransform.X.pIn(x) : fTransform.X.fIn(x); // save time by explicitly testing for identity function here
 
  927    int klo=0, khi=X.size()-1;
 
  929    if(khi < 0) throw c2_exception("Uninitialized interpolating function being evaluated"); 
 
  931    const float_type *XX=&X[lastKLow]; // make all fast checks short offsets from here
 
  933    if(!xInverted) { // select search depending on whether transformed X is increasing or decreasing
 
  934            if((XX[0] <= x) && (XX[1] >= x) ) { // already bracketed
 
  936            } else if((XX[1] <= x) && (XX[2] >= x)) { // in next bracket to the right
 
  938            } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) { // in next bracket to the left
 
  940            } else { // not bracketed, not close, start over
 
  941                             // search for new KLow
 
  944                            if(X[kk] > x) khi=kk;
 
  949            if((XX[0] >= x) && (XX[1] <= x) ) { // already bracketed
 
  951            } else if((XX[1] >= x) && (XX[2] <= x)) { // in next bracket to the right
 
  953            } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) { // in next bracket to the left
 
  955            } else { // not bracketed, not close, start over
 
  956                             // search for new KLow
 
  959                            if(X[kk] < x) khi=kk;
 
  968    float_type h=X[khi]-X[klo];
 
  970    float_type a=(X[khi]-x)/h; 
 
  972    float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi];
 
  973    float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0;
 
  975    float_type yp0=0; // the derivative in interpolating table coordinates
 
  976    float_type ypp0=0; // second derivative
 
  978    if(yprime || yprime2) {
 
  979            yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0; // the derivative in interpolating table coordinates
 
  980            ypp0=b*y2hi+a*y2lo; // second derivative
 
  983    if(fTransform.isIdentity) {
 
  984            if(yprime) *yprime=yp0;
 
  985            if(yprime2) *yprime2=ypp0;
 
  987    } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2);
 
  990 template <typename float_type> void interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound) 
 
  994    float_type xx=fTransform.X.fIn(bound);
 
  995    float_type h0=X[kh]-X[kl];
 
  996    float_type h1=xx-X[kl];
 
  997    float_type yextrap=F[kl]+((F[kh]-F[kl])/h0 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
 
  999    X.insert(X.begin(), xx);
 
 1000    F.insert(F.begin(), yextrap);
 
 1001    y2.insert(y2.begin(), y2.front()); // duplicate first or last element
 
 1002    Xraw.insert(Xraw.begin(), bound);
 
 1003    if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
 
 1004    else this->fXMax=bound;
 
 1006    //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
 
 1007    //for(int i=0; i<X.size(); i++) printf("%4d %10.4f %10.4f %10.4f %10.4f \n", i, Xraw[i], X[i], F[i], y2[i]);    
 
 1010 template <typename float_type> void interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound) 
 
 1012    int kl = X.size()-2 ;
 
 1014    float_type xx=fTransform.X.fIn(bound);
 
 1015    float_type h0=X[kh]-X[kl];
 
 1016    float_type h1=xx-X[kl];
 
 1017    float_type yextrap=F[kl]+((F[kh]-F[kl])/h0 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
 
 1019    X.insert(X.end(), xx);
 
 1020    F.insert(F.end(), yextrap);
 
 1021    y2.insert(y2.end(), y2.back()); // duplicate first or last element
 
 1022    Xraw.insert(Xraw.end(), bound);
 
 1023    if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
 
 1024    else this->fXMax=bound;
 
 1025    //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
 
 1026    //for(int i=0; i<X.size(); i++) printf("%4d %10.4f %10.4f %10.4f %10.4f \n", i, Xraw[i], X[i], F[i], y2[i]);
 
 1029 // return a new interpolating_function which is the unary function of an existing interpolating_function
 
 1030 // can also be used to generate a resampling of another c2_function on a different grid
 
 1031 // by creating a=interpolating_function(x,x)
 
 1032 // and doing b=a.unary_operator(c) where c is a c2_function (probably another interpolating_function)
 
 1034 template <typename float_type> interpolating_function_p<float_type>& 
 
 1035    interpolating_function_p<float_type>::unary_operator(const c2_function<float_type> &source) const 
 
 1038    std::vector<float_type>yv(np);
 
 1039    c2_ptr<float_type> comp(source(*this));
 
 1040    float_type yp0, yp1, ypp;
 
 1042    for(size_t i=1; i<np-1; i++) { 
 
 1043            yv[i]=source(fTransform.Y.fOut(F[i])); // copy pointwise the function of our data values
 
 1046    yv.front()=comp(Xraw.front(), &yp0, &ypp); // get derivative at front
 
 1047    yv.back()= comp(Xraw.back(), &yp1, &ypp); // get derivative at back
 
 1049    interpolating_function_p ©=clone();
 
 1050    copy.load(this->Xraw, yv, false, yp0, false, yp1);
 
 1055 template <typename float_type> void 
 
 1056 interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals, std::vector<float_type> &yvals) const throw()
 
 1060    yvals.resize(F.size());
 
 1062    for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]);
 
 1065 template <typename float_type> interpolating_function_p<float_type> & 
 
 1066    interpolating_function_p<float_type>::binary_operator(const c2_function<float_type> &rhs,
 
 1067            const c2_binary_function<float_type> *combining_stub) const
 
 1070    std::vector<float_type> yv(np);
 
 1071    c2_constant_p<float_type> fval(0);
 
 1072    float_type yp0, yp1, ypp;
 
 1074    c2_const_ptr<float_type> stub(*combining_stub);  // manage ownership
 
 1076    for(size_t i=1; i<np-1; i++) { 
 
 1077            fval.reset(fTransform.Y.fOut(F[i])); // update the constant function pointwise
 
 1078            yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0, (float_type *)0); // compute rhs & combine without derivatives
 
 1081    yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp); // get derivative at front
 
 1082    yv.back()= combining_stub->combine(*this, rhs, Xraw.back(),  &yp1, &ypp); // get derivative at back
 
 1084    interpolating_function_p ©=clone();
 
 1085    copy.load(this->Xraw, yv, false, yp0, false, yp1);
 
 1090 template <typename float_type> c2_inverse_function_p<float_type>::c2_inverse_function_p(const c2_function<float_type> &source) 
 
 1091    : c2_function<float_type>(), func(source)
 
 1093    float_type l=source.xmin();
 
 1094    float_type r=source.xmax();
 
 1095    start_hint=(l+r)*0.5; // guess that we start in the middle
 
 1096    //  compute our domain assuming the function is monotonic so its values on its domain boundaries are our domain
 
 1097    float_type ly=source(l);
 
 1098    float_type ry=source(r);
 
 1100            float_type t=ly; ly=ry; ry=t; 
 
 1102    this->set_domain(ly, ry);
 
 1105 template <typename float_type> float_type c2_inverse_function_p<float_type>::value_with_derivatives(
 
 1106                                    float_type x, float_type *yprime, float_type *yprime2
 
 1107                    ) const throw(c2_exception)
 
 1109    float_type l=this->func->xmin();
 
 1110    float_type r=this->func->xmax();
 
 1112    float_type y=this->func->find_root(l, r, get_start_hint(x), x, 0, &yp, &ypp);
 
 1114    if(yprime) *yprime=1.0/yp;
 
 1115    if(yprime2) *yprime2=-ypp/(yp*yp*yp);
 
 1119 //accumulated_histogram starts with binned data, generates the integral, and generates a piecewise linear interpolating_function
 
 1120 //If drop_zeros is true, it merges empty bins together before integration
 
 1121 //Note that the resulting interpolating_function is guaranteed to be increasing (if drop_zeros is false)
 
 1122 // or stricly increasing (if drop_zeros is true)
 
 1123 //If inverse_function is true, it drop zeros, integrates, and returns the inverse function which is useful
 
 1124 // for random number generation based on the input distribution.
 
 1125 //If normalize is true, the big end of the integral is scaled to 1. 
 
 1126 //If the data are passed in reverse order (large X first), the integral is carried out from the big end,
 
 1127 // and then the data are reversed to the result in in increasing X order.  
 
 1128 template <typename float_type> accumulated_histogram<float_type>::accumulated_histogram(
 
 1129                                                    const std::vector<float_type>binedges, const std::vector<float_type> binheights,
 
 1130                                                    bool normalize, bool inverse_function, bool drop_zeros) 
 
 1133    int np=binheights.size();
 
 1135    std::vector<float_type> be, bh;
 
 1136    if(drop_zeros || inverse_function) { //inverse functions cannot have any zero bins or they have vertical sections
 
 1137            if(binheights[0] || !inverse_function) { // conserve lower x bound if not an inverse function
 
 1138                    be.push_back(binedges[0]);
 
 1139                    bh.push_back(binheights[0]);
 
 1141            for(int i=1; i<np-1; i++) {
 
 1143                            be.push_back(binedges[i]);
 
 1144                            bh.push_back(binheights[i]);
 
 1147            if(binheights[np-1] || !inverse_function) {
 
 1148                    bh.push_back(binheights[np-1]);                 
 
 1149                    be.push_back(binedges[np-1]);
 
 1150                    be.push_back(binedges[np]); // push both sides of the last bin if needed
 
 1152            np=bh.size(); // set np to compressed size of bin array
 
 1157    std::vector<float_type> cum(np+1, 0.0);
 
 1158    for(int i=1; i<=np; i++) cum[i]=bh[i]*(be[i]-be[i-1])+cum[i-1]; // accumulate bins, leaving bin 0 as 0
 
 1159    if(be[1] < be[0]) { // if bins passed in backwards, reverse them
 
 1160            std::reverse(be.begin(), be.end());
 
 1161            std::reverse(cum.begin(), cum.end());
 
 1162            for(unsigned int i=0; i<cum.size(); i++) cum[i]*=-1; // flip sign on reversed data
 
 1165            float_type mmm=1.0/std::max(cum[0], cum[np]);
 
 1166            for(int i=0; i<=np; i++) cum[i]*=mmm;
 
 1168    if(inverse_function) interpolating_function_p<float_type>(cum, be); // use cum as x axis in inverse function
 
 1169    else interpolating_function_p<float_type>(be, cum); // else use lower bin edge as x axis
 
 1170    std::fill(this->y2.begin(), this->y2.end(), 0.0); // clear second derivatives, to we are piecewise linear
 
 1173 template <typename float_type> c2_piecewise_function_p<float_type>::c2_piecewise_function_p() 
 
 1174 : c2_function<float_type>(), lastKLow(-1)
 
 1176    this->sampling_grid=new std::vector<float_type>; // this always has a smapling grid
 
 1179 template <typename float_type> c2_piecewise_function_p<float_type>::~c2_piecewise_function_p() 
 
 1183 template <typename float_type> float_type c2_piecewise_function_p<float_type>::value_with_derivatives(
 
 1184              float_type x, float_type *yprime, float_type *yprime2
 
 1185              ) const throw(c2_exception)
 
 1188    size_t np=functions.size();
 
 1189    if(!np) throw c2_exception("attempting to evaluate an empty piecewise function");
 
 1191    if(x < this->xmin() || x > this->xmax()) {
 
 1192            std::ostringstream outstr;
 
 1193            outstr << "piecewise function argument " << x << " out of range " << this->xmin() << " -- " << this->xmax();
 
 1194            throw c2_exception(outstr.str().c_str());
 
 1199    if(lastKLow >= 0 && functions[lastKLow]->xmin() <= x && functions[lastKLow]->xmax() > x) {
 
 1203            while(khi-klo > 1) {
 
 1205                    if(functions[kk]->xmin() > x) khi=kk;
 
 1210    return functions[klo]->value_with_derivatives(x, yprime, yprime2);
 
 1213 template <typename float_type> void c2_piecewise_function_p<float_type>::append_function(
 
 1214    const c2_function<float_type> &func) throw(c2_exception)
 
 1216    c2_const_ptr<float_type> keepfunc(func);  //  manage function before we can throw any exceptions
 
 1217    if(functions.size()) { // check whether there are any gaps to fill, etc.
 
 1218            const c2_function<float_type> &tail=functions.back();
 
 1219            float_type x0=tail.xmax();
 
 1220            float_type x1=func.xmin();
 
 1222                    // must insert a connector if x0 < x1
 
 1223                    float_type y0=tail(x0);
 
 1224                    float_type y1=func(x1);
 
 1225                    c2_function<float_type> &connector=*new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0));
 
 1226                    connector.set_domain(x0,x1);
 
 1227                    functions.push_back(c2_const_ptr<float_type>(connector));
 
 1228                    this->sampling_grid->push_back(x1);
 
 1229            } else if(x0>x1) throw c2_exception("function domains not increasing in c2_piecewise_function");
 
 1231    functions.push_back(keepfunc);
 
 1232    // extend our domain to include all known functions
 
 1233    this->set_domain(functions.front()->xmin(), functions.back()->xmax());
 
 1234    // extend our sampling grid with the new function's grid, with the first point dropped to avoid duplicates
 
 1235    std::vector<float_type> newgrid;
 
 1236    func.get_sampling_grid(func.xmin(), func.xmax(), newgrid);
 
 1237    this->sampling_grid->insert(this->sampling_grid->end(), newgrid.begin()+1, newgrid.end());
 
 1240 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
 
 1241    float_type x0, const c2_function<float_type> &f0, float_type x2, const c2_function<float_type> &f2, 
 
 1242    bool auto_center, float_type y1)
 
 1243    : c2_function<float_type>()
 
 1245    c2_const_ptr<float_type> left(f0), right(f2); // make sure if these are unowned, they get deleted
 
 1246    c2_fblock<float_type> fb0, fb2;
 
 1248    f0.fill_fblock(fb0);
 
 1250    f2.fill_fblock(fb2);
 
 1251    init(fb0, fb2, auto_center, y1);
 
 1254 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
 
 1255    float_type x0, float_type y0, float_type yp0, float_type ypp0, 
 
 1256    float_type x2, float_type y2, float_type yp2, float_type ypp2, 
 
 1257    bool auto_center, float_type y1)
 
 1258    : c2_function<float_type>()
 
 1260    c2_fblock<float_type> fb0, fb2;
 
 1261    fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0;
 
 1262    fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2;   
 
 1263    init(fb0, fb2, auto_center, y1);
 
 1266 template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
 
 1267    const c2_fblock<float_type> &fb0,
 
 1268    const c2_fblock<float_type> &fb2,
 
 1269    bool auto_center, float_type y1)
 
 1270    : c2_function<float_type>()
 
 1272    init(fb0, fb2, auto_center, y1);
 
 1275 template <typename float_type> void c2_connector_function_p<float_type>::init(
 
 1276    const c2_fblock<float_type> &fb0,
 
 1277    const c2_fblock<float_type> &fb2,
 
 1278    bool auto_center, float_type y1)
 
 1280    float_type dx=(fb2.x-fb0.x)/2.0;
 
 1283    // scale derivs to put function on [-1,1] since mma  solution is done this way
 
 1284    float_type yp0=fb0.yp*dx;
 
 1285    float_type yp2=fb2.yp*dx;
 
 1286    float_type ypp0=fb0.ypp*dx*dx;
 
 1287    float_type ypp2=fb2.ypp*dx*dx;
 
 1289    float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625;
 
 1290    if(auto_center) y1=ff0; // forces ff to be 0 if we are auto-centering
 
 1292    // y[x_] = y1 + x (a + b x) + x [(x-1) (x+1)] (c + d x) + x (x-1)^2 (x+1)^2 (e + f x)
 
 1293    // y' = a + 2 b x + d x [(x+1)(x-1)] + (c + d x)(3x^2-1) + f x [(x+1)(x-1)]^2 + (e + f x)[(x+1)(x-1)](5x^2-1)  
 
 1294    // y'' = 2 b + 6x(c + d x) + 2d(3x^2-1) + 4x(e + f x)(5x^2-3) + 2f(x^2-1)(5x^2-1)
 
 1296    fa=(fb2.y - fb0.y)*0.5;
 
 1297    fb=(fb0.y + fb2.y)*0.5 - y1;
 
 1298    fc=(yp2+yp0-2.*fa)*0.25;
 
 1299    fd=(yp2-yp0-4.*fb)*0.25;
 
 1300    fe=(ypp2-ypp0-12.*fc)*0.0625;
 
 1302    this->set_domain(fb0.x, fb2.x); // this is where the function is valid
 
 1305 template <typename float_type> c2_connector_function_p<float_type>::~c2_connector_function_p() 
 
 1309 template <typename float_type> float_type c2_connector_function_p<float_type>::value_with_derivatives(
 
 1310    float_type x, float_type *yprime, float_type *yprime2
 
 1311    ) const throw(c2_exception)
 
 1313    float_type x0=this->xmin(), x2=this->xmax();
 
 1314    float_type dx=(x-(x0+x2)*0.5)*fhinv;
 
 1315    float_type q1=(x-x0)*(x-x2)*fhinv*fhinv; // exactly vanish all bits at both ends
 
 1316    float_type q2=dx*q1;
 
 1318    float_type r1=fa+fb*dx;
 
 1319    float_type r2=fc+fd*dx;
 
 1320    float_type r3=fe+ff*dx;
 
 1322    float_type y=fy1+dx*r1+q2*r2+q1*q2*r3;
 
 1324    if(yprime || yprime2) {
 
 1325            float_type q3=3*q1+2;
 
 1326            float_type q4=5*q1+4;
 
 1327            if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv;
 
 1328            if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4+r3*(2*dx*(5*q1+2)))*fhinv*fhinv;
 
 1333 // the recursive part of the sampler is agressively designed to minimize copying of data... lots of pointers
 
 1334 template <typename float_type> void c2_function<float_type>::sample_step(c2_sample_recur &rb) const throw(c2_exception)
 
 1336    std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion
 
 1340    top.depth=0; top.done=false; top.f0index=0; top.f2index=0;
 
 1342    // push storage for our initial elements
 
 1343    rb_stack.push_back(top);
 
 1344    rb_stack.back().f1=*rb.f0;
 
 1345    rb_stack.back().done=true;
 
 1347    rb_stack.push_back(top);
 
 1348    rb_stack.back().f1=*rb.f1;
 
 1349    rb_stack.back().done=true;
 
 1352            rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
 
 1353            rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
 
 1357    // now, push our first real element
 
 1358    top.f0index=0; // left element is stack[0]
 
 1359    top.f2index=1; // right element is stack[1]
 
 1360    rb_stack.push_back(top);
 
 1362    while(rb_stack.size() > 2) {
 
 1363            recur_item &back=rb_stack.back();
 
 1365                    rb_stack.pop_back();
 
 1370            c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1; 
 
 1371            c2_fblock<float_type> &f1=back.f1; // will hold new middle values
 
 1372            size_t f1index=rb_stack.size()-1; // our current offset
 
 1374            // std::cout << "processing: " << rb_stack.size() << " " << 
 
 1375            // (&back-&rb_stack.front())  << " " << back.depth << " " << f0.x << " " << f2.x << std::endl;
 
 1377            f1.x=0.5*(f0.x + f2.x); // center of interval
 
 1378            float_type dx2=0.5*(f2.x - f0.x);
 
 1380            // check for underflow on step size, which prevents us from achieving specified accuracy. 
 
 1381            if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) {
 
 1382                    std::ostringstream outstr;
 
 1383                    outstr << "Step size underflow in adaptive_sampling at depth=" << back.depth << ", x= " << f1.x;
 
 1384                    throw c2_exception(outstr.str().c_str());
 
 1389            if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) {
 
 1390                    // can't go any further if a nan has appeared
 
 1392                    throw c2_exception("NaN encountered while sampling function"); 
 
 1397                    // this is code from connector_function to compute the value at the midpoint
 
 1398                    // it is re-included here to avoid constructing a complete c2connector
 
 1399                    // just to find out if we are close enough
 
 1400                    float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625;
 
 1401                    // we are converging as at least x**5 and bisecting, so real error on final step is smaller
 
 1402                    eps=std::abs(ff0-f1.y)/32.0;   
 
 1404                    // there are two tolerances to meet... the shift in the estimate of the actual point,
 
 1405                    // and the difference between the current points and the extremum
 
 1406                    // build all the coefficients needed to construct the local parabola
 
 1407                    float_type ypcenter, ypp;
 
 1409                            // linear extrapolation error using exact derivs
 
 1410                            eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125; 
 
 1411                            ypcenter=2*f1.yp*dx2; // first deriv scaled so this interval is on [-1,1]
 
 1412                            ypp=2*(f2.yp-f0.yp)*dx2*dx2; // second deriv estimate scaled so this interval is on [-1,1]
 
 1414                            // linear interpolation error without derivs if we are at top level
 
 1415                            //  or 3-point parabolic interpolation estimates from previous level, if available
 
 1416                            ypcenter=(f2.y-f0.y)*0.5; // derivative estimate at center
 
 1417                            ypp=(f2.y+f0.y-2*f1.y); // second deriv estimate
 
 1418                            if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2; // penalize first step
 
 1419                            else eps=std::abs(f1.y-back.previous_estimate)*0.25; 
 
 1421                    float_type ypleft=ypcenter-ypp; // derivative at left edge
 
 1422                    float_type ypright=ypcenter+ypp; // derivative at right edge
 
 1423                    float_type extremum_eps=0;
 
 1424                    if((ypleft*ypright) <=0) // y' changes sign if we have an extremum
 
 1426                            // compute position and value of the extremum this way
 
 1427                            float_type xext=-ypcenter/ypp;
 
 1428                            float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp;
 
 1429                            // and then find the the smallest offset of it from a point, looking in the left or right side
 
 1430                            if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext));
 
 1431                            else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext));
 
 1433                    eps=std::max(eps, extremum_eps); // if previous shot was really bad, keep trying
 
 1436            if(eps < rb.abs_tol ||  eps < std::abs(f1.y)*rb.rel_tol) {
 
 1438                            // we've met the tolerance, and are building a function, append two connectors
 
 1439                            rb.out->append_function(
 
 1440                                    *new c2_connector_function_p<float_type>(f0, f1, true, 0.0)
 
 1442                            rb.out->append_function(
 
 1443                                    *new c2_connector_function_p<float_type>(f1, f2, true, 0.0)
 
 1446                    if(rb.xvals && rb.yvals) {
 
 1447                            rb.xvals->push_back(f0.x);
 
 1448                            rb.xvals->push_back(f1.x);
 
 1449                            rb.yvals->push_back(f0.y);
 
 1450                            rb.yvals->push_back(f1.y);
 
 1451                            // the value at f2 will get pushed in the next segment... it is not forgotten
 
 1454                    top.depth=back.depth+1; // increment depth counter
 
 1456                    // save the last things we need from back before a push happens, in case 
 
 1457                    // the push causes a reallocation and moves the whole stack.
 
 1458                    size_t f0index=back.f0index, f2index=back.f2index;
 
 1459                    float_type left=0, right=0;
 
 1461                            // compute three-point parabolic interpolation estimate of right-hand and left-hand midpoint
 
 1462                            left=(6*f1.y + 3*f0.y - f2.y) * 0.125;
 
 1463                            right=(6*f1.y + 3*f2.y - f0.y) * 0.125; 
 
 1466                    top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block                        
 
 1467                    top.previous_estimate=right; 
 
 1468                    rb_stack.push_back(top);
 
 1470                    top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block
 
 1471                    top.previous_estimate=left; 
 
 1472                    rb_stack.push_back(top);
 
 1477 template <typename float_type>  c2_piecewise_function_p<float_type> *
 
 1478    c2_function<float_type>::adaptively_sample(
 
 1479            float_type amin, float_type amax,
 
 1480            float_type abs_tol, float_type rel_tol,
 
 1481            int derivs, std::vector<float_type> *xvals, std::vector<float_type> *yvals) const throw(c2_exception)
 
 1483    c2_fblock<float_type> f0, f2;
 
 1485    std::vector< recur_item > rb_stack;
 
 1486    rb_stack.reserve(20); // enough for most operations
 
 1487    rb.rb_stack=&rb_stack;
 
 1489    if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>();
 
 1490    c2_ptr<float_type> pieces(*rb.out); // manage this function, if any, so it deletes on an exception
 
 1498    if(xvals && yvals) {
 
 1503    // create xgrid as a automatic-variable copy of the sampling grid so the exception handler correctly 
 
 1505    std::vector<float_type> xgrid;
 
 1506    get_sampling_grid(amin, amax, xgrid);
 
 1507    int np=xgrid.size();     
 
 1511    if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
 
 1512            // can't go any further if a nan has appeared
 
 1514            throw c2_exception("NaN encountered while sampling function"); 
 
 1517    for(int i=0; i<np-1; i++) {
 
 1518            f0=f2; // copy upper bound to lower before computing new upper bound
 
 1522            if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
 
 1523                    // can't go any further if a nan has appeared
 
 1525                    throw c2_exception("NaN encountered while sampling function"); 
 
 1528            rb.f0=&f0; rb.f1=&f2;
 
 1531    if(xvals && yvals) { // push final point in vector
 
 1532            xvals->push_back(f2.x);
 
 1533            yvals->push_back(f2.y);
 
 1536    if(rb.out) rb.out->set_sampling_grid(xgrid); // reflect old sampling grid, which still should be right
 
 1537    pieces.release_for_return(); // unmanage the piecewise_function so we can return it