Passed
Push — master ( 4157e1...9ef2b1 )
by Doug
60:35
created

Geodesic::lambda()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 71
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 7
eloc 45
nc 16
nop 10
dl 0
loc 71
rs 8.2666
c 1
b 1
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord\Geometry;
10
11
use PHPCoord\CoordinateOperation\GeographicValue;
12
use PHPCoord\Datum\Ellipsoid;
13
use PHPCoord\UnitOfMeasure\Length\Length;
14
use PHPCoord\UnitOfMeasure\Length\Metre;
15
16
use function abs;
17
use function atan2;
18
use function cos;
19
use function count;
20
use function deg2rad;
21
use function fmod;
22
use function hypot;
23
use function intdiv;
24
use function max;
25
use function min;
26
use function range;
27
use function round;
28
use function sin;
29
use function sqrt;
30
31
use const M_PI;
32
use const PHP_FLOAT_EPSILON;
33
use const PHP_FLOAT_MIN;
34
35
/**
36
 * This is a cut-down and "PHP native" translation of the distance calculation code found inside GeographicLib,
37
 * Original author/copyright Charles Karney (2011-2022) released under MIT license.
38
 * https://geographiclib.sourceforge.io/.
39
 */
40
class Geodesic
41
{
42
    private float $a;
43
    private float $b;
44
    private float $f;
45
    private float $f1;
46
    private float $e2;
47
    private float $ep2;
48
    private float $n;
49
50
    /**
51
     * @var float[]
52
     */
53
    private array $a3Coefficients;
54
55
    /**
56
     * @var float[]
57
     */
58
    private array $c3Coefficients;
59
60
    public function __construct(Ellipsoid $ellipsoid)
61
    {
62
        $this->a = $ellipsoid->getSemiMajorAxis()->asMetres()->getValue();
63
        $this->b = $ellipsoid->getSemiMinorAxis()->asMetres()->getValue();
64
        $this->f = $ellipsoid->getFlattening();
65
        $this->f1 = 1 - $this->f;
66
        $this->e2 = $ellipsoid->getEccentricitySquared();
67
        $this->ep2 = $this->e2 / $this->f1 ** 2;
68
        $this->n = $this->f / (2 - $this->f);
69
        $this->a3Coefficients = $this->a3Coefficients();
70
        $this->c3Coefficients = $this->c3Coefficients();
71
    }
72
73
    /**
74
     * @return float[]
75
     */
76
    private function a3Coefficients(): array
77
    {
78
        $A3x = [];
79
        $coeff = [
80
            -3, 128,
81
            -2, -3, 64,
82
            -1, -3, -1, 16,
83
            3, -1, -2, 8,
84
            1, -1, 2,
85
            1, 1,
86
        ];
87
        $o = 0;
88
        $k = 0;
89
        foreach (range(5, 0, -1) as $j) {
90
            $m = min(5 - $j, $j);
91
            $A3x[$k] = polyVal($m, $coeff, $o, $this->n) / $coeff[$o + $m + 1];
92
            ++$k;
93
            $o += $m + 2;
94
        }
95
96
        return $A3x;
97
    }
98
99
    /**
100
     * @return float[]
101
     */
102
    private function c3Coefficients(): array
103
    {
104
        $C3x = [];
105
106
        $coeff = [
107
            3, 128,
108
            2, 5, 128,
109
            -1, 3, 3, 64,
110
            -1, 0, 1, 8,
111
            -1, 1, 4,
112
            5, 256,
113
            1, 3, 128,
114
            -3, -2, 3, 64,
115
            1, -3, 2, 32,
116
            7, 512,
117
            -10, 9, 384,
118
            5, -9, 5, 192,
119
            7, 512,
120
            -14, 7, 512,
121
            21, 2560,
122
        ];
123
        $o = 0;
124
        $k = 0;
125
        foreach (range(1, 5) as $l) {
126
            foreach (range(5, $l, -1) as $j) {
127
                $m = min(5 - $j, $j);
128
                $C3x[$k] = polyVal($m, $coeff, $o, $this->n) / $coeff[$o + $m + 1]; // @phpstan-ignore binaryOp.invalid
129
                ++$k;
130
                $o += $m + 2;
131
            }
132
        }
133
134
        return $C3x;
135
    }
136
137
    /**
138
     * @return float[]
139
     */
140
    private function c3(float $eps): array
141
    {
142
        $c = [];
143
        $mult = 1;
144
        $o = 0;
145
        foreach (range(1, 5) as $l) {
146
            $m = 5 - $l;
147
            $mult *= $eps;
148
            $c[$l] = $mult * polyVal($m, $this->c3Coefficients, $o, $eps);
149
            $o += $m + 1;
150
        }
151
152
        return $c;
153
    }
154
155
    /**
156
     * @return float[]
157
     */
158
    private function lengths(
159
        float $eps,
160
        float $sig12,
161
        float $ssig1,
162
        float $csig1,
163
        float $dn1,
164
        float $ssig2,
165
        float $csig2,
166
        float $dn2,
167
    ): array {
168
        $C1a = c1($eps);
169
        $C2a = c2($eps);
170
        $A1 = a1($eps);
171
        $A1 = 1 + $A1;
172
        $A2 = a2($eps);
173
        $A2 = 1 + $A2;
174
        $m0x = $A1 - $A2;
175
        $B1 = (sinCosSeries($ssig2, $csig2, $C1a) - sinCosSeries($ssig1, $csig1, $C1a));
176
        $B2 = (sinCosSeries($ssig2, $csig2, $C2a) - sinCosSeries($ssig1, $csig1, $C2a));
177
        $J12 = $m0x * $sig12 + ($A1 * $B1 - $A2 * $B2);
178
        $s12b = $A1 * ($sig12 + $B1);
179
        $m12b = ($dn2 * ($csig1 * $ssig2) - $dn1 * ($ssig1 * $csig2) - $csig1 * $csig2 * $J12);
180
181
        return [$s12b, $m12b];
182
    }
183
184
    /**
185
     * @return float[]
186
     */
187
    private function inverseStart(
188
        float $sbet1,
189
        float $cbet1,
190
        float $sbet2,
191
        float $cbet2,
192
        float $lam12,
193
        float $slam12,
194
        float $clam12,
195
    ): array {
196
        $sig12 = -1;
197
        $dnm = 1;
198
        $sbet12 = $sbet2 * $cbet1 - $cbet2 * $sbet1;
199
        $cbet12 = $cbet2 * $cbet1 + $sbet2 * $sbet1;
200
        $sbet12a = $sbet2 * $cbet1;
201
        $sbet12a += $cbet2 * $sbet1;
202
203
        $shortline = ($cbet12 >= 0 && $sbet12 < 0.5 && $cbet2 * $lam12 < 0.5);
204
        if ($shortline) {
205
            $sbetm2 = ($sbet1 + $sbet2) ** 2;
206
            $sbetm2 /= $sbetm2 + ($cbet1 + $cbet2) ** 2;
207
            $dnm = sqrt(1 + $this->ep2 * $sbetm2);
208
            $omg12 = $lam12 / ($this->f1 * $dnm);
209
            $somg12 = sin($omg12);
210
            $comg12 = cos($omg12);
211
        } else {
212
            $somg12 = $slam12;
213
            $comg12 = $clam12;
214
        }
215
        $salp1 = $cbet2 * $somg12;
216
        $calp1 = $comg12 >= 0 ?
217
          $sbet12 + $cbet2 * $sbet1 * $somg12 ** 2 / (1 + $comg12) : $sbet12a - $cbet2 * $sbet1 * $somg12 ** 2 / (1 - $comg12);
218
219
        $ssig12 = hypot($salp1, $calp1);
220
        $csig12 = $sbet1 * $sbet2 + $cbet1 * $cbet2 * $comg12;
221
222
        if ($shortline && $ssig12 < PHP_FLOAT_EPSILON) {
223
            $sig12 = atan2($ssig12, $csig12);
224
        } elseif (!(abs($this->n) >= 0.1 || $csig12 >= 0 || $ssig12 >= 6 * abs($this->n) * M_PI * $cbet1 ** 2)) {
225
            $lam12x = atan2(-$slam12, -$clam12);
226
            $k2 = $sbet1 ** 2 * $this->ep2;
227
            $eps = $k2 / (2 * (1 + sqrt(1 + $k2)) + $k2);
228
            $lamscale = $this->f * $cbet1 * polyVal(5, $this->a3Coefficients, 0, $eps) * M_PI;
229
            $betscale = $lamscale * $cbet1;
230
            $x = $lam12x / $lamscale;
231
            $y = $sbet12a / $betscale;
232
233
            if ($y > -PHP_FLOAT_EPSILON && $x > -1 - PHP_FLOAT_EPSILON) {
234
                $salp1 = min(1.0, -$x);
235
                $calp1 = -sqrt(1 - $salp1 ** 2);
236
            } else {
237
                $k = astroid($x, $y);
238
                $omg12a = $lamscale * ($this->f >= 0 ? -$x * $k / (1 + $k) : -$y * (1 + $k) / $k);
239
                $somg12 = sin($omg12a);
240
                $comg12 = -cos($omg12a);
241
                $salp1 = $cbet2 * $somg12;
242
                $calp1 = $sbet12a - $cbet2 * $sbet1 * $somg12 ** 2 / (1 - $comg12);
243
            }
244
        }
245
        if (!($salp1 <= 0)) {
246
            [$salp1, $calp1] = normalise($salp1, $calp1);
247
        } else {
248
            $salp1 = 1;
249
            $calp1 = 0;
250
        }
251
252
        return [$sig12, $salp1, $calp1, $dnm];
253
    }
254
255
    /**
256
     * @return float[]
257
     */
258
    private function lambda(
259
        float $sbet1,
260
        float $cbet1,
261
        float $dn1,
262
        float $sbet2,
263
        float $cbet2,
264
        float $dn2,
265
        float $salp1,
266
        float $calp1,
267
        float $slam120,
268
        float $clam120,
269
    ): array {
270
        if ($sbet1 == 0 && $calp1 == 0) {
271
            $calp1 = -PHP_FLOAT_MIN;
272
        }
273
274
        $salp0 = $salp1 * $cbet1;
275
        $calp0 = hypot($calp1, $salp1 * $sbet1);
276
277
        $ssig1 = $sbet1;
278
        $somg1 = $salp0 * $sbet1;
279
        $csig1 = $comg1 = $calp1 * $cbet1;
280
        [$ssig1, $csig1] = normalise($ssig1, $csig1);
281
282
        $calp2 = $cbet2 !== $cbet1 || abs($sbet2) !== -$sbet1 ?
283
            sqrt(($calp1 * $cbet1) ** 2 + ($cbet1 < -$sbet1 ?
284
                    ($cbet2 - $cbet1) * ($cbet1 + $cbet2) :
285
                    ($sbet1 - $sbet2) * ($sbet1 + $sbet2))) /
286
            $cbet2 : abs($calp1);
287
288
        $ssig2 = $sbet2;
289
        $somg2 = $salp0 * $sbet2;
290
        $csig2 = $comg2 = $calp2 * $cbet2;
291
        [$ssig2, $csig2] = normalise($ssig2, $csig2);
292
293
        $sig12 = atan2(
294
            max(0.0, $csig1 * $ssig2 - $ssig1 * $csig2),
295
            $csig1 * $csig2 + $ssig1 * $ssig2
296
        );
297
298
        $somg12 = max(0.0, $comg1 * $somg2 - $somg1 * $comg2);
299
        $comg12 = $comg1 * $comg2 + $somg1 * $somg2;
300
        $eta = atan2(
301
            $somg12 * $clam120 - $comg12 * $slam120,
302
            $comg12 * $clam120 + $somg12 * $slam120
303
        );
304
305
        $k2 = $calp0 ** 2 * $this->ep2;
306
        $eps = $k2 / (2 * (1 + sqrt(1 + $k2)) + $k2);
307
        $C3a = $this->c3($eps);
308
        $B312 = (sinCosSeries($ssig2, $csig2, $C3a) - sinCosSeries($ssig1, $csig1, $C3a));
309
        $domg12 = -$this->f * polyVal(5, $this->a3Coefficients, 0, $eps) * $salp0 * ($sig12 + $B312);
310
        $lam12 = $eta + $domg12;
311
312
        if ($calp2 == 0) {
313
            $dlam12 = -2 * $this->f1 * $dn1 / $sbet1;
314
        } else {
315
            [, $dlam12] = $this->lengths(
316
                $eps,
317
                $sig12,
318
                $ssig1,
319
                $csig1,
320
                $dn1,
321
                $ssig2,
322
                $csig2,
323
                $dn2,
324
            );
325
            $dlam12 *= $this->f1 / ($calp2 * $cbet2);
326
        }
327
328
        return [$lam12, $sig12, $ssig1, $csig1, $ssig2, $csig2, $eps, $dlam12];
329
    }
330
331
    public function distance(GeographicValue $from, GeographicValue $to): Length
332
    {
333
        $lon12 = abs(angleDiff($from->getLongitude()->asDegrees()->getValue(), $to->getLongitude()->asDegrees()->getValue()));
334
        $lam12 = deg2rad($lon12);
335
        [$slam12, $clam12] = sinCos($lon12);
336
        $lon12s = (180 - $lon12);
337
338
        $lat1 = $from->getLatitude()->asDegrees()->getValue();
339
        $lat2 = $to->getLatitude()->asDegrees()->getValue();
340
        $swapp = abs($lat1) < abs($lat2) ? -1 : 1;
341
        if ($swapp < 0) {
342
            [$lat2, $lat1] = [$lat1, $lat2];
343
        }
344
        $latsign = copySign(1, -$lat1);
345
        $lat1 *= $latsign;
346
        $lat2 *= $latsign;
347
348
        [$sbet1, $cbet1] = sinCos($lat1);
349
        $sbet1 *= $this->f1;
350
        [$sbet1, $cbet1] = normalise($sbet1, $cbet1);
351
        $cbet1 = max(PHP_FLOAT_MIN, $cbet1);
352
353
        [$sbet2, $cbet2] = sinCos($lat2);
354
        $sbet2 *= $this->f1;
355
        [$sbet2, $cbet2] = normalise($sbet2, $cbet2);
356
        $cbet2 = max(PHP_FLOAT_MIN, $cbet2);
357
358
        $dn1 = sqrt(1 + $this->ep2 * $sbet1 ** 2);
359
        $dn2 = sqrt(1 + $this->ep2 * $sbet2 ** 2);
360
361
        $meridian = $lat1 == -90 || $slam12 == 0;
362
363
        if ($meridian) {
364
            $calp1 = $clam12;
365
            $calp2 = 1.0;
366
367
            $ssig1 = $sbet1;
368
            $csig1 = $calp1 * $cbet1;
369
            $ssig2 = $sbet2;
370
            $csig2 = $calp2 * $cbet2;
371
372
            $sig12 = atan2(
373
                max(0.0, $csig1 * $ssig2 - $ssig1 * $csig2),
374
                $csig1 * $csig2 + $ssig1 * $ssig2
375
            );
376
377
            [$s12x] = $this->lengths(
378
                $this->n,
379
                $sig12,
380
                $ssig1,
381
                $csig1,
382
                $dn1,
383
                $ssig2,
384
                $csig2,
385
                $dn2,
386
            );
387
        } elseif ($sbet1 == 0 && $lon12s >= $this->f * 180) {
388
            $s12x = $this->a * $lam12;
389
        } else {
390
            [$sig12, $salp1, $calp1, $dnm] = $this->inverseStart(
391
                $sbet1,
392
                $cbet1,
393
                $sbet2,
394
                $cbet2,
395
                $lam12,
396
                $slam12,
397
                $clam12,
398
            );
399
400
            if ($sig12 >= 0) {
401
                $s12x = $sig12 * $this->b * $dnm;
402
            } else {
403
                $numit = 0;
404
                $tripn = $tripb = false;
405
                $salp1a = PHP_FLOAT_MIN;
406
                $calp1a = 1.0;
407
                $salp1b = PHP_FLOAT_MIN;
408
                $calp1b = -1.0;
409
410
                while ($numit < 20) {
411
                    [$v, $sig12, $ssig1, $csig1, $ssig2, $csig2,
412
                        $eps, $dv] = $this->lambda(
413
                            $sbet1,
414
                            $cbet1,
415
                            $dn1,
416
                            $sbet2,
417
                            $cbet2,
418
                            $dn2,
419
                            $salp1,
420
                            $calp1,
421
                            $slam12,
422
                            $clam12,
423
                        );
424
                    if ($tripb || !(abs($v) >= ($tripn ? 8 : 1) * PHP_FLOAT_EPSILON)) {
425
                        break;
426
                    }
427
                    if ($v > 0 && ($calp1 / $salp1 > $calp1b / $salp1b)) {
428
                        $salp1b = $salp1;
429
                        $calp1b = $calp1;
430
                    } elseif ($v < 0 && ($calp1 / $salp1 < $calp1a / $salp1a)) {
431
                        $salp1a = $salp1;
432
                        $calp1a = $calp1;
433
                    }
434
435
                    ++$numit;
436
                    if ($dv > 0) {
437
                        $dalp1 = -$v / $dv;
438
                        $sdalp1 = sin($dalp1);
439
                        $cdalp1 = cos($dalp1);
440
                        $nsalp1 = $salp1 * $cdalp1 + $calp1 * $sdalp1;
441
                        if ($nsalp1 > 0 && abs($dalp1) < M_PI) {
442
                            $calp1 = $calp1 * $cdalp1 - $salp1 * $sdalp1;
443
                            $salp1 = $nsalp1;
444
                            [$salp1, $calp1] = normalise($salp1, $calp1);
445
                            $tripn = abs($v) <= 16 * PHP_FLOAT_EPSILON;
446
                            continue;
447
                        }
448
                    }
449
                    $salp1 = ($salp1a + $salp1b) / 2;
450
                    $calp1 = ($calp1a + $calp1b) / 2;
451
                    [$salp1, $calp1] = normalise($salp1, $calp1);
452
                    $tripn = false;
453
                    $tripb = (abs($salp1a - $salp1) + ($calp1a - $calp1) < PHP_FLOAT_EPSILON
454
                        || abs($salp1 - $salp1b) + ($calp1 - $calp1b) < PHP_FLOAT_EPSILON);
455
                }
456
                [$s12x] = $this->lengths(
457
                    $eps,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eps does not seem to be defined for all execution paths leading up to this point.
Loading history...
458
                    $sig12,
459
                    $ssig1,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ssig1 does not seem to be defined for all execution paths leading up to this point.
Loading history...
460
                    $csig1,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $csig1 does not seem to be defined for all execution paths leading up to this point.
Loading history...
461
                    $dn1,
462
                    $ssig2,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ssig2 does not seem to be defined for all execution paths leading up to this point.
Loading history...
463
                    $csig2,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $csig2 does not seem to be defined for all execution paths leading up to this point.
Loading history...
464
                    $dn2,
465
                );
466
467
                $s12x *= $this->b;
468
            }
469
        }
470
471
        return new Metre($s12x);
472
    }
473
}
474
475
function copySign(float $x, float $y): float
476
{
477
    if ($y >= 0) {
478
        return abs($x);
479
    } else {
480
        return -abs($x);
481
    }
482
}
483
484
/**
485
 * @return float[]
486
 */
