septic_hermite_detail.hpp 20 KB

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