septic_hermite_detail.hpp 20 KB

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