cardinal_b_spline.hpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // (C) Copyright Nick Thompson 2019.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_SPECIAL_CARDINAL_B_SPLINE_HPP
  6. #define BOOST_MATH_SPECIAL_CARDINAL_B_SPLINE_HPP
  7. #include <array>
  8. #include <cmath>
  9. #include <limits>
  10. #include <type_traits>
  11. namespace boost { namespace math {
  12. namespace detail {
  13. template<class Real>
  14. inline Real B1(Real x)
  15. {
  16. if (x < 0)
  17. {
  18. return B1(-x);
  19. }
  20. if (x < Real(1))
  21. {
  22. return 1 - x;
  23. }
  24. return Real(0);
  25. }
  26. }
  27. template<unsigned n, typename Real>
  28. Real cardinal_b_spline(Real x) {
  29. static_assert(!std::is_integral<Real>::value, "Does not work with integral types.");
  30. if (x < 0) {
  31. // All B-splines are even functions:
  32. return cardinal_b_spline<n, Real>(-x);
  33. }
  34. if (n==0)
  35. {
  36. if (x < Real(1)/Real(2)) {
  37. return Real(1);
  38. }
  39. else if (x == Real(1)/Real(2)) {
  40. return Real(1)/Real(2);
  41. }
  42. else {
  43. return Real(0);
  44. }
  45. }
  46. if (n==1)
  47. {
  48. return detail::B1(x);
  49. }
  50. Real supp_max = (n+1)/Real(2);
  51. if (x >= supp_max)
  52. {
  53. return Real(0);
  54. }
  55. // Fill v with values of B1:
  56. // At most two of these terms are nonzero, and at least 1.
  57. // There is only one non-zero term when n is odd and x = 0.
  58. std::array<Real, n> v;
  59. Real z = x + 1 - supp_max;
  60. for (unsigned i = 0; i < n; ++i)
  61. {
  62. v[i] = detail::B1(z);
  63. z += 1;
  64. }
  65. Real smx = supp_max - x;
  66. for (unsigned j = 2; j <= n; ++j)
  67. {
  68. Real a = (j + 1 - smx);
  69. Real b = smx;
  70. for(unsigned k = 0; k <= n - j; ++k)
  71. {
  72. v[k] = (a*v[k+1] + b*v[k])/Real(j);
  73. a += 1;
  74. b -= 1;
  75. }
  76. }
  77. return v[0];
  78. }
  79. template<unsigned n, typename Real>
  80. Real cardinal_b_spline_prime(Real x)
  81. {
  82. static_assert(!std::is_integral<Real>::value, "Cardinal B-splines do not work with integer types.");
  83. if (x < 0)
  84. {
  85. // All B-splines are even functions, so derivatives are odd:
  86. return -cardinal_b_spline_prime<n, Real>(-x);
  87. }
  88. if (n==0)
  89. {
  90. // Kinda crazy but you get what you ask for!
  91. if (x == Real(1)/Real(2))
  92. {
  93. return std::numeric_limits<Real>::infinity();
  94. }
  95. else
  96. {
  97. return Real(0);
  98. }
  99. }
  100. if (n==1)
  101. {
  102. if (x==0)
  103. {
  104. return Real(0);
  105. }
  106. if (x==1)
  107. {
  108. return -Real(1)/Real(2);
  109. }
  110. return Real(-1);
  111. }
  112. Real supp_max = (n+1)/Real(2);
  113. if (x >= supp_max)
  114. {
  115. return Real(0);
  116. }
  117. // Now we want to evaluate B_{n}(x), but stop at the second to last step and collect B_{n-1}(x+1/2) and B_{n-1}(x-1/2):
  118. std::array<Real, n> v;
  119. Real z = x + 1 - supp_max;
  120. for (unsigned i = 0; i < n; ++i)
  121. {
  122. v[i] = detail::B1(z);
  123. z += 1;
  124. }
  125. Real smx = supp_max - x;
  126. for (unsigned j = 2; j <= n - 1; ++j)
  127. {
  128. Real a = (j + 1 - smx);
  129. Real b = smx;
  130. for(unsigned k = 0; k <= n - j; ++k)
  131. {
  132. v[k] = (a*v[k+1] + b*v[k])/Real(j);
  133. a += 1;
  134. b -= 1;
  135. }
  136. }
  137. return v[1] - v[0];
  138. }
  139. template<unsigned n, typename Real>
  140. Real cardinal_b_spline_double_prime(Real x)
  141. {
  142. static_assert(!std::is_integral<Real>::value, "Cardinal B-splines do not work with integer types.");
  143. static_assert(n >= 3, "n>=3 for second derivatives of cardinal B-splines is required.");
  144. if (x < 0)
  145. {
  146. // All B-splines are even functions, so second derivatives are even:
  147. return cardinal_b_spline_double_prime<n, Real>(-x);
  148. }
  149. Real supp_max = (n+1)/Real(2);
  150. if (x >= supp_max)
  151. {
  152. return Real(0);
  153. }
  154. // Now we want to evaluate B_{n}(x), but stop at the second to last step and collect B_{n-1}(x+1/2) and B_{n-1}(x-1/2):
  155. std::array<Real, n> v;
  156. Real z = x + 1 - supp_max;
  157. for (unsigned i = 0; i < n; ++i)
  158. {
  159. v[i] = detail::B1(z);
  160. z += 1;
  161. }
  162. Real smx = supp_max - x;
  163. for (unsigned j = 2; j <= n - 2; ++j)
  164. {
  165. Real a = (j + 1 - smx);
  166. Real b = smx;
  167. for(unsigned k = 0; k <= n - j; ++k)
  168. {
  169. v[k] = (a*v[k+1] + b*v[k])/Real(j);
  170. a += 1;
  171. b -= 1;
  172. }
  173. }
  174. return v[2] - 2*v[1] + v[0];
  175. }
  176. template<unsigned n, class Real>
  177. Real forward_cardinal_b_spline(Real x)
  178. {
  179. static_assert(!std::is_integral<Real>::value, "Cardinal B-splines do not work with integral types.");
  180. return cardinal_b_spline<n>(x - (n+1)/Real(2));
  181. }
  182. }}
  183. #endif