1 /*
2 
3 Copyright (C) 2013 Rob Sykes <robs@users.sourceforge.net>
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING.  If not, see
17 <https://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include <cmath>
22 #include <cstdlib>
23 #include <cstdio>
24 #include <octave/oct.h>
25 #include <octave/lo-specfun.h>
26 
27 #if !defined M_PI
28 #define M_PI 3.14159265358979323846
29 #endif
30 
31 #if defined (__cplusplus) && __cplusplus > 201402L
32 #  define ATTR_FALLTHROUGH [[fallthrough]]
33 #elif defined (__GNUC__) && __GNUC__ >= 7
34 #  define ATTR_FALLTHROUGH __attribute__ ((__fallthrough__))
35 #else
36 #  define ATTR_FALLTHROUGH ((void) 0)
37 #endif
38 
39 #if DEBUG_ULTRWIN
40 #define SHOW1(x) fprintf(stderr, "%c%+.3e", " 1"[(x) > .5], (x) - ((x) > .5))
41 #endif
42 
43 #define EPSILON     (1./0x100000) /* For caller rounding error. */
44 #define BETA_MAX    (12*(1+EPSILON))
45 #define XMU_MIN     (.99*(1-EPSILON))
46 
47 
48 static double *
ultraspherical_win(int n,double mu,double xmu)49 ultraspherical_win(int n, double mu, double xmu)
50 {
51   double * w = NULL;
52   int bad = n < 1 || xmu < XMU_MIN || (!mu && xmu == 1) ||
53     (n > BETA_MAX * 2 && xmu * cos(M_PI * BETA_MAX / n) > 1);
54   if (!bad && (w = (double *)malloc(sizeof(*w)* n))) {
55     int i, j, k, l = 0, m = (n + 1) / 2, met;
56     double * divs = w + m - 1, c = 1 - 1 / (xmu * xmu), t, u, v[64], vp, s;
57     for (i = 0; i < (int)(sizeof(v) / sizeof(v[0])); v[i++] = 0);
58     if (n > 1) for (i = 0; i < m; l = j - (j <= i++)) {
59       vp = *v, s = *v = i? (*v + v[1]) * mu * (divs[i] = 1./i) : 1;
60       for (met = 0, j = 1, u = 1; ; ++l, v[l] = vp * (i - l) / (mu + l - 1)) {
61         #define _ t = v[j], v[j] += vp, vp = t, t = s, s += \
62             v[j] * (u *= c * (n - i - j) * divs[j]), met = s && s == t, ++j,
63         for (k = ((l-j+1) & ~7) + j; j < k && !met; _ _ _ _ _ _ _ _ (void)0);
64         for (; j <= l && !met; _ (void)0);
65         #undef _
66         if (met || !(j <= i)) break;
67       }
68       w[i] = s / (n - i - 1);
69     }
70     else w[0] = 1;
71     u = 1 / w[i = m - 1], w[i] = 1;
72     for (--i ; i >= 0; u *= (n - 2 - i + mu) / (n - 2 - i), w[i] *= u, --i);
73     for (i = 0; i < m; w[n - 1 - i] = w[i], ++i);
74   }
75   return w;
76 }
77 
78 
79 typedef struct {double f, fp;} uspv_t;
80 
81 
82 static uspv_t
ultraspherical_polyval(int n,double mu,double x,double const * divs)83 ultraspherical_polyval(int n, double mu, double x, double const *divs)
84 {
85   double fp = n > 0? 2 * x * mu : 1, fpp = 1, f;
86   uspv_t result;
87   int i, k;
88   #define _ f = (2*x*(i+mu)*fp - (i+2*mu-1)*fpp) * divs[i+1], fpp=fp, fp=f, ++i,
89   for (i = 1, k = i + ((n - i) & ~7); i < k; _ _ _ _ _ _ _ _ (void)0);
90   for (; i < n; _ (void)0);
91   #undef _
92   result.f = fp, result.fp = fpp;
93   return result;
94 }
95 
96 
97 #define MU_EPSILON  (1./0x4000)
98 #define EQ(mu,x)    (fabs((mu)-(x)) < MU_EPSILON)
99 
100 
101 static uspv_t
ultraspherical_polyval2(int n,double mu,double x,double const * divs)102 ultraspherical_polyval2(     /* With non-+ve integer protection */
103     int n, double mu, double x, double const * divs)
104 {
105   int sign = (~(int)floor(mu) & ~(~2u/2))? 1:-1; /* -ve if floor(mu) <0 & odd */
106   uspv_t r;
107   if (mu < MU_EPSILON && EQ(mu,(int)mu))
108     mu = floor(mu + .5) + MU_EPSILON * ((int)mu > mu? -1:1);
109   r = ultraspherical_polyval(n, mu, x, divs);
110   r.f *= sign, r.fp *= sign;
111   return r;
112 }
113 
114 
115 static double
find_zero(int n,double mu,int l,double extremum_mag,double ripple_ratio,double lower_bound,double const * divs)116 find_zero(int n, double mu, int l, double extremum_mag, double ripple_ratio,
117           double lower_bound, double const *divs)
118 {
119   double dx, x0, t, x, epsilon = 1e-10, one_over_deriv, target = 0;
120   int i, met = 0;
121   if (!divs)
122     return 0;
123   if (!l) {
124     double r = ripple_ratio;   /* FIXME: factor in weighted extremum_mag here */
125     x = r > 1 ? cosh(acosh(r) / n) : cos(acos(r) / n); /* invert chebpoly-1st */
126     x0 = x *= lower_bound / cos(M_PI * .5 / n) + epsilon;
127     target = log(extremum_mag * ripple_ratio);
128   }
129   else {
130     double cheb1 = cos(M_PI * (l - .5) / n), cheb2 = cos(M_PI * l / (n + 1));
131     if (mu < 1 - l && EQ((int)(mu+.5),mu+.5)) x = met = 1;
132     else if (EQ(mu,0)) x = cheb1, met = 1;               /* chebpoly-1st-kind */
133     else if (EQ(mu,1)) x = cheb2, met = 1;               /* chebpoly-2nd-kind */
134     else x = (cheb1 * cheb2) / (mu * cheb1 + (1 - mu) * cheb2);
135     x0 = x;
136   }
137   for (i = 0; i < 24 && !met; ++i, met = fabs(dx) < epsilon) {/*Newton-Raphson*/
138     uspv_t r = ultraspherical_polyval2(n, mu, x, divs);
139     if (!(t = ((2*mu + n-1) * r.fp - n*x * r.f)))         /* Fail if div by 0 */
140       break;
141     one_over_deriv = (1 - x*x) / t;    /* N-R slow for deriv~=1, so take log: */
142     if (!l) {                               /* d/dx(f(g(x))) = f'(g(x)).g'(x) */
143       one_over_deriv *= r.f;                             /* d/dx(log x) = 1/x */
144       if (r.f <= 0)                                 /* Fail if log of non-+ve */
145         break;
146       if (x + (dx = (target - log(r.f)) * one_over_deriv) <= lower_bound)
147         dx = (lower_bound - x) * .875;
148       x += dx;
149     }
150     else x += dx = -r.f * one_over_deriv;
151 #if DEBUG_ULTRWIN
152     fprintf(stderr, "1/deriv=%9.2e dx=%9.2e x=", one_over_deriv, dx);
153     SHOW1(x); fprintf(stderr, "\n");
154 #endif
155   }
156 #if DEBUG_ULTRWIN
157   fprintf(stderr, "find_zero(n=%i mu=%g l=%i target=%g r=%g x0=",
158       n, mu, l, target, ripple_ratio);
159   SHOW1(x0); fprintf(stderr, ") %s ", met? "converged to" : "FAILED at");
160   SHOW1(x); fprintf(stderr, " in %i iterations\n", i);
161 #else
162   static_cast<void>(x0);
163 #endif
164   return met? x : 0;
165 }
166 
167 
168 static double *
make_divs(int n,double ** divs)169 make_divs(int n, double **divs)
170 {
171   int i;
172   if (!*divs) {
173     *divs = (double *)malloc(n * sizeof(**divs));
174     if (*divs)
175       for (i = 0; i < n; (*divs)[i] = 1./(i+1), ++i);
176   }
177   return *divs? *divs - 1 : 0;
178 }
179 
180 
181 #define DIVS make_divs(n, &divs)
182 
183 
184 typedef enum {uswpt_Xmu, uswpt_Beta, uswpt_AttFirst, uswpt_AttLast} uswpt_t;
185 
186 
187 double *
ultraspherical_window(int n,double mu,double par,uswpt_t type,int even_norm,double * xmu_)188 ultraspherical_window(int n, double mu, double par, uswpt_t type, int even_norm,
189                       double *xmu_)
190 {
191   double * w = 0, xmu = 0, * divs = 0, last_extremum_pos = 0;
192 
193   if (n > 0 && fabs(mu) <= (8*(1+EPSILON))) switch (type) {
194     case uswpt_Beta:
195       xmu = mu == 1 && par == 1? 1 : par < .5 || par > BETA_MAX? 0 :
196         find_zero(n-1, mu, 1, 0, 0, 0, DIVS) / cos(M_PI * par / n);
197       break;
198 
199     case uswpt_AttFirst:
200       if (par < 0) break;
201       ATTR_FALLTHROUGH;
202 
203     case uswpt_AttLast:
204       if (type == uswpt_AttLast && mu >= 0 && par < 0);
205       else if (!EQ(mu,0)) {
206         int extremum_num =
207           type == uswpt_AttLast? (int)((n-2)/2 +.5) : 1 + EQ(mu,-1.5);
208         double extremum_pos =
209           find_zero(n-2, mu+1, extremum_num, 0, 0, 0, DIVS);
210         double extremum_mag = !extremum_pos? 0 :
211           fabs(ultraspherical_polyval2(n-1, mu, extremum_pos, DIVS).f);
212         double xmu_lower_bound = !extremum_mag? 0 :
213           find_zero(n-1, mu, 1, 0, 0, 0, DIVS); /* 1st null */
214         xmu = !xmu_lower_bound? 0 : find_zero(
215             n-1, mu, 0, extremum_mag, pow(10, par/20), xmu_lower_bound, DIVS);
216         last_extremum_pos =
217           type == uswpt_AttLast? extremum_pos : last_extremum_pos;
218       }
219       else xmu = cosh(acosh(pow(10, par/20))/(n-1)); /* Cheby 1st kind */
220       break;
221 
222     default: case uswpt_Xmu: xmu = par; break;
223   }
224 #if DEBUG_ULTRWIN
225   fprintf(stderr, "n=%i mu=%.3f xmu=%.16g\n", n, mu, xmu);
226 #endif
227 
228   if (xmu > 0)
229     w = ultraspherical_win(n, mu, xmu);
230 
231   if (w && (~n & !!even_norm) && n > 2 && !(mu == 1 && xmu == 1)) {
232     int i = n / 2 - 1, j = 1;
233     double * d = DIVS, t = 0, s = -1, x = even_norm == 1? 0 : last_extremum_pos?
234       last_extremum_pos : find_zero(n-2, mu+1, i, 0, 0, 0, d);
235     x = x? M_PI/2 - acos(x/xmu) : 0;
236     for (; i >= 0; t += w[i] * d[j] * (s=-s) * (x?cos(j*x):1), --i, j += 2);
237     for (t = M_PI/4 / t, i = 0; t < 1 && i < n; w[i] *= t, ++i);
238 #if DEBUG_ULTRWIN
239     fprintf(stderr, "%snorm DFT(w.sinc πx) @ %g %.16g\n", t<1? "":"NO ", 2*x,t);
240 #endif
241   }
242   free(divs);
243   if (xmu_)
244     *xmu_ = xmu;
245   return w;
246 }
247 
248 
249 //----------------------------------------------------------------------------
250 
251 
252 DEFUN_DLD (__ultrwin__, args, ,
253   "-*- texinfo -*-\n\
254 @deftypefn {Loadable Function} {} __ultrwin__ (@var{m}, @var{mu}, @var{par}, @var{par_type}, @var{norm})\n\
255 Undocumented internal function.\n\
256 @end deftypefn")
257 {
258   octave_value_list retval;
259 
260   int nargin = args.length ();
261 
262   if (nargin != 5)
263     {
264       print_usage ();
265       return retval;
266     }
267 
268   for (octave_idx_type i = 0; i < nargin; i++)
269     if (! args(i).is_real_scalar ())
270       {
271         print_usage ();
272         return retval;
273       }
274 
275   int m = args(0).scalar_value ();
276   double mu = args(1).scalar_value ();
277   double par = args(2).scalar_value ();
278   uswpt_t par_type = static_cast<uswpt_t> (args(3).scalar_value ());
279   int even_norm = args(4).scalar_value ();
280   double xmu;
281   double *w = ultraspherical_window (m, mu, par, par_type, even_norm, &xmu);
282 
283   if (!w)
284     {
285       error ("ultrwin: parameter(s) out of range");
286       return retval;
287     }
288 
289   ColumnVector ww (m);
290   for (octave_idx_type i = 0; i < m; i++)
291     ww(i) = w[i];
292   free (w);
293 
294   retval(0) = ww;
295   retval(1) = xmu;
296 
297   return retval;
298 }
299 
300 /*
301 ## No test needed for internal helper function.
302 %!assert (1)
303 */
304