source: trunk/examples/extended/electromagnetic/TestEm7/include/c2_function.hh@ 1036

Last change on this file since 1036 was 807, checked in by garnier, 17 years ago

update

File size: 74.6 KB
Line 
1/**
2 * \file
3 * \brief Provides the headers for the general c2_function algebra which supports
4 * fast, flexible operations on piecewise-twice-differentiable functions
5 *
6 * \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05.
7 * \author Copyright 2005 __Vanderbilt University__. All rights reserved.
8 *
9 * \version c2_function.hh,v 1.53 2007/11/12 13:58:57 marcus Exp
10 */
11
12#ifndef __has_C2Functions_c2_h
13#define __has_C2Functions_c2_h 1
14
15#include <cmath>
16#include <vector>
17#include <string>
18
19/// \brief the exception class for c2_function operations.
20class c2_exception : public std::exception {
21public:
22 /// \brief construct the exception with an error message
23 /// \param msgcode the message
24 c2_exception(const char msgcode[]) : info(msgcode) { }
25 virtual ~c2_exception() throw() { }
26 /** Returns a C-style character string describing the general cause
27 * of the current error. */
28 virtual const char* what() const throw() { return info.c_str(); }
29private:
30 std::string info;
31};
32
33// put these forward references here, and with a bogus typename to make swig happy.
34template <typename float_type> class c2_composed_function;
35template <typename float_type> class c2_sum;
36template <typename float_type> class c2_diff;
37template <typename float_type> class c2_product;
38template <typename float_type> class c2_ratio;
39
40/**
41 \brief the parent class for all c2_functions.
42
43 c2_functions know their value, first, and second derivative at almost every point.
44 They can be efficiently combined with binary operators, via c2_binary_function,
45 composed via c2_composed_function,
46 have their roots found via find_root(),
47 and be adaptively integrated via partial_integrals() or integral().
48 They also can carry information with them about how to find 'interesting' points on the function.
49 This information is set with set_sampling_grid() and extracted with get_sampling_grid().
50
51 Particularly important subclasses are the interpolating functions classes,
52 interpolating_function , lin_log_interpolating_function, log_lin_interpolating_function,
53 log_log_interpolating_function, and arrhenius_interpolating_function,
54 as well as the template functions
55 inverse_integrated_density().
56
57 \warning
58 The composite flavors of c2_functions (c2_sum, c2_composed_function, c2_binary_function, e.g.) make no effort to manage
59 deletion of their component functions.
60 These are just container classes, and the user (along with normal automatic variable semantics)
61 is responsible for the lifetime of components.
62 Inappropriate attention to this can cause massive memory leaks.
63 However, in most cases these do exactly what is intended.
64 The classes will be left this way since the only other option is to use copy constructors on everything,
65 which would make this all very slow.
66
67 */
68template <typename float_type=double> class c2_function {
69public:
70 /// \brief get versioning information for the header file
71 /// \return the CVS Id string
72 const std::string cvs_header_vers() const { return
73 "c2_function.hh,v 1.53 2007/11/12 13:58:57 marcus Exp";
74 }
75
76 /// \brief get versioning information for the source file
77 /// \return the CVS Id string
78 const std::string cvs_file_vers() const ;
79
80public:
81 /// \brief destructor
82 virtual ~c2_function() { if(sampling_grid && !no_overwrite_grid) delete sampling_grid; }
83
84 /// \brief get the value and derivatives.
85 ///
86 /// There is required checking for null pointers on the derivatives,
87 /// and most implementations should operate faster if derivatives are not needed.
88 /// \param[in] x the point at which to evaluate the function
89 /// \param[out] yprime the first derivative (if pointer is non-null)
90 /// \param[out] yprime2 the second derivative (if pointer is non-null)
91 /// \return the value of the function
92 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) =0 ; // { return 0; };
93
94 /// \brief evaluate the function in the classic way, ignoring derivatives.
95 /// \param x the point at which to evaluate
96 /// \return the value of the function
97 inline float_type operator () (float_type x) const throw(c2_exception)
98 { return value_with_derivatives(x, (float_type *)0, (float_type *)0); }
99
100 /// \brief compose this function outside another.
101 /// \param inner the inner function
102 /// \return the composed function
103 c2_composed_function<float_type> & operator ()(const c2_function<float_type> &inner) const
104 { return *new c2_composed_function<float_type>((*this), inner); }
105
106 /// \brief get the value and derivatives.
107 ///
108 /// \param[in] x the point at which to evaluate the function
109 /// \param[out] yprime the first derivative (if pointer is non-null)
110 /// \param[out] yprime2 the second derivative (if pointer is non-null)
111 /// \return the value of the function
112 inline float_type operator () (float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
113 { return value_with_derivatives(x, yprime, yprime2); }
114
115 /// \brief solve f(x)==value very efficiently, with explicit knowledge of derivatives of the function
116 ///
117 /// find_root solves by iterated inverse quadratic extrapolation for a solution to f(x)=y. It
118 /// includes checks against bad convergence, so it should never be able to fail. Unlike typical
119 /// secant method or fancier Brent's method finders, this does not depend in any strong wasy on the
120 /// brackets, unless the finder has to resort to successive approximations to close in on a root.
121 /// Often, it is possible to make the brackets equal to the domain of the function, if there is
122 /// any clue as to where the root lies, as given by the parameter \a start.
123 /// \param lower_bracket the lower bound for the search
124 /// \param upper_bracket the upper bound for the search. Function sign must be
125 /// opposite to that at \a lower_bracket
126 /// \param start starting value for the search
127 /// \param value the value of the function being sought (solves f(x) = \a value)
128 /// \param[out] error If pointer is zero, errors raise exception. Otherwise, returns error here.
129 /// \param[out] final_yprime If pointer is not zero, return derivative of function at root
130 /// \param[out] final_yprime2 If pointer is not zero, return second derivative of function at root
131 /// \return the position of the root.
132 float_type find_root(float_type lower_bracket, float_type upper_bracket, float_type start,
133 float_type value, int *error=0,
134 float_type *final_yprime=0, float_type *final_yprime2=0 ) const throw(c2_exception) ; // solve f(x)=value
135
136 /// \brief for points in xgrid, adaptively return Integral[f(x),{x,xgrid[i],xgrid[i+1]}] and return in vector, along with sum
137 ///
138 /// partial_integrals uses a method with an error O(dx**10) with full information from the derivatives,
139 /// and falls back to lower order methods if informed of incomplete derivatives.
140 /// It uses exact midpoint splitting of the intervals for recursion, resulting in no recomputation of the function
141 /// during recursive descent at previously computed points.
142 /// \param xgrid points between which to evaluate definite integrals.
143 /// \param partials if non-NULL, a vector in which to receive the partial integrals.
144 /// It will automatically be sized apprpropriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid
145 /// \param abs_tol the absolute error bound for each segment
146 /// \param rel_tol the fractional error bound for each segment.
147 /// If the error is smaller than either the relative or absolute tolerance, the integration step is finished.
148 /// \param derivs number of derivatives to trust, which sets the order of the integrator. The order
149 /// is 3*\a derivs + 4. \a derivs can be 0, 1, or 2.
150 /// \param adapt if true, use recursive adaptation, otherwise do simple evaluation on the grid provided
151 /// with no error checking.
152 /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error.
153 /// \return sum of partial integrals, whcih is the definite integral from the first value in \a xgrid to the last.
154 float_type partial_integrals(std::vector<float_type> xgrid, std::vector<float_type> *partials = 0,
155 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const;
156
157 /// \brief a fully-automated integrator which uses the information provided by the get_sampling_grid() function
158 /// to figure out what to do.
159 ///
160 /// It returns the integral of the function over the domain requested
161 /// with error tolerances as specified. It is just a front-end to partial_integrals()
162 ///
163 /// \param xmin lower bound of the domain for integration
164 /// \param xmax upper bound of the domain for integration
165 /// \param partials if non-NULL, a vector in which to receive the partial integrals.
166 /// It will automatically be sized apprpropriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid
167 /// \param abs_tol the absolute error bound for each segment
168 /// \param rel_tol the fractional error bound for each segment.
169 /// If the error is smaller than either the relative or absolute tolerance, the integration step is finished.
170 /// \param derivs number of derivatives to trust, which sets the order of the integrator. The order
171 /// is 3*\a derivs + 4. \a derivs can be 0, 1, or 2.
172 /// \param adapt if true, use recursive adaptation, otherwise do simple evaluation on the grid provided
173 /// with no error checking.
174 /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error.
175 /// \return sum of partial integrals, whcih is the definite integral from the first value in \a xgrid to the last.
176 float_type integral(float_type xmin, float_type xmax, std::vector<float_type> *partials = 0,
177 float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const;
178
179 /// \brief return the lower bound of the domain for this function as set by set_domain()
180 inline float_type xmin() const { return fXMin; }
181 /// \brief return the upper bound of the domain for this function as set by set_domain()
182 inline float_type xmax() const { return fXMax; }
183 /// \brief set the domain for this function.
184 void set_domain(float_type xmin, float_type xmax) { fXMin=xmin; fXMax=xmax; }
185
186 /// \brief this is a counter owned by the function but which can be used to monitor efficiency of algorithms.
187 ///
188 /// It is not maintained automatically in general!
189 /// The adaptive integrator and root finder do clear it at the start and update it for performance checking.
190 /// \return number of evaluations logged since last reset.
191 volatile int get_evaluations() const { return evaluations; }
192 /// \brief reset the counter
193 void reset_evaluations() const { evaluations=0; } // evaluations are 'invisible' to constant
194 /// \brief count evaluations
195 inline void increment_evaluations() const { evaluations++; }
196
197 /// \brief check that a vector is monotonic, throw an exception if not, and return a flag if it is reversed
198 ///
199 /// \param data a vector of data points which are expected to be monotonic.
200 /// \param message an informative string to include in an exception if this throws c2_exception
201 /// \return true if in decreasing order, false if increasing
202 bool check_monotonicity(const std::vector<float_type> &data, const char message[]) throw(c2_exception);
203
204 /// \brief establish a grid of 'interesting' points on the function.
205 ///
206 /// The sampling grid describes a reasonable initial set of points to look at the function.
207 /// this should generally be set at a scale which is quite coarse, and sufficient for initializing
208 /// adaptive integration or possibly root bracketing. For sampling a function to build a new interpolating
209 /// function, one may want to refine this for accuracy. However, interpolating_functions themselves
210 /// return their original X grid by default, so refining the grid in this case might be a bad idea.
211 /// \param grid a vector of abscissas. The contents is copied into an internal vector, so the \a grid can be discarded after passingin.
212 virtual void set_sampling_grid(const std::vector<float_type> &grid) throw(c2_exception);
213
214 /// \brief return the grid of 'interesting' points along this function which lie in the region requested
215 ///
216 /// if a sampling grid is defined, work from there, otherwise return vector of (xmin, xmax)
217 /// \param xmin the lower bound for which the function is to be sampled
218 /// \param xmax the upper bound for which the function is to be sampled
219 /// \return a new vector containing the samplng grid.
220 virtual std::vector<float_type> &get_sampling_grid(float_type xmin, float_type xmax) const ;
221
222 /// \brief clean up endpoints on a grid of points
223 ///
224 /// \param[in,out] result the sampling grid with excessively closely space endpoints removed.
225 /// The grid is modified in place.
226 void preen_sampling_grid(std::vector<float_type> *result) const;
227 /// \brief refine a grid by splitting each interval into more intervals
228 /// \param grid the grid to refine
229 /// \param refinement the number of new steps for each old step
230 /// \return a new sampling grid with more points.
231 std::vector<float_type> & refine_sampling_grid(const std::vector<float_type> &grid, size_t refinement) const;
232
233 /// \brief create a new c2_function from this one which is normalized on the interval
234 /// \param xmin lower bound of the domain for integration
235 /// \param xmax upper bound of the domain for integration
236 /// \param norm the desired integral for the function over the region
237 /// \return a new c2_function with the desired \a norm.
238 c2_function<float_type> &normalized_function(float_type xmin, float_type xmax, float_type norm=1.0);
239 /// \brief create a new c2_function from this one which is square-normalized on the interval
240 /// \param xmin lower bound of the domain for integration
241 /// \param xmax upper bound of the domain for integration
242 /// \param norm the desired integral for the function over the region
243 /// \return a new c2_function with the desired \a norm.
244 c2_function<float_type> &square_normalized_function(float_type xmin, float_type xmax, float_type norm=1.0);
245 /// \brief create a new c2_function from this one which is square-normalized with the provided \a weight on the interval
246 /// \param xmin lower bound of the domain for integration
247 /// \param xmax upper bound of the domain for integration
248 /// \param weight a c2_function providing the weight
249 /// \param norm the desired integral for the function over the region
250 /// \return a new c2_function with the desired \a norm.
251 c2_function<float_type> &square_normalized_function(float_type xmin, float_type xmax, const c2_function<float_type> &weight, float_type norm=1.0);
252
253 /// \brief factory function to create a c2_sum from an regular algebraic expression.
254 /// \note
255 /// be very wary of ownership issues if this is used in a complex expression.
256 c2_sum<float_type> &operator + (const c2_function<float_type> &rhs) { return *new c2_sum<float_type>(*this, rhs); }
257 /// \brief factory function to create a c2_diff from an regular algebraic expression.
258 /// \note
259 /// be very wary of ownership issues if this is used in a complex expression.
260 c2_diff<float_type> &operator - (const c2_function<float_type> &rhs) { return *new c2_diff<float_type>(*this, rhs); }
261 /// \brief factory function to create a c2_product from an regular algebraic expression.
262 /// \note
263 /// be very wary of ownership issues if this is used in a complex expression.
264 c2_product<float_type> &operator * (const c2_function<float_type> &rhs) { return *new c2_product<float_type>(*this, rhs); }
265 /// \brief factory function to create a c2_ratio from an regular algebraic expression.
266 /// \note
267 /// be very wary of ownership issues if this is used in a complex expression.
268 c2_ratio<float_type> &operator / (const c2_function<float_type> &rhs) { return *new c2_ratio<float_type>(*this, rhs); }
269
270
271
272 std::vector<float_type> *sampling_grid;
273 bool no_overwrite_grid;
274
275protected:
276 c2_function(const c2_function<float_type> &src) : sampling_grid(0),
277 no_overwrite_grid(false),
278 fXMin(src.fXMin), fXMax(src.fXMax), rootInitialized(false)
279 {} // copy constructor only copies domain, and is only for internal use
280 c2_function() :
281 sampling_grid(0), no_overwrite_grid(0),
282 fXMin(-std::numeric_limits<float_type>::max()),
283 fXMax(std::numeric_limits<float_type>::max()),
284 rootInitialized(false)
285 {} // prevent accidental naked construction (impossible any since this has pure virtual methods)
286
287 // this should only be called very early on, by a constructor, before anyone else
288 // sets a sampling grid, or it will leak memory
289 virtual void set_sampling_grid_pointer(std::vector<float_type> &grid)
290 {
291 if (sampling_grid && !no_overwrite_grid) delete sampling_grid; // grid was ours, lose it.
292 sampling_grid=&grid; no_overwrite_grid=1;
293 }
294
295 float_type fXMin, fXMax;
296 mutable int evaluations;
297
298private:
299 /// \brief structure used for recursion in adaptive integrator.
300 ///
301 /// Contains all the information for the function at one point.
302 struct c2_integrate_fblock { float_type x, y, yp, ypp; };
303 /// \brief structure used to pass information recursively.
304 ///
305 /// the \a abs_tol is scaled by a factor of two at each division.
306 /// Everything else is just passed down.
307 struct c2_integrate_recur { struct c2_integrate_fblock *f0, *f1, *f2;
308 float_type abs_tol, rel_tol, *lr, eps_scale, extrap_coef, extrap2;
309 int depth, derivs;
310 bool adapt, extrapolate;
311 };
312
313 /// \brief Carry out the recursive subdivision and integration.
314 ///
315 /// This passes information recursively through the \a recur block pointer
316 /// to allow very efficient recursion.
317 /// \param rb a pointer to the recur struct.
318 float_type integrate_step(struct c2_integrate_recur &rb) const;
319
320 /// these carry a memory of the last root bracketing,
321 /// to avoid the necessity of evaluating the function on the brackets every time
322 /// if the brackets have not been changed.
323 mutable float_type lastRootLowerX, lastRootUpperX, lastRootLowerY, lastRootUpperY;
324 mutable int rootInitialized;
325
326};
327
328/// \brief a container into which any other c2_function can be dropped, to allow expressions
329/// with replacable components.
330///
331///It is useful for plugging different InterpolatingFunctions into a c2_function expression.
332///It saves a lot of effort in other places with casting away const declarations.
333///
334/// It is also useful as a wrapper for a function if it is necessary to have a copy of a function
335/// which has a different domain or sampling grid than the parent function. This can be
336/// be used, for example, to patch badly-behaved functions with c2_piecewise_function by
337/// taking the parent function, creating two plugins of it with domains on each side of the
338/// nasty bit, and then inserting a nice function in the hole.
339template <typename float_type=double> class c2_plugin_function : public c2_function<float_type> {
340public:
341 /// \brief construct the container with no function
342 c2_plugin_function() : c2_function<float_type>(), func(0), owns(false) {}
343 /// \brief construct the container with a pre-defined function
344 c2_plugin_function(const c2_function<float_type> &f) : c2_function<float_type>(), func(0), owns(false) { set_function(f); }
345 /// \brief fill the container with a new function
346 void set_function(const c2_function<float_type> &f)
347 {
348 if(owns && func) delete func;
349 func=&f; set_domain(f.xmin(), f.xmax());
350 set_sampling_grid_pointer(*f.sampling_grid);
351 owns=false;
352 }
353 /// \copydoc c2_function::value_with_derivatives
354 /// Uses the internal function pointer set by set_function().
355 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
356 {
357 if(!func) throw c2_exception("c2_plugin_function<float_type> called uninitialized");
358 return this->func->value_with_derivatives(x, yprime, yprime2);
359 }
360 /// \brief clear the function
361 ///
362 /// Any attempt to use this c2_plugin_function throws an exception if the saved function is cleared.
363 void unset_function(void) { if(owns && func) delete func; func=0; owns=false; }
364 /// \brief destructor
365 ~c2_plugin_function() { if(owns && func) delete func; }
366 /// \brief tell us we should delete the function at destruction. NOT sticky when function is reset
367 void set_ownership() { this->owns=true; }
368
369protected:
370 const c2_function<float_type> *func;
371 bool owns;
372
373};
374
375/// \brief Provides support for c2_function objects which are constructed from two other c2_function
376/// objects.
377///
378/// It provides a very primitive ownership concept, so that the creator can tag a function
379/// as being owned by this function, so that when this function is deleted, the owned function will be deleted, too.
380/// This allows a piece of code to create various c2_function objects, combine them with binary operators,
381/// appropriately mark wich ones have no other possible owners, and return the final function with
382/// reasonable faith that everything will get cleaned up when the final function is deleted. Note that
383/// none of this marking is automatic, to keep this class very lightweight.
384template <typename float_type=double> class c2_binary_function : public c2_function<float_type> {
385public:
386
387
388 /// \brief function to manage the binary operation, used by c2_binary_function::value_with_derivatives()
389 ///
390 /// Normally not used alone, but can be used to combine functions in other contexts.
391 /// See interpolating_function::binary_operator() for an example.
392 /// \param left the function on the left of the binary operator or outside the composition
393 /// \param right the function to the right of the operator or inside the composition
394 /// \param[in] x the point at which to evaluate the function
395 /// \param[out] yprime the first derivative (if pointer is non-null)
396 /// \param[out] yprime2 the second derivative (if pointer is non-null)
397 /// \return the value of the function
398 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
399 float_type x, float_type *yprime, float_type *yprime2) const =0;
400
401 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception)
402 {
403 return this->combine(this->Left, this->Right, x, yprime, yprime2);
404 }
405
406 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup
407 ///
408 /// upon destruction, this will cause disposal of the left member function
409 void set_left_ownership(void) { leftown=true; }
410 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup
411 ///
412 /// upon destruction, this will cause disposal of the right member function
413 void set_right_ownership(void) { rightown=true; }
414 /// \brief allow c2_binary_function to remember ownership of contained functions for automatic cleanup
415 ///
416 /// upon destruction, this will cause disposal of both member functions
417 void set_ownership(void) { leftown=rightown=true; }
418 /// \brief destructor executes disposal of member functions if flagged
419 ///
420 /// depends on judicious use of set_ownership(), set_right_ownership(), or set_left_ownership()
421 virtual ~c2_binary_function() {
422 if(leftown) delete &Left;
423 if(rightown) delete &Right;
424 }
425
426protected:
427 /// \brief construct the binary function
428 /// \param left the c2_function to be used in the left side of the binary relation
429 /// \param right the c2_function to be used in the right side of the binary relation
430 c2_binary_function( const c2_function<float_type> &left, const c2_function<float_type> &right) :
431 c2_function<float_type>(),
432 Left(left), Right(right), leftown(false), rightown(false)
433 {
434 set_domain(
435 (left.xmin() > right.xmin()) ? left.xmin() : right.xmin(),
436 (left.xmax() < right.xmax()) ? left.xmax() : right.xmax()
437 );
438 }
439
440 /// \brief construct a 'stub' c2_binary_function, which provides access to the combine() function
441 /// \note Do not evaluate a 'stub' ever. It is only used so that combine() can be called
442 c2_binary_function() : c2_function<float_type>(),
443 Left(*((c2_function<float_type> *)0)), Right(*((c2_function<float_type> *)0)) {}
444
445 const c2_function<float_type> &Left, &Right;
446 bool leftown, rightown;
447
448};
449
450/// \brief Create a very lightweight method to return a scalar multiple of another function.
451///
452template <typename float_type=double> class c2_scaled_function : public c2_plugin_function<float_type> {
453public:
454 /// \brief construct the function with its scale factor.
455 ///
456 /// \param outer the function to be scaled
457 /// \param scale the multiplicative scale factor
458 c2_scaled_function(const c2_function<float_type> &outer, float_type scale) :
459 c2_plugin_function<float_type>(outer), yscale(scale) { }
460
461 /// \brief set a new scale factor
462 /// \param scale the new factor
463 void reset(float_type scale) { yscale=scale; }
464
465 /// \copydoc c2_function::value_with_derivatives
466 ///
467 /// provide our own value_with_derivatives which bypasses the combiner for quicker operation
468 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception)
469 {
470 float_type y=this->func->value_with_derivatives(x, yprime, yprime2);
471 if(yprime) (*yprime)*=yscale;
472 if(yprime2) (*yprime2)*=yscale;
473 return y*yscale;
474 }
475
476protected:
477 c2_scaled_function<float_type>() {} // hide default constructor, since its use is almost always an error.
478 float_type yscale;
479};
480
481/// \brief A container into which any other c2_function can be dropped.
482///
483/// It allows a function to be pre-evaluated at a point, and used at multiple places in an expression
484/// efficiently. If it is re-evaluated at the previous point, it returns the remembered values;
485/// otherwise, it re-evauates the function at the new point.
486///
487template <typename float_type=double> class c2_cached_function : public c2_plugin_function<float_type> {
488public:
489 /// \brief construct the container
490 ///
491 /// \param f the function to be cached
492 c2_cached_function(const c2_function<float_type> &f) : c2_plugin_function<float_type>(f), init(false) {}
493 /// \copydoc c2_function::value_with_derivatives
494 ///
495 /// Checks to see if the function is being re-evaluated at the previous point, and
496 /// returns remembered values if so.
497 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
498 {
499 if(!init || x != x0) {
500 y=this->func->value_with_derivatives(x, &yp, &ypp);
501 x0=x;
502 init=true;
503 }
504 if(yprime) *yprime=yp;
505 if(yprime2) *yprime2=ypp;
506 return y;
507 }
508
509protected:
510 c2_cached_function() {} // hide default constructor, since its use is almost always an error.
511 mutable bool init;
512 mutable float_type x0, y, yp, ypp;
513
514};
515
516/// \brief Provides function composition (nesting)
517///
518/// This allows evaluation of \a f(g(x)) where \a f and \a g are c2_function objects.
519///
520/// \note See c2_binary_function for discussion of ownership.
521template <typename float_type=double> class c2_composed_function : public c2_binary_function<float_type> {
522public:
523
524 /// \brief construct \a outer( \a inner (x))
525 /// \note See c2_binary_function for discussion of ownership.
526 /// \param outer the outer function
527 /// \param inner the inner function
528 c2_composed_function(const c2_function<float_type> &outer, const c2_function<float_type> &inner) : c2_binary_function<float_type>(outer, inner) {}
529 /// \brief Create a stub just for the combiner to avoid statics.
530 c2_composed_function() : c2_binary_function<float_type>() {} ;
531
532 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
533 float_type x, float_type *yprime, float_type *yprime2) const
534 {
535 float_type y0, yp0, ypp0, y1, yp1, ypp1;
536 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;
537 if(yprime || yprime2) {
538 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1;
539 } else {
540 yp0p=ypp0p=yp1p=ypp1p=0;
541 }
542
543 y0=right.value_with_derivatives(x, yp0p, ypp0p);
544 y1=left.value_with_derivatives(y0, yp1p, ypp1p);
545 if(yprime) *yprime=yp1*yp0;
546 if(yprime2) *yprime2=ypp0*yp1+yp0*yp0*ypp1;
547 return y1;
548 }
549};
550
551/// \brief create a c2_function which is the sum of two other c2_function objects.
552///
553/// \note See c2_binary_function for discussion of ownership.
554template <typename float_type=double> class c2_sum : public c2_binary_function<float_type> {
555public:
556 /// \brief construct \a left + \a right
557 /// \note See c2_binary_function for discussion of ownership.
558 /// \param left the left function
559 /// \param right the right function
560 c2_sum(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}
561 /// \brief Create a stub just for the combiner to avoid statics.
562 c2_sum() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics
563
564 // function to do derivative arithmetic for sums
565 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
566 float_type x, float_type *yprime, float_type *yprime2) const
567 {
568 float_type y0, yp0, ypp0, y1, yp1, ypp1;
569 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;
570 if(yprime || yprime2) {
571 yp0p=&yp0; ypp0p=& ypp0; yp1p=&yp1; ypp1p=&ypp1;
572 } else {
573 yp0p=ypp0p=yp1p=ypp1p=0;
574 }
575 y0=left.value_with_derivatives(x, yp0p, ypp0p);
576 y1=right.value_with_derivatives(x, yp1p, ypp1p);
577 if(yprime) *yprime=yp0+yp1;
578 if(yprime2) *yprime2=ypp0+ypp1;
579 return y0+y1;
580 }
581};
582
583
584/// \brief create a c2_function which is the difference of two other c2_functions.
585///
586/// \note See c2_binary_function for discussion of ownership.
587template <typename float_type=double> class c2_diff : public c2_binary_function<float_type> {
588public:
589 /// \brief construct \a left - \a right
590 /// \note See c2_binary_function for discussion of ownership.
591 /// \param left the left function
592 /// \param right the right function
593 c2_diff(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}
594 /// \brief Create a stub just for the combiner to avoid statics.
595 c2_diff() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics
596
597 // function to do derivative arithmetic for diffs
598 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
599 float_type x, float_type *yprime, float_type *yprime2) const
600 {
601 float_type y0, yp0, ypp0, y1, yp1, ypp1;
602 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;
603 if(yprime || yprime2) {
604 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1;
605 } else {
606 yp0p=ypp0p=yp1p=ypp1p=0;
607 }
608 y0=left.value_with_derivatives(x, yp0p, ypp0p);
609 y1=right.value_with_derivatives(x, yp1p, ypp1p);
610 if(yprime) *yprime=yp0-yp1;
611 if(yprime2) *yprime2=ypp0-ypp1;
612 return y0-y1;
613 }
614};
615
616
617/// \brief create a c2_function which is the product of two other c2_functions.
618///
619/// \note See c2_binary_function for discussion of ownership.
620template <typename float_type=double> class c2_product : public c2_binary_function<float_type> {
621public:
622 /// \brief construct \a left * \a right
623 /// \note See c2_binary_function for discussion of ownership.
624 /// \param left the left function
625 /// \param right the right function
626 c2_product(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}
627 /// \brief Create a stub just for the combiner to avoid statics.
628 c2_product() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics
629
630 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
631 float_type x, float_type *yprime, float_type *yprime2) const
632 {
633 float_type y0, yp0, ypp0, y1, yp1, ypp1;
634 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;
635 if(yprime || yprime2) {
636 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1;
637 } else {
638 yp0p=ypp0p=yp1p=ypp1p=0;
639 }
640 y0=left.value_with_derivatives(x, yp0p, ypp0p);
641 y1=right.value_with_derivatives(x, yp1p, ypp1p);
642 if(yprime) *yprime=y1*yp0+y0*yp1;
643 if(yprime2) *yprime2=ypp0*y1+2.0*yp0*yp1+ypp1*y0;
644 return y0*y1;
645 }
646};
647
648
649/// \brief create a c2_function which is the ratio of two other c2_functions.
650///
651/// \note See c2_binary_function for discussion of ownership.
652template <typename float_type=double> class c2_ratio : public c2_binary_function<float_type> {
653public:
654 /// \brief construct \a left / \a right
655 /// \note See c2_binary_function for discussion of ownership.
656 /// \param left the left function
657 /// \param right the right function
658 c2_ratio(const c2_function<float_type> &left, const c2_function<float_type> &right) : c2_binary_function<float_type>(left, right) {}
659 /// \brief Create a stub just for the combiner to avoid statics.
660 c2_ratio() : c2_binary_function<float_type>() {} ; // create a stub just for the combiner to avoid statics
661
662 virtual float_type combine(const c2_function<float_type> &left, const c2_function<float_type> &right,
663 float_type x, float_type *yprime, float_type *yprime2) const
664 {
665 float_type y0, yp0, ypp0, y1, yp1, ypp1;
666 float_type *yp0p, *ypp0p, *yp1p, *ypp1p;
667 if(yprime || yprime2) {
668 yp0p=&yp0; ypp0p=&ypp0; yp1p=&yp1; ypp1p=&ypp1;
669 } else {
670 yp0p=ypp0p=yp1p=ypp1p=0;
671 }
672 y0=left.value_with_derivatives(x, yp0p, ypp0p);
673 y1=right.value_with_derivatives(x, yp1p, ypp1p);
674 if(yprime) *yprime=(yp0*y1-y0*yp1)/(y1*y1); // first deriv of ratio
675 if(yprime2) *yprime2=(y1*y1*ypp0+y0*(2*yp1*yp1-y1*ypp1)-2*y1*yp0*yp1)/(y1*y1*y1); // second deriv of ratio
676 return y0/y1;
677 }
678
679};
680
681/// \brief a c2_function which is constant : can do interpolating_function f2=f1 + c2_constant(11.3)
682template <typename float_type> class c2_constant : public c2_function<float_type> {
683public:
684 c2_constant(float_type x=0.0) : c2_function<float_type>(), value(x) {}
685 void reset(float_type val) { value=val; }
686 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
687 { if(yprime) *yprime=0; if(yprime2) *yprime2=0; return value; }
688
689private:
690 float_type value;
691};
692
693/**
694 \brief create a cubic spline interpolation of a set of (x,y) pairs
695
696 This is one of the main reasons for c2_function objects to exist.
697
698 It provides support for cubic spline interpolation of data provides from tables of \a x, \a y pairs.
699 It supports automatic, transparent linearization of the data before storing in its tables (through
700 subclasses such as
701 log_lin_interpolating_function, lin_log_interpolating_function, and
702 log_log_interpolating_function) to permit very high accuracy representations of data which have a suitable
703 structure. It provides utility functions LinearInterpolatingGrid() and LogLogInterpolatingGrid()
704 to create grids for mapping other functions onto a arithmetic or geometric grid.
705
706 In its simplest form, an untransformed cubic spline of a data set, using natural boundary conditions
707 (vanishing second derivative), is created as: \n
708 \code
709 std::vector<double> xvals(10), yvals(10);
710 // < fill in xvals and yvals >
711 interpolating_function<double> myfunc(xvals, yvals);
712 // and it can be evaluated at a point for its value only by:
713 double y=myfunc(x);
714 // or it can be evaluated with its derivatives by
715 double yprime, yprime2;
716 double y=myfunc(x,&yprime, &yprime2);
717 \endcode
718*/
719
720template <typename float_type=double> class interpolating_function : public c2_function<float_type> {
721public:
722 /// \brief create the most general interpolating_function which defaults to linear-linear space
723 ///
724 /// lots to say here, but see Numerical Recipes for a discussion of cubic splines.
725 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing.
726 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid.
727 /// \param f the list of function values.
728 /// \param lowerSlopeNatural if true, set y''(first point)=0, otherwise compute it from \a lowerSope
729 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false
730 /// \param upperSlopeNatural if true, set y''(last point)=0, otherwise compute it from \a upperSope
731 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false
732 /// \param inputXConversion a function (not a c2_function) which converts \a x into the internal space. \n
733 /// If this is NULL, use linear space and ignore inputXConversionPrime, inputXConversionDPrime
734 /// \param inputYConversion a function (not a c2_function) which converts \a y into the internal space. \n
735 /// If this is NULL, use linear space and ignore inputYConversionPrime, inputYConversionDPrime, outputYConversion
736 /// \param outputYConversion a function (not a c2_function) which converts \a y out of the internal space
737 /// \param inputXConversionPrime the derivative of \a inputXConversion
738 /// \param inputYConversionPrime the derivative of \a inputYConversion
739 /// \param inputXConversionDPrime the second derivative of \a inputXConversion
740 /// \param inputYConversionDPrime the second derivative of \a inputYConversion
741 interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f,
742 bool lowerSlopeNatural=true, float_type lowerSlope=0.0,
743 bool upperSlopeNatural=true, float_type upperSlope=0.0,
744 float_type (*inputXConversion)(float_type)=0,
745 float_type (*inputYConversion)(float_type)=0,
746 float_type (*outputYConversion)(float_type)=0,
747 float_type (*inputXConversionPrime)(float_type)=0,
748 float_type (*inputYConversionPrime)(float_type)=0,
749 float_type (*inputXConversionDPrime)(float_type)=0,
750 float_type (*inputYConversionDPrime)(float_type)=0
751 ) throw(c2_exception) : c2_function<float_type>()
752 { init(x, f, lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope,
753 inputXConversion, inputYConversion, outputYConversion,
754 inputXConversionPrime, inputYConversionPrime,
755 inputXConversionDPrime, inputYConversionDPrime
756 );
757 }
758
759 /// \brief copy constructor
760 /// \param rhs interpolating_function to copy from
761 interpolating_function(const interpolating_function <float_type> &rhs) : c2_function<float_type>(rhs),
762 Xraw(rhs.Xraw), X(rhs.X), F(rhs.F), y2(rhs.y2),
763 fXin(rhs.fXin), fYin(rhs.fYin), fYout(rhs.fYout),
764 fXinPrime(rhs.fXinPrime), fYinPrime(rhs.fYinPrime),
765 fXinDPrime(rhs.fXinDPrime), fYinDPrime(rhs.fYinDPrime) ,
766 xInverted(rhs.xInverted), lastKLow(-1)
767 { set_sampling_grid_pointer(Xraw); }
768
769 virtual ~interpolating_function() { } // just to suppress warnings about no virtual destructor
770
771 /// \brief retrieve copies of the x & y tables from which this was built
772 ///
773 /// This is often useful in the creation of new interpolating functions with transformed data.
774 /// The vectorswill have their sizes set correctly on return.
775 /// \param [in, out] xvals the abscissas
776 /// \param [in, out] yvals the ordinates
777 void get_data(std::vector<float_type> &xvals, std::vector<float_type> &yvals) const throw() ;
778
779 /// \brief enable extrapolation of the function below the tabulated data.
780 ///
781 /// This allows the interpolator to be extrapolated outside the bounds of the data,
782 /// using whatever derivatives it already had at the lower bound.
783 /// \param bound the abscissa to which the function should be extended.
784 void set_lower_extrapolation(float_type bound);
785 /// \brief enable extrapolation of the function above the tabulated data.
786 ///
787 /// This allows the interpolator to be extrapolated outside the bounds of the data,
788 /// using whatever derivatives it already had at the upper bound.
789 /// \param bound the abscissa to which the function should be extended.
790 void set_upper_extrapolation(float_type bound);
791
792 // these functions correctly combine the interpolating function with another interpolating function
793 // preserving the X bounds and mapping functions of the host (left hand) function.
794
795 /// \brief create a new interpolating_function which is the \a source
796 /// function applied to every point in the interpolating tables
797 ///
798 /// This carefully manages the derivative of the composed function at the two ends.
799 /// \param source the function to apply
800 /// \return a new interpolating_function with the same mappings for x and y
801 interpolating_function <float_type> & unary_operator(const c2_function<float_type> &source) const;
802
803 /// \brief create a new interpolating_function which is the parent interpolating_function
804 /// combined with \a rhs using \a combiner at every point in the interpolating tables
805 ///
806 /// This carefully manages the derivative of the composed function at the two ends.
807 /// \param rhs the function to apply
808 /// \param combining_stub a function which defines which binary operation to use.
809 /// \return a new interpolating_function with the same mappings for x and y
810 interpolating_function <float_type> & binary_operator(const c2_function<float_type> &rhs,
811 c2_binary_function<float_type> *combining_stub
812 ) const;
813
814 // InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
815 // when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
816 // can be upcast back to a c2_function to produce unprocessed binaries.
817
818 /// \brief produce a newly resampled interpolating_function which is the specified sum.
819 /// \param rhs the function to add, pointwise
820 /// \return a new interpolating_function
821 /// \note
822 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
823 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
824 /// can be upcast back to a c2_function to produce unprocessed binaries.
825 interpolating_function <float_type> & operator + (const c2_function<float_type> &rhs) const {
826 return binary_operator(rhs, new c2_sum<float_type>()); }
827 /// \brief produce a newly resampled interpolating_function which is the specified difference.
828 /// \param rhs the function to subtract, pointwise
829 /// \return a new interpolating_function
830 /// \note
831 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
832 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
833 /// can be upcast back to a c2_function to produce unprocessed binaries.
834 interpolating_function <float_type> & operator - (const c2_function<float_type> &rhs) const {
835 return binary_operator(rhs, new c2_diff<float_type>()); }
836 /// \brief produce a newly resampled interpolating_function which is the specified product.
837 /// \param rhs the function to multiply, pointwise
838 /// \return a new interpolating_function
839 /// \note
840 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
841 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
842 /// can be upcast back to a c2_function to produce unprocessed binaries.
843 interpolating_function <float_type> & operator * (const c2_function<float_type> &rhs) const {
844 return binary_operator(rhs, new c2_product<float_type>()); }
845 /// \brief produce a newly resampled interpolating_function which is the specified ratio.
846 /// \param rhs the function to divide, pointwise
847 /// \return a new interpolating_function
848 /// \note
849 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
850 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
851 /// can be upcast back to a c2_function to produce unprocessed binaries.
852 interpolating_function <float_type> & operator / (const c2_function<float_type> &rhs) const {
853 return binary_operator(rhs, new c2_ratio<float_type>()); }
854 /// \brief produce a newly resampled interpolating_function which is the specified sum.
855 /// \param rhs a constant to add, pointwise
856 /// \return a new interpolating_function
857 /// \note
858 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
859 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
860 /// can be upcast back to a c2_function to produce unprocessed binaries.
861 interpolating_function <float_type> & operator + (float_type rhs) const { return (*this)+c2_constant<float_type>(rhs); }
862 /// \brief produce a newly resampled interpolating_function which is the specified difference.
863 /// \param rhs a constant to subtract, pointwise
864 /// \return a new interpolating_function
865 /// \note
866 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
867 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
868 /// can be upcast back to a c2_function to produce unprocessed binaries.
869 interpolating_function <float_type> & operator - (float_type rhs) const { return (*this)-c2_constant<float_type>(rhs); }
870 /// \brief produce a newly resampled interpolating_function which is the specified product.
871 /// \param rhs a constant to multiply, pointwise
872 /// \return a new interpolating_function
873 /// \note
874 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
875 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
876 /// can be upcast back to a c2_function to produce unprocessed binaries.
877 interpolating_function <float_type> & operator * (float_type rhs) const { return (*this)*c2_constant<float_type>(rhs); }
878 /// \brief produce a newly resampled interpolating_function which is the specified ratio.
879 /// \param rhs a constant to divide, pointwise
880 /// \return a new interpolating_function
881 /// \note
882 /// InterpolatingFunctions override the c2_function operators, since they explicitly re-generate the interpolation table
883 /// when they are applied. If this is not desired, these operators are not virtual, so the interpolating_function
884 /// can be upcast back to a c2_function to produce unprocessed binaries.
885 interpolating_function <float_type> & operator / (float_type rhs) const { return (*this)/c2_constant<float_type>(rhs); }
886
887 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception);
888
889 /// \brief move value & derivatives into our internal coordinates (use splint to go the other way!)
890 /// \note why?
891 void localize_derivatives(float_type xraw, float_type y, float_type yprime, float_type yprime2, float_type *y0, float_type *yp0, float_type *ypp0) const;
892
893protected:
894
895 interpolating_function() : c2_function<float_type>() { } // default constructor is never used, prevent accidents by protecting it.
896
897 /// \brief do the dirty work of constructing the spline. See interpolating_function constructor for details.
898 void init(const std::vector<float_type> &, const std::vector<float_type> &,
899 bool lowerSlopeNatural, float_type lowerSlope,
900 bool upperSlopeNatural, float_type upperSlope,
901 float_type (*inputXConversion)(float_type)=0,
902 float_type (*inputXConversionPrime)(float_type)=0,
903 float_type (*inputXConversionDPrime)(float_type)=0,
904 float_type (*inputYConversion)(float_type)=0,
905 float_type (*inputYConversionPrime)(float_type)=0,
906 float_type (*inputYConversionDPrime)(float_type)=0,
907 float_type (*outputYConversion)(float_type)=0
908 ) throw(c2_exception) ;
909
910 std::vector<float_type> Xraw, X, F, y2;
911
912 float_type (*fXin)(float_type), (*fYin)(float_type), (*fYout)(float_type);
913 float_type (*fXinPrime)(float_type), (*fYinPrime)(float_type);
914 float_type (*fXinDPrime)(float_type), (*fYinDPrime)(float_type);
915
916 int xInverted;
917 mutable int lastKLow;
918};
919
920/// \brief An interpolatingFunction with X transformed into log space.
921///
922/// Most useful for functions looking like y=log(x) or any other function with a huge X dynamic range,
923/// and a slowly varying Y.
924template <typename float_type=double> class log_lin_interpolating_function : public interpolating_function <float_type> {
925public:
926 /// \brief Construct the function.
927 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing.
928 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid.
929 /// \param f the list of function values.
930 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LogLin space, otherwise compute it from \a lowerSope
931 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false
932 /// \param upperSlopeNatural if true, set y''(last point)=0 in LogLin space, otherwise compute it from \a upperSope
933 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false
934 log_lin_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f,
935 bool lowerSlopeNatural=true, float_type lowerSlope=0.0,
936 bool upperSlopeNatural=true, float_type upperSlope=0.0);
937protected:
938 log_lin_interpolating_function() {} // do not allow naked construction... it is usually an accident.
939};
940
941
942/// \brief An interpolatingFunction with Y transformed into log space.
943///
944/// Most useful for functions looking like y=exp(x)
945template <typename float_type=double> class lin_log_interpolating_function : public interpolating_function <float_type> {
946public:
947 /// \brief Construct the function.
948 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing.
949 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid.
950 /// \param f the list of function values.
951 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LinLog space, otherwise compute it from \a lowerSope
952 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false
953 /// \param upperSlopeNatural if true, set y''(last point)=0 in LinLog space, otherwise compute it from \a upperSope
954 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false
955 lin_log_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f,
956 bool lowerSlopeNatural=true, float_type lowerSlope=0.0,
957 bool upperSlopeNatural=true, float_type upperSlope=0.0);
958protected:
959 lin_log_interpolating_function() {} // do not allow naked construction... it is usually an accident.
960};
961
962
963/// \brief An interpolatingFunction with X and Y transformed into log space.
964///
965/// Most useful for functions looking like y=x^n or any other function with a huge X and Y dynamic range.
966template <typename float_type=double> class log_log_interpolating_function : public interpolating_function <float_type> {
967public:
968 /// \brief Construct the function.
969 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing.
970 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid.
971 /// \param f the list of function values.
972 /// \param lowerSlopeNatural if true, set y''(first point)=0 in LogLog space, otherwise compute it from \a lowerSope
973 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false
974 /// \param upperSlopeNatural if true, set y''(last point)=0 in LogLog space, otherwise compute it from \a upperSope
975 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false
976 log_log_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f,
977 bool lowerSlopeNatural=true, float_type lowerSlope=0.0,
978 bool upperSlopeNatural=true, float_type upperSlope=0.0);
979protected:
980 log_log_interpolating_function() {} // do not allow naked construction... it is usually an accident.
981};
982
983
984/// \brief An interpolating_function with X in reciprocal space and Y transformed in log space.
985///
986/// Most useful for thermodynamic types of data where Y is roughly A*exp(-B/x).
987/// Typical examples are reaction rate data, and thermistor calibration data.
988template <typename float_type=double> class arrhenius_interpolating_function : public interpolating_function <float_type> {
989public:
990 /// \brief Construct the function.
991 /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing.
992 /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid.
993 /// \param f the list of function values.
994 /// \param lowerSlopeNatural if true, set y''(first point)=0 in Arrhenius space, otherwise compute it from \a lowerSope
995 /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false
996 /// \param upperSlopeNatural if true, set y''(last point)=0 in Arrhenius space, otherwise compute it from \a upperSope
997 /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false
998 arrhenius_interpolating_function(const std::vector<float_type> &x, const std::vector<float_type> &f,
999 bool lowerSlopeNatural=true, float_type lowerSlope=0.0,
1000 bool upperSlopeNatural=true, float_type upperSlope=0.0);
1001protected:
1002 arrhenius_interpolating_function() {} // do not allow naked construction... it is usually an accident.
1003};
1004
1005/**
1006 \brief create a linear-linear interpolating grid with both x & y set to
1007 (xmin, xmin+dx, ... xmin + (count-1)*dx )
1008
1009 very useful for transformaiton with other functions e.g.
1010 \code
1011 f=c2_sin<double>::sin(LinearInterpolatingGrid(-0.1,0.1, 65))
1012 \endcode
1013 creates a spline table of sin(x) slightly beyond the first period
1014 \param xmin the starting point for the grid
1015 \param dx the step size for the grid
1016 \param count the number of points in the grid
1017 \return an identity interpolating_function with the requested grid
1018 */
1019template <typename float_type> interpolating_function <float_type> &linear_interpolating_grid(float_type xmin, float_type dx, int count) {
1020 std::vector<float_type> x(count);
1021 for(int i=0; i<count; i++) x[i]=xmin + i * dx;
1022 return *new interpolating_function <float_type>(x,x);
1023}
1024
1025/**
1026 \brief create a log-log interpolating grid with both x & y set to
1027 (xmin, xmin*dx, ... xmin * dx^(count-1) )
1028
1029 very useful for transformaiton with other functions e.g.
1030 \code
1031 f=c2_log<double>::log(LogLogInterpolatingGrid(2, 1.1, 65))
1032 \endcode
1033 creates a spline table of log(x)
1034 \param xmin the starting point for the grid
1035 \param dx the ratio between points
1036 \param count the number of points in the grid
1037 \return an identity log_log_interpolating_function with the requested grid
1038*/
1039template <typename float_type> log_log_interpolating_function <float_type> &log_log_interpolating_grid(float_type xmin, float_type dx, int count) {
1040 std::vector<float_type> x(count);
1041 x[0]=xmin;
1042 for(int i=1; i<count; i++) x[i]=dx*x[i-1];
1043 return *new log_log_interpolating_function<float_type>(x,x);
1044}
1045
1046/// \brief compute sin(x) with its derivatives.
1047///
1048/// Creates a singleton instance c2_sin::sin of itself for convenient access.
1049template <typename float_type=double> class c2_sin : public c2_function<float_type> {
1050public:
1051 /// \brief constructor. There is alread a singleton c2_sin::sin, which usually suffices.
1052 c2_sin() {}
1053
1054 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1055 { float_type q=std::sin(x); if(yprime) *yprime=std::cos(x); if(yprime2) *yprime2=-q; return q; }
1056
1057 /// \brief return a grid dynamically, suitable for use with trig functions with period 2*pi
1058 /// \param xmin the lower bound for the grid
1059 /// \param xmax upper bound for the grid
1060 /// \return a new sampling grid.
1061 virtual std::vector<float_type> &get_sampling_grid(float_type xmin, float_type xmax);
1062 /// \brief the static singleton
1063 static const c2_sin sin;
1064};
1065/// \brief compute cos(x) with its derivatives.
1066///
1067/// Creates a singleton instance c2_cos::cos of itself for convenient access.
1068template <typename float_type=double> class c2_cos : public c2_sin<float_type> {
1069public:
1070 /// \brief constructor. There is already a singleton c2_cos::cos, which usually suffices.
1071 c2_cos() {}
1072
1073 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1074 { float_type q=std::cos(x); if(yprime) *yprime=-std::sin(x); if(yprime2) *yprime2=-q; return q; }
1075 /// \brief the static singleton
1076 static const c2_cos cos;
1077};
1078/// \brief compute tan(x) with its derivatives.
1079///
1080/// Creates a singleton instance c2_tan::tan of itself for convenient access.
1081template <typename float_type=double> class c2_tan : public c2_function<float_type> {
1082public:
1083 /// \brief constructor. There is already a singleton c2_tan::tan, which usually suffices.
1084 c2_tan() {}
1085
1086 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1087 {
1088 float_type c=std::cos(x), s=std::sin(x);
1089 float_type t=s/c;
1090 float_type yp=1/(c*c);
1091 if(yprime) *yprime=yp; if(yprime2) *yprime2=2*t*yp;
1092 return t;
1093 }
1094 /// \brief the static singleton
1095 static const c2_tan tan;
1096};
1097/// \brief compute log(x) with its derivatives.
1098///
1099/// Creates a singleton instance c2_log::log of itself for convenient access.
1100template <typename float_type=double> class c2_log : public c2_function<float_type> {
1101public:
1102 /// \brief constructor. There is already a singleton c2_log::log, which usually suffices.
1103 c2_log() {}
1104
1105 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1106 { if(yprime) *yprime=1.0/x; if(yprime2) *yprime2=-1.0/(x*x); return std::log(x); }
1107 /// \brief the static singleton
1108 static const c2_log log;
1109};
1110/// \brief compute exp(x) with its derivatives.
1111///
1112/// Creates a singleton instance c2_exp::exp of itself for convenient access.
1113template <typename float_type=double> class c2_exp : public c2_function<float_type> {
1114public:
1115 /// \brief constructor. There is already a singleton c2_exp::exp, which usually suffices.
1116 c2_exp() {}
1117
1118 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1119 { float_type q=std::exp(x); if(yprime) *yprime=q; if(yprime2) *yprime2=q; return q; }
1120 /// \ brief the static singleton
1121 static const c2_exp exp;
1122};
1123/// \brief compute sqrt(x) with its derivatives.
1124///
1125/// Creates a singleton instance c2_sqrt::sqrt of itself for convenient access.
1126template <typename float_type=double> class c2_sqrt : public c2_function<float_type> {
1127public:
1128 /// \brief constructor. There is already a singleton c2_sqrt::sqrt, which usually suffices.
1129 c2_sqrt() {}
1130
1131 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1132 { float_type q=std::sqrt(x); if(yprime) *yprime=0.5/q; if(yprime2) *yprime2=-0.25/(x*q); return q; }
1133 /// \brief the static singleton
1134 static const c2_sqrt sqrt;
1135};
1136/// \brief compute scale/x with its derivatives.
1137///
1138/// Creates a singleton instance c2_recip:recip of itself for convenient access.
1139template <typename float_type=double> class c2_recip : public c2_function<float_type> {
1140public:
1141 /// \brief constructor. There is already a singleton c2_recip::recip, which usually suffices.
1142 c2_recip(float_type scale) : rscale(scale) {}
1143
1144 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1145 {
1146 float_type q=1.0/x;
1147 float_type y=rscale*q;
1148 if(yprime) *yprime=-y*q;
1149 if(yprime2) *yprime2=2*y*q*q;
1150 return y;
1151 }
1152 /// \brief reset the scale factor
1153 /// \param scale the new numerator
1154 void reset(float_type scale) { rscale=scale; }
1155 /// \brief the static singleton
1156 static const c2_recip recip;
1157private:
1158 float_type rscale;
1159};
1160/// \brief compute x with its derivatives.
1161///
1162/// Creates a singleton instance c2_identity::identity of itself for convenient access.
1163template <typename float_type=double> class c2_identity : public c2_function<float_type> {
1164public:
1165 /// \brief constructor. There is already a singleton c2_identity::identity, which usually suffices.
1166 c2_identity() {}
1167
1168 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1169 { if(yprime) *yprime=1.0; if(yprime2) *yprime2=0; return x; }
1170 /// \brief the static singleton
1171 static const c2_identity identity;
1172};
1173
1174/**
1175 \brief create a linear mapping of another function
1176
1177 for example, given a c2_function \a f
1178 \code
1179 c2_linear<double> L(1.2, 2.0, 3.0);
1180 c2_composed_function<double> &F=L(f);
1181 \endcode
1182 produces a new c2_function F=2.0+3.0*(\a f - 1.2)
1183*/
1184template <typename float_type=double> class c2_linear : public c2_function<float_type> {
1185public:
1186 /// \brief Construct the operator f=y0 + slope * (x-x0)
1187 /// \param x0 the x offset
1188 /// \param y0 the y-intercept i.e. f(x0)
1189 /// \param slope the slope of the mapping
1190 c2_linear(float_type x0, float_type y0, float_type slope) : xint(x0), intercept(y0), m(slope) {}
1191 /// \brief Change the slope and intercepts after construction.
1192 /// \param x0 the x offset
1193 /// \param y0 the y-intercept
1194 /// \param slope the slope of the mapping
1195 void reset(float_type x0, float_type y0, float_type slope) { xint=x0; intercept=y0; m=slope; }
1196 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1197 { if(yprime) *yprime=m; if(yprime2) *yprime2=0; return m*(x-xint)+intercept; }
1198
1199private:
1200 float_type xint, intercept, m;
1201protected:
1202 c2_linear() {} // do not allow naked construction... it is usually an accident.
1203};
1204
1205/**
1206\brief create a quadratic mapping of another function
1207
1208 for example, given a c2_function \a f
1209 \code
1210 c2_quadratic<double> Q(1.2, 2.0, 3.0, 4.0);
1211 c2_composed_function<double> &F=Q(f);
1212 \endcode
1213 produces a new c2_function F=2.0 + 3.0*(f-1.2) + 4.0*(f-1.2)^2
1214
1215 note that the parameters are overdetermined, but allows the flexibility of two different representations
1216
1217 */
1218template <typename float_type=double> class c2_quadratic : public c2_function<float_type> {
1219public:
1220 /// \brief Construct the operator
1221 /// \param x0 the center around which the powers are computed
1222 /// \param y0 the value of the function at \a x = \a x0
1223 /// \param xcoef the scale on the (\a x - \a x0) term
1224 /// \param x2coef the scale on the (\a x - \a x0)^2 term
1225 c2_quadratic(float_type x0, float_type y0, float_type xcoef, float_type x2coef) : intercept(y0), center(x0), a(x2coef), b(xcoef) {}
1226 /// Modify the mapping after construction
1227 /// \param x0 the new center around which the powers are computed
1228 /// \param y0 the new value of the function at \a x = \a x0
1229 /// \param xcoef the new scale on the (\a x - \a x0) term
1230 /// \param x2coef the new scale on the (\a x - \a x0)^2 term
1231 void reset(float_type x0, float_type y0, float_type xcoef, float_type x2coef) { intercept=y0; center=x0; a=x2coef; b=xcoef; }
1232 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1233 { float_type dx=x-center; if(yprime) *yprime=2*a*dx+b; if(yprime2) *yprime2=2*a; return a*dx*dx+b*dx+intercept; }
1234
1235private:
1236 float_type intercept, center, a, b;
1237protected:
1238 c2_quadratic() {} // do not allow naked construction... it is usually an accident.
1239};
1240
1241/**
1242\brief create a power law mapping of another function
1243
1244 for example, given a c2_function \a f
1245 \code
1246 c2_power_law<double> PLaw(1.2, 2.5);
1247 c2_composed_function<double> &F=PLaw(f);
1248 \endcode
1249 produces a new c2_function F=1.2 * f^2.5
1250
1251 */
1252template <typename float_type=double> class c2_power_law : public c2_function<float_type> {
1253public:
1254 /// \brief Construct the operator
1255 /// \param scale the multipler
1256 /// \param power the exponent
1257 c2_power_law(float_type scale, float_type power) : a(scale), b(power) {}
1258 /// \brief Modify the mapping after construction
1259 /// \param scale the new multipler
1260 /// \param power the new exponent
1261 void reset(float_type scale, float_type power) { a=scale; b=power; }
1262 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1263 { float_type q=a*std::pow(x,b-2); if(yprime) *yprime=b*q*x; if(yprime2) *yprime2=b*(b-1)*q; return q*x*x; }
1264
1265private:
1266 float_type a, b;
1267protected:
1268 c2_power_law() {} // do not allow naked construction... it is usually an accident.
1269};
1270
1271/**
1272\brief create the formal inverse function of another function
1273
1274 for example, given a c2_function \a f
1275 \code
1276 c2_inverse_function<double> inv(f);
1277 a=f(x);
1278 x1=inv(a);
1279 \endcode
1280 will return x1=x to machine precision. The important part of this
1281 is that the resulting function is a first-class c2_function, so it knows its
1282 derivatives, too, unlike the case of a simple root-finding inverse. This means
1283 it can be integrated (for example) quite efficiently.
1284
1285 Note that it is a subclass of c2_scaled_function only to manage ownership of another c2_function.
1286 */
1287template <typename float_type=double> class c2_inverse_function : public c2_plugin_function<float_type> {
1288public:
1289 /// \brief Construct the operator
1290 /// \param source the function to be inverted
1291 c2_inverse_function(const c2_function<float_type> &source);
1292 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception);
1293
1294 /// \brief give the function a hint as to where to look for its inverse
1295 /// \param hint the likely value of the inverse, which defaults to whatever the evaluation returned.
1296 void set_start_hint(float_type hint) const { start_hint=hint; }
1297
1298 /// \brief get the starting hint.
1299 ///
1300 /// This is virtual so if there is a better way, this can be easily overridden.
1301 /// It is used in value_with_derivatives() to guess where to start the root finder.
1302 /// \param x the abscissa for which an estimate is needed
1303 virtual float_type get_start_hint(float_type x) const { return start_hint; }
1304
1305protected:
1306 c2_inverse_function() {} // do not allow naked construction... it is usually an accident.
1307 mutable float_type start_hint;
1308};
1309
1310/**
1311 \brief
1312 An interpolating_function which is the cumulative integral of a histogram.
1313
1314 Note than binedges should be one element longer than binheights, since the lower & upper edges are specified.
1315 Note that this is a malformed spline, since the second derivatives are all zero, so it has less continuity.
1316 Also, note that the bin edges can be given in backwards order to generate the
1317 reversed accumulation (starting at the high end)
1318*/
1319
1320template <typename float_type=double> class accumulated_histogram : public interpolating_function <float_type> {
1321public:
1322 /// \brief Construct the integrated histogram
1323 /// \param binedges the edges of the bins in \a binheights. It should have one more element than \a binheights
1324 /// \param binheights the number of counts in each bin.
1325 /// \param normalize if true, normalize integral to 1
1326 /// \param inverse_function if true, drop zero channels, and return inverse function for random generation
1327 /// \param drop_zeros eliminate null bins before integrating, so integral is strictly monotonic.
1328 accumulated_histogram(const std::vector<float_type>binedges, const std::vector<float_type> binheights,
1329 bool normalize=false, bool inverse_function=false, bool drop_zeros=true);
1330
1331};
1332
1333/**
1334 \brief Construct a function useful for generation of random numbers from the given distribution
1335
1336 inverse_integrated_density<InterpolatingFunctionFlavor>() starts with a probability density c2_function, generates the integral,
1337 and generates an interpolating_function which, when evaluated using a uniform random on [0,1] returns values
1338 with a density distribution equal to the input distribution
1339 If the data are passed in reverse order (large X first), the integral is carried out from the big end.
1340
1341 \sa template <typename Intermediate, typename Final> Final inverse_integrated_density(const std::vector, c2_function &)
1342
1343 \param bincenters points at which to sample the c2_function \a binheights
1344 \param binheights a c2_function which describes the random number distribution to be produced.
1345 \return an interpolating_function of the type requested in the template which,
1346 if evaluated randomly with a uniform variate on [0,1) produces numbers
1347 distributed according to \a binheights
1348*/
1349
1350template <typename float_type, typename Final >
1351 Final &inverse_integrated_density(const std::vector<float_type> &bincenters, c2_function<float_type> &binheights)
1352{
1353 std::vector<float_type> integral;
1354
1355 // integrate from first to last bin in original order, leaving results in integral
1356 // ask for relative error of 1e-6 on each bin, with absolute error set to 0 (since we don't know the data scale).
1357 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6);
1358 // the integral vector now has partial integrals... it must be accumulated by summing
1359 integral.insert(integral.begin(), 0.0); // integral from start to start is 0
1360 float_type scale=1.0/sum;
1361 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale + integral[i-1];
1362 integral.back()=1.0; // force exact value on boundary
1363
1364 return *new Final(integral, bincenters,
1365 false, 1.0/(scale*binheights(bincenters.front() )),
1366 false, 1.0/(scale*binheights(bincenters.back() ))
1367 ); // use integral as x axis in inverse function
1368}
1369
1370/**
1371 \brief Construct a function useful for generation of random numbers from the given distribution
1372
1373 \code
1374 template <typename Intermediate, typename Final>
1375 Final & inverse_integrated_density(const std::vector &bincenters, const std::vector &binheights)
1376 \endcode
1377 is a variant of \code
1378 template <typename Final>
1379 Final & inverse_integrated_density(const std::vector &bincenters, c2_function &binheights)
1380 \endcode
1381 which takes two std::vectors and generates the intermediate interpolating_function required for
1382 inverse_integrated_density(), and then calls it.
1383
1384 \param bincenters points at which \a binheights are defined
1385 \param binheights an std::vector which describes the random number distribution to be produced.
1386 \return an interpolating_function of the type requested in the template which,
1387 if evaluated randomly with a uniform variate on [0,1) produces numbers
1388 distributed according to \a binheights
1389*/
1390
1391template <typename float_type, typename Intermediate, typename Final> Final
1392 &inverse_integrated_density(const std::vector<float_type> &bincenters, const std::vector<float_type> &binheights)
1393{
1394 std::vector<float_type> be(bincenters), bh(binheights);
1395
1396 if(be[1] < be[0]) { // reverse data for interpolator if x axis passed in backwards
1397 std::reverse(be.begin(), be.end());
1398 std::reverse(bh.begin(), bh.end());
1399 }
1400
1401 Intermediate temp(be, bh); // create a temporary interpolating_function to integrate
1402 Final &result=inverse_integrated_density<Final>(bincenters, temp);
1403
1404 return result;
1405}
1406
1407/// \brief create a c2_function which smoothly connects two other c2_functions.
1408///
1409/// This takes two points and generates a polynomial which matches two c2_function arguments
1410/// at those two points, with two derivatives at each point, and an arbitrary value at the center of the
1411/// region. It is useful for splicing together functions over rough spots (0/0, for example).
1412///
1413/// If \a auto_center is true, the value at the midpoint is computed so that the resulting polynomial is
1414/// of order 5. If \a auto_center is false, the value \a y1 is used at the midpoint, resulting in a
1415/// polynomial of order 6.
1416template <typename float_type=double> class c2_connector_function : public c2_function<float_type> {
1417public:
1418 /// \brief construct the container
1419 /// \param f1 the function on the left side to be connected
1420 /// \param f2 the function on the right side to be connected
1421 /// \param x0 the point at which to match \a f1 and its derivatives
1422 /// \param x2 the point at which to match \a f2 and its derivatives
1423 /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint
1424 /// \param y1 the value to match at the midpoint, if \a auto_center is false
1425 /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects \a f1 and \a f2
1426 c2_connector_function(const c2_function<float_type> &f1, const c2_function<float_type> &f2, float_type x0, float_type x2,
1427 bool auto_center, float_type y1);
1428 /// \brief destructor
1429 virtual ~c2_connector_function();
1430 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception);
1431protected:
1432 float_type fx1, fhinv, fdx, fy1, fa, fb, fc, fd, fe, ff;
1433};
1434
1435
1436
1437/// \brief create a c2_function which is a piecewise assembly of other c2_functions.
1438///
1439/// The functions must have increasing, non-overlapping domains. Any empty space
1440/// between functions will be filled with a linear interpolation.
1441/// \note The creation of the container results in the creation of an explicit sampling grid.
1442/// If this is used with functions with a large domain, or which generate very dense sampling grids,
1443/// it could eat a lot of memory. Do not abuse this by using functions which can generate gigantic grids.
1444///
1445/// See c2_plugin_function for a discussion of how this might be used.
1446template <typename float_type=double> class c2_piecewise_function : public c2_function<float_type> {
1447public:
1448 /// \brief construct the container
1449 c2_piecewise_function();
1450 /// \brief destructor
1451 virtual ~c2_piecewise_function();
1452 virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception);
1453 /// \brief append a new function to the sequence
1454 ///
1455 /// This takes a c2_function, and appends it onto the end of the piecewise collection.
1456 /// The domain of the function (which MUST be set) specifies the place it will be used in
1457 /// the final function. If the domain exactly abuts the domain of the previous function, it
1458 /// will be directly attached. If there is a gap, the gap will be filled in by linear interpolation.
1459 /// If the function being appended is to be deleted automatically when this container is deleted, set the pass_ownership flag.
1460 /// \param func a c2_function with a defined domain to be appended to the collection
1461 /// \param pass_ownership if set, \a func will be deleted when the container is destroyed
1462 void append_function(c2_function<float_type> &func, bool pass_ownership) throw (c2_exception);
1463protected:
1464 std::vector<c2_function<float_type> *> functions;
1465 std::vector<bool> owns;
1466 mutable int lastKLow;
1467};
1468
1469#include "c2_function.icc"
1470
1471#endif
Note: See TracBrowser for help on using the repository browser.