source: trunk/examples/extended/electromagnetic/TestEm7/include/c2_function.icc @ 1279

Last change on this file since 1279 was 1230, checked in by garnier, 14 years ago

update to geant4.9.3

File size: 58.6 KB
Line 
1//
2// ********************************************************************
3// * License and Disclaimer                                           *
4// *                                                                  *
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.                             *
10// *                                                                  *
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.         *
17// *                                                                  *
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// ********************************************************************
25//
26//
27/**
28 *  \file
29 *  \brief Provides code for the general c2_function algebra which supports
30 *  fast, flexible operations on piecewise-twice-differentiable functions
31 *
32 *  \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05.
33 *  \author Copyright 2005 __Vanderbilt University__. All rights reserved.
34 *
35 *      \version c2_function.cc,v 1.169 2008/05/22 12:45:19 marcus Exp
36 */
37
38#include <iostream>
39#include <vector>
40#include <algorithm>
41#include <cstdlib>
42#include <numeric>
43#include <functional>
44#include <iterator>
45#include <cmath>
46#include <limits>
47#include <sstream>
48
49template <typename float_type> const std::string c2_function<float_type>::cvs_file_vers() const
50{ return "c2_function.cc,v 1.169 2008/05/22 12:45:19 marcus Exp"; }
51
52// find a pre-bracketed root of a c2_function, which is a MUCH easier job than general root finding
53// since the derivatives are known exactly, and smoothness is guaranteed.
54// this searches for f(x)=value, to make life a little easier than always searching for f(x)=0
55template <typename float_type> float_type c2_function<float_type>::find_root(float_type lower_bracket, float_type upper_bracket,
56        float_type start, float_type value, int *error,
57        float_type *final_yprime, float_type *final_yprime2) const throw(c2_exception)
58{
59        // find f(x)=value within the brackets, using the guarantees of smoothness associated with a c2_function
60        // can use local f(x)=a*x**2 + b*x + c and solve quadratic to find root, then iterate
61       
62        float_type yp, yp2; // we will make unused pointers point here, to save null checks later
63        if (!final_yprime) final_yprime=&yp;
64        if (!final_yprime2) final_yprime2=&yp2;
65       
66        float_type ftol=5*(std::numeric_limits<float_type>::epsilon()*std::abs(value)+std::numeric_limits<float_type>::min());
67        float_type xtol=5*(std::numeric_limits<float_type>::epsilon()*(std::abs(upper_bracket)+std::abs(lower_bracket))+std::numeric_limits<float_type>::min());
68       
69        float_type root=start; // start looking in the middle
70        if(error) *error=0; // start out with error flag set to OK, if it is expected
71       
72        float_type c, b;
73       
74        if(!root_info) {
75                root_info=new struct c2_root_info;
76                root_info->inited=false;
77        }
78        // this new logic is to keep track of where we were before, and lower the number of
79        // function evaluations if we are searching inside the same bracket as before.
80        // Since this root finder has, very often, the bracket of the entire domain of the function,
81        // this makes a big difference, especially to c2_inverse_function
82        if(!root_info->inited || upper_bracket != root_info->upper.x || lower_bracket != root_info->lower.x) {
83                root_info->upper.x=upper_bracket;
84                fill_fblock(root_info->upper);         
85                root_info->lower.x=lower_bracket;
86                fill_fblock(root_info->lower);
87                root_info->inited=true;
88        }
89       
90        float_type clower=root_info->lower.y-value;
91        if(!clower) {
92                *final_yprime=root_info->lower.yp;
93                *final_yprime2=root_info->lower.ypp;
94                return lower_bracket;
95        }
96       
97        float_type cupper=root_info->upper.y-value;
98        if(!cupper) {
99                *final_yprime=root_info->upper.yp;
100                *final_yprime2=root_info->upper.ypp;
101                return upper_bracket;
102        }
103        const float_type lower_sign = (clower < 0) ? -1 : 1;
104       
105        if(lower_sign*cupper >0) {
106                // argh, no sign change in here!
107                if(error) { *error=1; return 0.0; }
108                else {
109                        std::ostringstream outstr;
110                        outstr << "unbracketed root in find_root at xlower= " << lower_bracket << ", xupper= " << upper_bracket;
111                        outstr << ", value= " << value << ": bailing";
112                        throw c2_exception(outstr.str().c_str());
113                }
114        }
115       
116   float_type delta=upper_bracket-lower_bracket; // first error step
117   c=value_with_derivatives(root, final_yprime, final_yprime2)-value; // compute initial values
118   b=*final_yprime; // make a local copy for readability
119   increment_evaluations();
120   
121   while(
122                         std::abs(delta) > xtol && // absolute x step check
123                         std::abs(c) > ftol && // absolute y tolerance
124                         std::abs(c) > xtol*std::abs(b) // comparison to smallest possible Y step from derivative
125                 )
126        {
127           float_type a=(*final_yprime2)/2; // second derivative is 2*a
128           float_type disc=b*b-4*a*c;
129           // std::cout << std::endl << "find_root_debug a,b,c,d " << a << " " << b << " " << c << " " << disc << std::endl;
130           
131           if(disc >= 0) {
132                   float_type q=-0.5*((b>=0)?(b+std::sqrt(disc)):(b-std::sqrt(disc)));
133                   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
134                   else delta=q/a;
135                   root+=delta;
136           }
137           
138           if(disc < 0 || root<lower_bracket || root>upper_bracket ||
139                      std::abs(delta) >= 0.5*(upper_bracket-lower_bracket)) {
140                      // if we jump out of the bracket, or aren't converging well, bisect
141                   root=0.5*(lower_bracket+upper_bracket);
142                   delta=upper_bracket-lower_bracket;
143           }
144           c=value_with_derivatives(root, final_yprime, final_yprime2)-value; // compute initial values
145           if(c2_isnan(c)) {
146                   bad_x_point=root;
147                   return c; // return the nan if a computation failed
148           }
149           b=*final_yprime; // make a local copy for readability
150           increment_evaluations();
151
152           // now, close in bracket on whichever side this still brackets
153           if(c*lower_sign < 0.0) {
154                   cupper=c;
155                   upper_bracket=root;
156           } else {
157                   clower=c;
158                   lower_bracket=root;
159           }
160           // std::cout << "find_root_debug x, y, dx " << root << " "  << c << " " << delta << std::endl;
161   }
162   return root;
163}
164
165/* def partial_integrals(self, xgrid):
166        Return the integrals of a function between the sampling points xgrid.  The sum is the definite integral.
167        This method uses an exact integration of the polynomial which matches the values and derivatives at the
168        endpoints of a segment.  Its error scales as h**6, if the input functions really are smooth. 
169        This could very well be used as a stepper for adaptive Romberg integration.
170        For InterpolatingFunctions, it is likely that the Simpson's rule integrator is sufficient.
171        #the weights come from an exact mathematica solution to the 5th order polynomial with the given values & derivatives
172        #yint = (y0+y1)*dx/2 + dx^2*(yp0-yp1)/10 + dx^3 * (ypp0+ypp1)/120 )
173*/
174
175// the recursive part of the integrator is agressively designed to minimize copying of data... lots of pointers
176template <typename float_type> float_type c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const throw(c2_exception)
177{
178        std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion
179        rb_stack.clear();
180
181        recur_item top;
182        top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0;
183       
184        // push storage for our initial elements
185        rb_stack.push_back(top);
186        rb_stack.back().f1=*rb.f0;
187        rb_stack.back().done=true; // this element will never be evaluated further
188       
189        rb_stack.push_back(top);
190        rb_stack.back().f1=*rb.f1;
191        rb_stack.back().done=true; // this element will never be evaluated further
192               
193        if(!rb.inited) {
194                switch(rb.derivs) {
195                        case 0:
196                                rb.eps_scale=0.1; rb.extrap_coef=16; break;
197                        case 1:
198                                rb.eps_scale=0.1; rb.extrap_coef=64; break;
199                        case 2:
200                                rb.eps_scale=0.02; rb.extrap_coef=1024; break;
201                        default:
202                                throw c2_exception("derivs must be 0, 1 or 2 in partial_integrals");
203                }
204               
205                rb.extrap2=1.0/(rb.extrap_coef-1.0);
206                rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
207                rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
208                rb.inited=true;
209        }
210       
211        // now, push our first real element
212        top.f0index=0; // left element is stack[0]
213        top.f2index=1; // right element is stack[1]
214        top.abs_tol=rb.abs_tol;
215        rb_stack.push_back(top);
216               
217        while(rb_stack.size() > 2) {
218                recur_item &back=rb_stack.back();
219                if(back.done) {
220                        float_type sum=back.step_sum;
221                        rb_stack.pop_back();
222                        rb_stack.back().step_sum+=sum; // bump our sum up to the parent
223                        continue;
224                }
225                back.done=true;
226               
227                c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1;
228                c2_fblock<float_type> &f1=back.f1; // will hold new middle values
229                size_t f1index=rb_stack.size()-1; // our current offset
230                float_type abs_tol=back.abs_tol;
231               
232                f1.x=0.5*(f0.x + f2.x); // center of interval
233                float_type dx2=0.5*(f2.x - f0.x);
234
235                // check for underflow on step size, which prevents us from achieving specified accuracy.
236                if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) {
237                        std::ostringstream outstr;
238                        outstr << "Step size underflow in adaptive_partial_integrals at depth=" << back.depth << ", x= " << f1.x;
239                        throw c2_exception(outstr.str().c_str());
240                }
241               
242                fill_fblock(f1);
243                if(c2_isnan(f1.y)) {
244                        bad_x_point=f1.x;
245                        return f1.y; // can't go any further if a nan has appeared
246                }
247               
248                bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad;
249                bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad;
250               
251                // select the real derivative count based on whether we are at a point where derivatives exist
252                int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2);
253                               
254                if(!back.depth) { // top level, total has not been initialized yet
255                        switch(derivs) { // create estimate of next lower order for first try
256                                case 0:
257                                        back.previous_estimate=(f0.y+f2.y)*dx2; break;
258                                case 1:
259                                        back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break;
260                                case 2:
261                                        back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y) +  2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break;
262                                default:
263                                        back.previous_estimate=0.0; // just to suppress missing default warnings
264                        }
265                }
266               
267                float_type left, right;
268               
269                // pre-compute constants so all multiplies use a small dynamic range
270                // constants for 0 derivative integrator
271                static const float_type c0c1=5./12., c0c2=8./12., c0c3=-1./12.;
272                // constants for 1 derivative integrator
273                static const float_type c1c1=101./240., c1c2=128./240., c1c3=11./240.,
274                        c1c4=13./240., c1c5=-40./240., c1c6=-3./240.;
275                // constants for 2 derivative integrator
276                static const float_type c2c1=169./40320., c2c2=1024./ 40320., c2c3=-41./40320.,
277                        c2c4=2727./40320., c2c5=-5040./40320., c2c6=423./40320.,
278                        c2c7=17007./40320., c2c8=24576./40320., c2c9=-1263./40320.;
279               
280                switch(derivs) {
281                        case 2:
282                                // use ninth-order estimates for each side, from full set of all values (!) (Thanks, Mathematica!)
283                                left=   ( ( (c2c1*f0.ypp + c2c2*f1.ypp +  c2c3*f2.ypp)*dx2 +
284                                                        (c2c4*f0.yp + c2c5*f1.yp +  c2c6*f2.yp) )*dx2 +
285                                                  (c2c7*f0.y + c2c8*f1.y +  c2c9*f2.y) )* dx2;
286                                right=  ( ( (c2c1*f2.ypp + c2c2*f1.ypp +  c2c3*f0.ypp)*dx2 -
287                                                        (c2c4*f2.yp + c2c5*f1.yp +  c2c6*f0.yp) )*dx2 +
288                                                  (c2c7*f2.y + c2c8*f1.y +  c2c9*f0.y) )* dx2;
289                                // std::cout << f0.x << " " << f1.x << " " << f2.x <<  std::endl ;
290                                // std::cout << f0.y << " " << f1.y << " " << f2.y << " " << left << " " << right << " " << total << std::endl ;
291                                break;
292                        case 1:
293                                left=   ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y) + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ;
294                                right=  ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y) - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ;
295                                break;
296                        case 0:
297                                left=   (c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2;
298                                right=  (c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2;
299                                break;
300                        default:
301                                left=right=0.0; // suppress warnings about missing default
302                                break;
303                }
304               
305                float_type lrsum=left+right;
306
307                bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs); // only extrapolate if no trouble with derivs
308                float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale;
309                if(extrapolate) eps*=rb.eps_scale;
310               
311                if(rb.adapt && eps > abs_tol &&  eps > std::abs(lrsum)*rb.rel_tol) {
312                        // tolerance not met, subdivide & recur
313                        if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5; // each half has half the error budget
314                        top.abs_tol=abs_tol;
315                        top.depth=back.depth+1;
316                       
317                        // save the last things we need from back before a push happens, in case
318                        // the push causes a reallocation and moves the whole stack.
319                        size_t f0index=back.f0index, f2index=back.f2index;
320                       
321                        top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block
322                        top.previous_estimate=right;
323                        rb_stack.push_back(top);
324
325                        top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block
326                        top.previous_estimate=left;
327                        rb_stack.push_back(top);
328
329                } else if(extrapolate) {
330                        // extrapolation only happens on leaf nodes, where the tolerance was met.
331                        back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2;
332                } else {
333                        back.step_sum+=lrsum;
334                }
335        }
336        return rb_stack.back().step_sum; // last element on the stack holds the sum
337}
338
339template <typename float_type> bool c2_function<float_type>::check_monotonicity(
340        const std::vector<float_type> &data, const char message[]) const throw(c2_exception)
341{
342        size_t np=data.size();
343        if(np < 2) return false;  // one point has no direction!
344       
345        bool rev=(data[1] < data[0]); // which way do data point?
346        size_t i;
347       
348        if(!rev) for(i = 2; i < np && (data[i-1] < data[i]) ; i++);
349        else for(i = 2; i < np &&(data[i-1] > data[i]) ; i++);
350       
351        if(i != np) throw c2_exception(message);
352       
353        return rev;
354}
355
356template <typename float_type> void c2_function<float_type>::set_sampling_grid(const std::vector<float_type> &grid) throw(c2_exception)
357{
358        bool rev=check_monotonicity(grid, "set_sampling_grid: sampling grid not monotonic");
359       
360        if(!sampling_grid || no_overwrite_grid) sampling_grid=new std::vector<float_type>;
361        (*sampling_grid)=grid; no_overwrite_grid=0; 
362       
363        if(rev) std::reverse(sampling_grid->begin(), sampling_grid->end()); // make it increasing
364}
365
366template <typename float_type> void c2_function<float_type>::
367        get_sampling_grid(float_type xmin, float_type xmax, std::vector<float_type> &grid) const
368{
369        std::vector<float_type> *result=&grid;
370        result->clear();
371       
372        if( !(sampling_grid) || !(sampling_grid->size()) || (xmax <= sampling_grid->front()) || (xmin >= sampling_grid->back()) ) {
373                // nothing is known about the function in this region, return xmin and xmax             
374                result->push_back(xmin);
375                result->push_back(xmax);
376        } else {       
377                std::vector<float_type> &sg=*sampling_grid; // just a shortcut
378                int np=sg.size();
379                int klo=0, khi=np-1, firstindex=0, lastindex=np-1;
380               
381                result->push_back(xmin);
382
383                if(xmin > sg.front() ) {
384                        // hunt through table for position bracketing starting point
385                        while(khi-klo > 1) {
386                                int km=(khi+klo)/2;
387                                if(sg[km] > xmin) khi=km;
388                                else klo=km;
389                        }
390                        khi=klo+1;
391                        // khi now points to first point definitively beyond our first point, or last point of array
392                        firstindex=khi;
393                        khi=np-1; // restart upper end of search
394                }
395               
396                if(xmax < sg.back()) {
397                        // hunt through table for position bracketing starting point
398                        while(khi-klo > 1) {
399                                int km=(khi+klo)/2;
400                                if(sg[km] > xmax) khi=km;
401                                else klo=km;
402                        }
403                        khi=klo+1;
404                        // khi now points to first point definitively beyond our last point, or last point of array
405                        lastindex=klo;
406                }
407               
408                int initsize=result->size();
409                result->resize(initsize+(lastindex-firstindex+2));
410                std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1, result->begin()+initsize);
411                result->back()=xmax;
412
413                //  this is the unrefined sampling grid... now check for very close points on front & back and fix if needed.
414                preen_sampling_grid(result);
415        }
416}
417
418template <typename float_type> void c2_function<float_type>::preen_sampling_grid(std::vector<float_type> *result) const
419{               
420        //  this is the unrefined sampling grid... now check for very close points on front & back and fix if needed.
421        if(result->size() > 2) { // may be able to prune dangerously close points near the ends if there are at least 3 points
422                bool deleteit=false;
423                float_type x0=(*result)[0], x1=(*result)[1];
424                float_type dx1=x1-x0;
425               
426                float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()*(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
427                if(dx1 < ftol) deleteit=true;
428                float_type dx2=(*result)[2]-x0;
429                if(dx1/dx2 < 0.1) deleteit=true; // endpoint is very close to internal interesting point
430               
431                if(deleteit) result->erase(result->begin()+1); // delete redundant interesting point
432        }
433       
434        if(result->size() > 2) { // may be able to prune dangerously close points near the ends if there are at least 3 points
435                bool deleteit=false;
436                int pos=result->size()-3;
437                float_type x0=(*result)[pos+1], x1=(*result)[pos+2];
438                float_type dx1=x1-x0;
439               
440                float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()*(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
441                if(dx1 < ftol) deleteit=true;
442                float_type dx2=x1-(*result)[pos];
443                if(dx1/dx2 < 0.1) deleteit=true; // endpoint is very close to internal interesting point
444               
445                if(deleteit) result->erase(result->end()-2); // delete redundant interesting point
446        }
447}
448
449template <typename float_type> void c2_function<float_type>::
450        refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const
451{
452        size_t np=grid.size();
453        size_t count=(np-1)*refinement + 1;
454        float_type dxscale=1.0/refinement;
455       
456        std::vector<float_type> result(count);
457       
458        for(size_t i=0; i<(np-1); i++) {
459                float_type x=grid[i];
460                float_type dx=(grid[i+1]-x)*dxscale;
461                for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x;
462        }
463        result.back()=grid.back();
464        grid=result; // copy the expanded grid back to the input
465}
466
467template <typename float_type> float_type c2_function<float_type>::integral(float_type xmin, float_type xmax, std::vector<float_type> *partials,
468         float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate) const throw(c2_exception)
469{
470        if(xmin==xmax) {
471                if(partials) partials->clear();
472                return 0.0;
473        }
474        std::vector<float_type> grid;
475        get_sampling_grid(xmin, xmax, grid);
476        float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol, derivs, adapt, extrapolate);
477        return intg;
478}
479
480template <typename float_type> c2_function<float_type> &c2_function<float_type>::normalized_function(float_type xmin, float_type xmax, float_type norm)
481        const throw(c2_exception)
482{
483        float_type intg=integral(xmin, xmax);
484        return *new c2_scaled_function_p<float_type>(*this, norm/intg);
485}
486
487template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(float_type xmin, float_type xmax, float_type norm)
488        const throw(c2_exception)
489{
490        c2_ptr<float_type> mesquared((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this));
491       
492        std::vector<float_type> grid;
493        get_sampling_grid(xmin, xmax, grid);           
494        float_type intg=mesquared->partial_integrals(grid);
495       
496        return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
497}
498
499template <typename float_type> c2_function<float_type> &c2_function<float_type>::square_normalized_function(
500                float_type xmin, float_type xmax, const c2_function<float_type> &weight, float_type norm)
501        const throw(c2_exception)
502{
503        c2_ptr<float_type> weighted((*new c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight);
504
505        std::vector<float_type> grid;
506        get_sampling_grid(xmin, xmax, grid);   
507        float_type intg=weighted->partial_integrals(grid);
508
509        return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
510}
511
512template <typename float_type> float_type c2_function<float_type>::partial_integrals(
513        std::vector<float_type> xgrid, std::vector<float_type> *partials,
514        float_type abs_tol, float_type rel_tol, int derivs, bool adapt, bool extrapolate)
515        const throw(c2_exception)
516{
517        int np=xgrid.size();
518       
519        c2_fblock<float_type> f0, f2;
520        struct c2_integrate_recur rb;
521        rb.rel_tol=rel_tol;
522        rb.extrapolate=extrapolate;
523        rb.adapt=adapt;
524        rb.derivs=derivs;
525        std::vector< recur_item > rb_stack;
526        rb_stack.reserve(20); // enough for most operations
527        rb.rb_stack=&rb_stack;
528        rb.inited=false;
529        float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front());
530       
531        if(partials) partials->resize(np-1);
532       
533        float_type sum=0.0;
534       
535        f2.x=xgrid[0];
536        fill_fblock(f2);
537        if(c2_isnan(f2.y)) {
538                bad_x_point=f2.x;
539                return f2.y; // can't go any further if a nan has appeared
540        }
541       
542        for(int i=0; i<np-1; i++) {
543                f0=f2; // copy upper bound to lower before computing new upper bound
544               
545                f2.x=xgrid[i+1];
546                fill_fblock(f2);
547                if(c2_isnan(f2.y)) {
548                        bad_x_point=f2.x;
549                        return f2.y; // can't go any further if a nan has appeared
550                }
551               
552                rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv; // distribute error tolerance over whole domain
553                rb.f0=&f0; rb.f1=&f2;
554                float_type ps=integrate_step(rb);
555                sum+=ps;
556                if(partials) (*partials)[i]=ps;
557                if(c2_isnan(ps)) break; // NaN stops integration
558        }
559        return sum;
560}
561
562// generate a sampling grid at points separated by dx=5, which is intentionally
563// incommensurate with pi and 2*pi so grid errors are somewhat randomized
564template <typename float_type> void c2_sin_p<float_type>::
565        get_sampling_grid(float_type xmin, float_type xmax,  std::vector<float_type> &grid) const
566{
567        grid.clear();   
568        for(; xmin < xmax; xmin+=5.0) grid.push_back(xmin);
569        grid.push_back(xmax);
570        this->preen_sampling_grid(&grid);
571}
572
573template <typename float_type> float_type c2_function_transformation<float_type>::evaluate(
574                float_type xraw,
575                float_type y, float_type yp0, float_type ypp0,
576                float_type *yprime, float_type *yprime2) const
577{
578        y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y);
579               
580        if(yprime || yprime2) {
581
582                float_type yp, yp2;
583                if(X.fHasStaticTransforms && Y.fHasStaticTransforms) {
584                        float_type fpi=1.0/Y.pInPrime(y);
585                        float_type gp=X.pInPrime(xraw);
586                        // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
587                        yp=gp*yp0*fpi; // transformed derivative
588                        yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0  - Y.pInDPrime(y)*yp*yp )*fpi;
589                } else {
590                        float_type fpi=1.0/Y.fInPrime(y);
591                        float_type gp=X.fInPrime(xraw);
592                        // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
593                        yp=gp*yp0*fpi; // transformed derivative
594                        yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0  - Y.fInDPrime(y)*yp*yp )*fpi;
595                }
596                if(yprime) *yprime=yp;
597                if(yprime2) *yprime2=yp2;
598        }       
599        return y;
600}
601
602//  The constructor
603template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load(
604                                                                const std::vector<float_type> &x, const std::vector<float_type> &f,
605                                                                bool lowerSlopeNatural, float_type lowerSlope,
606                                                                bool upperSlopeNatural, float_type upperSlope,
607                                                                bool splined
608                ) throw(c2_exception)
609{
610        c2_ptr<float_type> keepme(*this);
611        X= x;
612        F= f;
613       
614        // Xraw is useful in some of the arithmetic operations between interpolating functions
615        Xraw=x;
616
617        set_domain(std::min(Xraw.front(), Xraw.back()),std::max(Xraw.front(), Xraw.back()));
618               
619        if(x.size() != f.size()) {
620                throw c2_exception("interpolating_function::init() -- x & y inputs are of different size");
621        }
622       
623        size_t np=X.size(); // they are the same now, so lets take a short cut
624       
625        if(np < 2) {
626                throw c2_exception("interpolating_function::init() -- input < 2 elements ");
627        }
628       
629        bool xraw_rev=check_monotonicity(Xraw,
630                        "interpolating_function::init() non-monotonic raw x input"); // which way does raw X point?  sampling grid MUST be increasing
631
632        if(!xraw_rev) { // we can use pointer to raw X values if they are in the right order
633                set_sampling_grid_pointer(Xraw); // our intial grid of x values is certainly a good guess for 'interesting' points
634        } else {
635                set_sampling_grid(Xraw); // make a copy of it, and assure it is in right order
636        }
637       
638        if(fTransform.X.fTransformed) { // check if X scale is nonlinear, and if so, do transform
639                if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]);
640                if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]);
641                for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]);
642        }       
643        if(fTransform.Y.fTransformed) {  // check if Y scale is nonlinear, and if so, do transform
644                if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]);
645                if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]);
646                for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]);
647        }
648                 
649        xInverted=check_monotonicity(X,
650                "interpolating_function::init() non-monotonic transformed x input");
651       
652        if(splined) spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
653        else y2.assign(np,0.0);
654       
655        lastKLow=0;
656        keepme.release_for_return();
657        return *this;
658}
659
660/*
661//  The constructor
662template <typename float_type> interpolating_function_p<float_type> & interpolating_function_p<float_type>::load_pairs(
663                                                                std::vector<std::pair<float_type, float_type> > &data,
664                                                                bool lowerSlopeNatural, float_type lowerSlope,
665                                                                bool upperSlopeNatural, float_type upperSlope,
666                                                                bool splined
667                ) throw(c2_exception)
668{
669        c2_ptr<float_type> keepme(*this);
670       
671        size_t np=data.size();
672        if(np < 2) {
673                throw c2_exception("interpolating_function::init() -- input < 2 elements ");
674        }
675       
676        // sort into ascending order
677        std::sort(data.begin(), data.end(), comp_pair);
678       
679        std::vector<float_type> xtmp, ytmp;
680        xtmp.reserve(np);
681        ytmp.reserve(np);
682        for (size_t i=0; i<np; i++) {
683                xtmp.push_back(data[i].first);
684                ytmp.push_back(data[i].second);
685        }
686        this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope, splined);
687       
688        keepme.release_for_return();
689        return *this;
690}
691
692template <typename float_type> interpolating_function_p<float_type> &
693        interpolating_function_p<float_type>::load_random_generator_function(
694        const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights)
695        throw(c2_exception)
696{       
697        c2_ptr<float_type> keepme(*this);
698
699        std::vector<float_type> integral;
700        c2_const_ptr<float_type> keepit(binheights); // manage function... not really needed here, but always safe.
701        // integrate from first to last bin in original order, leaving results in integral
702        // ask for relative error of 1e-6 on each bin, with absolute error set to 0 (since we don't know the data scale).
703        float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6);
704        // the integral vector now has partial integrals... it must be accumulated by summing
705        integral.insert(integral.begin(), 0.0); // integral from start to start is 0
706        float_type scale=1.0/sum;
707        for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale + integral[i-1];
708        integral.back()=1.0; // force exact value on boundary
709       
710        this->load(integral, bincenters,
711                                           false, 1.0/(scale*binheights(bincenters.front() )),
712                                           false, 1.0/(scale*binheights(bincenters.back() ))
713                                           ); // use integral as x axis in inverse function
714        keepme.release_for_return();
715        return *this;
716}
717
718template <typename float_type> interpolating_function_p<float_type> &
719        interpolating_function_p<float_type>::load_random_generator_bins(
720        const std::vector<float_type> &bins, const std::vector<float_type> &binheights)
721        throw(c2_exception)
722{       
723        c2_ptr<float_type> keepme(*this);
724
725        size_t np=binheights.size();
726        std::vector<float_type> integral(np+1), bin_edges(np+1);
727       
728        // compute the integral based on estimates of the bin edges from the given bin centers...
729        // except for bin 0 & final bin, the edge of a bin is halfway between then center of the
730        // bin and the center of the previous/next bin.
731        // This gives width[n] = (center[n+1]+center[n])/2 - (center[n]+center[n-1])/2 = (center[n+1]-center[n-1])/2
732        // for the edges, assume a bin of width (center[1]-center[0]) or (center[np-1]-center[np-2])
733        // be careful that absolute values are used in case data are reversed.
734
735        if(bins.size() == binheights.size()+1) {
736                bin_edges=bins; // edges array was passed in
737        } else if (bins.size() == binheights.size()) {
738                bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5; // edge bin
739                for(size_t i=1; i<np; i++) {
740                        bin_edges[i]=(bins[i]+bins[i-1])*0.5;
741                }
742                bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5; // edge bin
743        } else {
744                throw c2_exception("inconsistent bin vectors passed to load_random_generator_bins");
745        }
746       
747        float_type running_sum=0.0;
748        for(size_t i=0; i<np; i++) {
749                integral[i]=running_sum;
750                if(!binheights[i]) throw c2_exception("empty bin passed to load_random_generator_bins");
751                running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]);
752        }
753        float_type scale=1.0/running_sum;
754        for(size_t i=0; i<np; i++) integral[i]*=scale;
755        integral.back()=1.0; // force exactly correct value on boundary
756        this->load(integral, bin_edges,
757                  false, 1.0/(scale*binheights.front()),
758                  false, 1.0/(scale*binheights.back())
759                  ); // use integral as x axis in inverse function
760        keepme.release_for_return();
761        return *this;
762}
763*/
764
765//  The spline table generator
766template <typename float_type> void interpolating_function_p<float_type>::spline(
767         bool lowerSlopeNatural, float_type lowerSlope,
768         bool upperSlopeNatural, float_type upperSlope
769         ) throw(c2_exception)
770{
771// construct spline tables here. 
772        // this code is a re-translation of the pythonlabtools spline algorithm from pythonlabtools.sourceforge.net
773        size_t np=X.size();
774        std::vector<float_type> u(np),  dy(np-1), dx(np-1), dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1);
775       
776        std::transform(X.begin()+1, X.end(), X.begin(), dx.begin(), std::minus<float_type>() ); // dx=X[1:] - X [:-1]
777        for(size_t i=0; i<dxi.size(); i++) dxi[i]=1.0/dx[i]; // dxi = 1/dx
778        for(size_t i=0; i<dx2i.size(); i++) dx2i[i]=1.0/(X[i+2]-X[i]);
779       
780        std::transform(F.begin()+1, F.end(), F.begin(), dy.begin(), std::minus<float_type>() ); // dy = F[i+1]-F[i]
781        std::transform(dx2i.begin(), dx2i.end(), dx.begin(), siga.begin(), std::multiplies<float_type>()); // siga = dx[:-1]*dx2i
782        std::transform(dxi.begin(), dxi.end(), dy.begin(), dydx.begin(), std::multiplies<float_type>()); // dydx=dy/dx
783               
784        // u[i]=(y[i+1]-y[i])/float(x[i+1]-x[i]) - (y[i]-y[i-1])/float(x[i]-x[i-1])
785        std::transform(dydx.begin()+1, dydx.end(), dydx.begin(), u.begin()+1, std::minus<float_type>() ); // incomplete rendering of u = dydx[1:]-dydx[:-1]
786       
787        y2.resize(np,0.0); 
788       
789        if(lowerSlopeNatural) {
790                y2[0]=u[0]=0.0;
791        } else {
792                y2[0]= -0.5;
793                u[0]=(3.0*dxi[0])*(dy[0]*dxi[0] -lowerSlope);
794        }
795       
796        for(size_t i=1; i < np -1; i++) { // the inner loop
797                float_type sig=siga[i-1];
798                float_type p=sig*y2[i-1]+2.0;
799                y2[i]=(sig-1.0)/p;
800                u[i]=(6.0*u[i]*dx2i[i-1] - sig*u[i-1])/p;
801        }
802       
803        float_type qn, un;
804       
805        if(upperSlopeNatural) {
806                qn=un=0.0;
807        } else {
808                qn= 0.5;
809                un=(3.0*dxi[dxi.size()-1])*(upperSlope- dy[dy.size()-1]*dxi[dxi.size()-1] );
810        }
811       
812        y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0);
813        for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1];
814}
815
816template <typename float_type> interpolating_function_p<float_type> &interpolating_function_p<float_type>::sample_function(
817                        const c2_function<float_type> &func,
818                        float_type xmin, float_type xmax, float_type abs_tol, float_type rel_tol,
819                        bool lowerSlopeNatural, float_type lowerSlope,
820                        bool upperSlopeNatural, float_type upperSlope
821 ) throw(c2_exception)
822{
823        c2_ptr<float_type> keepme(*this);
824       
825        const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y; // shortcuts
826       
827         // set up our params to look like the samplng function for now
828         sampler_function=func;
829         std::vector<float_type> grid;
830         func.get_sampling_grid(xmin, xmax, grid);
831         size_t gsize=grid.size();
832         if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]);
833         set_sampling_grid_pointer(grid);
834         
835        // float_type xmin1=fXin(xmin), xmax1=fXin(xmax); // bounds in transformed space
836         // get a list of points needed in transformed space, directly into our tables
837         this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol, 8*rel_tol, 0, &X, &F);
838         // clear the sampler function now, since otherwise our value_with_derivatives is broken
839         sampler_function.unset_function();
840         
841         xInverted=check_monotonicity(X,
842                  "interpolating_function::init() non-monotonic transformed x input");
843         
844         size_t np=X.size();
845         
846         // Xraw is useful in some of the arithmetic operations between interpolating functions
847         if(!XX.fTransformed) Xraw=X;
848         else {
849                 Xraw.resize(np);
850                 for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]);
851                 Xraw.front()=xmin;
852                 Xraw.back()=xmax;
853         }
854                 
855         bool xraw_rev=check_monotonicity(Xraw,
856                  "interpolating_function::init() non-monotonic raw x input");
857                  // which way does raw X point?  sampling grid MUST be increasing
858         
859         if(!xraw_rev) { // we can use pointer to raw X values if they are in the right order
860                 set_sampling_grid_pointer(Xraw);
861                 // our intial grid of x values is certainly a good guess for 'interesting' points
862         } else {
863                 set_sampling_grid(Xraw); // make a copy of it, and assure it is in right order
864         }
865         
866         if(XX.fTransformed) { // check if X scale is nonlinear, and if so, do transform
867                 if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(xmin);
868                 if(!upperSlopeNatural) upperSlope /= XX.fInPrime(xmax);
869         }     
870         if(YY.fTransformed) {  // check if Y scale is nonlinear, and if so, do transform
871                 if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(xmin));
872                 if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(xmax));
873         }
874         // note that each of ends has 3 points with two equal gaps, since they were obtained by bisection
875         // so the step sizes are easy to get
876         // the 'natural slope' option for sampled functions has a different meaning than
877         // for normal splines.  In this case, the derivative is adjusted to make the
878         // second derivative constant on the last two points at each end
879         // which is consistent with the error sampling technique we used to get here
880         if(lowerSlopeNatural) {
881                 float_type hlower=X[1]-X[0];
882                 lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower;
883                 lowerSlopeNatural=false; // it's not the usual meaning of natural any more
884         }
885         if(upperSlopeNatural) {
886                 float_type hupper=X[np-1]-X[np-2];
887                 upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper;
888                 upperSlopeNatural=false; // it's not the usual meaning of natural any more
889         }
890         this->set_domain(xmin, xmax);
891         
892         spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
893         lastKLow=0;
894         keepme.release_for_return();
895         return *this;
896}
897
898//  This function is the reason for this class to exist
899// it computes the interpolated function, and (if requested) its proper first and second derivatives including all coordinate transforms
900template <typename float_type> float_type interpolating_function_p<float_type>::value_with_derivatives(
901                                float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
902{
903        if(sampler_function.valid()) {
904                // if this is non-null, we are sampling data for later, so just return raw function
905                // however, transform it into our sampling space, first.
906                if(yprime) *yprime=0;
907                if(yprime2) *yprime2=0;
908                sampler_function->increment_evaluations();
909                return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x))); // derivatives are completely undefined
910        }
911                                       
912        if(x < this->xmin() || x > this->xmax()) {
913                std::ostringstream outstr;
914                outstr << "Interpolating function argument " << x << " out of range " << this->xmin() << " -- " << this ->xmax() << ": bailing";
915                throw c2_exception(outstr.str().c_str());
916    }
917       
918        float_type xraw=x;
919       
920        if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms?
921                fTransform.X.pIn(x) : fTransform.X.fIn(x); // save time by explicitly testing for identity function here
922       
923        int klo=0, khi=X.size()-1;
924
925        if(khi < 0) throw c2_exception("Uninitialized interpolating function being evaluated");
926       
927        const float_type *XX=&X[lastKLow]; // make all fast checks short offsets from here
928       
929        if(!xInverted) { // select search depending on whether transformed X is increasing or decreasing
930                if((XX[0] <= x) && (XX[1] >= x) ) { // already bracketed
931                        klo=lastKLow;
932                } else if((XX[1] <= x) && (XX[2] >= x)) { // in next bracket to the right
933                        klo=lastKLow+1;         
934                } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) { // in next bracket to the left
935                        klo=lastKLow-1;         
936                } else { // not bracketed, not close, start over
937                                 // search for new KLow
938                        while(khi-klo > 1) {
939                                int km=(khi+klo)/2;
940                                if(X[km] > x) khi=km;
941                                else klo=km;
942                        }
943                }
944        } else {
945                if((XX[0] >= x) && (XX[1] <= x) ) { // already bracketed
946                        klo=lastKLow;
947                } else if((XX[1] >= x) && (XX[2] <= x)) { // in next bracket to the right
948                        klo=lastKLow+1;         
949                } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) { // in next bracket to the left
950                        klo=lastKLow-1;         
951                } else { // not bracketed, not close, start over
952                                 // search for new KLow
953                        while(khi-klo > 1) {
954                                int km=(khi+klo)/2;
955                                if(X[km] < x) khi=km;
956                                else klo=km;
957                        }
958                }
959        }
960       
961        khi=klo+1;
962        lastKLow=klo;
963       
964        float_type h=X[khi]-X[klo];
965       
966        float_type a=(X[khi]-x)/h;
967        float_type b=1.0-a;
968        float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi];
969        float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0;
970
971        float_type yp0=0; // the derivative in interpolating table coordinates
972        float_type ypp0=0; // second derivative
973       
974        if(yprime || yprime2) {
975                yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0; // the derivative in interpolating table coordinates
976                ypp0=b*y2hi+a*y2lo; // second derivative
977        }
978       
979        if(fTransform.isIdentity) {
980                if(yprime) *yprime=yp0;
981                if(yprime2) *yprime2=ypp0;
982                return y;       
983        } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2);
984}
985
986template <typename float_type> void interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound)
987{
988        int kl = 0 ;
989        int kh=kl+1;
990        float_type xx=fTransform.X.fIn(bound);
991        float_type h0=X[kh]-X[kl];
992        float_type h1=xx-X[kl];
993        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;
994       
995        X.insert(X.begin(), xx);
996        F.insert(F.begin(), yextrap);
997        y2.insert(y2.begin(), y2.front()); // duplicate first or last element
998        Xraw.insert(Xraw.begin(), bound);
999        if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1000        else this->fXMax=bound;
1001       
1002        //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
1003        //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]);   
1004}
1005
1006template <typename float_type> void interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound)
1007{
1008        int kl = X.size()-2 ;
1009        int kh=kl+1;
1010        float_type xx=fTransform.X.fIn(bound);
1011        float_type h0=X[kh]-X[kl];
1012        float_type h1=xx-X[kl];
1013        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;
1014       
1015        X.insert(X.end(), xx);
1016        F.insert(F.end(), yextrap);
1017        y2.insert(y2.end(), y2.back()); // duplicate first or last element
1018        Xraw.insert(Xraw.end(), bound);
1019        if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1020        else this->fXMax=bound;
1021        //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
1022        //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]);
1023}
1024
1025// return a new interpolating_function which is the unary function of an existing interpolating_function
1026// can also be used to generate a resampling of another c2_function on a different grid
1027// by creating a=interpolating_function(x,x)
1028// and doing b=a.unary_operator(c) where c is a c2_function (probably another interpolating_function)
1029
1030template <typename float_type> interpolating_function_p<float_type>&
1031        interpolating_function_p<float_type>::unary_operator(const c2_function<float_type> &source) const
1032{
1033        size_t np=X.size();
1034        std::vector<float_type>yv(np);
1035        c2_ptr<float_type> comp(source(*this));
1036        float_type yp0, yp1, ypp;
1037       
1038        for(size_t i=1; i<np-1; i++) {
1039                yv[i]=source(fTransform.Y.fOut(F[i])); // copy pointwise the function of our data values
1040        }
1041               
1042        yv.front()=comp(Xraw.front(), &yp0, &ypp); // get derivative at front
1043        yv.back()= comp(Xraw.back(), &yp1, &ypp); // get derivative at back
1044       
1045        interpolating_function_p &copy=clone();
1046        copy.load(this->Xraw, yv, false, yp0, false, yp1);
1047       
1048        return copy;
1049}
1050
1051template <typename float_type> void
1052interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals, std::vector<float_type> &yvals) const throw()
1053{
1054       
1055        xvals=Xraw;
1056        yvals.resize(F.size());
1057       
1058        for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]);
1059}
1060
1061template <typename float_type> interpolating_function_p<float_type> &
1062        interpolating_function_p<float_type>::binary_operator(const c2_function<float_type> &rhs,
1063                const c2_binary_function<float_type> *combining_stub) const
1064{       
1065        size_t np=X.size();     
1066        std::vector<float_type> yv(np);
1067        c2_constant_p<float_type> fval(0);
1068        float_type yp0, yp1, ypp;
1069       
1070        c2_const_ptr<float_type> stub(*combining_stub);  // manage ownership
1071       
1072        for(size_t i=1; i<np-1; i++) {
1073                fval.reset(fTransform.Y.fOut(F[i])); // update the constant function pointwise
1074                yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0, (float_type *)0); // compute rhs & combine without derivatives
1075        }
1076       
1077        yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp); // get derivative at front
1078        yv.back()= combining_stub->combine(*this, rhs, Xraw.back(),  &yp1, &ypp); // get derivative at back
1079       
1080        interpolating_function_p &copy=clone();
1081        copy.load(this->Xraw, yv, false, yp0, false, yp1);
1082       
1083        return copy;
1084}
1085
1086template <typename float_type> c2_inverse_function_p<float_type>::c2_inverse_function_p(const c2_function<float_type> &source)
1087        : c2_function<float_type>(), func(source)
1088{
1089        float_type l=source.xmin();
1090        float_type r=source.xmax();
1091        start_hint=(l+r)*0.5; // guess that we start in the middle
1092        //  compute our domain assuming the function is monotonic so its values on its domain boundaries are our domain
1093        float_type ly=source(l);
1094        float_type ry=source(r);
1095        if (ly > ry) {
1096                float_type t=ly; ly=ry; ry=t;
1097        }
1098        set_domain(ly, ry);
1099}
1100
1101template <typename float_type> float_type c2_inverse_function_p<float_type>::value_with_derivatives(
1102                                        float_type x, float_type *yprime, float_type *yprime2
1103                        ) const throw(c2_exception)
1104{
1105        float_type l=this->func->xmin();
1106        float_type r=this->func->xmax();
1107        float_type yp, ypp;
1108        float_type y=this->func->find_root(l, r, get_start_hint(x), x, 0, &yp, &ypp);
1109        start_hint=y;
1110        if(yprime) *yprime=1.0/yp;
1111        if(yprime2) *yprime2=-ypp/(yp*yp*yp);
1112        return y;
1113}
1114
1115//accumulated_histogram starts with binned data, generates the integral, and generates a piecewise linear interpolating_function
1116//If drop_zeros is true, it merges empty bins together before integration
1117//Note that the resulting interpolating_function is guaranteed to be increasing (if drop_zeros is false)
1118//      or stricly increasing (if drop_zeros is true)
1119//If inverse_function is true, it drop zeros, integrates, and returns the inverse function which is useful
1120//      for random number generation based on the input distribution.
1121//If normalize is true, the big end of the integral is scaled to 1.
1122//If the data are passed in reverse order (large X first), the integral is carried out from the big end,
1123//      and then the data are reversed to the result in in increasing X order. 
1124template <typename float_type> accumulated_histogram<float_type>::accumulated_histogram(
1125                                                        const std::vector<float_type>binedges, const std::vector<float_type> binheights,
1126                                                        bool normalize, bool inverse_function, bool drop_zeros)
1127{
1128       
1129        int np=binheights.size();
1130       
1131        std::vector<float_type> be, bh;
1132        if(drop_zeros || inverse_function) { //inverse functions cannot have any zero bins or they have vertical sections
1133                if(binheights[0] || !inverse_function) { // conserve lower x bound if not an inverse function
1134                        be.push_back(binedges[0]);
1135                        bh.push_back(binheights[0]);
1136                }
1137                for(int i=1; i<np-1; i++) {
1138                        if(binheights[i]) {
1139                                be.push_back(binedges[i]);
1140                                bh.push_back(binheights[i]);
1141                        }
1142                }
1143                if(binheights[np-1] || !inverse_function) {
1144                        bh.push_back(binheights[np-1]);                 
1145                        be.push_back(binedges[np-1]);
1146                        be.push_back(binedges[np]); // push both sides of the last bin if needed
1147                }
1148                np=bh.size(); // set np to compressed size of bin array
1149        } else {
1150                be=binedges;
1151                bh=binheights;
1152        }
1153        std::vector<float_type> cum(np+1, 0.0);
1154        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
1155        if(be[1] < be[0]) { // if bins passed in backwards, reverse them
1156                std::reverse(be.begin(), be.end());
1157                std::reverse(cum.begin(), cum.end());
1158                for(unsigned int i=0; i<cum.size(); i++) cum[i]*=-1; // flip sign on reversed data
1159        }
1160        if(normalize) {
1161                float_type m=1.0/std::max(cum[0], cum[np]);
1162                for(int i=0; i<=np; i++) cum[i]*=m;
1163        }
1164        if(inverse_function) interpolating_function_p<float_type>(cum, be); // use cum as x axis in inverse function
1165        else interpolating_function_p<float_type>(be, cum); // else use lower bin edge as x axis
1166        std::fill(this->y2.begin(), this->y2.end(), 0.0); // clear second derivatives, to we are piecewise linear
1167}
1168
1169template <typename float_type> c2_piecewise_function_p<float_type>::c2_piecewise_function_p()
1170: c2_function<float_type>(), lastKLow(-1)
1171{
1172        this->sampling_grid=new std::vector<float_type>; // this always has a smapling grid
1173}
1174
1175template <typename float_type> c2_piecewise_function_p<float_type>::~c2_piecewise_function_p()
1176{
1177}
1178
1179template <typename float_type> float_type c2_piecewise_function_p<float_type>::value_with_derivatives(
1180                  float_type x, float_type *yprime, float_type *yprime2
1181                  ) const throw(c2_exception)
1182{
1183       
1184        size_t np=functions.size();
1185        if(!np) throw c2_exception("attempting to evaluate an empty piecewise function");
1186       
1187        if(x < this->xmin() || x > this->xmax()) {
1188                std::ostringstream outstr;
1189                outstr << "piecewise function argument " << x << " out of range " << this->xmin() << " -- " << this->xmax();
1190                throw c2_exception(outstr.str().c_str());
1191        }
1192
1193        int klo=0;
1194       
1195        if(lastKLow >= 0 && functions[lastKLow]->xmin() <= x && functions[lastKLow]->xmax() > x) {
1196                klo=lastKLow;
1197        } else {
1198                int khi=np;
1199                while(khi-klo > 1) {
1200                        int km=(khi+klo)/2;
1201                        if(functions[km]->xmin() > x) khi=km;
1202                        else klo=km;
1203                }
1204        }
1205        lastKLow=klo;
1206        return functions[klo]->value_with_derivatives(x, yprime, yprime2);
1207}
1208
1209template <typename float_type> void c2_piecewise_function_p<float_type>::append_function(
1210        const c2_function<float_type> &func) throw(c2_exception)
1211{
1212        c2_const_ptr<float_type> keepfunc(func);  //  manage function before we can throw any exceptions
1213        if(functions.size()) { // check whether there are any gaps to fill, etc.
1214                const c2_function<float_type> &tail=functions.back();
1215                float_type x0=tail.xmax();
1216                float_type x1=func.xmin();
1217                if(x0 < x1) {
1218                        // must insert a connector if x0 < x1
1219                        float_type y0=tail(x0);
1220                        float_type y1=func(x1);
1221                        c2_function<float_type> &connector=*new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0));
1222                        connector.set_domain(x0,x1);
1223                        functions.push_back(c2_const_ptr<float_type>(connector));
1224                        this->sampling_grid->push_back(x1);
1225                } else if(x0>x1) throw c2_exception("function domains not increasing in c2_piecewise_function");
1226        }
1227        functions.push_back(keepfunc);
1228        // extend our domain to include all known functions
1229        this->set_domain(functions.front()->xmin(), functions.back()->xmax());
1230        // extend our sampling grid with the new function's grid, with the first point dropped to avoid duplicates
1231        std::vector<float_type> newgrid;
1232        func.get_sampling_grid(func.xmin(), func.xmax(), newgrid);
1233        this->sampling_grid->insert(this->sampling_grid->end(), newgrid.begin()+1, newgrid.end());
1234}
1235
1236template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
1237        float_type x0, const c2_function<float_type> &f0, float_type x2, const c2_function<float_type> &f2,
1238        bool auto_center, float_type y1)
1239        : c2_function<float_type>()
1240{
1241        c2_const_ptr<float_type> left(f0), right(f2); // make sure if these are unowned, they get deleted
1242        c2_fblock<float_type> fb0, fb2;
1243        fb0.x=x0;
1244        f0.fill_fblock(fb0);
1245        fb2.x=x2;
1246        f2.fill_fblock(fb2);
1247        init(fb0, fb2, auto_center, y1);
1248}
1249
1250template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
1251        float_type x0, float_type y0, float_type yp0, float_type ypp0,
1252        float_type x2, float_type y2, float_type yp2, float_type ypp2,
1253        bool auto_center, float_type y1)
1254        : c2_function<float_type>()
1255{
1256        c2_fblock<float_type> fb0, fb2;
1257        fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0;
1258        fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2;   
1259        init(fb0, fb2, auto_center, y1);
1260}
1261
1262template <typename float_type> c2_connector_function_p<float_type>::c2_connector_function_p(
1263        const c2_fblock<float_type> &fb0,
1264        const c2_fblock<float_type> &fb2,
1265        bool auto_center, float_type y1)
1266        : c2_function<float_type>()
1267{
1268        init(fb0, fb2, auto_center, y1);
1269}
1270
1271template <typename float_type> void c2_connector_function_p<float_type>::init(
1272        const c2_fblock<float_type> &fb0,
1273        const c2_fblock<float_type> &fb2,
1274        bool auto_center, float_type y1)
1275{
1276        float_type dx=(fb2.x-fb0.x)/2.0;
1277        fhinv=1.0/dx;
1278
1279        // scale derivs to put function on [-1,1] since mma  solution is done this way
1280        float_type yp0=fb0.yp*dx;
1281        float_type yp2=fb2.yp*dx;
1282        float_type ypp0=fb0.ypp*dx*dx;
1283        float_type ypp2=fb2.ypp*dx*dx;
1284       
1285        float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625;
1286        if(auto_center) y1=ff0; // forces ff to be 0 if we are auto-centering
1287       
1288        // 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)
1289        // 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) 
1290        // 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)
1291        fy1=y1;
1292        fa=(fb2.y - fb0.y)*0.5;
1293        fb=(fb0.y + fb2.y)*0.5 - y1;
1294        fc=(yp2+yp0-2.*fa)*0.25;
1295        fd=(yp2-yp0-4.*fb)*0.25;
1296        fe=(ypp2-ypp0-12.*fc)*0.0625;
1297        ff=(ff0 - y1);
1298        this->set_domain(fb0.x, fb2.x); // this is where the function is valid
1299}
1300
1301template <typename float_type> c2_connector_function_p<float_type>::~c2_connector_function_p()
1302{
1303}
1304
1305template <typename float_type> float_type c2_connector_function_p<float_type>::value_with_derivatives(
1306        float_type x, float_type *yprime, float_type *yprime2
1307        ) const throw(c2_exception)
1308{
1309        float_type x0=this->xmin(), x2=this->xmax();
1310        float_type dx=(x-(x0+x2)*0.5)*fhinv;
1311        float_type q1=(x-x0)*(x-x2)*fhinv*fhinv; // exactly vanish all bits at both ends
1312        float_type q2=dx*q1;
1313       
1314        float_type r1=fa+fb*dx;
1315        float_type r2=fc+fd*dx;
1316        float_type r3=fe+ff*dx;
1317       
1318        float_type y=fy1+dx*r1+q2*r2+q1*q2*r3;
1319       
1320        if(yprime || yprime2) {
1321                float_type q3=3*q1+2;
1322                float_type q4=5*q1+4;
1323                if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv;
1324                if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4+r3*(2*dx*(5*q1+2)))*fhinv*fhinv;
1325        }
1326        return y;
1327}
1328
1329// the recursive part of the sampler is agressively designed to minimize copying of data... lots of pointers
1330template <typename float_type> void c2_function<float_type>::sample_step(c2_sample_recur &rb) const throw(c2_exception)
1331{
1332        std::vector< recur_item > &rb_stack=*rb.rb_stack; // heap-based stack of data for recursion
1333        rb_stack.clear();
1334       
1335        recur_item top;
1336        top.depth=0; top.done=false; top.f0index=0; top.f2index=0;
1337       
1338        // push storage for our initial elements
1339        rb_stack.push_back(top);
1340        rb_stack.back().f1=*rb.f0;
1341        rb_stack.back().done=true;
1342
1343        rb_stack.push_back(top);
1344        rb_stack.back().f1=*rb.f1;
1345        rb_stack.back().done=true;
1346       
1347        if(!rb.inited) {
1348                rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
1349                rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
1350                rb.inited=true;
1351        }
1352
1353        // now, push our first real element
1354        top.f0index=0; // left element is stack[0]
1355        top.f2index=1; // right element is stack[1]
1356        rb_stack.push_back(top);
1357
1358        while(rb_stack.size() > 2) {
1359                recur_item &back=rb_stack.back();
1360                if(back.done) {
1361                        rb_stack.pop_back();
1362                        continue;
1363                }
1364                back.done=true;
1365
1366                c2_fblock<float_type> &f0=rb_stack[back.f0index].f1, &f2=rb_stack[back.f2index].f1;
1367                c2_fblock<float_type> &f1=back.f1; // will hold new middle values
1368                size_t f1index=rb_stack.size()-1; // our current offset
1369
1370                // std::cout << "processing: " << rb_stack.size() << " " <<
1371                // (&back-&rb_stack.front())  << " " << back.depth << " " << f0.x << " " << f2.x << std::endl;
1372
1373                f1.x=0.5*(f0.x + f2.x); // center of interval
1374                float_type dx2=0.5*(f2.x - f0.x);
1375                       
1376                // check for underflow on step size, which prevents us from achieving specified accuracy.
1377                if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2) < rb.abs_tol_min) {
1378                        std::ostringstream outstr;
1379                        outstr << "Step size underflow in adaptive_sampling at depth=" << back.depth << ", x= " << f1.x;
1380                        throw c2_exception(outstr.str().c_str());
1381                }
1382               
1383                fill_fblock(f1);
1384               
1385                if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) {
1386                        // can't go any further if a nan has appeared
1387                        bad_x_point=f1.x;
1388                        throw c2_exception("NaN encountered while sampling function");
1389                }
1390
1391                float_type eps;
1392                if(rb.derivs==2) {
1393                        // this is code from connector_function to compute the value at the midpoint
1394                        // it is re-included here to avoid constructing a complete c2connector
1395                        // just to find out if we are close enough
1396                        float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625;
1397                        // we are converging as at least x**5 and bisecting, so real error on final step is smaller
1398                        eps=std::abs(ff0-f1.y)/32.0;   
1399                } else {
1400                        // there are two tolerances to meet... the shift in the estimate of the actual point,
1401                        // and the difference between the current points and the extremum
1402                        // build all the coefficients needed to construct the local parabola
1403                        float_type ypcenter, ypp;
1404                        if (rb.derivs==1) {
1405                                // linear extrapolation error using exact derivs
1406                                eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125;
1407                                ypcenter=2*f1.yp*dx2; // first deriv scaled so this interval is on [-1,1]
1408                                ypp=2*(f2.yp-f0.yp)*dx2*dx2; // second deriv estimate scaled so this interval is on [-1,1]
1409                        } else {
1410                                // linear interpolation error without derivs if we are at top level
1411                                //  or 3-point parabolic interpolation estimates from previous level, if available
1412                                ypcenter=(f2.y-f0.y)*0.5; // derivative estimate at center
1413                                ypp=(f2.y+f0.y-2*f1.y); // second deriv estimate
1414                                if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2; // penalize first step
1415                                else eps=std::abs(f1.y-back.previous_estimate)*0.25;
1416                        }
1417                        float_type ypleft=ypcenter-ypp; // derivative at left edge
1418                        float_type ypright=ypcenter+ypp; // derivative at right edge
1419                        float_type extremum_eps=0;
1420                        if((ypleft*ypright) <=0) // y' changes sign if we have an extremum
1421                        {
1422                                // compute position and value of the extremum this way
1423                                float_type xext=-ypcenter/ypp;
1424                                float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp;
1425                                // and then find the the smallest offset of it from a point, looking in the left or right side
1426                                if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext));
1427                                else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext));
1428                        }
1429                        eps=std::max(eps, extremum_eps); // if previous shot was really bad, keep trying
1430                }
1431               
1432                if(eps < rb.abs_tol ||  eps < std::abs(f1.y)*rb.rel_tol) {
1433                        if(rb.out) {
1434                                // we've met the tolerance, and are building a function, append two connectors
1435                                rb.out->append_function(
1436                                        *new c2_connector_function_p<float_type>(f0, f1, true, 0.0)
1437                                );
1438                                rb.out->append_function(
1439                                        *new c2_connector_function_p<float_type>(f1, f2, true, 0.0)
1440                                );
1441                        }
1442                        if(rb.xvals && rb.yvals) {
1443                                rb.xvals->push_back(f0.x);
1444                                rb.xvals->push_back(f1.x);
1445                                rb.yvals->push_back(f0.y);
1446                                rb.yvals->push_back(f1.y);
1447                                // the value at f2 will get pushed in the next segment... it is not forgotten
1448                        }
1449                } else {
1450                        top.depth=back.depth+1; // increment depth counter
1451
1452                        // save the last things we need from back before a push happens, in case
1453                        // the push causes a reallocation and moves the whole stack.
1454                        size_t f0index=back.f0index, f2index=back.f2index;
1455                        float_type left=0, right=0;
1456                        if(rb.derivs==0) {
1457                                // compute three-point parabolic interpolation estimate of right-hand and left-hand midpoint
1458                                left=(6*f1.y + 3*f0.y - f2.y) * 0.125;
1459                                right=(6*f1.y + 3*f2.y - f0.y) * 0.125;
1460                        }
1461                       
1462                        top.f0index=f1index; top.f2index=f2index; // insert pointers to right side data into our recursion block                       
1463                        top.previous_estimate=right;
1464                        rb_stack.push_back(top);
1465
1466                        top.f0index=f0index; top.f2index=f1index; // insert pointers to left side data into our recursion block
1467                        top.previous_estimate=left;
1468                        rb_stack.push_back(top);
1469                }
1470        }
1471}
1472
1473template <typename float_type>  c2_piecewise_function_p<float_type> *
1474        c2_function<float_type>::adaptively_sample(
1475                float_type xmin, float_type xmax,
1476                float_type abs_tol, float_type rel_tol,
1477                int derivs, std::vector<float_type> *xvals, std::vector<float_type> *yvals) const throw(c2_exception)
1478{
1479        c2_fblock<float_type> f0, f2;
1480        c2_sample_recur rb;
1481        std::vector< recur_item > rb_stack;
1482        rb_stack.reserve(20); // enough for most operations
1483        rb.rb_stack=&rb_stack;
1484        rb.out=0;
1485        if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>();
1486        c2_ptr<float_type> pieces(*rb.out); // manage this function, if any, so it deletes on an exception
1487        rb.rel_tol=rel_tol;
1488        rb.abs_tol=abs_tol;
1489        rb.xvals=xvals;
1490        rb.yvals=yvals;
1491        rb.derivs=derivs;
1492        rb.inited=false;
1493       
1494        if(xvals && yvals) {
1495                xvals->clear();
1496                yvals->clear();
1497        }
1498         
1499        // create xgrid as a automatic-variable copy of the sampling grid so the exception handler correctly
1500        // disposes of it.
1501        std::vector<float_type> xgrid;
1502        get_sampling_grid(xmin, xmax, xgrid);
1503        int np=xgrid.size();     
1504
1505        f2.x=xgrid[0];
1506        fill_fblock(f2);
1507        if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1508                // can't go any further if a nan has appeared
1509                bad_x_point=f2.x;
1510                throw c2_exception("NaN encountered while sampling function");
1511        }
1512
1513        for(int i=0; i<np-1; i++) {
1514                f0=f2; // copy upper bound to lower before computing new upper bound
1515
1516                f2.x=xgrid[i+1];
1517                fill_fblock(f2);
1518                if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1519                        // can't go any further if a nan has appeared
1520                        bad_x_point=f2.x;
1521                        throw c2_exception("NaN encountered while sampling function");
1522                }
1523
1524                rb.f0=&f0; rb.f1=&f2;
1525                sample_step(rb);
1526        }
1527        if(xvals && yvals) { // push final point in vector
1528                xvals->push_back(f2.x);
1529                yvals->push_back(f2.y);
1530        }
1531       
1532        if(rb.out) rb.out->set_sampling_grid(xgrid); // reflect old sampling grid, which still should be right
1533        pieces.release_for_return(); // unmanage the piecewise_function so we can return it
1534        return rb.out;
1535}
1536
1537template <typename float_type, typename Final>
1538 interpolating_function_p<float_type> & inverse_integrated_density_function(
1539        const std::vector<float_type> &bincenters, const c2_function<float_type> &binheights)
1540        throw(c2_exception)
1541{       
1542        return (new Final())->load_random_generator_function(bincenters, binheights);
1543}
1544
1545template <typename float_type, typename Final>
1546        interpolating_function_p<float_type> & inverse_integrated_density_bins(
1547        const std::vector<float_type> &bins, const std::vector<float_type> &binheights)
1548        throw(c2_exception)
1549{       
1550        return (new Final())->load_random_generator_bins(bins, binheights);
1551}
Note: See TracBrowser for help on using the repository browser.