69 lines
2.8 KiB
Go
69 lines
2.8 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integrate
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
// TestIntegrateFilonRejectsInfiniteBounds pins the refusal: an infinite
|
|
// bound used to fall into an unrepresentable panel count (the conversion
|
|
// of the ceiling of infinity) and come back as a quiet NaN pair with a
|
|
// nil error, and with k = 0 it sampled the amplitude at infinity. The
|
|
// error contract refuses NaN bounds and an infinite frequency; an
|
|
// infinite bound is the same breach and answers the same way.
|
|
func TestIntegrateFilonRejectsInfiniteBounds(t *testing.T) {
|
|
f := func(x float64) (float64, error) { return math.Exp(-x), nil }
|
|
cases := []struct {
|
|
label string
|
|
a, b, k float64
|
|
}{
|
|
{"upper tail", 0, math.Inf(1), 1},
|
|
{"lower tail", math.Inf(-1), 0, 1},
|
|
{"whole line", math.Inf(-1), math.Inf(1), 1},
|
|
{"zero frequency upper tail", 0, math.Inf(1), 0},
|
|
{"span product overflow", 0, 1e100, 1e300},
|
|
}
|
|
for _, c := range cases {
|
|
cos, sin, err := IntegrateFilon(f, c.a, c.b, c.k, FilonOptions{})
|
|
if err == nil {
|
|
t.Fatalf("%s: [%g, %g] at k=%g integrated to (%g, %g) with no error, want a refusal",
|
|
c.label, c.a, c.b, c.k, cos, sin)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestIntegrateFilonRejectsUnrepresentablePanels pins the two refusals
|
|
// the infinite-bound guard alone does not reach. A panel estimate that
|
|
// stays finite but sits beyond the int range converted to garbage: a
|
|
// wrapped negative count iterated zero times and answered a quiet zero,
|
|
// and a saturated count would iterate forever. And two finite bounds
|
|
// far enough apart that their span overflows leave an infinite panel
|
|
// width, where a constant amplitude stays finite and the carrier phase
|
|
// 0·Inf comes back as a quiet NaN pair. Both answer with the error
|
|
// contract instead.
|
|
func TestIntegrateFilonRejectsUnrepresentablePanels(t *testing.T) {
|
|
constant := func(float64) (float64, error) { return 1, nil }
|
|
cases := []struct {
|
|
label string
|
|
a, b, k float64
|
|
opts FilonOptions
|
|
f func(x float64) (float64, error)
|
|
}{
|
|
{"finite estimate beyond the int range", 0, 1e8, 1e300, FilonOptions{}, constant},
|
|
{"finite estimate just above the int range", 0, 1e18, 6e2, FilonOptions{}, constant},
|
|
{"overflowing span at zero frequency", -math.MaxFloat64, math.MaxFloat64, 0, FilonOptions{}, constant},
|
|
{"overflowing span with forced panels", -math.MaxFloat64, math.MaxFloat64, 0, FilonOptions{Panels: 2}, constant},
|
|
{"overflowing span with a frequency", -math.MaxFloat64, math.MaxFloat64, 1, FilonOptions{}, constant},
|
|
}
|
|
for _, c := range cases {
|
|
cos, sin, err := IntegrateFilon(c.f, c.a, c.b, c.k, c.opts)
|
|
if err == nil {
|
|
t.Fatalf("%s: [%g, %g] at k=%g integrated to (%g, %g) with no error, want a refusal",
|
|
c.label, c.a, c.b, c.k, cos, sin)
|
|
}
|
|
}
|
|
}
|