Coverage Report

Created: 2026-07-16 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/latitudes.cpp
Line
Count
Source
1
/*
2
 * Project:  PROJ
3
 *
4
 * Helper functions to compute latitudes
5
 *
6
 * Some from Map Projections - A Working Manual. 1987. John P. Snyder
7
 * https://neacsu.net/docs/geodesy/snyder/2-general/sect_3/
8
 *
9
 * Copyright (c) 2025 Javier Jimenez Shaw
10
 */
11
12
#include <exception>
13
#include <math.h>
14
15
#include "proj_internal.h"
16
17
/*****************************************************************************/
18
356
double pj_conformal_lat(double phi, const PJ *P) {
19
    /***********************************
20
     * The conformal latitude chi, in terms of the geodetic latitude, phi.
21
     *
22
     * phi: geodetic latitude, in radians
23
     * returns: conformal latitude, chi, in radians
24
     * Copied from merc.cpp
25
     ***********************************/
26
356
    if (P->e == 0.0)
27
6
        return phi;
28
29
    // Instead of calling tan and sin, call sin and cos which the compiler
30
    // optimizes to a single call to sincos.
31
350
    double sphi = sin(phi), cphi = cos(phi);
32
350
    return atan(sinh(asinh(sphi / cphi) - P->e * atanh(P->e * sphi)));
33
356
}
34
35
/*****************************************************************************/
36
0
double pj_conformal_lat_inverse(double chi, const PJ *P) {
37
    /***********************************
38
     * The geodetic latitude, phi, in terms of the conformal latitude, chi.
39
     *
40
     * chi: conformal latitude, in radians
41
     * returns: geodetic latitude, phi, in radians
42
     * Copied from merc.cpp
43
     ***********************************/
44
0
    if (P->e == 0.0)
45
0
        return chi;
46
47
0
    return atan(pj_sinhpsi2tanphi(P->ctx, tan(chi), P->e));
48
0
}
49
50
/*****************************************************************************/
51
52
// Computes coefficient q such that authalic_latitude = beta = asin(q / qp)
53
// where qp is q at phi=90deg, i.e. qp = pj_authalic_lat_q(1, e, one_es)
54
// Cf  Snyder (3-11) and (3-12)
55
14.2k
double pj_authalic_lat_q(double sinphi, const PJ *P) {
56
14.2k
    constexpr double EPSILON = 1e-7;
57
14.2k
    if (P->e >= EPSILON) {
58
14.2k
        const double e_sinphi = P->e * sinphi;
59
14.2k
        const double one_minus_e_sinphi_sq = 1.0 - e_sinphi * e_sinphi;
60
61
        /* avoid zero division, fail gracefully */
62
14.2k
        if (one_minus_e_sinphi_sq == 0.0)
63
0
            return HUGE_VAL;
64
65
        // Snyder uses 0.5 * ln((1-e*sinphi)/(1+e*sinphi) which is
66
        // -atanh(e*sinphi)
67
14.2k
        return P->one_es *
68
14.2k
               (sinphi / one_minus_e_sinphi_sq + atanh(e_sinphi) / P->e);
69
14.2k
    } else
70
0
        return 2 * sinphi;
71
14.2k
}
72
73
/*****************************************************************************/
74
75
// Computes coefficients needed for conversions between geographic and authalic
76
// latitude.  These are preferred over the analytical expressions for |n| <
77
// 0.01.  However the inverse series is used to start the inverse method for
78
// large |n|.
79
80
// Ensure we use the cutoff in n consistently
81
20.3k
#define PROJ_AUTHALIC_SERIES_VALID(n) (fabs(n) < 0.01)
82
863
double *pj_authalic_lat_compute_coeffs(double n) {
83
863
    double *APA;
84
863
    constexpr int Lmax = int(AuxLat::ORDER);
85
863
    const int APA_SIZE = Lmax * (PROJ_AUTHALIC_SERIES_VALID(n) ? 2 : 1);
86
863
    if ((APA = (double *)malloc(APA_SIZE * sizeof(double))) != nullptr) {
87
863
        pj_auxlat_coeffs(n, AuxLat::AUTHALIC, AuxLat::GEOGRAPHIC, APA);
88
863
        if (PROJ_AUTHALIC_SERIES_VALID(n))
89
831
            pj_auxlat_coeffs(n, AuxLat::GEOGRAPHIC, AuxLat::AUTHALIC,
90
831
                             APA + Lmax);
91
863
    }
92
863
    return APA;
93
863
}
94
95
/*****************************************************************************/
96
97
// Computes authalic latitude from the geographic latitude.
98
// qp is q at phi=90deg, i.e. qp = pj_authalic_lat_q(1, e, one_es)
99
double pj_authalic_lat(double phi, double sinphi, double cosphi,
100
18.5k
                       const double *APA, const PJ *P, double qp) {
101
18.5k
    if (PROJ_AUTHALIC_SERIES_VALID(P->n)) {
102
17.2k
        constexpr int Lmax = int(AuxLat::ORDER);
103
17.2k
        return pj_auxlat_convert(phi, sinphi, cosphi, APA + Lmax);
104
17.2k
    } else {
105
        // This result is ill-conditioned near the poles.  So don't use this if
106
        // the series are accurate.
107
1.34k
        const double q = pj_authalic_lat_q(sinphi, P);
108
1.34k
        double ratio = q / qp;
109
110
1.34k
        if (fabs(ratio) > 1) {
111
            /* Rounding error. */
112
0
            ratio = ratio > 0 ? 1 : -1;
113
0
        }
114
1.34k
        return asin(ratio);
115
1.34k
    }
116
18.5k
}
117
118
/*****************************************************************************/
119
120
// Compute the geographic latitude from beta = authalic_latitude
121
// where APA = pj_compute_coefficients_for_inverse_authalic_lat() and
122
// qp = pj_authalic_lat_q(1, P->e, P->one_es)
123
double pj_authalic_lat_inverse(double beta, const double *APA, const PJ *P,
124
0
                               double qp) {
125
0
    double phi = pj_auxlat_convert(beta, APA);
126
0
    if (PROJ_AUTHALIC_SERIES_VALID(P->n))
127
0
        return phi;
128
    // If the flattening is large, solve
129
    //   f(phi) = qp*sin(beta)/(1-e^2) - q(phi)/(1-e^2) = 0
130
    // for phi, using Newton's method, where
131
    //   q(phi)/(1-e^2) = sin(phi)/(1 - e^2*sin(phi)^2) + atanh(e*sin(phi))/e
132
    // and
133
    //   df(phi)/dphi = - dq(phi)/dphi / (1-e^2)
134
    //                = - 2 * (1-e^2) * cos(phi) / (1 - e^2*sinphi^2)^2
135
    // This is subject to large roundoff errors near the poles, so only use
136
    // this if the series isn't accurate.
137
0
    const double q = sin(beta) * qp;
138
0
    const double q_div_one_minus_es = q / P->one_es;
139
0
    for (int i = 0; i < 10; ++i) {
140
0
        const double sinphi = sin(phi);
141
0
        const double cosphi = cos(phi);
142
0
        const double one_minus_es_sin2phi = 1 - P->es * (sinphi * sinphi);
143
        // Snyder uses 0.5 * ln((1-e*sinphi)/(1+e*sinphi) which is
144
        // -atanh(e*sinphi)
145
0
        const double dphi =
146
0
            (one_minus_es_sin2phi * one_minus_es_sin2phi) / (2 * cosphi) *
147
0
            (q_div_one_minus_es - sinphi / one_minus_es_sin2phi -
148
0
             atanh(P->e * sinphi) / P->e);
149
0
        if (!(fabs(dphi) >= 1e-15))
150
0
            break;
151
0
        phi += dphi;
152
0
    }
153
0
    return phi;
154
0
}
155
#undef PROJ_AUTHALIC_SERIES_VALID
156
157
// The following routines pj_auxlat_coeffs, pj_polyvol, pj_clenshaw,
158
// pj_auxlat_convert (3 signatures) provide a uniform interface for converting
159
// between any pair of auxiliary latitudes using series expansions in the third
160
// flattening, n.  There are 6 (= AuxLat::NUMBER) auxiliary latitudes
161
// supported labeled by
162
//
163
//   AuxLat::GEOGRAPHIC for geographic latitude, phi
164
//   AuxLat::PARAMETRIC for parametric latitude, beta
165
//   AuxLat::GEOCENTRIC for geocentric latitude, theta
166
//   AuxLat::RECTIFYING for rectifying latitude, mu
167
//   AuxLat::CONFORMAL for conformal latitude, chi
168
//   AuxLat::AUTHALIC for authlatic latitude, xi
169
//
170
// This is adapted from
171
//
172
//   C. F. F. Karney, On auxiliary latitudes,
173
//   Survey Review 56, 165-180 (2024)
174
//   https://doi.org/10.1080/00396265.2023.2217604
175
//   Preprint: https://arxiv.org/abs/2212.05818
176
//
177
// The typical calling sequence is
178
//
179
//   constexpr int L = int(AuxLat::ORDER);
180
//   // Managing the memory for the coefficient array is the
181
//   // responsibility of the calling routine.
182
//   double F[L];
183
//   // Fill F[] with coefficients to convert conformal to geographic
184
//   pj_auxlat_coeffs(P->n, AuxLat::CONFORMAL, AuxLat::GEOGRAPHIC, F);
185
//   ...
186
//   double chi = 1;                 // known conformal latitude
187
//   // compute corresponding geographic latitude
188
//   double phi = pj_auxlat_convert(chi, F);
189
//
190
// The conversions are Fourier series in the auxiliary latitude where each
191
// coefficient is given as a Taylor series in n truncated at order 6 (=
192
// AuxLat::ORDER).  This suffices to give full double precision accuracy for
193
// |f| <= 1/150 and probably provide satisfactory results for |f| <= 1/50.  The
194
// coefficients for these Taylor series are given by matrices listed in
195
// Eqs. (A1-A28) of this paper.
196
//
197
// These coefficients are bundled up into a single array coeffs in
198
// pj_auxlat_coeffs.  Only the upper triangular portion of the matrices are
199
// included.  Furthermore, half the coefficients for the conversions between
200
// any of phi, bete, theta, and mu are zero (the Taylor series are expansions
201
// in n^2), these zero elements are excluded.
202
//
203
// The coefficient array, coeffs, is machine-generated by the Maxima code
204
// auxlat.mac bundled with GeographicLib.  To use
205
//
206
// * Ensure that Lmax (set near the top of the file) is set to 6 (=
207
//   AuxLat::ORDER).
208
// * run
209
//     $ maxima
210
//     Maxima 5.47.0 https://maxima.sourceforge.io
211
//     (%i1) load("auxlat.mac")$
212
//     (%i2) writecppproj()$
213
//     ....
214
//     #<CLOSED OUTPUT BUFFERED FILE-STREAM CHARACTER auxvalsproj6.cpp>
215
// * The results are in the file auxvalsproj6.cpp
216
//
217
// Only a subset of the conversion matrices are written out.  To add others,
218
// include them in the list "required" in writecppproj().  The conversions
219
// currently supported are
220
//
221
//   phi <-> mu for meridian distance
222
//   phi <-> chi for tmerc
223
//   phi <-> xi for authalic latitude conversions
224
//   chi <-> mu for tmerc
225
//
226
// Because all the matrices are concatenated together into a single array,
227
// coeff, an auxiliary array, ptrs, or length 37 = AUXNUMBER^2 + 1, is written
228
// out to give the starting point of any particular matrix.
229
//
230
// Input:
231
//   n -- the third flattening (a-b)/(a+b)
232
//   auxin, auxout -- compute the coefficients for converting auxin (zeta) to
233
//     auxout (eta).
234
// Output:
235
//   F -- F[eta,zeta] = C[eta,zeta] . P(n), where C is a matrix of constants
236
//     and P(n) = [n, n^2, n^3, ...]^T; the first AuxLat::ORDER elements of F
237
//     are filled.
238
15.6k
void pj_auxlat_coeffs(double n, AuxLat auxin, AuxLat auxout, double F[]) {
239
    // Generated by Maxima on 2025-03-23 19:13:00-04:00
240
15.6k
    constexpr int Lmax = 6;
241
15.6k
    static_assert(Lmax == int(AuxLat::ORDER),
242
15.6k
                  "Mismatch between maxima and AuxLat::ORDER");
243
15.6k
    static const double coeffs[] = {
244
        // C[phi,phi] skipped
245
        // C[phi,beta] skipped
246
        // C[phi,theta] skipped
247
        // C[phi,mu]; even coeffs only
248
15.6k
        3.0 / 2.0, -27.0 / 32.0, 269.0 / 512.0, 21.0 / 16.0, -55.0 / 32.0,
249
15.6k
        6759.0 / 4096.0, 151.0 / 96.0, -417.0 / 128.0, 1097.0 / 512.0,
250
15.6k
        -15543.0 / 2560.0, 8011.0 / 2560.0, 293393.0 / 61440.0,
251
        // C[phi,chi]
252
15.6k
        2.0, -2.0 / 3.0, -2.0, 116.0 / 45.0, 26.0 / 45.0, -2854.0 / 675.0,
253
15.6k
        7.0 / 3.0, -8.0 / 5.0, -227.0 / 45.0, 2704.0 / 315.0, 2323.0 / 945.0,
254
15.6k
        56.0 / 15.0, -136.0 / 35.0, -1262.0 / 105.0, 73814.0 / 2835.0,
255
15.6k
        4279.0 / 630.0, -332.0 / 35.0, -399572.0 / 14175.0, 4174.0 / 315.0,
256
15.6k
        -144838.0 / 6237.0, 601676.0 / 22275.0,
257
        // C[phi,xi]
258
15.6k
        4.0 / 3.0, 4.0 / 45.0, -16.0 / 35.0, -2582.0 / 14175.0,
259
15.6k
        60136.0 / 467775.0, 28112932.0 / 212837625.0, 46.0 / 45.0,
260
15.6k
        152.0 / 945.0, -11966.0 / 14175.0, -21016.0 / 51975.0,
261
15.6k
        251310128.0 / 638512875.0, 3044.0 / 2835.0, 3802.0 / 14175.0,
262
15.6k
        -94388.0 / 66825.0, -8797648.0 / 10945935.0, 6059.0 / 4725.0,
263
15.6k
        41072.0 / 93555.0, -1472637812.0 / 638512875.0, 768272.0 / 467775.0,
264
15.6k
        455935736.0 / 638512875.0, 4210684958.0 / 1915538625.0,
265
        // C[beta,phi] skipped
266
        // C[beta,beta] skipped
267
        // C[beta,theta] skipped
268
        // C[beta,mu] skipped
269
        // C[beta,chi] skipped
270
        // C[beta,xi] skipped
271
        // C[theta,phi] skipped
272
        // C[theta,beta] skipped
273
        // C[theta,theta] skipped
274
        // C[theta,mu] skipped
275
        // C[theta,chi] skipped
276
        // C[theta,xi] skipped
277
        // C[mu,phi]; even coeffs only
278
15.6k
        -3.0 / 2.0, 9.0 / 16.0, -3.0 / 32.0, 15.0 / 16.0, -15.0 / 32.0,
279
15.6k
        135.0 / 2048.0, -35.0 / 48.0, 105.0 / 256.0, 315.0 / 512.0,
280
15.6k
        -189.0 / 512.0, -693.0 / 1280.0, 1001.0 / 2048.0,
281
        // C[mu,beta] skipped
282
        // C[mu,theta] skipped
283
        // C[mu,mu] skipped
284
        // C[mu,chi]
285
15.6k
        1.0 / 2.0, -2.0 / 3.0, 5.0 / 16.0, 41.0 / 180.0, -127.0 / 288.0,
286
15.6k
        7891.0 / 37800.0, 13.0 / 48.0, -3.0 / 5.0, 557.0 / 1440.0,
287
15.6k
        281.0 / 630.0, -1983433.0 / 1935360.0, 61.0 / 240.0, -103.0 / 140.0,
288
15.6k
        15061.0 / 26880.0, 167603.0 / 181440.0, 49561.0 / 161280.0,
289
15.6k
        -179.0 / 168.0, 6601661.0 / 7257600.0, 34729.0 / 80640.0,
290
15.6k
        -3418889.0 / 1995840.0, 212378941.0 / 319334400.0,
291
        // C[mu,xi] skipped
292
        // C[chi,phi]
293
15.6k
        -2.0, 2.0 / 3.0, 4.0 / 3.0, -82.0 / 45.0, 32.0 / 45.0, 4642.0 / 4725.0,
294
15.6k
        5.0 / 3.0, -16.0 / 15.0, -13.0 / 9.0, 904.0 / 315.0, -1522.0 / 945.0,
295
15.6k
        -26.0 / 15.0, 34.0 / 21.0, 8.0 / 5.0, -12686.0 / 2835.0, 1237.0 / 630.0,
296
15.6k
        -12.0 / 5.0, -24832.0 / 14175.0, -734.0 / 315.0, 109598.0 / 31185.0,
297
15.6k
        444337.0 / 155925.0,
298
        // C[chi,beta] skipped
299
        // C[chi,theta] skipped
300
        // C[chi,mu]
301
15.6k
        -1.0 / 2.0, 2.0 / 3.0, -37.0 / 96.0, 1.0 / 360.0, 81.0 / 512.0,
302
15.6k
        -96199.0 / 604800.0, -1.0 / 48.0, -1.0 / 15.0, 437.0 / 1440.0,
303
15.6k
        -46.0 / 105.0, 1118711.0 / 3870720.0, -17.0 / 480.0, 37.0 / 840.0,
304
15.6k
        209.0 / 4480.0, -5569.0 / 90720.0, -4397.0 / 161280.0, 11.0 / 504.0,
305
15.6k
        830251.0 / 7257600.0, -4583.0 / 161280.0, 108847.0 / 3991680.0,
306
15.6k
        -20648693.0 / 638668800.0,
307
        // C[chi,chi] skipped
308
        // C[chi,xi] skipped
309
        // C[xi,phi]
310
15.6k
        -4.0 / 3.0, -4.0 / 45.0, 88.0 / 315.0, 538.0 / 4725.0,
311
15.6k
        20824.0 / 467775.0, -44732.0 / 2837835.0, 34.0 / 45.0, 8.0 / 105.0,
312
15.6k
        -2482.0 / 14175.0, -37192.0 / 467775.0, -12467764.0 / 212837625.0,
313
15.6k
        -1532.0 / 2835.0, -898.0 / 14175.0, 54968.0 / 467775.0,
314
15.6k
        100320856.0 / 1915538625.0, 6007.0 / 14175.0, 24496.0 / 467775.0,
315
15.6k
        -5884124.0 / 70945875.0, -23356.0 / 66825.0, -839792.0 / 19348875.0,
316
15.6k
        570284222.0 / 1915538625.0,
317
        // C[xi,beta] skipped
318
        // C[xi,theta] skipped
319
        // C[xi,mu] skipped
320
        // C[xi,chi] skipped
321
        // C[xi,xi] skipped
322
15.6k
    };
323
15.6k
    static constexpr int ptrs[] = {
324
15.6k
        0,   0,   0,   0,   12,  33,  54,  54,  54,  54,  54,  54, 54,
325
15.6k
        54,  54,  54,  54,  54,  54,  66,  66,  66,  66,  87,  87, 108,
326
15.6k
        108, 108, 129, 129, 129, 150, 150, 150, 150, 150, 150,
327
15.6k
    };
328
15.6k
    static_assert(sizeof(ptrs) / sizeof(int) ==
329
15.6k
                      int(AuxLat::NUMBER) * int(AuxLat::NUMBER) + 1,
330
15.6k
                  "Mismatch in size of ptrs array");
331
15.6k
    static_assert(sizeof(coeffs) / sizeof(double) ==
332
15.6k
                      ptrs[int(AuxLat::NUMBER) * int(AuxLat::NUMBER)],
333
15.6k
                  "Mismatch in size of coeffs array");
334
15.6k
    if (!(auxin >= AuxLat(0) && auxin < AuxLat::NUMBER && auxout >= AuxLat(0) &&
335
15.6k
          auxout < AuxLat::NUMBER))
336
0
        throw std::out_of_range("Bad specification for auxiliary latitude");
337
15.6k
    int k = int(AuxLat::NUMBER) * int(auxout) + int(auxin), o = ptrs[k];
338
15.6k
    if (o == ptrs[k + 1])
339
0
        throw std::out_of_range(
340
0
            "Unsupported conversion between auxiliary latitudes");
341
15.6k
    double d = n, n2 = n * n;
342
15.6k
    if (auxin <= AuxLat::RECTIFYING && auxout <= AuxLat::RECTIFYING) {
343
30.9k
        for (int l = 0; l < Lmax; ++l) {
344
26.4k
            int m = (Lmax - l - 1) / 2; // order of polynomial in n^2
345
26.4k
            F[l] = d * pj_polyval(n2, coeffs + o, m);
346
26.4k
            o += m + 1;
347
            // coverity[copy_paste_error]
348
26.4k
            d *= n;
349
26.4k
        }
350
11.2k
    } else {
351
78.6k
        for (int l = 0; l < Lmax; ++l) {
352
67.4k
            int m = (Lmax - l - 1); // order of polynomial in n
353
67.4k
            F[l] = d * pj_polyval(n, coeffs + o, m);
354
67.4k
            o += m + 1;
355
67.4k
            d *= n;
356
67.4k
        }
357
11.2k
    }
358
    // assert (o == ptrs[k+1]);
359
15.6k
}
360
361
// Evaluation sum(p[i] * x^i, i, 0, N) via Horner's method.  N.B. p is of
362
// length N+1.
363
98.5k
double pj_polyval(double x, const double p[], int N) {
364
98.5k
    double y = N < 0 ? 0 : p[N];
365
307k
    for (; N > 0;)
366
208k
        y = y * x + p[--N];
367
98.5k
    return y;
368
98.5k
}
369
370
// Evaluate y = sum(F[k] * sin((2*k+2) * zeta), k, 0, K-1) by Clenshaw
371
// summation.  zeta is specify by its sine and cosine, szeta and czeta.
372
237k
double pj_clenshaw(double szeta, double czeta, const double F[], int K) {
373
    // Approx operation count = (K + 5) mult and (2 * K + 2) add
374
237k
    double u0 = 0, u1 = 0,                         // accumulators for sum
375
237k
        X = 2 * (czeta - szeta) * (czeta + szeta); // 2 * cos(2*zeta)
376
1.66M
    for (; K > 0;) {
377
1.42M
        double t = X * u0 - u1 + F[--K];
378
1.42M
        u1 = u0;
379
1.42M
        u0 = t;
380
1.42M
    }
381
237k
    return 2 * szeta * czeta * u0; // sin(2*zeta) * u0
382
237k
}
383
384
// Convert auxiliary latitude zeta to eta, given coefficients F produced by
385
// pj_auxlats_coeffs(n, zeta, eta, F).  K is the size of F (defaults to
386
// AuxLat::ORDER).
387
197k
double pj_auxlat_convert(double zeta, const double F[], int K) {
388
197k
    return pj_auxlat_convert(zeta, sin(zeta), cos(zeta), F, K);
389
197k
}
390
391
// Convert auxiliary latitude zeta to eta, given coefficients F produced by
392
// pj_auxlats_coeffs(n, zeta, eta, F).  In this signature, szeta and czeta (the
393
// sine and cosine of zeta) are given as inputs.  K is the size of F (defaults
394
// to AuxLat::ORDER).
395
double pj_auxlat_convert(double zeta, double szeta, double czeta,
396
237k
                         const double F[], int K) {
397
237k
    return zeta + pj_clenshaw(szeta, czeta, F, K);
398
237k
}
399
400
// Convert auxiliary latitude zeta to eta, given coefficients F produced by
401
// pj_auxlats_coeffs(n, zeta, eta, F).  K is the size of F (defaults to
402
// AuxLat::ORDER).  In this signature, the sine and cosine of eta are returned.
403
// This provides high relative accuracy near the poles.
404
void pj_auxlat_convert(double szeta, double czeta, double &seta, double &ceta,
405
0
                       const double F[], int K) {
406
0
    double delta = pj_clenshaw(szeta, czeta, F, K), sdelta = sin(delta),
407
0
           cdelta = cos(delta);
408
0
    seta = szeta * cdelta + czeta * sdelta;
409
0
    ceta = czeta * cdelta - szeta * sdelta;
410
0
}
411
412
// Compute the rectifying radius = quarter meridian / (pi/2 * a).  The accuracy
413
// of this series is the same as those used for the computation of the
414
// auxiliary latitudes.
415
4.59k
double pj_rectifying_radius(double n) {
416
    // Expansion of (quarter meridian) / ((a+b)/2 * pi/2) as series in n^2;
417
    // these coefficients are ( (2*k - 3)!! / (2*k)!! )^2 for k = 0..3
418
4.59k
    static const double coeff_rad[] = {1, 1.0 / 4, 1.0 / 64, 1.0 / 256};
419
4.59k
    return pj_polyval(n * n, coeff_rad, 3) / (1 + n);
420
4.59k
}