487
function normalise(float $x, float $y): array
488
{
489
    $r = hypot($x, $y);
490
491
    return [$x / $r, $y / $r];
492
}
493
494
/**
495
 * @param float[] $p
496
 */
497
function polyVal(int $N, array $p, int $s, float $x): float
498
{
499
    $y = $p[$s];
500
    while ($N > 0) {
501
        --$N;
502
        ++$s;
503
        $y = $y * $x + $p[$s];
504
    }
505
506
    return $y;
507
}
508
509
function angleDiff(float $x, float $y): float
510
{
511
    $d = ($y - $x);
512
    if ($d < -180) {
513
        $d += 360;
514
    }
515
516
    return $d;
517
}
518
519
/**
520
 * @return float[]
521
 */
522
function sinCos(float $x): array
523
{
524
    $r = fmod($x, 360);
525
    $q = (int) round($r / 90);
526
    $r -= 90 * $q;
527
    $r = deg2rad($r);
528
    $s = sin($r);
529
    $c = cos($r);
530
    $q %= 4;
531
    if ($q < 0) {
532
        $q += 4;
533
    }
534
    if ($q == 1) {
535
        [$s, $c] = [$c, -$s];
536
    } elseif ($q == 2) {
537
        [$s, $c] = [-$s, -$c];
538
    } elseif ($q == 3) {
539
        [$s, $c] = [-$c, $s];
540
    }
541
542
    return [$s, $c];
543
}
544
545
/**
546
 * @param float[] $c
547
 */
