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