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)
6 #ifndef BOOST_MATH_INTERPOLATORS_DETAIL_CARDINAL_TRIGONOMETRIC_HPP
7 #define BOOST_MATH_INTERPOLATORS_DETAIL_CARDINAL_TRIGONOMETRIC_HPP
11 #include <boost/math/constants/constants.hpp>
13 #ifdef BOOST_HAS_FLOAT128
17 namespace boost { namespace math { namespace interpolators { namespace detail {
19 template<typename Real>
20 class cardinal_trigonometric_detail {
22 cardinal_trigonometric_detail(const Real* data, size_t length, Real t0, Real h)
28 throw std::domain_error("Not implemented.");
38 class cardinal_trigonometric_detail<float> {
40 cardinal_trigonometric_detail(const float* data, size_t length, float t0, float h) : m_t0{t0}, m_h{h}
44 throw std::logic_error("At least one sample is required.");
48 throw std::logic_error("The step size must be > 0");
50 // The period sadly must be stored, since the complex vector has length that cannot be used to recover the period:
52 m_complex_vector_size = length/2 + 1;
53 m_gamma = fftwf_alloc_complex(m_complex_vector_size);
54 // The const_cast is legitimate: FFTW does not change the data as long as FFTW_ESTIMATE is provided.
55 fftwf_plan plan = fftwf_plan_dft_r2c_1d(length, const_cast<float*>(data), m_gamma, FFTW_ESTIMATE);
56 // FFTW says a null plan is impossible with the basic interface we are using, and I have no reason to doubt them.
57 // But it just feels weird not to check this:
60 throw std::logic_error("A null fftw plan was created.");
64 fftwf_destroy_plan(plan);
67 for (size_t k = 0; k < m_complex_vector_size; ++k)
69 m_gamma[k][0] /= denom;
70 m_gamma[k][1] /= denom;
75 m_gamma[m_complex_vector_size -1][0] /= 2;
76 // numerically, m_gamma[m_complex_vector_size -1][1] should be zero . . .
77 // I believe, but need to check, that FFTW guarantees that it is identically zero.
81 cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
83 cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
85 cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
87 float operator()(float t) const
91 using boost::math::constants::two_pi;
93 float s = m_gamma[0][0];
94 float x = two_pi<float>()*(t - m_t0)/m_T;
96 // boost::math::cos_pi with a redefinition of x? Not now . . .
99 fftwf_complex b{0, 0};
102 for (size_t k = m_complex_vector_size - 1; k >= 1; --k) {
103 u[0] = b[0]*z[0] - b[1]*z[1];
104 u[1] = b[0]*z[1] + b[1]*z[0];
105 b[0] = m_gamma[k][0] + u[0];
106 b[1] = m_gamma[k][1] + u[1];
109 s += 2*(b[0]*z[0] - b[1]*z[1]);
113 float prime(float t) const
117 using boost::math::constants::two_pi;
119 float x = two_pi<float>()*(t - m_t0)/m_T;
123 fftwf_complex b{0, 0};
126 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
128 u[0] = b[0]*z[0] - b[1]*z[1];
129 u[1] = b[0]*z[1] + b[1]*z[0];
130 b[0] = k*m_gamma[k][0] + u[0];
131 b[1] = k*m_gamma[k][1] + u[1];
133 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
134 return -2*two_pi<float>()*(b[1]*z[0] + b[0]*z[1])/m_T;
137 float double_prime(float t) const
141 using boost::math::constants::two_pi;
143 float x = two_pi<float>()*(t - m_t0)/m_T;
147 fftwf_complex b{0, 0};
150 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
152 u[0] = b[0]*z[0] - b[1]*z[1];
153 u[1] = b[0]*z[1] + b[1]*z[0];
154 b[0] = k*k*m_gamma[k][0] + u[0];
155 b[1] = k*k*m_gamma[k][1] + u[1];
157 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
158 return -2*two_pi<float>()*two_pi<float>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
166 float integrate() const
168 return m_T*m_gamma[0][0];
171 float squared_l2() const
174 // Always add smallest to largest for accuracy.
175 for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
177 s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
180 s += m_gamma[0][0]*m_gamma[0][0];
185 ~cardinal_trigonometric_detail()
199 fftwf_complex* m_gamma;
200 size_t m_complex_vector_size;
205 class cardinal_trigonometric_detail<double> {
207 cardinal_trigonometric_detail(const double* data, size_t length, double t0, double h) : m_t0{t0}, m_h{h}
211 throw std::logic_error("At least one sample is required.");
215 throw std::logic_error("The step size must be > 0");
218 m_complex_vector_size = length/2 + 1;
219 m_gamma = fftw_alloc_complex(m_complex_vector_size);
220 fftw_plan plan = fftw_plan_dft_r2c_1d(length, const_cast<double*>(data), m_gamma, FFTW_ESTIMATE);
223 throw std::logic_error("A null fftw plan was created.");
227 fftw_destroy_plan(plan);
229 double denom = length;
230 for (size_t k = 0; k < m_complex_vector_size; ++k)
232 m_gamma[k][0] /= denom;
233 m_gamma[k][1] /= denom;
238 m_gamma[m_complex_vector_size -1][0] /= 2;
242 cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
244 cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
246 cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
248 double operator()(double t) const
252 using boost::math::constants::two_pi;
254 double s = m_gamma[0][0];
255 double x = two_pi<double>()*(t - m_t0)/m_T;
259 fftw_complex b{0, 0};
262 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
264 u[0] = b[0]*z[0] - b[1]*z[1];
265 u[1] = b[0]*z[1] + b[1]*z[0];
266 b[0] = m_gamma[k][0] + u[0];
267 b[1] = m_gamma[k][1] + u[1];
270 s += 2*(b[0]*z[0] - b[1]*z[1]);
274 double prime(double t) const
278 using boost::math::constants::two_pi;
280 double x = two_pi<double>()*(t - m_t0)/m_T;
284 fftw_complex b{0, 0};
287 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
289 u[0] = b[0]*z[0] - b[1]*z[1];
290 u[1] = b[0]*z[1] + b[1]*z[0];
291 b[0] = k*m_gamma[k][0] + u[0];
292 b[1] = k*m_gamma[k][1] + u[1];
294 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
295 return -2*two_pi<double>()*(b[1]*z[0] + b[0]*z[1])/m_T;
298 double double_prime(double t) const
302 using boost::math::constants::two_pi;
304 double x = two_pi<double>()*(t - m_t0)/m_T;
308 fftw_complex b{0, 0};
311 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
313 u[0] = b[0]*z[0] - b[1]*z[1];
314 u[1] = b[0]*z[1] + b[1]*z[0];
315 b[0] = k*k*m_gamma[k][0] + u[0];
316 b[1] = k*k*m_gamma[k][1] + u[1];
318 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
319 return -2*two_pi<double>()*two_pi<double>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
322 double period() const
327 double integrate() const
329 return m_T*m_gamma[0][0];
332 double squared_l2() const
335 for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
337 s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
340 s += m_gamma[0][0]*m_gamma[0][0];
344 ~cardinal_trigonometric_detail()
357 fftw_complex* m_gamma;
358 size_t m_complex_vector_size;
363 class cardinal_trigonometric_detail<long double> {
365 cardinal_trigonometric_detail(const long double* data, size_t length, long double t0, long double h) : m_t0{t0}, m_h{h}
369 throw std::logic_error("At least one sample is required.");
373 throw std::logic_error("The step size must be > 0");
376 m_complex_vector_size = length/2 + 1;
377 m_gamma = fftwl_alloc_complex(m_complex_vector_size);
378 fftwl_plan plan = fftwl_plan_dft_r2c_1d(length, const_cast<long double*>(data), m_gamma, FFTW_ESTIMATE);
381 throw std::logic_error("A null fftw plan was created.");
385 fftwl_destroy_plan(plan);
387 long double denom = length;
388 for (size_t k = 0; k < m_complex_vector_size; ++k)
390 m_gamma[k][0] /= denom;
391 m_gamma[k][1] /= denom;
394 if (length % 2 == 0) {
395 m_gamma[m_complex_vector_size -1][0] /= 2;
399 cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
401 cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
403 cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
405 long double operator()(long double t) const
409 using boost::math::constants::two_pi;
411 long double s = m_gamma[0][0];
412 long double x = two_pi<long double>()*(t - m_t0)/m_T;
416 fftwl_complex b{0, 0};
418 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
420 u[0] = b[0]*z[0] - b[1]*z[1];
421 u[1] = b[0]*z[1] + b[1]*z[0];
422 b[0] = m_gamma[k][0] + u[0];
423 b[1] = m_gamma[k][1] + u[1];
426 s += 2*(b[0]*z[0] - b[1]*z[1]);
430 long double prime(long double t) const
434 using boost::math::constants::two_pi;
436 long double x = two_pi<long double>()*(t - m_t0)/m_T;
440 fftwl_complex b{0, 0};
443 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
445 u[0] = b[0]*z[0] - b[1]*z[1];
446 u[1] = b[0]*z[1] + b[1]*z[0];
447 b[0] = k*m_gamma[k][0] + u[0];
448 b[1] = k*m_gamma[k][1] + u[1];
450 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
451 return -2*two_pi<long double>()*(b[1]*z[0] + b[0]*z[1])/m_T;
454 long double double_prime(long double t) const
458 using boost::math::constants::two_pi;
460 long double x = two_pi<long double>()*(t - m_t0)/m_T;
464 fftwl_complex b{0, 0};
467 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
469 u[0] = b[0]*z[0] - b[1]*z[1];
470 u[1] = b[0]*z[1] + b[1]*z[0];
471 b[0] = k*k*m_gamma[k][0] + u[0];
472 b[1] = k*k*m_gamma[k][1] + u[1];
474 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
475 return -2*two_pi<long double>()*two_pi<long double>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
478 long double period() const
483 long double integrate() const
485 return m_T*m_gamma[0][0];
488 long double squared_l2() const
491 for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
493 s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
496 s += m_gamma[0][0]*m_gamma[0][0];
500 ~cardinal_trigonometric_detail()
513 fftwl_complex* m_gamma;
514 size_t m_complex_vector_size;
517 #ifdef BOOST_HAS_FLOAT128
519 class cardinal_trigonometric_detail<__float128> {
521 cardinal_trigonometric_detail(const __float128* data, size_t length, __float128 t0, __float128 h) : m_t0{t0}, m_h{h}
525 throw std::logic_error("At least one sample is required.");
529 throw std::logic_error("The step size must be > 0");
532 m_complex_vector_size = length/2 + 1;
533 m_gamma = fftwq_alloc_complex(m_complex_vector_size);
534 fftwq_plan plan = fftwq_plan_dft_r2c_1d(length, reinterpret_cast<__float128*>(const_cast<__float128*>(data)), m_gamma, FFTW_ESTIMATE);
537 throw std::logic_error("A null fftw plan was created.");
541 fftwq_destroy_plan(plan);
543 __float128 denom = length;
544 for (size_t k = 0; k < m_complex_vector_size; ++k)
546 m_gamma[k][0] /= denom;
547 m_gamma[k][1] /= denom;
551 m_gamma[m_complex_vector_size -1][0] /= 2;
555 cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
557 cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
559 cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
561 __float128 operator()(__float128 t) const
565 using boost::math::constants::two_pi;
567 __float128 s = m_gamma[0][0];
568 __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
572 fftwq_complex b{0, 0};
574 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
576 u[0] = b[0]*z[0] - b[1]*z[1];
577 u[1] = b[0]*z[1] + b[1]*z[0];
578 b[0] = m_gamma[k][0] + u[0];
579 b[1] = m_gamma[k][1] + u[1];
582 s += 2*(b[0]*z[0] - b[1]*z[1]);
586 __float128 prime(__float128 t) const
590 using boost::math::constants::two_pi;
592 __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
596 fftwq_complex b{0, 0};
599 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
601 u[0] = b[0]*z[0] - b[1]*z[1];
602 u[1] = b[0]*z[1] + b[1]*z[0];
603 b[0] = k*m_gamma[k][0] + u[0];
604 b[1] = k*m_gamma[k][1] + u[1];
606 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
607 return -2*two_pi<__float128>()*(b[1]*z[0] + b[0]*z[1])/m_T;
610 __float128 double_prime(__float128 t) const
614 using boost::math::constants::two_pi;
616 __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
620 fftwq_complex b{0, 0};
623 for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
625 u[0] = b[0]*z[0] - b[1]*z[1];
626 u[1] = b[0]*z[1] + b[1]*z[0];
627 b[0] = k*k*m_gamma[k][0] + u[0];
628 b[1] = k*k*m_gamma[k][1] + u[1];
630 // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
631 return -2*two_pi<__float128>()*two_pi<__float128>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
634 __float128 period() const
639 __float128 integrate() const
641 return m_T*m_gamma[0][0];
644 __float128 squared_l2() const
647 for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
649 s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
652 s += m_gamma[0][0]*m_gamma[0][0];
656 ~cardinal_trigonometric_detail()
670 fftwq_complex* m_gamma;
671 size_t m_complex_vector_size;