// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: MIT package signal import ( "strings" "testing" "sourcedock.dev/petrbalvin/tensor/internal/core" ) // Complex-refusal pins: complex inputs that reached FloatAt's nil-int // branch, degenerate kernels and empty windows that answered NaN, and // contract gaps the sibling entry points had already closed. // TestResampleComplexRefusal: Resample and Decimate widened a complex // series through widenFloats, which panicked instead of refusing. func TestResampleComplexRefusal(t *testing.T) { c := mustComplexes(t, []complex128{1 + 1i, 2, 3 - 1i, 4}, 4) if _, err := Resample(c, 2, 1, 0); err == nil || !strings.Contains(err.Error(), "complex") { t.Fatalf("Resample on complex: err = %v", err) } if _, err := Decimate(c, 2, 0); err == nil || !strings.Contains(err.Error(), "complex") { t.Fatalf("Decimate on complex: err = %v", err) } } // TestNUFFTComplexCoordinates: complex coordinates reached FloatAt's // nil-int branch and panicked. func TestNUFFTComplexCoordinates(t *testing.T) { x := mustComplexes(t, []complex128{0.1 + 0.2i, -0.2}, 2) c := mustComplexes(t, []complex128{1, 2i}, 2) if _, err := NUFFTType1(x, c, 8); err == nil || !strings.Contains(err.Error(), "real") { t.Fatalf("NUFFTType1 with complex coordinates: err = %v", err) } } // TestAnalyticSignalComplexRefusal: the analytic signal is defined for // a real series; a complex one was silently transformed. func TestAnalyticSignalComplexRefusal(t *testing.T) { c := mustComplexes(t, []complex128{1, 2, 3, 4}, 4) if _, err := AnalyticSignal(c); err == nil || !strings.Contains(err.Error(), "complex") { t.Fatalf("AnalyticSignal on complex: err = %v", err) } } // TestConvComplexBiasRefusal: the convolutions gate the input and the // kernel on dtype but widened the bias through FloatAt, whose complex // branch reads a nil payload and panics. A complex bias is refused // like a complex kernel. func TestConvComplexBiasRefusal(t *testing.T) { bias := mustComplexes(t, []complex128{1 + 2i}, 1) in2 := mustFloats(t, []float64{1, 2, 3, 4, 5, 6, 7, 8}, 1, 1, 2, 4) ker2 := mustFloats(t, []float64{1, 0, 0, 1}, 1, 1, 2, 2) in3 := mustFloats(t, []float64{1, 2, 3, 4, 5, 6, 7, 8}, 1, 1, 2, 2, 2) ker3 := mustFloats(t, []float64{1, 1, 1, 1, 1, 1, 1, 1}, 1, 1, 2, 2, 2) in1 := mustFloats(t, []float64{1, 2, 3, 4}, 1, 1, 4) ker1 := mustFloats(t, []float64{1, 1}, 1, 1, 2) for name, fn := range map[string]func() (*core.Array, error){ "Conv1D": func() (*core.Array, error) { return Conv1D(in1, ker1, bias, 1, 0, 1) }, "Conv2D": func() (*core.Array, error) { return Conv2D(in2, ker2, bias, 1, 0) }, "Conv3D": func() (*core.Array, error) { return Conv3D(in3, ker3, bias, 1, [3]int{0, 0, 0}, [3]int{1, 1, 1}) }, "ConvTranspose2D": func() (*core.Array, error) { return ConvTranspose2D(in2, ker2, bias, 1, 0) }, } { if _, err := fn(); err == nil || !strings.Contains(err.Error(), "complex bias") { t.Fatalf("%s with a complex bias: err = %v", name, err) } } } // TestDecimateOneTap: taps = 1 divided by taps−1 = 0 in the Kaiser // window and every output sample came back NaN; a one-tap kernel is // the identity, so decimation keeps every factor-th sample. func TestDecimateOneTap(t *testing.T) { x := mustFromFloats(t, []float64{10, 11, 12, 13, 14, 15, 16, 17}, 8) out, err := Decimate(x, 2, 1) if err != nil { t.Fatalf("Decimate taps=1: %v", err) } if out.Len() != 4 { t.Fatalf("output length %d, want 4", out.Len()) } for i := range 4 { if got := out.FloatAt(i); got != float64(10+2*i) { t.Fatalf("out[%d] = %v, want %d", i, got, 10+2*i) } } } // TestAdaptivePoolEmptySpatial: a zero-length spatial dimension left // every window empty and max answered -Inf while avg divided by zero; // both are errors now, in every rank. func TestAdaptivePoolEmptySpatial(t *testing.T) { empty1D, err := core.Zeros(core.Float, 1, 1, 0) if err != nil { t.Fatal(err) } empty2D, err := core.Zeros(core.Float, 1, 1, 0, 4) if err != nil { t.Fatal(err) } empty3D, err := core.Zeros(core.Float, 1, 1, 2, 0, 4) if err != nil { t.Fatal(err) } for name, fn := range map[string]func() (*core.Array, error){ "max1D": func() (*core.Array, error) { return AdaptiveMaxPool1D(empty1D, 2) }, "max2D": func() (*core.Array, error) { return AdaptiveMaxPool2D(empty2D, 2, 2) }, "max3D": func() (*core.Array, error) { return AdaptiveMaxPool3D(empty3D, 2, 2, 2) }, "avg1D": func() (*core.Array, error) { return AdaptiveAvgPool1D(empty1D, 2) }, "avg2D": func() (*core.Array, error) { return AdaptiveAvgPool2D(empty2D, 2, 2) }, "avg3D": func() (*core.Array, error) { return AdaptiveAvgPool3D(empty3D, 2, 2, 2) }, } { if _, err := fn(); err == nil || !strings.Contains(err.Error(), "spatial") { t.Fatalf("%s on an empty spatial dimension: err = %v", name, err) } } }