1 //  (C) Copyright Nick Thompson 2017.
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 
6 #ifndef BOOST_MATH_SPECIAL_CHEBYSHEV_TRANSFORM_HPP
7 #define BOOST_MATH_SPECIAL_CHEBYSHEV_TRANSFORM_HPP
8 #include <cmath>
9 #include <type_traits>
10 #include <fftw3.h>
11 #include <boost/math/constants/constants.hpp>
12 #include <boost/math/special_functions/chebyshev.hpp>
13 
14 #ifdef BOOST_HAS_FLOAT128
15 #include <quadmath.h>
16 #endif
17 
18 namespace boost { namespace math {
19 
20 namespace detail{
21 
22 template <class T>
23 struct fftw_cos_transform;
24 
25 template<>
26 struct fftw_cos_transform<double>
27 {
fftw_cos_transformboost::math::detail::fftw_cos_transform28    fftw_cos_transform(int n, double* data1, double* data2)
29    {
30       plan = fftw_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
31    }
~fftw_cos_transformboost::math::detail::fftw_cos_transform32    ~fftw_cos_transform()
33    {
34       fftw_destroy_plan(plan);
35    }
executeboost::math::detail::fftw_cos_transform36    void execute(double* data1, double* data2)
37    {
38       fftw_execute_r2r(plan, data1, data2);
39    }
cosboost::math::detail::fftw_cos_transform40    static double cos(double x) { return std::cos(x); }
fabsboost::math::detail::fftw_cos_transform41    static double fabs(double x) { return std::fabs(x); }
42 private:
43    fftw_plan plan;
44 };
45 
46 template<>
47 struct fftw_cos_transform<float>
48 {
fftw_cos_transformboost::math::detail::fftw_cos_transform49    fftw_cos_transform(int n, float* data1, float* data2)
50    {
51       plan = fftwf_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
52    }
~fftw_cos_transformboost::math::detail::fftw_cos_transform53    ~fftw_cos_transform()
54    {
55       fftwf_destroy_plan(plan);
56    }
executeboost::math::detail::fftw_cos_transform57    void execute(float* data1, float* data2)
58    {
59       fftwf_execute_r2r(plan, data1, data2);
60    }
cosboost::math::detail::fftw_cos_transform61    static float cos(float x) { return std::cos(x); }
fabsboost::math::detail::fftw_cos_transform62    static float fabs(float x) { return std::fabs(x); }
63 private:
64    fftwf_plan plan;
65 };
66 
67 template<>
68 struct fftw_cos_transform<long double>
69 {
fftw_cos_transformboost::math::detail::fftw_cos_transform70    fftw_cos_transform(int n, long double* data1, long double* data2)
71    {
72       plan = fftwl_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
73    }
~fftw_cos_transformboost::math::detail::fftw_cos_transform74    ~fftw_cos_transform()
75    {
76       fftwl_destroy_plan(plan);
77    }
executeboost::math::detail::fftw_cos_transform78    void execute(long double* data1, long double* data2)
79    {
80       fftwl_execute_r2r(plan, data1, data2);
81    }
cosboost::math::detail::fftw_cos_transform82    static long double cos(long double x) { return std::cos(x); }
fabsboost::math::detail::fftw_cos_transform83    static long double fabs(long double x) { return std::fabs(x); }
84 private:
85    fftwl_plan plan;
86 };
87 #ifdef BOOST_HAS_FLOAT128
88 template<>
89 struct fftw_cos_transform<__float128>
90 {
fftw_cos_transformboost::math::detail::fftw_cos_transform91    fftw_cos_transform(int n, __float128* data1, __float128* data2)
92    {
93       plan = fftwq_plan_r2r_1d(n, data1, data2, FFTW_REDFT10, FFTW_ESTIMATE);
94    }
~fftw_cos_transformboost::math::detail::fftw_cos_transform95    ~fftw_cos_transform()
96    {
97       fftwq_destroy_plan(plan);
98    }
executeboost::math::detail::fftw_cos_transform99    void execute(__float128* data1, __float128* data2)
100    {
101       fftwq_execute_r2r(plan, data1, data2);
102    }
cosboost::math::detail::fftw_cos_transform103    static __float128 cos(__float128 x) { return cosq(x); }
fabsboost::math::detail::fftw_cos_transform104    static __float128 fabs(__float128 x) { return fabsq(x); }
105 private:
106    fftwq_plan plan;
107 };
108 
109 #endif
110 }
111 
112 template<class Real>
113 class chebyshev_transform
114 {
115 public:
116     template<class F>
chebyshev_transform(const F & f,Real a,Real b,Real tol=500* std::numeric_limits<Real>::epsilon (),size_t max_refinements=16)117     chebyshev_transform(const F& f, Real a, Real b,
118        Real tol = 500 * std::numeric_limits<Real>::epsilon(),
119        size_t max_refinements = 16) : m_a(a), m_b(b)
120     {
121         if (a >= b)
122         {
123             throw std::domain_error("a < b is required.\n");
124         }
125         using boost::math::constants::half;
126         using boost::math::constants::pi;
127         using std::cos;
128         using std::abs;
129         Real bma = (b-a)*half<Real>();
130         Real bpa = (b+a)*half<Real>();
131         size_t n = 256;
132         std::vector<Real> vf;
133 
134         size_t refinements = 0;
135         while(refinements < max_refinements)
136         {
137             vf.resize(n);
138             m_coeffs.resize(n);
139 
140             detail::fftw_cos_transform<Real> plan(static_cast<int>(n), vf.data(), m_coeffs.data());
141             Real inv_n = 1/static_cast<Real>(n);
142             for(size_t j = 0; j < n/2; ++j)
143             {
144                 // Use symmetry cos((j+1/2)pi/n) = - cos((n-1-j+1/2)pi/n)
145                 Real y = detail::fftw_cos_transform<Real>::cos(pi<Real>()*(j+half<Real>())*inv_n);
146                 vf[j] = f(y*bma + bpa)*inv_n;
147                 vf[n-1-j]= f(bpa-y*bma)*inv_n;
148             }
149 
150             plan.execute(vf.data(), m_coeffs.data());
151             Real max_coeff = 0;
152             for (auto const & coeff : m_coeffs)
153             {
154                 if (detail::fftw_cos_transform<Real>::fabs(coeff) > max_coeff)
155                 {
156                     max_coeff = detail::fftw_cos_transform<Real>::fabs(coeff);
157                 }
158             }
159             size_t j = m_coeffs.size() - 1;
160             while (abs(m_coeffs[j])/max_coeff < tol)
161             {
162                 --j;
163             }
164             // If ten coefficients are eliminated, the we say we've done all
165             // we need to do:
166             if (n - j > 10)
167             {
168                 m_coeffs.resize(j+1);
169                 return;
170             }
171 
172             n *= 2;
173             ++refinements;
174         }
175     }
176 
operator ()(Real x) const177     inline Real operator()(Real x) const
178     {
179         return chebyshev_clenshaw_recurrence(m_coeffs.data(), m_coeffs.size(), m_a, m_b, x);
180     }
181 
182     // Integral over entire domain [a, b]
integrate() const183     Real integrate() const
184     {
185           Real Q = m_coeffs[0]/2;
186           for(size_t j = 2; j < m_coeffs.size(); j += 2)
187           {
188               Q += -m_coeffs[j]/((j+1)*(j-1));
189           }
190           return (m_b - m_a)*Q;
191     }
192 
coefficients() const193     const std::vector<Real>& coefficients() const
194     {
195         return m_coeffs;
196     }
197 
prime(Real x) const198     Real prime(Real x) const
199     {
200         Real z = (2*x - m_a - m_b)/(m_b - m_a);
201         Real dzdx = 2/(m_b - m_a);
202         if (m_coeffs.size() < 2)
203         {
204             return 0;
205         }
206         Real b2 = 0;
207         Real d2 = 0;
208         Real b1 = m_coeffs[m_coeffs.size() -1];
209         Real d1 = 0;
210         for(size_t j = m_coeffs.size() - 2; j >= 1; --j)
211         {
212             Real tmp1 = 2*z*b1 - b2 + m_coeffs[j];
213             Real tmp2 = 2*z*d1 - d2 + 2*b1;
214             b2 = b1;
215             b1 = tmp1;
216 
217             d2 = d1;
218             d1 = tmp2;
219         }
220         return dzdx*(z*d1 - d2 + b1);
221     }
222 
223 private:
224     std::vector<Real> m_coeffs;
225     Real m_a;
226     Real m_b;
227 };
228 
229 }}
230 #endif
231