548
function sinCosSeries(float $sinx, float $cosx, array $c): float
549
{
550
    $k = count($c);
551
    $n = $k - 1;
552
    $ar = 2 * ($cosx - $sinx) * ($cosx + $sinx);
553
    $y1 = 0;
554
    if ($n & 1) {
555
        --$k;
556
        $y0 = $c[$k];
557
    } else {
558
        $y0 = 0;
559
    }
560
    $n = intdiv($n, 2);
561
    while ($n--) {
562
        --$k;
563
        $y1 = $ar * $y0 - $y1 + $c[$k];
564
        --$k;
565
        $y0 = $ar * $y1 - $y0 + $c[$k];
566
    }
567
568
    return 2 * $sinx * $cosx * $y0;
569
}
570
571
function astroid(float $x, float $y): float
572
{
573
    $p = $x ** 2;
574
    $q = $y ** 2;
575
    $r = ($p + $q - 1) / 6;
576
    if (!($q == 0 && $r <= 0)) {
577
        $S = $p * $q / 4;
578
        $r2 = $r ** 2;
579
        $r3 = $r * $r2;
580
        $disc = $S * ($S + 2 * $r3);
581
        $u = $r;
582
        if ($disc >= 0) {
583
            $T3 = $S + $r3;
584
            $T3 += $T3 < 0 ? -sqrt($disc) : sqrt($disc);
585
            $T = ($T3 ** (1 / 3));
586
            $u += $T != 0 ? ($T + ($r2 / $T)) : 0;
587
        } else {
588
            $ang = atan2(sqrt(-$disc), -($S + $r3));
589
            $u += 2 * $r * cos($ang / 3);
590
        }
591
        $v = sqrt($u ** 2 + $q);
592
        $uv = $u < 0 ? ($q / ($v - $u)) : $u + $v;
593
        $w = ($uv - $q) / (2 * $v);
594
        $k = $uv / (sqrt($uv + $w ** 2) + $w);
595
    } else {
596
        $k = 0;
597
    }
598
599
    return $k;
600
}
601
602
function a1(float $eps): float
603
{
604
    $coeff = [
605
        1, 4, 64, 0, 256,
606
    ];
607
    $t = polyVal(3, $coeff, 0, $eps ** 2) / 256;
608
609
    return ($t + $eps) / (1 - $eps);
610
}
611
612
/**
613
 * @return float[]
614
 */
