11#include < gtest/gtest.h>
22
33#include < ALFI/spline/quadratic.h>
4+ #include < ALFI/dist.h>
5+
6+ #include " ../test_utils.h"
7+
8+ const auto test_data_path = TEST_DATA_DIR " /spline/quadratic.toml" ;
9+
10+ const auto test_data = toml::parse_file(test_data_path);
11+
12+ void test_quadratic_spline (double epsilon_coeffs, double epsilon_values) {
13+ const auto & test_cases = test_data[" test_cases" ].ref <toml::array>();
14+
15+ test_cases.for_each ([&](const toml::table& test_case) {
16+ const auto & type = test_case[" type" ].ref <std::string>();
17+ const auto & X = to_vector<double >(test_case[" X" ].ref <toml::array>());
18+ const auto & Y = to_vector<double >(test_case[" Y" ].ref <toml::array>());
19+ const auto & coeffs = to_vector<double >(test_case[" coeffs" ].ref <toml::array>());
20+ const auto & xx = to_vector<double >(test_case[" xx" ].ref <toml::array>());
21+ const auto & yy = to_vector<double >(test_case[" yy" ].ref <toml::array>());
22+
23+ const auto t = [&]() -> alfi::spline::QuadraticSpline<>::Type {
24+ if (type == " semi-not-a-knot" ) {
25+ return alfi::spline::QuadraticSpline<>::Types::SemiNotAKnot{};
26+ } else if (type == " semi-natural" ) {
27+ return alfi::spline::QuadraticSpline<>::Types::SemiNatural{};
28+ } else {
29+ throw std::runtime_error{" Unexpected type:" + type};
30+ }
31+ }();
32+
33+ const auto spline = alfi::spline::QuadraticSpline<>(X, Y, t);
34+ expect_eq (spline.coeffs (), coeffs, epsilon_coeffs);
35+ const auto values = spline.eval (xx);
36+ expect_eq (values, yy, epsilon_values);
37+ });
38+ }
39+
40+ TEST (QuadraticSplineTest, TestData) {
41+ test_quadratic_spline (1e-14 , 1e-14 );
42+ }
443
544TEST (QuadraticSplineTest, General) {
645 const std::vector<double > X = {0 , 1 , 2 , 3 };
@@ -12,6 +51,73 @@ TEST(QuadraticSplineTest, General) {
1251 }
1352}
1453
54+ TEST (QuadraticSplineTest, Conditions) {
55+ const size_t n = 10 ;
56+ const double a = -1 , b = 1 ;
57+ const auto X = alfi::dist::uniform (n, a, b);
58+ const auto dX = alfi::util::arrays::diff (X);
59+ const auto Y = alfi::dist::chebyshev_2 (n, a, b);
60+ // natural-start, natural-end, semi-natural
61+ auto spline1 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::NaturalStart{});
62+ EXPECT_NEAR (0 , 2 *spline1.coeffs ()[0 ], 1e-17 );
63+ auto spline2 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::NaturalEnd{});
64+ EXPECT_NEAR (0 , 2 *spline2.coeffs ()[3 *8 ], 1e-17 );
65+ const auto spline3 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::SemiNatural{});
66+ ASSERT_EQ (spline1.coeffs ().size (), spline3.coeffs ().size ());
67+ ASSERT_EQ (spline2.coeffs ().size (), spline3.coeffs ().size ());
68+ for (size_t i = 0 ; i < spline3.coeffs ().size (); ++i) {
69+ EXPECT_EQ (spline3.coeffs ()[i], (spline1.coeffs ()[i] + spline2.coeffs ()[i]) / 2 );
70+ }
71+ // not-a-knot-start, not-a-knot-end, semi-not-a-knot
72+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::NotAKnotStart{});
73+ EXPECT_EQ (2 *spline1.coeffs ()[0 ], 2 *spline1.coeffs ()[3 *1 ]);
74+ spline2.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::NotAKnotEnd{});
75+ EXPECT_EQ (2 *spline2.coeffs ()[3 *7 ], 2 *spline2.coeffs ()[3 *8 ]);
76+ const auto spline4 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::SemiNotAKnot{});
77+ ASSERT_EQ (spline1.coeffs ().size (), spline4.coeffs ().size ());
78+ ASSERT_EQ (spline2.coeffs ().size (), spline4.coeffs ().size ());
79+ for (size_t i = 0 ; i < spline4.coeffs ().size (); ++i) {
80+ EXPECT_EQ (spline4.coeffs ()[i], (spline1.coeffs ()[i] + spline2.coeffs ()[i]) / 2 );
81+ }
82+ // semi-semi
83+ const auto spline5 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::SemiSemi{});
84+ ASSERT_EQ (spline3.coeffs ().size (), spline5.coeffs ().size ());
85+ ASSERT_EQ (spline4.coeffs ().size (), spline5.coeffs ().size ());
86+ for (size_t i = 0 ; i < spline5.coeffs ().size (); ++i) {
87+ EXPECT_NEAR (spline5.coeffs ()[i], (spline3.coeffs ()[i] + spline3.coeffs ()[i]) / 2 , 1e-15 );
88+ }
89+ // clamped
90+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::ClampedStart{10 });
91+ EXPECT_EQ (10 , spline1.coeffs ()[1 ]);
92+ spline2.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::ClampedEnd{10 });
93+ EXPECT_NEAR (10 , 2 *spline2.coeffs ()[3 *8 ]*dX[8 ] + spline2.coeffs ()[3 *8 +1 ], 1e-14 );
94+ const auto spline6 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::SemiClamped{10 , 10 });
95+ ASSERT_EQ (spline1.coeffs ().size (), spline6.coeffs ().size ());
96+ ASSERT_EQ (spline2.coeffs ().size (), spline6.coeffs ().size ());
97+ for (size_t i = 0 ; i < spline6.coeffs ().size (); ++i) {
98+ EXPECT_EQ (spline6.coeffs ()[i], (spline1.coeffs ()[i] + spline2.coeffs ()[i]) / 2 );
99+ }
100+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::Clamped{5 , 10 });
101+ EXPECT_EQ (10 , spline1.coeffs ()[3 *5 +1 ]);
102+ EXPECT_NEAR (10 , 2 *spline1.coeffs ()[3 *4 ]*dX[8 ] + spline1.coeffs ()[3 *4 +1 ], 1e-15 );
103+ // fixed-second
104+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::FixedSecondStart{10 });
105+ EXPECT_EQ (10 , 2 *spline1.coeffs ()[0 ]);
106+ spline2.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::FixedSecondEnd{10 });
107+ EXPECT_EQ (10 , 2 *spline2.coeffs ()[3 *8 ]);
108+ const auto spline7 = alfi::spline::QuadraticSpline<>(X, Y, alfi::spline::QuadraticSpline<>::Types::SemiFixedSecond{10 , 10 });
109+ ASSERT_EQ (spline1.coeffs ().size (), spline7.coeffs ().size ());
110+ ASSERT_EQ (spline2.coeffs ().size (), spline7.coeffs ().size ());
111+ for (size_t i = 0 ; i < spline7.coeffs ().size (); ++i) {
112+ EXPECT_EQ (spline7.coeffs ()[i], (spline1.coeffs ()[i] + spline2.coeffs ()[i]) / 2 );
113+ }
114+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::FixedSecond{5 , 10 });
115+ EXPECT_EQ (10 , 2 *spline1.coeffs ()[3 *5 ]);
116+ // not-a-knot
117+ spline1.construct (X, Y, alfi::spline::QuadraticSpline<>::Types::NotAKnot{2 });
118+ EXPECT_EQ (2 *spline1.coeffs ()[3 *1 ], 2 *spline1.coeffs ()[3 *2 ]);
119+ }
120+
15121TEST (QuadraticSplineTest, Moving) {
16122 std::vector<double > X = {0 , 1 , 2 , 3 };
17123 const std::vector<double > Y = {5 , 2 , 10 , 4 };
0 commit comments