ALFI
Advanced Library for Function Interpolation
Loading...
Searching...
No Matches
cubic.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <iostream>
5#include <cassert>
6#include <cmath>
7#include <variant>
8
9#include "../config.h"
10#include "../util/arrays.h"
11#include "../util/misc.h"
12#include "../util/spline.h"
13
14namespace alfi::spline {
15 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
17 public:
18 struct Conditions final {
19 struct Clamped final {
20 Clamped(SizeT point_idx, Number df) : point_idx(std::move(point_idx)), df(std::move(df)) {}
22 Number df;
23 };
24 struct FixedSecond final {
25 FixedSecond(SizeT point_idx, Number d2f) : point_idx(std::move(point_idx)), d2f(std::move(d2f)) {}
27 Number d2f;
28 };
29 struct FixedThird final {
30 FixedThird(SizeT segment_idx, Number d3f) : segment_idx(std::move(segment_idx)), d3f(std::move(d3f)) {}
32 Number d3f;
33 };
34 struct NotAKnot final {
35 explicit NotAKnot(SizeT point_idx) : point_idx(std::move(point_idx)) {}
37 };
38 };
39
40 using Condition = std::variant<typename Conditions::Clamped,
43 typename Conditions::NotAKnot>;
44
45 struct Types final {
51 struct Natural final {};
56 struct NotAKnot final {};
60 struct Periodic final {};
65 struct ParabolicEnds final {};
69 struct Clamped final {
70 Clamped(Number df_1, Number df_n) : df_1(std::move(df_1)), df_n(std::move(df_n)) {}
71 Number df_1, df_n;
72 };
73
76 struct FixedSecond final {
77 FixedSecond(Number d2f_1, Number d2f_n) : d2f_1(std::move(d2f_1)), d2f_n(std::move(d2f_n)) {}
78 Number d2f_1, d2f_n;
79 };
80
84 struct FixedThird final {
85 FixedThird(Number d3f_1, Number d3f_n) : d3f_1(std::move(d3f_1)), d3f_n(std::move(d3f_n)) {}
86 Number d3f_1, d3f_n;
87 };
88
93 struct NotAKnotStart final {};
99 struct NotAKnotEnd final {};
103 struct SemiNotAKnot final {};
104 struct Custom final {
105 Custom(Condition condition1, Condition condition2) : cond1(std::move(condition1)), cond2(std::move(condition2)) {}
107 };
109 };
110
111 using Type = std::variant<typename Types::Natural,
112 typename Types::NotAKnot,
113 typename Types::Periodic,
114 typename Types::ParabolicEnds,
115 typename Types::Clamped,
116 typename Types::FixedSecond,
117 typename Types::FixedThird,
118 typename Types::NotAKnotStart,
119 typename Types::NotAKnotEnd,
120 typename Types::SemiNotAKnot,
121 typename Types::Custom>;
122
123 static Container<Number> compute_coeffs(const Container<Number>& X, const Container<Number>& Y, Type type = typename Types::Default{}) {
124 if (X.size() != Y.size()) {
125 std::cerr << "Error in function " << __FUNCTION__
126 << ": Vectors X (of size " << X.size()
127 << ") and Y (of size " << Y.size()
128 << ") are not the same size. Returning an empty array..." << std::endl;
129 return {};
130 }
131
132 const SizeT n = X.size();
133
134 if (n <= 1) {
136 }
137
138 if (std::holds_alternative<typename Types::Natural>(type)) {
139 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedSecond{0, 0}, typename Conditions::FixedSecond{n - 1, 0}});
140 } else if (std::holds_alternative<typename Types::NotAKnot>(type)) {
141 if (n <= 4) {
143 }
144 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::NotAKnot{n - 2}});
145 } else if (std::holds_alternative<typename Types::ParabolicEnds>(type)) {
146 if (n <= 3) {
148 }
149 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedThird{0, 0}, typename Conditions::FixedThird{n - 2, 0}});
150 } else if (const auto* c = std::get_if<typename Types::Clamped>(&type)) {
151 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::Clamped{0, c->df_1}, typename Conditions::Clamped{n - 1, c->df_n}});
152 } else if (const auto* fs = std::get_if<typename Types::FixedSecond>(&type)) {
153 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedSecond{0, fs->d2f_1}, typename Conditions::FixedSecond{n - 1, fs->d2f_n}});
154 } else if (const auto* ft = std::get_if<typename Types::FixedThird>(&type)) {
155 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedThird{0, ft->d3f_1}, typename Conditions::FixedThird{n - 2, ft->d3f_n}});
156 } else if (std::holds_alternative<typename Types::NotAKnotStart>(type)) {
157 if (n <= 4) {
159 }
160 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::NotAKnot{2}});
161 } else if (std::holds_alternative<typename Types::NotAKnotEnd>(type)) {
162 if (n <= 4) {
164 }
165 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{n - 3}, typename Conditions::NotAKnot{n - 2}});
166 } else if (std::holds_alternative<typename Types::SemiNotAKnot>(type)) {
167 if (n <= 4) {
169 }
171 }
172
173 Container<Number> coeffs(4 * (n - 1));
174
175 // i1 - index of first constructed segment
176 // i2 - index of first after last constructed segment
177 SizeT i1 = 0, i2 = n - 1;
178
179 const auto dX = util::arrays::diff(X), dY = util::arrays::diff(Y);
180
181 if (std::holds_alternative<typename Types::Periodic>(type)) {
182 if (n == 2) {
184 }
185
186 Container<Number> C(n);
187 if (n == 3) {
188 C[0] = 3 * (dY[0]/dX[0] - dY[1]/dX[1]) / (dX[0] + dX[1]);
189 C[1] = -C[0];
190 C[2] = C[0];
191 } else {
192 Container<Number> diag(n - 1), right(n - 1);
193 for (SizeT i = 0; i < n - 2; ++i) {
194 diag[i] = 2 * (dX[i] + dX[i+1]);
195 right[i] = 3 * (dY[i+1]/dX[i+1] - dY[i]/dX[i]);
196 }
197 diag[n-2] = 2 * (dX[n-2] + dX[0]);
198 right[n-2] = 3 * (dY[0]/dX[0] - dY[n-2]/dX[n-2]);
199
200 Container<Number> last_row(n - 2), last_col(n - 2);
201 last_row[0] = last_col[0] = dX[0];
202 for (SizeT i = 1; i < n - 3; ++i) {
203 last_row[i] = last_col[i] = 0;
204 }
205 last_row[n-3] = last_col[n-3] = dX[n-2];
206
207 for (SizeT i = 0; i < n - 3; ++i) {
208 const auto m1 = dX[i+1] / diag[i];
209 diag[i+1] -= m1 * dX[i+1];
210 last_col[i+1] -= m1 * last_col[i];
211 right[i+1] -= m1 * right[i];
212 const auto m2 = last_row[i] / diag[i];
213 last_row[i+1] -= m2 * dX[i+1];
214 diag[n-2] -= m2 * last_col[i];
215 right[n-2] -= m2 * right[i];
216 }
217 diag[n-2] -= last_row[n-3] / diag[n-3] * last_col[n-3];
218 right[n-2] -= last_row[n-3] / diag[n-3] * right[n-3];
219
220 C[n-1] = right[n-2] / diag[n-2];
221 C[n-2] = (right[n-3] - last_col[n-3] * C[n-1]) / diag[n-3];
222 for (SizeT i = n - 3; i >= 1; --i) {
223 C[i] = (right[i-1] - dX[i]*C[i+1] - last_col[i-1]*C[n-1]) / diag[i-1];
224 }
225 C[0] = C[n-1];
226 }
227
228 for (SizeT i = 0, index = 0; i < n - 1; ++i) {
229 coeffs[index++] = (C[i+1] - C[i]) / (3*dX[i]);
230 coeffs[index++] = C[i];
231 coeffs[index++] = dY[i]/dX[i] - dX[i] * ((2*C[i] + C[i+1]) / 3);
232 coeffs[index++] = Y[i];
233 }
234 } else if (auto* custom = std::get_if<typename Types::Custom>(&type)) {
235 // special case
236 if (std::holds_alternative<typename Conditions::NotAKnot>(custom->cond1)
237 && std::holds_alternative<typename Conditions::NotAKnot>(custom->cond2)
238 && n <= 3) {
240 }
241
242 // check conditions
243 for (const auto* cond : {&custom->cond1, &custom->cond2}) {
244 if (const auto* c = std::get_if<typename Conditions::Clamped>(cond)) {
245 if (c->point_idx < 0 || c->point_idx >= n) {
246 std::cerr << "Error in function " << __FUNCTION__
247 << ": point index for condition 'Clamped' is out of bounds: "
248 << "expected to be non-negative and less than " << n << ", but got " << c->point_idx
249 << ". Returning an empty array..." << std::endl;
250 return {};
251 }
252 } else if (const auto* fs = std::get_if<typename Conditions::FixedSecond>(cond)) {
253 if (fs->point_idx < 0 || fs->point_idx >= n) {
254 std::cerr << "Error in function " << __FUNCTION__
255 << ": point index for condition 'FixedSecond' is out of bounds: "
256 << "expected to be non-negative and less than " << n << ", but got " << fs->point_idx
257 << ". Returning an empty array..." << std::endl;
258 return {};
259 }
260 } else if (const auto* ft = std::get_if<typename Conditions::FixedThird>(cond)) {
261 if (ft->segment_idx < 0 || ft->segment_idx >= n - 1) {
262 std::cerr << "Error in function " << __FUNCTION__
263 << ": segment index for condition 'FixedThird' is out of bounds: "
264 << "expected to be non-negative and less than " << n - 1 << ", but got " << ft->segment_idx
265 << ". Returning an empty array..." << std::endl;
266 return {};
267 }
268 } else if (const auto* nak = std::get_if<typename Conditions::NotAKnot>(cond)) {
269 if (nak->point_idx < 1 || nak->point_idx >= n - 1) {
270 std::cerr << "Error in function " << __FUNCTION__
271 << ": point index for condition 'NotAKnot' is out of bounds: "
272 << "expected to be positive and less than " << n - 1 << ", but got " << nak->point_idx
273 << ". Returning an empty array..." << std::endl;
274 return {};
275 }
276 } else {
277 std::cerr << "Error in function " << __FUNCTION__ << ": Unknown condition of 'Custom' type. This should not have happened. "
278 << "Please report this to the developers. Returning an empty array..." << std::endl;
279 return {};
280 }
281 }
282
283 // check if same conditions on one point and return a somewhat reasonable compromise solution
284 if (const auto c1 = std::get_if<typename Conditions::Clamped>(&custom->cond1),
285 c2 = std::get_if<typename Conditions::Clamped>(&custom->cond2);
286 c1 && c2) {
287 if (c1->point_idx == c2->point_idx) {
288 const auto df = (c1->df + c2->df) / 2;
289 if (c1->point_idx < n / 2) {
290 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::Clamped{c1->point_idx, df}, typename Conditions::NotAKnot{n - 2}});
291 } else {
292 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::Clamped{c1->point_idx, df}});
293 }
294 }
295 } else if (const auto fs1 = std::get_if<typename Conditions::FixedSecond>(&custom->cond1),
296 fs2 = std::get_if<typename Conditions::FixedSecond>(&custom->cond2);
297 fs1 && fs2) {
298 if (fs1->point_idx == fs2->point_idx) {
299 const auto d2f = (fs1->d2f + fs2->d2f) / 2;
300 if (fs1->point_idx < n / 2) {
301 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedSecond{fs1->point_idx, d2f}, typename Conditions::NotAKnot{n - 2}});
302 } else {
303 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::FixedSecond{fs1->point_idx, d2f}});
304 }
305 }
306 } else if (const auto ft1 = std::get_if<typename Conditions::FixedThird>(&custom->cond1),
307 ft2 = std::get_if<typename Conditions::FixedThird>(&custom->cond2);
308 ft1 && ft2) {
309 if (ft1->segment_idx == ft2->segment_idx) {
310 if (n == 2) {
311 const auto i = ft1->segment_idx;
312 const auto c = -dX[i] * (ft1->d3f + ft2->d3f) / 8;
313 coeffs[4*i] = -2*c / (3*dX[i]);
314 coeffs[4*i+1] = c;
315 coeffs[4*i+2] = dY[i]/dX[i] - dX[i] * c / 3;
316 coeffs[4*i+3] = Y[i];
317 i1 = i;
318 i2 = i + 1;
319 goto post_main_block;
320 } else {
321 const auto d3f = (ft1->d3f + ft2->d3f) / 2;
322 if (ft1->segment_idx < n / 2) {
323 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::FixedThird{ft1->segment_idx, d3f}, typename Conditions::NotAKnot{n - 2}});
324 } else {
325 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::FixedThird{ft1->segment_idx, d3f}});
326 }
327 }
328 }
329 } else if (const auto nak1 = std::get_if<typename Conditions::NotAKnot>(&custom->cond1),
330 nak2 = std::get_if<typename Conditions::NotAKnot>(&custom->cond2);
331 nak1 && nak2) {
332 if (nak1->point_idx == nak2->point_idx) {
333 if (nak1->point_idx < n / 2) {
334 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{nak1->point_idx}, typename Conditions::NotAKnot{n - 2}});
335 } else {
336 return compute_coeffs(X, Y, typename Types::Custom{typename Conditions::NotAKnot{1}, typename Conditions::NotAKnot{nak1->point_idx}});
337 }
338 }
339 }
340
341 const auto set_segment_indices = [&]() {
342 std::visit(util::misc::overload{
343 [&](const typename Conditions::Clamped& c) { i1 = c.point_idx; },
344 [&](const typename Conditions::FixedSecond& fs) { i1 = fs.point_idx; },
345 [&](const typename Conditions::FixedThird& ft) { i1 = ft.segment_idx; },
346 [&](const typename Conditions::NotAKnot& nak) { i1 = nak.point_idx - 1; },
347 }, custom->cond1);
348 std::visit(util::misc::overload{
349 [&](const typename Conditions::Clamped& c) { i2 = c.point_idx; },
350 [&](const typename Conditions::FixedSecond& fs) { i2 = fs.point_idx; },
351 [&](const typename Conditions::FixedThird& ft) { i2 = ft.segment_idx + 1; },
352 [&](const typename Conditions::NotAKnot& nak) { i2 = nak.point_idx + 1; },
353 }, custom->cond2);
354 };
355 set_segment_indices();
356 // swap if wrong order and set again
357 if (i2 <= i1) {
358 std::swap(custom->cond1, custom->cond2);
359 set_segment_indices();
360 }
361
362 // special case: both conditions on one point
363 // only possible for Clamped and FixedSecond
364 if (i2 <= i1) {
365 assert(i1 == i2);
366 const auto i = i1;
367 const auto* c
368 = std::holds_alternative<typename Conditions::Clamped>(custom->cond1)
369 ? std::get_if<typename Conditions::Clamped>(&custom->cond1)
370 : std::get_if<typename Conditions::Clamped>(&custom->cond2);
371 const auto* fs
372 = std::holds_alternative<typename Conditions::FixedSecond>(custom->cond1)
373 ? std::get_if<typename Conditions::FixedSecond>(&custom->cond1)
374 : std::get_if<typename Conditions::FixedSecond>(&custom->cond2);
375 assert(c && fs);
376 if (i > 0) {
377 coeffs[4*(i-1)] = (fs->d2f/2 - c->df/dX[i-1] + dY[i-1]/dX[i-1]/dX[i-1]) / dX[i-1];
378 coeffs[4*(i-1)+1] = 3*c->df/dX[i-1] - 3*dY[i-1]/dX[i-1]/dX[i-1] - fs->d2f;
379 coeffs[4*(i-1)+2] = 3*dY[i-1]/dX[i-1] + fs->d2f*dX[i-1]/2 - 2*c->df;
380 coeffs[4*(i-1)+3] = Y[i-1];
381 i1 = i - 1;
382 }
383 if (i < n - 1) {
384 coeffs[4*i] = (dY[i]/dX[i]/dX[i] - c->df/dX[i] - fs->d2f/2) / dX[i];
385 coeffs[4*i+1] = fs->d2f/2;
386 coeffs[4*i+2] = c->df;
387 coeffs[4*i+3] = Y[i];
388 i2 = i + 1;
389 }
390 goto post_main_block;
391 }
392
393 // number of points in the "subspline"
394 const auto m = i2 - i1 + 1;
395
396 // common equations
397 Container<Number> lower(m), diag(m), upper(m), right(m);
398 for (SizeT i = i1 + 1; i < i1 + m - 1; ++i) {
399 const auto j = i - i1;
400 lower[j] = dX[i-1];
401 diag[j] = 2 * (dX[i-1] + dX[i]);
402 upper[j] = dX[i];
403 right[j] = 3 * (dY[i]/dX[i] - dY[i-1]/dX[i-1]);
404 }
405 lower[0] = NAN;
406 upper[m-1] = NAN;
407
408 // unique equations
409 std::visit(util::misc::overload{
410 [&](const typename Conditions::Clamped& c) {
411 diag[0] = 2*dX[i1];
412 upper[0] = dX[i1];
413 right[0] = 3 * (dY[i1]/dX[i1] - c.df);
414 },
415 [&](const typename Conditions::FixedSecond& fs) {
416 diag[0] = 1;
417 upper[0] = 0;
418 right[0] = fs.d2f / 2;
419 },
420 [&](const typename Conditions::FixedThird& ft) {
421 diag[0] = 1;
422 upper[0] = -1;
423 right[0] = -dX[i1] * ft.d3f / 2;
424 },
425 [&](const typename Conditions::NotAKnot&) {
426 diag[0] = dX[i1] - dX[i1+1];
427 upper[0] = 2*dX[i1] + dX[i1+1];
428 right[0] = dX[i1] / (dX[i1]+dX[i1+1]) * 3 * (dY[i1+1]/dX[i1+1] - dY[i1]/dX[i1]);
429 },
430 }, custom->cond1);
431 std::visit(util::misc::overload{
432 [&](const typename Conditions::Clamped& c) {
433 lower[m-1] = dX[i1+m-2];
434 diag[m-1] = 2*dX[i1+m-2];
435 right[m-1] = 3 * (c.df - dY[i1+m-2]/dX[i1+m-2]);
436 },
437 [&](const typename Conditions::FixedSecond& fs) {
438 lower[m-1] = 0;
439 diag[m-1] = 1;
440 right[m-1] = fs.d2f / 2;
441 },
442 [&](const typename Conditions::FixedThird& ft) {
443 lower[m-1] = -1;
444 diag[m-1] = 1;
445 right[m-1] = dX[i1+m-2] * ft.d3f / 2;
446 },
447 [&](const typename Conditions::NotAKnot&) {
448 lower[m-1] = 2*dX[i1+m-2] + dX[i1+m-3];
449 diag[m-1] = dX[i1+m-2] - dX[i1+m-3];
450 right[m-1] = dX[i1+m-2] / (dX[i1+m-2]+dX[i1+m-3]) * 3 * (dY[i1+m-2]/dX[i1+m-2] - dY[i1+m-3]/dX[i1+m-3]);
451 },
452 }, custom->cond2);
453
455 std::move(lower), std::move(diag), std::move(upper), std::move(right));
456
457 for (SizeT i = 0, index = 4 * i1; i < m - 1; ++i) {
458 const auto j = i + i1;
459 coeffs[index++] = (C[i+1] - C[i]) / (3*dX[j]);
460 coeffs[index++] = C[i];
461 coeffs[index++] = dY[j]/dX[j] - dX[j] * ((2*C[i] + C[i+1]) / 3);
462 coeffs[index++] = Y[j];
463 }
464
465 // additional corrections
466 if (const auto* ft = std::get_if<typename Conditions::FixedThird>(&custom->cond1)) {
467 coeffs[4*i1] = ft->d3f / 6;
468 }
469 if (const auto* ft = std::get_if<typename Conditions::FixedThird>(&custom->cond2)) {
470 coeffs[4*(i2-1)] = ft->d3f / 6;
471 }
472 } else {
473 std::cerr << "Error in function " << __FUNCTION__ << ": Unknown type. This should not have happened. "
474 << "Please report this to the developers. Returning an empty array..." << std::endl;
475 return {};
476 }
477
478 post_main_block:
479 for (SizeT iter = 0; iter < i1; ++iter) {
480 const auto i = i1 - 1 - iter;
481 coeffs[4*i] = (coeffs[4*(i+1)+1] - coeffs[4*(i+1)+2]/dX[i] + dY[i]/dX[i]/dX[i]) / dX[i];
482 coeffs[4*i+1] = coeffs[4*(i+1)+1] - 3 * coeffs[4*i] * dX[i];
483 coeffs[4*i+2] = dY[i]/dX[i] - coeffs[4*i]*dX[i]*dX[i] - coeffs[4*i+1]*dX[i];
484 coeffs[4*i+3] = Y[i];
485 }
486
487 for (SizeT i = i2; i < n - 1; ++i) {
488 coeffs[4*i+3] = Y[i];
489 coeffs[4*i+2] = 3 * coeffs[4*(i-1)]*dX[i-1]*dX[i-1] + 2 * coeffs[4*(i-1)+1]*dX[i-1] + coeffs[4*(i-1)+2];
490 coeffs[4*i+1] = 3 * coeffs[4*(i-1)]*dX[i-1] + coeffs[4*(i-1)+1];
491 coeffs[4*i] = (dY[i]/dX[i]/dX[i] - coeffs[4*i+1] - coeffs[4*i+2]/dX[i]) / dX[i];
492 }
493
494 return coeffs;
495 }
496
497 CubicSpline() = default;
498
499 template <typename ContainerXType>
500 CubicSpline(ContainerXType&& X, const Container<Number>& Y, Type type = typename Types::Default{}) {
501 construct(std::forward<ContainerXType>(X), Y, type);
502 }
503
504 CubicSpline(const CubicSpline& other) = default;
505 CubicSpline(CubicSpline&& other) noexcept = default;
506
507 CubicSpline& operator=(const CubicSpline& other) = default;
508 CubicSpline& operator=(CubicSpline&& other) noexcept = default;
509
510 template <typename ContainerXType>
511 void construct(ContainerXType&& X, const Container<Number>& Y, Type type = typename Types::Default{}) {
512 if (X.size() != Y.size()) {
513 std::cerr << "Error in function " << __FUNCTION__
514 << ": Vectors X (of size " << X.size()
515 << ") and Y (of size " << Y.size()
516 << ") are not the same size. Doing nothing..." << std::endl;
517 return;
518 }
519 auto coeffs = compute_coeffs(X, Y, type);
520 if (coeffs.empty() && !X.empty()) {
521 std::cerr << "Error in function " << __FUNCTION__
522 << ": failed to construct coefficients. Not changing the object..." << std::endl;
523 return;
524 }
525 _type = type;
526 _X = std::forward<ContainerXType>(X);
527 _coeffs = std::move(coeffs);
528 }
529
530 Number eval(const Number& x) const {
531 return eval(x, std::distance(_X.begin(), util::misc::first_leq_or_begin(_X.begin(), _X.end(), x)));
532 }
533 Number eval(const Number& x, SizeT segment) const {
534 if (_coeffs.empty()) {
535 return NAN;
536 } else if (_coeffs.size() == 1) {
537 return _coeffs[0];
538 }
539 segment = std::clamp(segment, static_cast<SizeT>(0), static_cast<SizeT>(_X.size() - 2));
540 const Number x_seg = x - _X[segment];
541 return ((_coeffs[4*segment] * x_seg + _coeffs[4*segment+1]) * x_seg + _coeffs[4*segment+2]) * x_seg + _coeffs[4*segment+3];
542 }
543
544 Container<Number> eval(const Container<Number>& xx, bool sorted = true) const {
545 Container<Number> result(xx.size());
546 if (sorted) {
547 for (SizeT i = 0, i_x = 0; i < xx.size(); ++i) {
548 const Number& x = xx[i];
549 while (i_x + 1 < _X.size() && x >= _X[i_x+1])
550 ++i_x;
551 result[i] = eval(x, i_x);
552 }
553 } else {
554 for (SizeT i = 0; i < xx.size(); ++i) {
555 result[i] = eval(xx[i]);
556 }
557 }
558 return result;
559 }
560
561 Number operator()(const Number& x) const {
562 return eval(x);
563 }
564 Container<Number> operator()(const Container<Number>& xx) const {
565 return eval(xx);
566 }
567
568 Type type() const {
569 return _type;
570 }
571
572 const Container<Number>& X() const & {
573 return _X;
574 }
575 Container<Number>&& X() && {
576 return std::move(_X);
577 }
578
579 const Container<Number>& coeffs() const & {
580 return _coeffs;
581 }
582 Container<Number>&& coeffs() && {
583 return std::move(_coeffs);
584 }
585
586 static std::pair<SizeT, SizeT> segment(SizeT index) {
587 return {4*index, 4*(index+1)};
588 }
589
590 private:
591 Type _type = typename Types::Default{};
592 Container<Number> _X = {};
593 Container<Number> _coeffs = {};
594 };
595}
const Container< Number > & X() const &
Definition cubic.h:572
Type type() const
Definition cubic.h:568
CubicSpline(const CubicSpline &other)=default
CubicSpline(CubicSpline &&other) noexcept=default
Number eval(const Number &x, SizeT segment) const
Definition cubic.h:533
static Container< Number > compute_coeffs(const Container< Number > &X, const Container< Number > &Y, Type type=typename Types::Default{})
Definition cubic.h:123
static std::pair< SizeT, SizeT > segment(SizeT index)
Definition cubic.h:586
CubicSpline & operator=(const CubicSpline &other)=default
std::variant< typename Conditions::Clamped, typename Conditions::FixedSecond, typename Conditions::FixedThird, typename Conditions::NotAKnot > Condition
Definition cubic.h:40
const Container< Number > & coeffs() const &
Definition cubic.h:579
std::variant< typename Types::Natural, typename Types::NotAKnot, typename Types::Periodic, typename Types::ParabolicEnds, typename Types::Clamped, typename Types::FixedSecond, typename Types::FixedThird, typename Types::NotAKnotStart, typename Types::NotAKnotEnd, typename Types::SemiNotAKnot, typename Types::Custom > Type
Definition cubic.h:111
Number operator()(const Number &x) const
Definition cubic.h:561
CubicSpline(ContainerXType &&X, const Container< Number > &Y, Type type=typename Types::Default{})
Definition cubic.h:500
Container< Number > && X() &&
Definition cubic.h:575
void construct(ContainerXType &&X, const Container< Number > &Y, Type type=typename Types::Default{})
Definition cubic.h:511
CubicSpline & operator=(CubicSpline &&other) noexcept=default
Container< Number > operator()(const Container< Number > &xx) const
Definition cubic.h:564
Container< Number > && coeffs() &&
Definition cubic.h:582
Container< Number > eval(const Container< Number > &xx, bool sorted=true) const
Definition cubic.h:544
Number eval(const Number &x) const
Definition cubic.h:530
Definition cubic.h:14
Container< Number > diff(const Container< Number > &container)
Definition arrays.h:43
Container< Number > mean(const Container< Number > &container1, const Container< Number > &container2)
Definition arrays.h:55
Container< Number > tridiag_solve(Container< Number > &&lower, Container< Number > &&diag, Container< Number > &&upper, Container< Number > &&right)
Solves a tridiagonal system of linear equations.
Definition linalg.h:150
Iterator first_leq_or_begin(Iterator begin, Iterator end, const T &value)
Definition misc.h:27
overload(Ts...) -> overload< Ts... >
Container< Number > simple_spline(const Container< Number > &X, const Container< Number > &Y, SizeT degree)
Definition spline.h:11
ALFI_DEFAULT_NUMBER DefaultNumber
Definition config.h:17
ALFI_SIZE_TYPE SizeT
Definition config.h:22
ALFI_DEFAULT_CONTAINER< Number > DefaultContainer
Definition config.h:20
Clamped(SizeT point_idx, Number df)
Definition cubic.h:20
FixedSecond(SizeT point_idx, Number d2f)
Definition cubic.h:25
FixedThird(SizeT segment_idx, Number d3f)
Definition cubic.h:30
NotAKnot(SizeT point_idx)
Definition cubic.h:35
Clamped(Number df_1, Number df_n)
Definition cubic.h:70
Number df_1
Definition cubic.h:71
Number df_n
Definition cubic.h:71
Condition cond1
Definition cubic.h:106
Custom(Condition condition1, Condition condition2)
Definition cubic.h:105
Condition cond2
Definition cubic.h:106
FixedSecond(Number d2f_1, Number d2f_n)
Definition cubic.h:77
FixedThird(Number d3f_1, Number d3f_n)
Definition cubic.h:85
Natural Default
Definition cubic.h:108