]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/math/interpolators/detail/quintic_hermite_detail.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / math / interpolators / detail / quintic_hermite_detail.hpp
CommitLineData
f67539c2
TL
1/*
2 * Copyright Nick Thompson, 2020
3 * Use, modification and distribution are subject to the
4 * Boost Software License, Version 1.0. (See accompanying file
5 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 */
7#ifndef BOOST_MATH_INTERPOLATORS_DETAIL_QUINTIC_HERMITE_DETAIL_HPP
8#define BOOST_MATH_INTERPOLATORS_DETAIL_QUINTIC_HERMITE_DETAIL_HPP
9#include <algorithm>
10#include <stdexcept>
11#include <sstream>
1e59de90 12#include <limits>
f67539c2
TL
13#include <cmath>
14
20effc67
TL
15namespace boost {
16namespace math {
17namespace interpolators {
18namespace detail {
f67539c2
TL
19
20template<class RandomAccessContainer>
21class quintic_hermite_detail {
22public:
23 using Real = typename RandomAccessContainer::value_type;
24 quintic_hermite_detail(RandomAccessContainer && x, RandomAccessContainer && y, RandomAccessContainer && dydx, RandomAccessContainer && d2ydx2) : x_{std::move(x)}, y_{std::move(y)}, dydx_{std::move(dydx)}, d2ydx2_{std::move(d2ydx2)}
25 {
26 if (x_.size() != y_.size())
27 {
28 throw std::domain_error("Number of abscissas must = number of ordinates.");
29 }
30 if (x_.size() != dydx_.size())
31 {
32 throw std::domain_error("Numbers of derivatives must = number of abscissas.");
33 }
34 if (x_.size() != d2ydx2_.size())
35 {
36 throw std::domain_error("Number of second derivatives must equal number of abscissas.");
37 }
38 if (x_.size() < 2)
39 {
40 throw std::domain_error("At least 2 abscissas are required.");
41 }
42 Real x0 = x_[0];
43 for (decltype(x_.size()) i = 1; i < x_.size(); ++i)
44 {
45 Real x1 = x_[i];
46 if (x1 <= x0)
47 {
48 throw std::domain_error("Abscissas must be sorted in strictly increasing order x0 < x1 < ... < x_{n-1}");
49 }
50 x0 = x1;
51 }
52 }
53
54 void push_back(Real x, Real y, Real dydx, Real d2ydx2)
55 {
56 using std::abs;
57 using std::isnan;
58 if (x <= x_.back())
59 {
60 throw std::domain_error("Calling push_back must preserve the monotonicity of the x's");
61 }
62 x_.push_back(x);
63 y_.push_back(y);
64 dydx_.push_back(dydx);
65 d2ydx2_.push_back(d2ydx2);
66 }
67
68 inline Real operator()(Real x) const
69 {
70 if (x < x_[0] || x > x_.back())
71 {
72 std::ostringstream oss;
73 oss.precision(std::numeric_limits<Real>::digits10+3);
74 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
75 << x_[0] << ", " << x_.back() << "]";
76 throw std::domain_error(oss.str());
77 }
78 // We need t := (x-x_k)/(x_{k+1}-x_k) \in [0,1) for this to work.
79 // Sadly this neccessitates this loathesome check, otherwise we get t = 1 at x = xf.
80 if (x == x_.back())
81 {
82 return y_.back();
83 }
84
85 auto it = std::upper_bound(x_.begin(), x_.end(), x);
86 auto i = std::distance(x_.begin(), it) -1;
87 Real x0 = *(it-1);
88 Real x1 = *it;
89 Real y0 = y_[i];
90 Real y1 = y_[i+1];
91 Real v0 = dydx_[i];
92 Real v1 = dydx_[i+1];
93 Real a0 = d2ydx2_[i];
94 Real a1 = d2ydx2_[i+1];
95
96 Real dx = (x1-x0);
97 Real t = (x-x0)/dx;
98 Real t2 = t*t;
99 Real t3 = t2*t;
100
101 // See the 'Basis functions' section of:
102 // https://www.rose-hulman.edu/~finn/CCLI/Notes/day09.pdf
103 // Also: https://github.com/MrHexxx/QuinticHermiteSpline/blob/master/HermiteSpline.cs
104 Real y = (1- t3*(10 + t*(-15 + 6*t)))*y0;
105 y += t*(1+ t2*(-6 + t*(8 -3*t)))*v0*dx;
106 y += t2*(1 + t*(-3 + t*(3-t)))*a0*dx*dx/2;
107 y += t3*((1 + t*(-2 + t))*a1*dx*dx/2 + (-4 + t*(7 - 3*t))*v1*dx + (10 + t*(-15 + 6*t))*y1);
108 return y;
109 }
110
111 inline Real prime(Real x) const
112 {
113 if (x < x_[0] || x > x_.back())
114 {
115 std::ostringstream oss;
116 oss.precision(std::numeric_limits<Real>::digits10+3);
117 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
118 << x_[0] << ", " << x_.back() << "]";
119 throw std::domain_error(oss.str());
120 }
121 if (x == x_.back())
122 {
123 return dydx_.back();
124 }
125
126 auto it = std::upper_bound(x_.begin(), x_.end(), x);
127 auto i = std::distance(x_.begin(), it) -1;
128 Real x0 = *(it-1);
129 Real x1 = *it;
130 Real dx = x1 - x0;
131
132 Real y0 = y_[i];
133 Real y1 = y_[i+1];
134 Real v0 = dydx_[i];
135 Real v1 = dydx_[i+1];
136 Real a0 = d2ydx2_[i];
137 Real a1 = d2ydx2_[i+1];
138 Real t= (x-x0)/dx;
139 Real t2 = t*t;
140
141 Real dydx = 30*t2*(1 - 2*t + t*t)*(y1-y0)/dx;
142 dydx += (1-18*t*t + 32*t*t*t - 15*t*t*t*t)*v0 - t*t*(12 - 28*t + 15*t*t)*v1;
143 dydx += (t*dx/2)*((2 - 9*t + 12*t*t - 5*t*t*t)*a0 + t*(3 - 8*t + 5*t*t)*a1);
144 return dydx;
145 }
146
147 inline Real double_prime(Real x) const
148 {
149 if (x < x_[0] || x > x_.back())
150 {
151 std::ostringstream oss;
152 oss.precision(std::numeric_limits<Real>::digits10+3);
153 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
154 << x_[0] << ", " << x_.back() << "]";
155 throw std::domain_error(oss.str());
156 }
157 if (x == x_.back())
158 {
159 return d2ydx2_.back();
160 }
161
162 auto it = std::upper_bound(x_.begin(), x_.end(), x);
163 auto i = std::distance(x_.begin(), it) -1;
164 Real x0 = *(it-1);
165 Real x1 = *it;
166 Real dx = x1 - x0;
167
168 Real y0 = y_[i];
169 Real y1 = y_[i+1];
170 Real v0 = dydx_[i];
171 Real v1 = dydx_[i+1];
172 Real a0 = d2ydx2_[i];
173 Real a1 = d2ydx2_[i+1];
174 Real t = (x-x0)/dx;
175
176 Real d2ydx2 = 60*t*(1 + t*(-3 + 2*t))*(y1-y0)/(dx*dx);
177 d2ydx2 += 12*t*(-3 + t*(8 - 5*t))*v0/dx;
178 d2ydx2 -= 12*t*(2 + t*(-7 + 5*t))*v1/dx;
179 d2ydx2 += (1 + t*(-9 + t*(18 - 10*t)))*a0;
180 d2ydx2 += t*(3 + t*(-12 + 10*t))*a1;
181
182 return d2ydx2;
183 }
184
185 friend std::ostream& operator<<(std::ostream & os, const quintic_hermite_detail & m)
186 {
187 os << "(x,y,y') = {";
188 for (size_t i = 0; i < m.x_.size() - 1; ++i) {
189 os << "(" << m.x_[i] << ", " << m.y_[i] << ", " << m.dydx_[i] << ", " << m.d2ydx2_[i] << "), ";
190 }
191 auto n = m.x_.size()-1;
192 os << "(" << m.x_[n] << ", " << m.y_[n] << ", " << m.dydx_[n] << ", " << m.d2ydx2_[n] << ")}";
193 return os;
194 }
195
196 int64_t bytes() const
197 {
198 return 4*x_.size()*sizeof(x_);
199 }
200
201 std::pair<Real, Real> domain() const
202 {
203 return {x_.front(), x_.back()};
204 }
205
206private:
207 RandomAccessContainer x_;
208 RandomAccessContainer y_;
209 RandomAccessContainer dydx_;
210 RandomAccessContainer d2ydx2_;
211};
212
213
214template<class RandomAccessContainer>
215class cardinal_quintic_hermite_detail {
216public:
217 using Real = typename RandomAccessContainer::value_type;
218 cardinal_quintic_hermite_detail(RandomAccessContainer && y, RandomAccessContainer && dydx, RandomAccessContainer && d2ydx2, Real x0, Real dx)
219 : y_{std::move(y)}, dy_{std::move(dydx)}, d2y_{std::move(d2ydx2)}, x0_{x0}, inv_dx_{1/dx}
220 {
221 if (y_.size() != dy_.size())
222 {
223 throw std::domain_error("Numbers of derivatives must = number of abscissas.");
224 }
225 if (y_.size() != d2y_.size())
226 {
227 throw std::domain_error("Number of second derivatives must equal number of abscissas.");
228 }
229 if (y_.size() < 2)
230 {
231 throw std::domain_error("At least 2 abscissas are required.");
232 }
233 if (dx <= 0)
234 {
235 throw std::domain_error("dx > 0 is required.");
236 }
237
238 for (auto & dy : dy_)
239 {
240 dy *= dx;
241 }
242
243 for (auto & d2y : d2y_)
244 {
245 d2y *= (dx*dx)/2;
246 }
247 }
248
249
250 inline Real operator()(Real x) const
251 {
252 const Real xf = x0_ + (y_.size()-1)/inv_dx_;
253 if (x < x0_ || x > xf)
254 {
255 std::ostringstream oss;
256 oss.precision(std::numeric_limits<Real>::digits10+3);
257 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
258 << x0_ << ", " << xf << "]";
259 throw std::domain_error(oss.str());
260 }
261 if (x == xf)
262 {
263 return y_.back();
264 }
265 return this->unchecked_evaluation(x);
266 }
267
268 inline Real unchecked_evaluation(Real x) const
269 {
270 using std::floor;
271 Real s = (x-x0_)*inv_dx_;
272 Real ii = floor(s);
273 auto i = static_cast<decltype(y_.size())>(ii);
274 Real t = s - ii;
275 if (t == 0)
276 {
277 return y_[i];
278 }
279 Real y0 = y_[i];
280 Real y1 = y_[i+1];
281 Real dy0 = dy_[i];
282 Real dy1 = dy_[i+1];
283 Real d2y0 = d2y_[i];
284 Real d2y1 = d2y_[i+1];
285
286 // See the 'Basis functions' section of:
287 // https://www.rose-hulman.edu/~finn/CCLI/Notes/day09.pdf
288 // Also: https://github.com/MrHexxx/QuinticHermiteSpline/blob/master/HermiteSpline.cs
289 Real y = (1- t*t*t*(10 + t*(-15 + 6*t)))*y0;
290 y += t*(1+ t*t*(-6 + t*(8 -3*t)))*dy0;
291 y += t*t*(1 + t*(-3 + t*(3-t)))*d2y0;
292 y += t*t*t*((1 + t*(-2 + t))*d2y1 + (-4 + t*(7 -3*t))*dy1 + (10 + t*(-15 + 6*t))*y1);
293 return y;
294 }
295
296 inline Real prime(Real x) const
297 {
298 const Real xf = x0_ + (y_.size()-1)/inv_dx_;
299 if (x < x0_ || x > xf)
300 {
301 std::ostringstream oss;
302 oss.precision(std::numeric_limits<Real>::digits10+3);
303 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
304 << x0_ << ", " << xf << "]";
305 throw std::domain_error(oss.str());
306 }
307 if (x == xf)
308 {
309 return dy_.back()*inv_dx_;
310 }
311
312 return this->unchecked_prime(x);
313 }
314
315 inline Real unchecked_prime(Real x) const
316 {
317 using std::floor;
318 Real s = (x-x0_)*inv_dx_;
319 Real ii = floor(s);
320 auto i = static_cast<decltype(y_.size())>(ii);
321 Real t = s - ii;
322 if (t == 0)
323 {
324 return dy_[i]*inv_dx_;
325 }
326 Real y0 = y_[i];
327 Real y1 = y_[i+1];
328 Real dy0 = dy_[i];
329 Real dy1 = dy_[i+1];
330 Real d2y0 = d2y_[i];
331 Real d2y1 = d2y_[i+1];
332
333 Real dydx = 30*t*t*(1 - 2*t + t*t)*(y1-y0);
334 dydx += (1-18*t*t + 32*t*t*t - 15*t*t*t*t)*dy0 - t*t*(12 - 28*t + 15*t*t)*dy1;
335 dydx += t*((2 - 9*t + 12*t*t - 5*t*t*t)*d2y0 + t*(3 - 8*t + 5*t*t)*d2y1);
336 dydx *= inv_dx_;
337 return dydx;
338 }
339
340 inline Real double_prime(Real x) const
341 {
342 const Real xf = x0_ + (y_.size()-1)/inv_dx_;
343 if (x < x0_ || x > xf) {
344 std::ostringstream oss;
345 oss.precision(std::numeric_limits<Real>::digits10+3);
346 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
347 << x0_ << ", " << xf << "]";
348 throw std::domain_error(oss.str());
349 }
350 if (x == xf)
351 {
352 return d2y_.back()*2*inv_dx_*inv_dx_;
353 }
354
355 return this->unchecked_double_prime(x);
356 }
357
358 inline Real unchecked_double_prime(Real x) const
359 {
360 using std::floor;
361 Real s = (x-x0_)*inv_dx_;
362 Real ii = floor(s);
363 auto i = static_cast<decltype(y_.size())>(ii);
364 Real t = s - ii;
365 if (t==0)
366 {
367 return d2y_[i]*2*inv_dx_*inv_dx_;
368 }
369
370 Real y0 = y_[i];
371 Real y1 = y_[i+1];
372 Real dy0 = dy_[i];
373 Real dy1 = dy_[i+1];
374 Real d2y0 = d2y_[i];
375 Real d2y1 = d2y_[i+1];
376
377 Real d2ydx2 = 60*t*(1 - 3*t + 2*t*t)*(y1 - y0)*inv_dx_*inv_dx_;
378 d2ydx2 += (12*t)*((-3 + 8*t - 5*t*t)*dy0 - (2 - 7*t + 5*t*t)*dy1);
379 d2ydx2 += (1 - 9*t + 18*t*t - 10*t*t*t)*d2y0*(2*inv_dx_*inv_dx_) + t*(3 - 12*t + 10*t*t)*d2y1*(2*inv_dx_*inv_dx_);
380 return d2ydx2;
381 }
382
383 int64_t bytes() const
384 {
385 return 3*y_.size()*sizeof(Real) + 2*sizeof(Real);
386 }
387
388 std::pair<Real, Real> domain() const
389 {
390 Real xf = x0_ + (y_.size()-1)/inv_dx_;
391 return {x0_, xf};
392 }
393
394private:
395 RandomAccessContainer y_;
396 RandomAccessContainer dy_;
397 RandomAccessContainer d2y_;
398 Real x0_;
399 Real inv_dx_;
400};
401
402
403template<class RandomAccessContainer>
404class cardinal_quintic_hermite_detail_aos {
405public:
406 using Point = typename RandomAccessContainer::value_type;
407 using Real = typename Point::value_type;
408 cardinal_quintic_hermite_detail_aos(RandomAccessContainer && data, Real x0, Real dx)
409 : data_{std::move(data)} , x0_{x0}, inv_dx_{1/dx}
410 {
411 if (data_.size() < 2)
412 {
413 throw std::domain_error("At least two points are required to interpolate using cardinal_quintic_hermite_aos");
414 }
415
416 if (data_[0].size() != 3)
417 {
418 throw std::domain_error("Each datum passed to the cardinal_quintic_hermite_aos must have three elements: {y, y', y''}");
419 }
420 if (dx <= 0)
421 {
422 throw std::domain_error("dx > 0 is required.");
423 }
424
425 for (auto & datum : data_)
426 {
427 datum[1] *= dx;
428 datum[2] *= (dx*dx/2);
429 }
430 }
431
432
433 inline Real operator()(Real x) const
434 {
435 const Real xf = x0_ + (data_.size()-1)/inv_dx_;
436 if (x < x0_ || x > xf)
437 {
438 std::ostringstream oss;
439 oss.precision(std::numeric_limits<Real>::digits10+3);
440 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
441 << x0_ << ", " << xf << "]";
442 throw std::domain_error(oss.str());
443 }
444 if (x == xf)
445 {
446 return data_.back()[0];
447 }
448 return this->unchecked_evaluation(x);
449 }
450
451 inline Real unchecked_evaluation(Real x) const
452 {
453 using std::floor;
454 Real s = (x-x0_)*inv_dx_;
455 Real ii = floor(s);
456 auto i = static_cast<decltype(data_.size())>(ii);
457 Real t = s - ii;
458 if (t == 0)
459 {
460 return data_[i][0];
461 }
462
463 Real y0 = data_[i][0];
464 Real dy0 = data_[i][1];
465 Real d2y0 = data_[i][2];
466 Real y1 = data_[i+1][0];
467 Real dy1 = data_[i+1][1];
468 Real d2y1 = data_[i+1][2];
469
470 Real y = (1 - t*t*t*(10 + t*(-15 + 6*t)))*y0;
471 y += t*(1 + t*t*(-6 + t*(8 - 3*t)))*dy0;
472 y += t*t*(1 + t*(-3 + t*(3 - t)))*d2y0;
473 y += t*t*t*((1 + t*(-2 + t))*d2y1 + (-4 + t*(7 - 3*t))*dy1 + (10 + t*(-15 + 6*t))*y1);
474 return y;
475 }
476
477 inline Real prime(Real x) const
478 {
479 const Real xf = x0_ + (data_.size()-1)/inv_dx_;
480 if (x < x0_ || x > xf)
481 {
482 std::ostringstream oss;
483 oss.precision(std::numeric_limits<Real>::digits10+3);
484 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
485 << x0_ << ", " << xf << "]";
486 throw std::domain_error(oss.str());
487 }
488 if (x == xf)
489 {
490 return data_.back()[1]*inv_dx_;
491 }
492
493 return this->unchecked_prime(x);
494 }
495
496 inline Real unchecked_prime(Real x) const
497 {
498 using std::floor;
499 Real s = (x-x0_)*inv_dx_;
500 Real ii = floor(s);
501 auto i = static_cast<decltype(data_.size())>(ii);
502 Real t = s - ii;
503 if (t == 0)
504 {
505 return data_[i][1]*inv_dx_;
506 }
507
508
509 Real y0 = data_[i][0];
510 Real y1 = data_[i+1][0];
511 Real v0 = data_[i][1];
512 Real v1 = data_[i+1][1];
513 Real a0 = data_[i][2];
514 Real a1 = data_[i+1][2];
515
516 Real dy = 30*t*t*(1 - 2*t + t*t)*(y1-y0);
517 dy += (1-18*t*t + 32*t*t*t - 15*t*t*t*t)*v0 - t*t*(12 - 28*t + 15*t*t)*v1;
518 dy += t*((2 - 9*t + 12*t*t - 5*t*t*t)*a0 + t*(3 - 8*t + 5*t*t)*a1);
519 return dy*inv_dx_;
520 }
521
522 inline Real double_prime(Real x) const
523 {
524 const Real xf = x0_ + (data_.size()-1)/inv_dx_;
525 if (x < x0_ || x > xf)
526 {
527 std::ostringstream oss;
528 oss.precision(std::numeric_limits<Real>::digits10+3);
529 oss << "Requested abscissa x = " << x << ", which is outside of allowed range ["
530 << x0_ << ", " << xf << "]";
531 throw std::domain_error(oss.str());
532 }
533 if (x == xf)
534 {
535 return data_.back()[2]*2*inv_dx_*inv_dx_;
536 }
537
538 return this->unchecked_double_prime(x);
539 }
540
541 inline Real unchecked_double_prime(Real x) const
542 {
543 using std::floor;
544 Real s = (x-x0_)*inv_dx_;
545 Real ii = floor(s);
546 auto i = static_cast<decltype(data_.size())>(ii);
547 Real t = s - ii;
548 if (t == 0) {
549 return data_[i][2]*2*inv_dx_*inv_dx_;
550 }
551 Real y0 = data_[i][0];
552 Real dy0 = data_[i][1];
553 Real d2y0 = data_[i][2];
554 Real y1 = data_[i+1][0];
555 Real dy1 = data_[i+1][1];
556 Real d2y1 = data_[i+1][2];
557
558 Real d2ydx2 = 60*t*(1 - 3*t + 2*t*t)*(y1 - y0)*inv_dx_*inv_dx_;
559 d2ydx2 += (12*t)*((-3 + 8*t - 5*t*t)*dy0 - (2 - 7*t + 5*t*t)*dy1);
560 d2ydx2 += (1 - 9*t + 18*t*t - 10*t*t*t)*d2y0*(2*inv_dx_*inv_dx_) + t*(3 - 12*t + 10*t*t)*d2y1*(2*inv_dx_*inv_dx_);
561 return d2ydx2;
562 }
563
564 int64_t bytes() const
565 {
566 return data_.size()*data_[0].size()*sizeof(Real) + 2*sizeof(Real);
567 }
568
569 std::pair<Real, Real> domain() const
570 {
571 Real xf = x0_ + (data_.size()-1)/inv_dx_;
572 return {x0_, xf};
573 }
574
575private:
576 RandomAccessContainer data_;
577 Real x0_;
578 Real inv_dx_;
579};
580
20effc67
TL
581}
582}
583}
f67539c2
TL
584}
585#endif