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