615
function c1(float $eps): array
616
{
617
    $c = [];
618
    $coeff = [
619
        -1, 6, -16, 32,
620
        -9, 64, -128, 2048,
621
        9, -16, 768,
622
        3, -5, 512,
623
        -7, 1280,
624
        -7, 2048,
625
    ];
626
    $d = $eps;
627
    $o = 0;
628
    foreach (range(1, 6) as $l) {
629
        $m = intdiv(6 - $l, 2);
630
        $c[$l] = $d * polyVal($m, $coeff, $o, $eps ** 2) / $coeff[$o + $m + 1];
631
        $o += $m + 2;
632
        $d *= $eps;
633
    }
634
635
    return $c;
636
}
637
638
function a2(float $eps): float
639
{
640
    $coeff = [
641
        -11, -28, -192, 0, 256,
642
    ];
643
    $t = polyVal(3, $coeff, 0, $eps ** 2) / $coeff[4];
644
645
    return ($t - $eps) / (1 + $eps);
646
}
647
648
/**
649
 * @return float[]
650
 */
651
function c2(float $eps): array
652
{
653
    $c = [];
654
    $coeff = [
655
        1, 2, 16, 32,
656
        35, 64, 384, 2048,
657
        15, 80, 768,
658
        7, 35, 512,
659
        63, 1280,
660
        77, 2048,
661
    ];
662
    $d = $eps;
663
    $o = 0;
664
    foreach (range(1, 6) as $l) {
665
        $m = intdiv(6 - $l, 2);
666
        $c[$l] = $d * polyVal($m, $coeff, $o, $eps ** 2) / $coeff[$o + $m + 1];
667
        $o += $m + 2;
668
        $d *= $eps;
669
    }
670
671
    return $c;
672
}
673