Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-23 09:21:02

0001 //
0002 // ********************************************************************
0003 // * License and Disclaimer                                           *
0004 // *                                                                  *
0005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
0006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
0007 // * conditions of the Geant4 Software License,  included in the file *
0008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
0009 // * include a list of copyright holders.                             *
0010 // *                                                                  *
0011 // * Neither the authors of this software system, nor their employing *
0012 // * institutes,nor the agencies providing financial support for this *
0013 // * work  make  any representation or  warranty, express or implied, *
0014 // * regarding  this  software system or assume any liability for its *
0015 // * use.  Please see the license in the file  LICENSE  and URL above *
0016 // * for the full disclaimer and the limitation of liability.         *
0017 // *                                                                  *
0018 // * This  code  implementation is the result of  the  scientific and *
0019 // * technical work of the GEANT4 collaboration.                      *
0020 // * By using,  copying,  modifying or  distributing the software (or *
0021 // * any work based  on the software)  you  agree  to acknowledge its *
0022 // * use  in  resulting  scientific  publications,  and indicate your *
0023 // * acceptance of all terms of the Geant4 Software license.          *
0024 // ********************************************************************
0025 //
0026 //
0027 /**
0028  *  \file electromagnetic/TestEm7/include/c2_function.icc
0029  *  \brief Provides code for the general c2_function algebra which supports 
0030  *  fast, flexible operations on piecewise-twice-differentiable functions
0031  *
0032  *  \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05.
0033  *  \author Copyright 2005 __Vanderbilt University__. All rights reserved.
0034  *
0035  *\version c2_function.cc 488 2012-04-04 11:30:21Z marcus 
0036  */
0037  
0038  //
0039 
0040 #include <iostream>
0041 #include <vector>
0042 #include <algorithm>
0043 #include <cstdlib>
0044 #include <numeric>
0045 #include <functional>
0046 #include <iterator>
0047 #include <cmath>
0048 #include <limits>
0049 #include <sstream>
0050 
0051 template <typename float_type> const std::string 
0052  c2_function<float_type>::cvs_file_vers() const 
0053 { return "c2_function.cc 488 2012-04-04 11:30:21Z marcus "; }
0054 
0055 template <typename float_type> float_type 
0056  c2_function<float_type>::find_root(float_type lower_bracket, 
0057  float_type upper_bracket, 
0058  float_type start, float_type value, int *error,
0059  float_type *final_yprime, float_type *final_yprime2) const 
0060  /* throw(c2_exception) */ 
0061 {
0062 // find f(x)=value within the brackets, using the guarantees of 
0063 // smoothness associated with a c2_function
0064 // can use local f(x)=a*x**2 + b*x + c and solve quadratic to find root, 
0065 // then iterate
0066 
0067   float_type yp, yp2; 
0068   if (!final_yprime) final_yprime=&yp;
0069   if (!final_yprime2) final_yprime2=&yp2;
0070 
0071   float_type ftol=5*(std::numeric_limits<float_type>::epsilon()
0072                    *std::abs(value)+std::numeric_limits<float_type>::min());
0073   float_type xtol=5*(std::numeric_limits<float_type>::epsilon()
0074                    *(std::abs(upper_bracket)+std::abs(lower_bracket))
0075                     +std::numeric_limits<float_type>::min());
0076 
0077   float_type root=start; // start looking in the middle
0078   if(error) *error=0; // start out with error flag set to OK, if it is expected
0079 
0080   float_type c, b;
0081   if(!root_info) {
0082     root_info=new struct c2_root_info;
0083     root_info->inited=false;
0084   }
0085 // this new logic is to keep track of where we were before, 
0086 // and lower the number of 
0087 // function evaluations if we are searching inside the same bracket as before.
0088 // Since this root finder has, very often, the bracket of the entire 
0089 // domain of the function,
0090 // this makes a big difference, especially to c2_inverse_function
0091   if(!root_info->inited || upper_bracket != root_info->upper.x 
0092      || lower_bracket != root_info->lower.x) {
0093     root_info->upper.x=upper_bracket;
0094     fill_fblock(root_info->upper);
0095     root_info->lower.x=lower_bracket;
0096     fill_fblock(root_info->lower);
0097     root_info->inited=true;
0098   }
0099 
0100   float_type clower=root_info->lower.y-value;
0101   if(!clower) {
0102     *final_yprime=root_info->lower.yp;
0103     *final_yprime2=root_info->lower.ypp;
0104     return lower_bracket;
0105   }
0106 
0107   float_type cupper=root_info->upper.y-value;
0108 if(!cupper) {
0109 *final_yprime=root_info->upper.yp;
0110 *final_yprime2=root_info->upper.ypp;
0111 return upper_bracket;
0112 }
0113 const float_type lower_sign = (clower < 0) ? -1 : 1;
0114 
0115 if(lower_sign*cupper >0) { 
0116 // argh, no sign change in here!
0117 if(error) { *error=1; return 0.0; }
0118 else { 
0119 std::ostringstream outstr;
0120 outstr << "unbracketed root in find_root at xlower= " << lower_bracket 
0121        << ", xupper= " << upper_bracket;
0122 outstr << ", value= " << value << ": bailing"; 
0123 throw c2_exception(outstr.str().c_str());
0124 }
0125 }
0126 
0127    float_type delta=upper_bracket-lower_bracket; // first error step
0128    c=value_with_derivatives(root, final_yprime, final_yprime2)-value; 
0129    b=*final_yprime; // make a local copy for readability
0130    increment_evaluations();
0131    
0132    while(
0133  std::abs(delta) > xtol && // absolute x step check
0134  std::abs(c) > ftol && // absolute y tolerance
0135  std::abs(c) > xtol*std::abs(b) 
0136  ) 
0137 {
0138    float_type a=(*final_yprime2)/2; // second derivative is 2*a
0139    float_type disc=b*b-4*a*c;
0140    // std::cout << std::endl << "find_root_debug a,b,c,d " << a 
0141    //<< " " << b << " " << c << " " << disc << std::endl; 
0142    
0143    if(disc >= 0) {
0144    float_type q=-0.5*((b>=0)?(b+std::sqrt(disc)):(b-std::sqrt(disc)));
0145    if(q*q > std::abs(a*c)) delta=c/q; 
0146    // since x1=q/a, x2=c/q, x1/x2=q^2/ac, this picks smaller step
0147    else delta=q/a;
0148    root+=delta;
0149    }
0150    
0151    if(disc < 0 || root<lower_bracket || root>upper_bracket ||
0152       std::abs(delta) >= 0.5*(upper_bracket-lower_bracket)) { 
0153       // if we jump out of the bracket, or aren't converging well, bisect
0154    root=0.5*(lower_bracket+upper_bracket);
0155    delta=upper_bracket-lower_bracket;
0156    }
0157    c=value_with_derivatives(root, final_yprime, final_yprime2)-value; 
0158    // compute initial values
0159    if(c2_isnan(c)) {
0160    bad_x_point=root;
0161    return c; // return the nan if a computation failed
0162    }
0163    b=*final_yprime; // make a local copy for readability
0164    increment_evaluations();
0165 
0166    // now, close in bracket on whichever side this still brackets
0167    if(c*lower_sign < 0.0) {
0168    cupper=c;
0169    upper_bracket=root;
0170    } else {
0171    clower=c;
0172    lower_bracket=root;
0173    }
0174    // std::cout << "find_root_debug x, y, dx " << root << " "  
0175    // << c << " " << delta << std::endl;
0176    }
0177    return root;
0178 }
0179 
0180 /* def partial_integrals(self, xgrid):
0181 Return the integrals of a function between the sampling points xgrid.  
0182 The sum is the definite integral.
0183 This method uses an exact integration of the polynomial which matches 
0184 the values and derivatives at the 
0185 endpoints of a segment.  Its error scales as h**6, if the input functions 
0186 really are smooth.  
0187 This could very well be used as a stepper for adaptive Romberg integration.
0188 For InterpolatingFunctions, it is likely that the Simpson's rule integrator 
0189 is sufficient.
0190 #the weights come from an exact mathematica solution to the 5th order 
0191 polynomial with the given values & derivatives
0192 #yint = (y0+y1)*dx/2 + dx^2*(yp0-yp1)/10 + dx^3 * (ypp0+ypp1)/120 )
0193 */
0194 
0195 // the recursive part of the integrator is agressively designed to 
0196 // minimize copying of data... lots of pointers
0197 template <typename float_type> float_type 
0198 c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const 
0199 /* throw(c2_exception) */
0200 {
0201 std::vector< recur_item > &rb_stack=*rb.rb_stack; 
0202 // heap-based stack of data for recursion
0203 rb_stack.clear();
0204 
0205 recur_item top;
0206 top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0;
0207 
0208 // push storage for our initial elements
0209 rb_stack.push_back(top);
0210 rb_stack.back().f1=*rb.f0;
0211 rb_stack.back().done=true; // this element will never be evaluated further
0212 
0213 rb_stack.push_back(top);
0214 rb_stack.back().f1=*rb.f1;
0215 rb_stack.back().done=true; // this element will never be evaluated further
0216 
0217 if(!rb.inited) {
0218 switch(rb.derivs) {
0219 case 0:
0220 rb.eps_scale=0.1; rb.extrap_coef=16; break;
0221 case 1:
0222 rb.eps_scale=0.1; rb.extrap_coef=64; break;
0223 case 2:
0224 rb.eps_scale=0.02; rb.extrap_coef=1024; break;
0225 default:
0226 throw c2_exception("derivs must be 0, 1 or 2 in partial_integrals");
0227 }
0228 
0229 rb.extrap2=1.0/(rb.extrap_coef-1.0);
0230 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
0231 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
0232 rb.inited=true;
0233 }
0234 
0235 // now, push our first real element
0236 top.f0index=0; // left element is stack[0]
0237 top.f2index=1; // right element is stack[1]
0238 top.abs_tol=rb.abs_tol;
0239 rb_stack.push_back(top);
0240 
0241 while(rb_stack.size() > 2) {
0242 recur_item &back=rb_stack.back();
0243 if(back.done) {
0244 float_type sum=back.step_sum;
0245 rb_stack.pop_back();
0246 rb_stack.back().step_sum+=sum; // bump our sum up to the parent
0247 continue;
0248 }
0249 back.done=true;
0250 
0251 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, 
0252 &f2=rb_stack[back.f2index].f1; 
0253 c2_fblock<float_type> &f1=back.f1; // will hold new middle values
0254 size_t f1index=rb_stack.size()-1; // our current offset
0255 float_type abs_tol=back.abs_tol;
0256 
0257 f1.x=0.5*(f0.x + f2.x); // center of interval
0258 float_type dx2=0.5*(f2.x - f0.x);
0259 
0260 // check for underflow on step size
0261 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance 
0262 || std::abs(dx2) < rb.abs_tol_min) {
0263 std::ostringstream outstr;
0264 outstr << "Step size underflow in adaptive_partial_integrals at depth=" 
0265        << back.depth << ", x= " << f1.x;
0266 throw c2_exception(outstr.str().c_str());
0267 }
0268 
0269 fill_fblock(f1);
0270 if(c2_isnan(f1.y)) {
0271 bad_x_point=f1.x;
0272 return f1.y; // can't go any further if a nan has appeared
0273 }
0274 
0275 bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad;
0276 bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad;
0277 
0278 // select the real derivative count based on whether we are at 
0279 // a point where derivatives exist
0280 int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2);
0281 
0282 if(!back.depth) { // top level, total has not been initialized yet
0283 switch(derivs) { // create estimate of next lower order for first try
0284 case 0:
0285 back.previous_estimate=(f0.y+f2.y)*dx2; break;
0286 case 1:
0287 back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break;
0288 case 2:
0289 back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y) 
0290 +  2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break;
0291 default:
0292 back.previous_estimate=0.0; // just to suppress missing default warnings
0293 }
0294 } 
0295 
0296 float_type left, right;
0297 
0298 // pre-compute constants so all multiplies use a small dynamic range
0299 // note the forced csting of the denominators to assure full precision 
0300 // for (e.g.) long double
0301 // constants for 0 derivative integrator
0302 static const float_type c0c1=5./((float_type)12.), 
0303 c0c2=8./((float_type)12.), c0c3=-1./((float_type)12.);
0304 // constants for 1 derivative integrator
0305 static const float_type c1c1=101./((float_type)240.), 
0306 c1c2=128./((float_type)240.), c1c3=11./((float_type)240.),
0307 c1c4=13./((float_type)240.), c1c5=-40./((float_type)240.), 
0308 c1c6=-3./((float_type)240.);
0309 // constants for 2 derivative integrator
0310 static const float_type c2c1=169./((float_type)40320.), 
0311 c2c2=1024./ ((float_type)40320.), c2c3=-41./((float_type)40320.),
0312 c2c4=2727./((float_type)40320.), c2c5=-5040./((float_type)40320.), 
0313 c2c6=423./((float_type)40320.),
0314 c2c7=17007./((float_type)40320.), c2c8=24576./((float_type)40320.), 
0315 c2c9=-1263./((float_type)40320.);
0316 
0317 switch(derivs) {
0318 case 2:
0319 // use ninth-order estimates for each side, from full set of all values (!)
0320 left=( ( (c2c1*f0.ypp + c2c2*f1.ypp +  c2c3*f2.ypp)*dx2 + 
0321 (c2c4*f0.yp + c2c5*f1.yp +  c2c6*f2.yp) )*dx2 + 
0322   (c2c7*f0.y + c2c8*f1.y +  c2c9*f2.y) )* dx2;
0323 right=( ( (c2c1*f2.ypp + c2c2*f1.ypp +  c2c3*f0.ypp)*dx2 - 
0324 (c2c4*f2.yp + c2c5*f1.yp +  c2c6*f0.yp) )*dx2 + 
0325   (c2c7*f2.y + c2c8*f1.y +  c2c9*f0.y) )* dx2;
0326 // std::cout << f0.x << " " << f1.x << " " << f2.x <<  std::endl ;
0327 // std::cout << f0.y << " " << f1.y << " " << f2.y << " " << left 
0328 // << " " << right << " " << total << std::endl ;
0329 break;
0330 case 1:
0331 left= ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y) 
0332 + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ;
0333 right= ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y) 
0334 - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ;
0335 break;
0336 case 0:
0337 left=(c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2;
0338 right=(c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2;
0339 break;
0340 default:
0341 left=right=0.0; // suppress warnings about missing default
0342 break;
0343 }
0344 
0345 float_type lrsum=left+right;
0346 
0347 bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs); 
0348 // only extrapolate if no trouble with derivs
0349 float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale; 
0350 if(extrapolate) eps*=rb.eps_scale; 
0351 
0352 if(rb.adapt && eps > abs_tol &&  eps > std::abs(lrsum)*rb.rel_tol) {
0353 // tolerance not met, subdivide & recur
0354 if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5; 
0355 // each half has half the error budget
0356 top.abs_tol=abs_tol;
0357 top.depth=back.depth+1;
0358 
0359 // save the last things we need from back before a push happens, in case 
0360 // the push causes a reallocation and moves the whole stack.
0361 size_t f0index=back.f0index, f2index=back.f2index;
0362 
0363 top.f0index=f1index; top.f2index=f2index; 
0364 // insert pointers to right side data into our recursion block
0365 top.previous_estimate=right;
0366 rb_stack.push_back(top);
0367 
0368 top.f0index=f0index; top.f2index=f1index; 
0369 // insert pointers to left side data into our recursion block
0370 top.previous_estimate=left;
0371 rb_stack.push_back(top);
0372 
0373 } else if(extrapolate) {
0374 // extrapolation only happens on leaf nodes, where the tolerance was met.
0375 back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2; 
0376 } else {
0377 back.step_sum+=lrsum; 
0378 }
0379 }
0380 return rb_stack.back().step_sum; // last element on the stack holds the sum
0381 }
0382 
0383 template <typename float_type> bool c2_function<float_type>::check_monotonicity(
0384 const std::vector<float_type> &data, 
0385 const char message[]) const /* throw(c2_exception) */
0386 {
0387 size_t np=data.size();
0388 if(np < 2) return false;  // one point has no direction!
0389 
0390 bool rev=(data[1] < data[0]); // which way do data point?
0391 size_t i;
0392 
0393 if(!rev) for(i = 2; i < np && (data[i-1] < data[i]) ; i++) { }
0394 else for(i = 2; i < np &&(data[i-1] > data[i]) ; i++) { }
0395 
0396 if(i != np) throw c2_exception(message);
0397 
0398 return rev;
0399 }
0400 
0401 template <typename float_type> void 
0402 c2_function<float_type>::set_sampling_grid(const std::vector<float_type> &grid)
0403  /* throw(c2_exception) */
0404 { 
0405 bool rev=this->check_monotonicity(grid, 
0406 "set_sampling_grid: sampling grid not monotonic");
0407 
0408 if(!sampling_grid || no_overwrite_grid) sampling_grid=
0409 new std::vector<float_type>;
0410 (*sampling_grid)=grid; no_overwrite_grid=0;  
0411 
0412 if(rev) std::reverse(sampling_grid->begin(), sampling_grid->end()); 
0413 // make it increasing
0414 }
0415 
0416 template <typename float_type> void c2_function<float_type>::
0417 get_sampling_grid(float_type amin, float_type amax, 
0418 std::vector<float_type> &grid) const 
0419 {
0420 std::vector<float_type> *result=&grid;
0421 result->clear();
0422 
0423 if( !(sampling_grid) || !(sampling_grid->size()) 
0424  || (amax <= sampling_grid->front()) || (amin >= sampling_grid->back()) ) { 
0425 // nothing is known about the function in this region, return amin and amax
0426 result->push_back(amin);
0427 result->push_back(amax);
0428 } else {
0429 std::vector<float_type> &sg=*sampling_grid; // just a shortcut
0430 int np=sg.size();
0431 int klo=0, khi=np-1, firstindex=0, lastindex=np-1;
0432 
0433 result->push_back(amin);
0434 
0435 if(amin > sg.front() ) {
0436 // hunt through table for position bracketing starting point
0437 while(khi-klo > 1) {
0438 int kk=(khi+klo)/2;
0439 if(sg[kk] > amin) khi=kk;
0440 else klo=kk;
0441 }
0442 khi=klo+1;
0443 // khi now points to first point definitively beyond our first point, 
0444 // or last point of array
0445 firstindex=khi;
0446 khi=np-1; // restart upper end of search
0447 }
0448 
0449 if(amax < sg.back()) {
0450 // hunt through table for position bracketing starting point
0451 while(khi-klo > 1) {
0452 int kk=(khi+klo)/2;
0453 if(sg[kk] > amax) khi=kk;
0454 else klo=kk;
0455 }
0456 khi=klo+1;
0457 // khi now points to first point definitively beyond our last point, 
0458 // or last point of array
0459 lastindex=klo;
0460 }
0461 
0462 int initsize=result->size();
0463 result->resize(initsize+(lastindex-firstindex+2));
0464 std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1, 
0465 result->begin()+initsize);
0466 result->back()=amax;
0467 
0468 //  this is the unrefined sampling grid... now check for very close 
0469 // points on front & back and fix if needed.
0470 preen_sampling_grid(result);
0471 }
0472 }
0473 
0474 template <typename float_type> void 
0475 c2_function<float_type>::preen_sampling_grid(std::vector<float_type> *result) 
0476 const
0477 {
0478 //  this is the unrefined sampling grid... 
0479 // now check for very close points on front & back and fix if needed.
0480 if(result->size() > 2) { 
0481 // may be able to prune dangerously close points near the ends 
0482 // if there are at least 3 points
0483 bool deleteit=false;
0484 float_type x0=(*result)[0], x1=(*result)[1];
0485 float_type dx1=x1-x0;
0486 
0487 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
0488 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
0489 if(dx1 < ftol) deleteit=true;
0490 float_type dx2=(*result)[2]-x0;
0491 if(dx1/dx2 < 0.1) deleteit=true; 
0492 // endpoint is very close to internal interesting point
0493 
0494 if(deleteit) result->erase(result->begin()+1); 
0495 // delete redundant interesting point
0496 }
0497 
0498 if(result->size() > 2) { 
0499 // may be able to prune dangerously close points near the ends 
0500 // if there are at least 3 points
0501 bool deleteit=false;
0502 int pos=result->size()-3;
0503 float_type x0=(*result)[pos+1], x1=(*result)[pos+2];
0504 float_type dx1=x1-x0;
0505 
0506 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
0507 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
0508 if(dx1 < ftol) deleteit=true;
0509 float_type dx2=x1-(*result)[pos];
0510 if(dx1/dx2 < 0.1) deleteit=true; 
0511 // endpoint is very close to internal interesting point
0512 
0513 if(deleteit) result->erase(result->end()-2); 
0514 // delete redundant interesting point
0515 }
0516 }
0517 
0518 template <typename float_type> void c2_function<float_type>::
0519 refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const
0520 {
0521 size_t np=grid.size();
0522 size_t count=(np-1)*refinement + 1;
0523 float_type dxscale=1.0/refinement;
0524 
0525 std::vector<float_type> result(count);
0526 
0527 for(size_t i=0; i<(np-1); i++) {
0528 float_type x=grid[i];
0529 float_type dx=(grid[i+1]-x)*dxscale;
0530 for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x;
0531 }
0532 result.back()=grid.back();
0533 grid=result; // copy the expanded grid back to the input
0534 }
0535 
0536 template <typename float_type> float_type 
0537 c2_function<float_type>::integral(float_type amin, float_type amax, 
0538 std::vector<float_type> *partials,
0539  float_type abs_tol, float_type rel_tol, int derivs, 
0540 bool adapt, bool extrapolate) const /* throw(c2_exception) */
0541 {
0542 if(amin==amax) {
0543 if(partials) partials->clear();
0544 return 0.0;
0545 }
0546 std::vector<float_type> grid;
0547 get_sampling_grid(amin, amax, grid);
0548 float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol, 
0549 derivs, adapt, extrapolate);
0550 return intg;
0551 }
0552 
0553 template <typename float_type> c2_function<float_type> 
0554 &c2_function<float_type>::normalized_function(float_type amin, 
0555 float_type amax, float_type norm) 
0556 const /* throw(c2_exception) */
0557 {
0558 float_type intg=integral(amin, amax);
0559 return *new c2_scaled_function_p<float_type>(*this, norm/intg);
0560 }
0561 
0562 template <typename float_type> c2_function<float_type> 
0563 &c2_function<float_type>::square_normalized_function(float_type amin, 
0564 float_type amax, float_type norm)
0565 const /* throw(c2_exception) */
0566 {
0567 c2_ptr<float_type> mesquared((*new 
0568 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this));
0569 
0570 std::vector<float_type> grid;
0571 get_sampling_grid(amin, amax, grid);
0572 float_type intg=mesquared->partial_integrals(grid);
0573 
0574 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
0575 }
0576 
0577 template <typename float_type> c2_function<float_type> 
0578 &c2_function<float_type>::square_normalized_function(
0579 float_type amin, float_type amax, const c2_function<float_type> &weight, 
0580 float_type norm) 
0581 const /* throw(c2_exception) */
0582 {
0583 c2_ptr<float_type> weighted((*new 
0584 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight);
0585 
0586 std::vector<float_type> grid;
0587 get_sampling_grid(amin, amax, grid);
0588 float_type intg=weighted->partial_integrals(grid);
0589 
0590 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
0591 }
0592 
0593 template <typename float_type> float_type 
0594 c2_function<float_type>::partial_integrals(
0595 std::vector<float_type> xgrid, std::vector<float_type> *partials,
0596 float_type abs_tol, float_type rel_tol, int derivs, 
0597 bool adapt, bool extrapolate) 
0598 const /* throw(c2_exception) */
0599 {
0600 int np=xgrid.size();
0601 
0602 c2_fblock<float_type> f0, f2;
0603 struct c2_integrate_recur rb;
0604 rb.rel_tol=rel_tol;
0605 rb.extrapolate=extrapolate;
0606 rb.adapt=adapt;
0607 rb.derivs=derivs;
0608 std::vector< recur_item > rb_stack;
0609 rb_stack.reserve(20); // enough for most operations
0610 rb.rb_stack=&rb_stack;
0611 rb.inited=false;
0612 float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front());
0613 
0614 if(partials) partials->resize(np-1);
0615 
0616 float_type sum=0.0;
0617 
0618 f2.x=xgrid[0];
0619 fill_fblock(f2);
0620 if(c2_isnan(f2.y)) {
0621 bad_x_point=f2.x;
0622 return f2.y; // can't go any further if a nan has appeared
0623 }
0624 
0625 for(int i=0; i<np-1; i++) {
0626 f0=f2; // copy upper bound to lower before computing new upper bound
0627 
0628 f2.x=xgrid[i+1];
0629 fill_fblock(f2);
0630 if(c2_isnan(f2.y)) {
0631 bad_x_point=f2.x;
0632 return f2.y; // can't go any further if a nan has appeared
0633 }
0634 
0635 rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv; 
0636 // distribute error tolerance over whole domain
0637 rb.f0=&f0; rb.f1=&f2; 
0638 float_type psss=integrate_step(rb);
0639 sum+=psss;
0640 if(partials) (*partials)[i]=psss;
0641 if(c2_isnan(psss)) break; // NaN stops integration
0642 }
0643 return sum;
0644 }
0645 
0646 // generate a sampling grid at points separated by dx=5, which is intentionally
0647 // incommensurate with pi and 2*pi so grid errors are somewhat randomized
0648 template <typename float_type> void c2_sin_p<float_type>::
0649 get_sampling_grid(float_type amin, float_type amax,  
0650 std::vector<float_type> &grid) const
0651 {
0652 grid.clear();
0653 for(; amin < amax; amin+=5.0) grid.push_back(amin);
0654 grid.push_back(amax);
0655 this->preen_sampling_grid(&grid);
0656 }
0657 
0658 template <typename float_type> float_type 
0659 c2_function_transformation<float_type>::evaluate(
0660 float_type xraw, 
0661 float_type y, float_type yp0, float_type ypp0,
0662 float_type *yprime, float_type *yprime2) const
0663 {
0664 y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y); 
0665 
0666 if(yprime || yprime2) {
0667 
0668 float_type yp, yp2;
0669 if(X.fHasStaticTransforms && Y.fHasStaticTransforms) {
0670 float_type fpi=1.0/Y.pInPrime(y);
0671 float_type gp=X.pInPrime(xraw);
0672 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
0673 yp=gp*yp0*fpi; // transformed derivative
0674 yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0  - Y.pInDPrime(y)*yp*yp )*fpi; 
0675 } else {
0676 float_type fpi=1.0/Y.fInPrime(y);
0677 float_type gp=X.fInPrime(xraw);
0678 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
0679 yp=gp*yp0*fpi; // transformed derivative
0680 yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0  - Y.fInDPrime(y)*yp*yp )*fpi; 
0681 } 
0682 if(yprime) *yprime=yp;
0683 if(yprime2) *yprime2=yp2;
0684 }
0685 return y;
0686 }
0687 
0688 //  The constructor
0689 template <typename float_type> interpolating_function_p<float_type> 
0690 &interpolating_function_p<float_type>::load(
0691 const std::vector<float_type> &x, const std::vector<float_type> &f, 
0692 bool lowerSlopeNatural, float_type lowerSlope, 
0693 bool upperSlopeNatural, float_type upperSlope,
0694 bool splined
0695 ) /* throw(c2_exception) */ 
0696 {
0697 c2_ptr<float_type> keepme(*this);
0698 X= x;
0699 F= f;
0700 
0701 Xraw=x;
0702 
0703 this->set_domain(std::min(Xraw.front(), Xraw.back()),
0704 std::max(Xraw.front(), Xraw.back()));
0705 
0706 if(x.size() != f.size()) {
0707 throw c2_exception(
0708 "interpolating_function::init() -- x & y inputs are of different size");
0709 }
0710 
0711 size_t np=X.size(); // they are the same now, so lets take a short cut
0712 
0713 if(np < 2) {
0714 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
0715 }
0716 
0717 bool xraw_rev=this->check_monotonicity(Xraw, 
0718 "interpolating_function::init() non-monotonic raw x input"); 
0719 // which way does raw X point?  sampling grid MUST be increasing
0720 
0721 if(!xraw_rev) { 
0722 // we can use pointer to raw X values if they are in the right order
0723 this->set_sampling_grid_pointer(Xraw); 
0724 // our intial grid of x values is certainly a good guess for points
0725 } else {
0726 this->set_sampling_grid(Xraw); 
0727 // make a copy of it, and assure it is in right order
0728 }
0729 
0730 if(fTransform.X.fTransformed) { 
0731 // check if X scale is nonlinear, and if so, do transform
0732 if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]);
0733 if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]);
0734 for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]);
0735 }
0736 if(fTransform.Y.fTransformed) {  
0737 // check if Y scale is nonlinear, and if so, do transform
0738 if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]);
0739 if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]);
0740 for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]);
0741 }
0742   
0743 xInverted=this->check_monotonicity(X, 
0744 "interpolating_function::init() non-monotonic transformed x input"); 
0745 
0746 if(splined) spline(lowerSlopeNatural, lowerSlope, 
0747 upperSlopeNatural, upperSlope);
0748 else y2.assign(np,0.0);
0749 
0750 lastKLow=0; 
0751 keepme.release_for_return();
0752 return *this;
0753 }
0754 
0755 
0756 //  The constructor
0757 template <typename float_type> interpolating_function_p<float_type> 
0758 &interpolating_function_p<float_type>::load_pairs(
0759 std::vector<std::pair<float_type, float_type> > &data, 
0760 bool lowerSlopeNatural, float_type lowerSlope, 
0761 bool upperSlopeNatural, float_type upperSlope,
0762 bool splined
0763 ) /* throw(c2_exception) */ 
0764 {
0765 c2_ptr<float_type> keepme(*this);
0766 
0767 size_t np=data.size();
0768 if(np < 2) {
0769 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
0770 }
0771 
0772 // sort into ascending order
0773 std::sort(data.begin(), data.end(), comp_pair);
0774 
0775 std::vector<float_type> xtmp, ytmp;
0776 xtmp.reserve(np);
0777 ytmp.reserve(np);
0778 for (size_t i=0; i<np; i++) {
0779 xtmp.push_back(data[i].first);
0780 ytmp.push_back(data[i].second);
0781 }
0782 this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope, 
0783 upperSlopeNatural, upperSlope, splined);
0784 
0785 keepme.release_for_return();
0786 return *this;
0787 }
0788 
0789 template <typename float_type> interpolating_function_p<float_type> & 
0790 interpolating_function_p<float_type>::load_random_generator_function(
0791 const std::vector<float_type> &bincenters, 
0792 const c2_function<float_type> &binheights)
0793 /* throw(c2_exception) */
0794 {
0795 c2_ptr<float_type> keepme(*this);
0796 
0797 std::vector<float_type> integral;
0798 c2_const_ptr<float_type> keepit(binheights); 
0799 // integrate from first to last bin in original order, 
0800 // leaving results in integral
0801 // ask for relative error of 1e-6 on each bin, with absolute 
0802 // error set to 0 (since we don't know the data scale).
0803 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6); 
0804 integral.insert(integral.begin(), 0.0); // integral from start to start is 0
0805 float_type scale=1.0/sum;
0806 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale 
0807 + integral[i-1];
0808 integral.back()=1.0; // force exact value on boundary
0809 
0810 this->load(integral, bincenters, 
0811    false, 1.0/(scale*binheights(bincenters.front() )), 
0812    false, 1.0/(scale*binheights(bincenters.back() ))
0813    ); // use integral as x axis in inverse function
0814 keepme.release_for_return();
0815 return *this;
0816 }
0817 
0818 template <typename float_type> interpolating_function_p<float_type> & 
0819 interpolating_function_p<float_type>::load_random_generator_bins(
0820 const std::vector<float_type> &bins, 
0821 const std::vector<float_type> &binheights, bool splined)
0822 /* throw(c2_exception) */
0823 {
0824 c2_ptr<float_type> keepme(*this);
0825 
0826 size_t np=binheights.size();
0827 std::vector<float_type> integral(np+1), bin_edges(np+1);
0828 
0829 // compute the integral based on estimates of the bin edges 
0830 // from the given bin centers...
0831 // except for bin 0 & final bin, the edge of a bin is halfway 
0832 // between then center of the 
0833 // bin and the center of the previous/next bin.
0834 
0835 if(bins.size() == binheights.size()+1) {
0836 bin_edges=bins; // edges array was passed in
0837 } else if (bins.size() == binheights.size()) {
0838 bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5; // edge bin
0839 for(size_t i=1; i<np; i++) {
0840 bin_edges[i]=(bins[i]+bins[i-1])*0.5;
0841 }
0842 bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5; // edge bin
0843 } else {
0844 throw c2_exception(
0845 "inconsistent bin vectors passed to load_random_generator_bins");
0846 }
0847 
0848 float_type running_sum=0.0;
0849 for(size_t i=0; i<np; i++) {
0850 integral[i]=running_sum;
0851 if(!binheights[i]) throw c2_exception(
0852 "empty bin passed to load_random_generator_bins");
0853 running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]);
0854 }
0855 float_type scale=1.0/running_sum;
0856 for(size_t i=0; i<np; i++) integral[i]*=scale;
0857 integral.back()=1.0; // force exactly correct value on boundary
0858 this->load(integral, bin_edges, 
0859   false, 1.0/(scale*binheights.front()), 
0860   false, 1.0/(scale*binheights.back()),
0861   splined); // use integral as x axis in inverse function
0862 keepme.release_for_return();
0863 return *this;
0864 }
0865 
0866 
0867 //  The spline table generator
0868 template <typename float_type> void 
0869 interpolating_function_p<float_type>::spline(
0870  bool lowerSlopeNatural, float_type lowerSlope, 
0871  bool upperSlopeNatural, float_type upperSlope
0872  ) /* throw(c2_exception) */ 
0873 {
0874 // construct spline tables here.  
0875 // this code is a re-translation of the pythonlabtools spline 
0876 // algorithm from pythonlabtools.sourceforge.net
0877 size_t np=X.size();
0878 std::vector<float_type> u(np),  dy(np-1), dx(np-1), 
0879 dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1);
0880 
0881 std::transform(X.begin()+1, X.end(), X.begin(), dx.begin(), 
0882 std::minus<float_type>() ); // dx=X[1:] - X [:-1]
0883 for(size_t i=0; i<dxi.size(); i++) dxi[i]=1.0/dx[i]; // dxi = 1/dx
0884 for(size_t i=0; i<dx2i.size(); i++) dx2i[i]=1.0/(X[i+2]-X[i]); 
0885 
0886 std::transform(F.begin()+1, F.end(), F.begin(), dy.begin(), 
0887 std::minus<float_type>() ); // dy = F[i+1]-F[i]
0888 std::transform(dx2i.begin(), dx2i.end(), dx.begin(), siga.begin(), 
0889 std::multiplies<float_type>()); // siga = dx[:-1]*dx2i
0890 std::transform(dxi.begin(), dxi.end(), dy.begin(), dydx.begin(), 
0891 std::multiplies<float_type>()); // dydx=dy/dx
0892 
0893 // u[i]=(y[i+1]-y[i])/float(x[i+1]-x[i]) - (y[i]-y[i-1])/float(x[i]-x[i-1])
0894 std::transform(dydx.begin()+1, dydx.end(), dydx.begin(), u.begin()+1, 
0895 std::minus<float_type>() ); // incomplete rendering of u = dydx[1:]-dydx[:-1]
0896 
0897 y2.resize(np,0.0);  
0898 
0899 if(lowerSlopeNatural) {
0900 y2[0]=u[0]=0.0;
0901 } else {
0902 y2[0]= -0.5;
0903 u[0]=(3.0*dxi[0])*(dy[0]*dxi[0] -lowerSlope);
0904 }
0905 
0906 for(size_t i=1; i < np -1; i++) { // the inner loop
0907 float_type sig=siga[i-1];
0908 float_type p=sig*y2[i-1]+2.0;
0909 y2[i]=(sig-1.0)/p;
0910 u[i]=(6.0*u[i]*dx2i[i-1] - sig*u[i-1])/p;
0911 }
0912 
0913 float_type qn, un;
0914 
0915 if(upperSlopeNatural) {
0916 qn=un=0.0;
0917 } else {
0918 qn= 0.5;
0919 un=(3.0*dxi[dxi.size()-1])*(upperSlope- dy[dy.size()-1]*dxi[dxi.size()-1] );
0920 }
0921 
0922 y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0);
0923 for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1];
0924 }
0925 
0926 template <typename float_type> interpolating_function_p<float_type> 
0927 &interpolating_function_p<float_type>::sample_function(
0928 const c2_function<float_type> &func,
0929 float_type amin, float_type amax, float_type abs_tol, float_type rel_tol, 
0930 bool lowerSlopeNatural, float_type lowerSlope, 
0931 bool upperSlopeNatural, float_type upperSlope
0932  ) /* throw(c2_exception) */ 
0933 { 
0934 c2_ptr<float_type> keepme(*this);
0935 
0936 const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y;
0937 
0938  // set up our params to look like the samplng function for now
0939  sampler_function=func;
0940  std::vector<float_type> grid;
0941  func.get_sampling_grid(amin, amax, grid);
0942  size_t gsize=grid.size();
0943  if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]);
0944  this->set_sampling_grid_pointer(grid);
0945  
0946 this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol, 
0947 8*rel_tol, 0, &X, &F);
0948 // clear the sampler function now, since otherwise our 
0949 // value_with_derivatives is broken
0950  sampler_function.unset_function();
0951  
0952  xInverted=this->check_monotonicity(X, 
0953   "interpolating_function::init() non-monotonic transformed x input"); 
0954  
0955  size_t np=X.size();
0956  
0957  // Xraw is useful in some of the arithmetic operations between
0958 //  interpolating functions
0959  if(!XX.fTransformed) Xraw=X;
0960  else {
0961  Xraw.resize(np);
0962  for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]);
0963  Xraw.front()=amin;
0964  Xraw.back()=amax;
0965  }
0966   
0967  bool xraw_rev=this->check_monotonicity(Xraw, 
0968   "interpolating_function::init() non-monotonic raw x input"); 
0969   // which way does raw X point?  sampling grid MUST be increasing
0970  
0971  if(!xraw_rev) { 
0972  this->set_sampling_grid_pointer(Xraw); 
0973 
0974  } else {
0975  this->set_sampling_grid(Xraw); 
0976  }
0977  
0978  if(XX.fTransformed) { // check if X scale is nonlinear, and if so, do transform
0979  if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(amin);
0980  if(!upperSlopeNatural) upperSlope /= XX.fInPrime(amax);
0981  }
0982  if(YY.fTransformed) { 
0983  if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(amin));
0984  if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(amax));
0985  }
0986  // note that each of ends has 3 points with two equal gaps, 
0987 // since they were obtained by bisection
0988  // so the step sizes are easy to get
0989  // the 'natural slope' option for sampled functions has a 
0990 // different meaning than 
0991  // for normal splines.  In this case, the derivative is adjusted to make the
0992  // second derivative constant on the last two points at each end
0993  // which is consistent with the error sampling technique we used to get here
0994  if(lowerSlopeNatural) {
0995  float_type hlower=X[1]-X[0];
0996  lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower;
0997  lowerSlopeNatural=false; // it's not the usual meaning of natural any more 
0998  }
0999  if(upperSlopeNatural) {
1000  float_type hupper=X[np-1]-X[np-2];
1001  upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper;
1002  upperSlopeNatural=false; // it's not the usual meaning of natural any more 
1003  }
1004  this->set_domain(amin, amax);
1005  
1006  spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
1007  lastKLow=0;
1008  keepme.release_for_return();
1009  return *this;
1010 }
1011 
1012 //  This function is the reason for this class to exist
1013 // it computes the interpolated function, and (if requested) its proper 
1014 // first and second derivatives including all coordinate transforms
1015 template <typename float_type> float_type 
1016 interpolating_function_p<float_type>::value_with_derivatives(
1017 float_type x, float_type *yprime, float_type *yprime2) const /* throw(c2_exception) */
1018 {
1019 if(sampler_function.valid()) { 
1020 // if this is non-null, we are sampling data for later, 
1021 // so just return raw function
1022 // however, transform it into our sampling space, first.
1023 if(yprime) *yprime=0;
1024 if(yprime2) *yprime2=0;
1025 sampler_function->increment_evaluations();
1026 return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x))); 
1027 // derivatives are completely undefined
1028 }
1029 
1030 if(x < this->xmin() || x > this->xmax()) {
1031 std::ostringstream outstr;
1032 outstr << "Interpolating function argument " << x 
1033 << " out of range " << this->xmin() << " -- " << this ->xmax() << ": bailing";
1034 throw c2_exception(outstr.str().c_str());
1035     }
1036 
1037 float_type xraw=x;
1038 
1039 if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms? 
1040 fTransform.X.pIn(x) : fTransform.X.fIn(x); 
1041 // save time by explicitly testing for identity function here
1042 
1043 int klo=0, khi=X.size()-1;
1044 
1045 if(khi < 0) throw c2_exception(
1046 "Uninitialized interpolating function being evaluated");
1047 
1048 const float_type *XX=&X[lastKLow]; 
1049 // make all fast checks short offsets from here
1050 
1051 if(!xInverted) { 
1052 // select search depending on whether transformed X is increasing or decreasing
1053 if((XX[0] <= x) && (XX[1] >= x) ) { // already bracketed
1054 klo=lastKLow;
1055 } else if((XX[1] <= x) && (XX[2] >= x)) { // in next bracket to the right
1056 klo=lastKLow+1;
1057 } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) { 
1058 // in next bracket to the left
1059 klo=lastKLow-1;
1060 } else { // not bracketed, not close, start over
1061  // search for new KLow
1062 while(khi-klo > 1) {
1063 int kk=(khi+klo)/2;
1064 if(X[kk] > x) khi=kk;
1065 else klo=kk;
1066 }
1067 }
1068 } else {
1069 if((XX[0] >= x) && (XX[1] <= x) ) { // already bracketed
1070 klo=lastKLow;
1071 } else if((XX[1] >= x) && (XX[2] <= x)) { // in next bracket to the right
1072 klo=lastKLow+1;
1073 } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) { 
1074 // in next bracket to the left
1075 klo=lastKLow-1;
1076 } else { // not bracketed, not close, start over
1077  // search for new KLow
1078 while(khi-klo > 1) {
1079 int kk=(khi+klo)/2;
1080 if(X[kk] < x) khi=kk;
1081 else klo=kk;
1082 }
1083 }
1084 }
1085 
1086 khi=klo+1;
1087 lastKLow=klo; 
1088 
1089 float_type h=X[khi]-X[klo];
1090 
1091 float_type a=(X[khi]-x)/h; 
1092 float_type b=1.0-a;
1093 float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi];
1094 float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0;
1095 
1096 float_type yp0=0; // the derivative in interpolating table coordinates
1097 float_type ypp0=0; // second derivative
1098 
1099 if(yprime || yprime2) {
1100 yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0; 
1101 // the derivative in interpolating table coordinates
1102 ypp0=b*y2hi+a*y2lo; // second derivative
1103 }
1104 
1105 if(fTransform.isIdentity) {
1106 if(yprime) *yprime=yp0;
1107 if(yprime2) *yprime2=ypp0;
1108 return y;
1109 } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2);
1110 }
1111 
1112 template <typename float_type> void 
1113 interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound) 
1114 {
1115 int kl = 0 ;
1116 int kh=kl+1;
1117 float_type xx=fTransform.X.fIn(bound);
1118 float_type h0=X[kh]-X[kl];
1119 float_type h1=xx-X[kl];
1120 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0 
1121 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1122 
1123 X.insert(X.begin(), xx);
1124 F.insert(F.begin(), yextrap);
1125 y2.insert(y2.begin(), y2.front()); // duplicate first or last element
1126 Xraw.insert(Xraw.begin(), bound);
1127 if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1128 else this->fXMax=bound;
1129 
1130 }
1131 
1132 template <typename float_type> void 
1133 interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound) 
1134 {
1135 int kl = X.size()-2 ;
1136 int kh=kl+1;
1137 float_type xx=fTransform.X.fIn(bound);
1138 float_type h0=X[kh]-X[kl];
1139 float_type h1=xx-X[kl];
1140 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0 
1141 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1142 
1143 X.insert(X.end(), xx);
1144 F.insert(F.end(), yextrap);
1145 y2.insert(y2.end(), y2.back()); // duplicate first or last element
1146 Xraw.insert(Xraw.end(), bound);
1147 if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1148 else this->fXMax=bound;
1149 //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
1150 
1151 }
1152 
1153 template <typename float_type> interpolating_function_p<float_type>& 
1154 interpolating_function_p<float_type>::unary_operator(
1155 const c2_function<float_type> &source) const 
1156 {
1157 size_t np=X.size();
1158 std::vector<float_type>yv(np);
1159 c2_ptr<float_type> comp(source(*this));
1160 float_type yp0, yp1, ypp;
1161 
1162 for(size_t i=1; i<np-1; i++) { 
1163 yv[i]=source(fTransform.Y.fOut(F[i])); 
1164 }
1165 
1166 yv.front()=comp(Xraw.front(), &yp0, &ypp); // get derivative at front
1167 yv.back()= comp(Xraw.back(), &yp1, &ypp); // get derivative at back
1168 
1169 interpolating_function_p &copy=clone();
1170 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1171 
1172 return copy;
1173 }
1174 
1175 template <typename float_type> void 
1176 interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals, 
1177 std::vector<float_type> &yvals) const /*throw()*/
1178 {
1179 
1180 xvals=Xraw;
1181 yvals.resize(F.size());
1182 
1183 for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]);
1184 }
1185 
1186 template <typename float_type> interpolating_function_p<float_type> & 
1187 interpolating_function_p<float_type>::binary_operator(const 
1188 c2_function<float_type> &rhs,
1189 const c2_binary_function<float_type> *combining_stub) const
1190 {
1191 size_t np=X.size();
1192 std::vector<float_type> yv(np);
1193 c2_constant_p<float_type> fval(0);
1194 float_type yp0, yp1, ypp;
1195 
1196 c2_const_ptr<float_type> stub(*combining_stub);  // manage ownership
1197 
1198 for(size_t i=1; i<np-1; i++) { 
1199 fval.reset(fTransform.Y.fOut(F[i])); // update the constant function pointwise
1200 yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0, 
1201 (float_type *)0); // compute rhs & combine without derivatives
1202 }
1203 
1204 yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp); 
1205 yv.back()= combining_stub->combine(*this, rhs, Xraw.back(),  &yp1, &ypp); 
1206 
1207 interpolating_function_p &copy=clone();
1208 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1209 
1210 return copy;
1211 }
1212 
1213 template <typename float_type> 
1214 c2_inverse_function_p<float_type>::c2_inverse_function_p(
1215 const c2_function<float_type> &source) 
1216 : c2_function<float_type>(), func(source)
1217 {
1218 float_type l=source.xmin();
1219 float_type r=source.xmax();
1220 start_hint=(l+r)*0.5; // guess that we start in the middle
1221 
1222 float_type ly=source(l);
1223 float_type ry=source(r);
1224 if (ly > ry) {
1225 float_type t=ly; ly=ry; ry=t; 
1226 }
1227 this->set_domain(ly, ry);
1228 }
1229 
1230 template <typename float_type> float_type 
1231 c2_inverse_function_p<float_type>::value_with_derivatives(
1232 float_type x, float_type *yprime, float_type *yprime2
1233 ) const /* throw(c2_exception) */
1234 {
1235 float_type l=this->func->xmin();
1236 float_type r=this->func->xmax();
1237 float_type yp, ypp;
1238 float_type y=this->func->find_root(l, r, get_start_hint(x), x, 0, &yp, &ypp);
1239 start_hint=y;
1240 if(yprime) *yprime=1.0/yp;
1241 if(yprime2) *yprime2=-ypp/(yp*yp*yp);
1242 return y;
1243 }
1244 
1245 template <typename float_type> 
1246 accumulated_histogram<float_type>::accumulated_histogram(
1247 const std::vector<float_type>binedges, const std::vector<float_type> binheights,
1248 bool normalize, bool inverse_function, bool drop_zeros) 
1249 {
1250 
1251 int np=binheights.size();
1252 
1253 std::vector<float_type> be, bh;
1254 if(drop_zeros || inverse_function) { 
1255 if(binheights[0] || !inverse_function) { 
1256 be.push_back(binedges[0]);
1257 bh.push_back(binheights[0]);
1258 }
1259 for(int i=1; i<np-1; i++) {
1260 if(binheights[i]) {
1261 be.push_back(binedges[i]);
1262 bh.push_back(binheights[i]);
1263 }
1264 }
1265 if(binheights[np-1] || !inverse_function) {
1266 bh.push_back(binheights[np-1]);
1267 be.push_back(binedges[np-1]);
1268 be.push_back(binedges[np]); // push both sides of the last bin if needed
1269 }
1270 np=bh.size(); // set np to compressed size of bin array
1271 } else {
1272 be=binedges;
1273 bh=binheights;
1274 }
1275 std::vector<float_type> cum(np+1, 0.0);
1276 for(int i=1; i<=np; i++) cum[i]=bh[i]*(be[i]-be[i-1])+cum[i-1]; 
1277 // accumulate bins, leaving bin 0 as 0
1278 if(be[1] < be[0]) { // if bins passed in backwards, reverse them
1279 std::reverse(be.begin(), be.end());
1280 std::reverse(cum.begin(), cum.end());
1281 for(unsigned int i=0; i<cum.size(); i++) cum[i]*=-1; 
1282 // flip sign on reversed data
1283 }
1284 if(normalize) {
1285 float_type mmm=1.0/std::max(cum[0], cum[np]);
1286 for(int i=0; i<=np; i++) cum[i]*=mmm;
1287 }
1288 if(inverse_function) interpolating_function_p<float_type>(cum, be); 
1289 // use cum as x axis in inverse function
1290 else interpolating_function_p<float_type>(be, cum); 
1291 // else use lower bin edge as x axis
1292 std::fill(this->y2.begin(), this->y2.end(), 0.0); 
1293 // clear second derivatives, to we are piecewise linear
1294 }
1295 
1296 template <typename float_type> 
1297 c2_piecewise_function_p<float_type>::c2_piecewise_function_p() 
1298 : c2_function<float_type>(), lastKLow(-1)
1299 {
1300 this->sampling_grid=new std::vector<float_type>; 
1301 }
1302 
1303 template <typename float_type> 
1304 c2_piecewise_function_p<float_type>::~c2_piecewise_function_p() 
1305 {
1306 }
1307 
1308 template <typename float_type> float_type 
1309 c2_piecewise_function_p<float_type>::value_with_derivatives(
1310   float_type x, float_type *yprime, float_type *yprime2
1311   ) const /* throw(c2_exception) */
1312 {
1313 
1314 size_t np=functions.size();
1315 if(!np) throw c2_exception(
1316 "attempting to evaluate an empty piecewise function");
1317 
1318 if(x < this->xmin() || x > this->xmax()) {
1319 std::ostringstream outstr;
1320 outstr << "piecewise function argument " << x << " out of range " 
1321 << this->xmin() << " -- " << this->xmax();
1322 throw c2_exception(outstr.str().c_str());
1323 }
1324 
1325 int klo=0;
1326 
1327 if(lastKLow >= 0 && functions[lastKLow]->xmin() <= x 
1328 && functions[lastKLow]->xmax() > x) {
1329 klo=lastKLow;
1330 } else {
1331 int khi=np;
1332 while(khi-klo > 1) {
1333 int kk=(khi+klo)/2;
1334 if(functions[kk]->xmin() > x) khi=kk;
1335 else klo=kk;
1336 }
1337 }
1338 lastKLow=klo;
1339 return functions[klo]->value_with_derivatives(x, yprime, yprime2);
1340 }
1341 
1342 template <typename float_type> void 
1343 c2_piecewise_function_p<float_type>::append_function(
1344 const c2_function<float_type> &func) /* throw(c2_exception) */
1345 {
1346 c2_const_ptr<float_type> keepfunc(func); 
1347 if(functions.size()) { // check whether there are any gaps to fill, etc.
1348 const c2_function<float_type> &tail=functions.back();
1349 float_type x0=tail.xmax();
1350 float_type x1=func.xmin();
1351 if(x0 < x1) {
1352 // must insert a connector if x0 < x1
1353 float_type y0=tail(x0);
1354 float_type y1=func(x1);
1355 c2_function<float_type> &connector=
1356 *new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0));
1357 connector.set_domain(x0,x1);
1358 functions.push_back(c2_const_ptr<float_type>(connector));
1359 this->sampling_grid->push_back(x1);
1360 } else if(x0>x1) throw c2_exception(
1361 "function domains not increasing in c2_piecewise_function");
1362 }
1363 functions.push_back(keepfunc);
1364 // extend our domain to include all known functions
1365 this->set_domain(functions.front()->xmin(), functions.back()->xmax());
1366 // extend our sampling grid with the new function's grid, 
1367 // with the first point dropped to avoid duplicates
1368 std::vector<float_type> newgrid;
1369 func.get_sampling_grid(func.xmin(), func.xmax(), newgrid);
1370 this->sampling_grid->insert(this->sampling_grid->end(), 
1371 newgrid.begin()+1, newgrid.end());
1372 }
1373 
1374 template <typename float_type> 
1375 c2_connector_function_p<float_type>::c2_connector_function_p(
1376 float_type x0, const c2_function<float_type> &f0, float_type x2, 
1377 const c2_function<float_type> &f2, 
1378 bool auto_center, float_type y1)
1379 : c2_function<float_type>()
1380 {
1381 c2_const_ptr<float_type> left(f0), right(f2); 
1382 c2_fblock<float_type> fb0, fb2;
1383 fb0.x=x0;
1384 f0.fill_fblock(fb0);
1385 fb2.x=x2;
1386 f2.fill_fblock(fb2);
1387 init(fb0, fb2, auto_center, y1);
1388 }
1389 
1390 template <typename float_type> 
1391 c2_connector_function_p<float_type>::c2_connector_function_p(
1392 float_type x0, float_type y0, float_type yp0, float_type ypp0, 
1393 float_type x2, float_type y2, float_type yp2, float_type ypp2, 
1394 bool auto_center, float_type y1)
1395 : c2_function<float_type>()
1396 {
1397 c2_fblock<float_type> fb0, fb2;
1398 fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0;
1399 fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2;
1400 init(fb0, fb2, auto_center, y1);
1401 }
1402 
1403 template <typename float_type> 
1404 c2_connector_function_p<float_type>::c2_connector_function_p(
1405 const c2_fblock<float_type> &fb0,
1406 const c2_fblock<float_type> &fb2,
1407 bool auto_center, float_type y1)
1408 : c2_function<float_type>()
1409 {
1410 init(fb0, fb2, auto_center, y1);
1411 }
1412 
1413 template <typename float_type> void c2_connector_function_p<float_type>::init(
1414 const c2_fblock<float_type> &fb0,
1415 const c2_fblock<float_type> &fb2,
1416 bool auto_center, float_type y1)
1417 {
1418 float_type dx=(fb2.x-fb0.x)/2.0;
1419 fhinv=1.0/dx;
1420 
1421 // scale derivs to put function on [-1,1] since mma  solution is done this way
1422 float_type yp0=fb0.yp*dx;
1423 float_type yp2=fb2.yp*dx;
1424 float_type ypp0=fb0.ypp*dx*dx;
1425 float_type ypp2=fb2.ypp*dx*dx;
1426 
1427 float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625;
1428 if(auto_center) y1=ff0; // forces ff to be 0 if we are auto-centering
1429 
1430 // y[x_] = y1 + x (a + b x) + x [(x-1) (x+1)] (c + d x) 
1431 // + x (x-1)^2 (x+1)^2 (e + f x)
1432 // y' = a + 2 b x + d x [(x+1)(x-1)] + (c + d x)(3x^2-1) 
1433 // + f x [(x+1)(x-1)]^2 + (e + f x)[(x+1)(x-1)](5x^2-1)  
1434 // y'' = 2 b + 6x(c + d x) + 2d(3x^2-1) + 4x(e + f x)(5x^2-3) 
1435 // + 2f(x^2-1)(5x^2-1)
1436 fy1=y1; 
1437 fa=(fb2.y - fb0.y)*0.5;
1438 fb=(fb0.y + fb2.y)*0.5 - y1;
1439 fc=(yp2+yp0-2.*fa)*0.25;
1440 fd=(yp2-yp0-4.*fb)*0.25;
1441 fe=(ypp2-ypp0-12.*fc)*0.0625;
1442 ff=(ff0 - y1);
1443 this->set_domain(fb0.x, fb2.x); // this is where the function is valid
1444 }
1445 
1446 template <typename float_type> 
1447 c2_connector_function_p<float_type>::~c2_connector_function_p() 
1448 {
1449 }
1450 
1451 template <typename float_type> float_type 
1452 c2_connector_function_p<float_type>::value_with_derivatives(
1453 float_type x, float_type *yprime, float_type *yprime2
1454 ) const /* throw(c2_exception) */
1455 {
1456 float_type x0=this->xmin(), x2=this->xmax();
1457 float_type dx=(x-(x0+x2)*0.5)*fhinv;
1458 float_type q1=(x-x0)*(x-x2)*fhinv*fhinv; 
1459 float_type q2=dx*q1;
1460 
1461 float_type r1=fa+fb*dx;
1462 float_type r2=fc+fd*dx;
1463 float_type r3=fe+ff*dx;
1464 
1465 float_type y=fy1+dx*r1+q2*r2+q1*q2*r3;
1466 
1467 if(yprime || yprime2) {
1468 float_type q3=3*q1+2;
1469 float_type q4=5*q1+4;
1470 if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv;
1471 if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4
1472 +r3*(2*dx*(5*q1+2)))*fhinv*fhinv;
1473 }
1474 return y;
1475 }
1476 
1477 // the recursive part of the sampler is agressively designed 
1478 // to minimize copying of data... lots of pointers
1479 template <typename float_type> void 
1480 c2_function<float_type>::sample_step(c2_sample_recur &rb) const 
1481 /* throw(c2_exception) */
1482 {
1483 std::vector< recur_item > &rb_stack=*rb.rb_stack; 
1484 // heap-based stack of data for recursion
1485 rb_stack.clear();
1486 
1487 recur_item top;
1488 top.depth=0; top.done=false; top.f0index=0; top.f2index=0;
1489 
1490 // push storage for our initial elements
1491 rb_stack.push_back(top);
1492 rb_stack.back().f1=*rb.f0;
1493 rb_stack.back().done=true;
1494 
1495 rb_stack.push_back(top);
1496 rb_stack.back().f1=*rb.f1;
1497 rb_stack.back().done=true;
1498 
1499 if(!rb.inited) {
1500 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
1501 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
1502 rb.inited=true;
1503 }
1504 
1505 // now, push our first real element
1506 top.f0index=0; // left element is stack[0]
1507 top.f2index=1; // right element is stack[1]
1508 rb_stack.push_back(top);
1509 
1510 while(rb_stack.size() > 2) {
1511 recur_item &back=rb_stack.back();
1512 if(back.done) {
1513 rb_stack.pop_back();
1514 continue;
1515 }
1516 back.done=true;
1517 
1518 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, 
1519 &f2=rb_stack[back.f2index].f1; 
1520 c2_fblock<float_type> &f1=back.f1; // will hold new middle values
1521 size_t f1index=rb_stack.size()-1; // our current offset
1522 
1523 
1524 f1.x=0.5*(f0.x + f2.x); // center of interval
1525 float_type dx2=0.5*(f2.x - f0.x);
1526 
1527 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) 
1528 < rb.abs_tol_min) {
1529 std::ostringstream outstr;
1530 outstr << "Step size underflow in adaptive_sampling at depth=" 
1531 << back.depth << ", x= " << f1.x;
1532 throw c2_exception(outstr.str().c_str());
1533 }
1534 
1535 fill_fblock(f1);
1536 
1537 if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) {
1538 // can't go any further if a nan has appeared
1539 bad_x_point=f1.x;
1540 throw c2_exception("NaN encountered while sampling function"); 
1541 }
1542 
1543 float_type eps; 
1544 if(rb.derivs==2) {
1545 // this is code from connector_function to compute the value at the midpoint
1546 // it is re-included here to avoid constructing a complete c2connector
1547 // just to find out if we are close enough
1548 float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2 
1549 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625;
1550 // we are converging as at least x**5 and bisecting, 
1551 // so real error on final step is smaller
1552 eps=std::abs(ff0-f1.y)/32.0;   
1553 } else {
1554 // there are two tolerances to meet... the shift in the estimate 
1555 // of the actual point,
1556 // and the difference between the current points and the extremum
1557 // build all the coefficients needed to construct the local parabola
1558 float_type ypcenter, ypp;
1559 if (rb.derivs==1) {
1560 // linear extrapolation error using exact derivs
1561 eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125; 
1562 ypcenter=2*f1.yp*dx2; // first deriv scaled so this interval is on [-1,1]
1563 ypp=2*(f2.yp-f0.yp)*dx2*dx2; 
1564 } else {
1565 // linear interpolation error without derivs if we are at top level
1566 //  or 3-point parabolic interpolation estimates from previous level
1567 ypcenter=(f2.y-f0.y)*0.5; // derivative estimate at center
1568 ypp=(f2.y+f0.y-2*f1.y); // second deriv estimate
1569 if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2; 
1570 // penalize first step
1571 else eps=std::abs(f1.y-back.previous_estimate)*0.25; 
1572 }
1573 float_type ypleft=ypcenter-ypp; // derivative at left edge
1574 float_type ypright=ypcenter+ypp; // derivative at right edge
1575 float_type extremum_eps=0;
1576 if((ypleft*ypright) <=0) // y' changes sign if we have an extremum
1577 {
1578 // compute position and value of the extremum this way
1579 float_type xext=-ypcenter/ypp;
1580 float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp;
1581 // and then find the the smallest offset of it from a point, 
1582 // looking in the left or right side
1583 if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext));
1584 else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext));
1585 }
1586 eps=std::max(eps, extremum_eps); // if previous shot was really bad, keep trying
1587 }
1588 
1589 if(eps < rb.abs_tol ||  eps < std::abs(f1.y)*rb.rel_tol) {
1590 if(rb.out) {
1591 // we've met the tolerance, and are building a function, append two connectors
1592 rb.out->append_function(
1593 *new c2_connector_function_p<float_type>(f0, f1, true, 0.0)
1594 );
1595 rb.out->append_function(
1596 *new c2_connector_function_p<float_type>(f1, f2, true, 0.0)
1597 );
1598 }
1599 if(rb.xvals && rb.yvals) {
1600 rb.xvals->push_back(f0.x);
1601 rb.xvals->push_back(f1.x);
1602 rb.yvals->push_back(f0.y);
1603 rb.yvals->push_back(f1.y);
1604 // the value at f2 will get pushed in the next segment... it is not forgotten
1605 }
1606 } else {
1607 top.depth=back.depth+1; // increment depth counter
1608 
1609 // save the last things we need from back before a push happens, in case 
1610 // the push causes a reallocation and moves the whole stack.
1611 size_t f0index=back.f0index, f2index=back.f2index;
1612 float_type left=0, right=0;
1613 if(rb.derivs==0) {
1614 // compute three-point parabolic interpolation estimate of right-hand 
1615 // and left-hand midpoint
1616 left=(6*f1.y + 3*f0.y - f2.y) * 0.125;
1617 right=(6*f1.y + 3*f2.y - f0.y) * 0.125; 
1618 }
1619 
1620 top.f0index=f1index; top.f2index=f2index; 
1621 // insert pointers to right side data into our recursion block
1622 top.previous_estimate=right; 
1623 rb_stack.push_back(top);
1624 
1625 top.f0index=f0index; top.f2index=f1index; 
1626 // insert pointers to left side data into our recursion block
1627 top.previous_estimate=left; 
1628 rb_stack.push_back(top);
1629 }
1630 }
1631 }
1632 
1633 template <typename float_type>  c2_piecewise_function_p<float_type> *
1634 c2_function<float_type>::adaptively_sample(
1635 float_type amin, float_type amax,
1636 float_type abs_tol, float_type rel_tol,
1637 int derivs, std::vector<float_type> *xvals, 
1638 std::vector<float_type> *yvals) const /* throw(c2_exception) */ 
1639 {
1640 c2_fblock<float_type> f0, f2;
1641 c2_sample_recur rb;
1642 std::vector< recur_item > rb_stack;
1643 rb_stack.reserve(20); // enough for most operations
1644 rb.rb_stack=&rb_stack;
1645 rb.out=0;
1646 if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>();
1647 c2_ptr<float_type> pieces(*rb.out);
1648 rb.rel_tol=rel_tol;
1649 rb.abs_tol=abs_tol;
1650 rb.xvals=xvals;
1651 rb.yvals=yvals;
1652 rb.derivs=derivs;
1653 rb.inited=false;
1654 
1655 if(xvals && yvals) {
1656 xvals->clear();
1657 yvals->clear();
1658 }
1659  
1660 // create xgrid as a automatic-variable copy of the sampling 
1661 // grid so the exception handler correctly 
1662 // disposes of it.
1663 std::vector<float_type> xgrid;
1664 get_sampling_grid(amin, amax, xgrid);
1665 int np=xgrid.size(); 
1666 
1667 f2.x=xgrid[0];
1668 fill_fblock(f2);
1669 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1670 // can't go any further if a nan has appeared
1671 bad_x_point=f2.x;
1672 throw c2_exception("NaN encountered while sampling function"); 
1673 }
1674 
1675 for(int i=0; i<np-1; i++) {
1676 f0=f2; // copy upper bound to lower before computing new upper bound
1677 
1678 f2.x=xgrid[i+1];
1679 fill_fblock(f2);
1680 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1681 // can't go any further if a nan has appeared
1682 bad_x_point=f2.x;
1683 throw c2_exception("NaN encountered while sampling function"); 
1684 }
1685 
1686 rb.f0=&f0; rb.f1=&f2;
1687 sample_step(rb);
1688 }
1689 if(xvals && yvals) { // push final point in vector
1690 xvals->push_back(f2.x);
1691 yvals->push_back(f2.y);
1692 }
1693 
1694 if(rb.out) rb.out->set_sampling_grid(xgrid); 
1695 // reflect old sampling grid, which still should be right
1696 pieces.release_for_return(); 
1697 // unmanage the piecewise_function so we can return it
1698 return rb.out;
1699 }