199 lines
3.9 KiB
Go
199 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"math"
|
|
"math/cmplx"
|
|
"testing"
|
|
)
|
|
|
|
func TestFFTBasic(t *testing.T) {
|
|
// Test with simple data
|
|
data := []complex128{
|
|
complex(1, 0),
|
|
complex(2, 0),
|
|
complex(3, 0),
|
|
complex(4, 0),
|
|
}
|
|
|
|
result := FFT(data)
|
|
|
|
// Check that result has same length
|
|
if len(result) != len(data) {
|
|
t.Errorf("FFT result length %d, expected %d", len(result), len(data))
|
|
}
|
|
|
|
// Check that result is not all zeros
|
|
allZero := true
|
|
for _, val := range result {
|
|
if cmplx.Abs(val) > 1e-10 {
|
|
allZero = false
|
|
break
|
|
}
|
|
}
|
|
if allZero {
|
|
t.Error("FFT result is all zeros")
|
|
}
|
|
}
|
|
|
|
func TestFFTPowerOfTwo(t *testing.T) {
|
|
// Test with non-power-of-2 length
|
|
data := []complex128{
|
|
complex(1, 0),
|
|
complex(2, 0),
|
|
complex(3, 0),
|
|
complex(4, 0),
|
|
complex(5, 0),
|
|
}
|
|
|
|
result := FFT(data)
|
|
|
|
// Should be padded to next power of 2 (8)
|
|
expectedLen := 8
|
|
if len(result) != expectedLen {
|
|
t.Errorf("FFT result length %d, expected %d", len(result), expectedLen)
|
|
}
|
|
}
|
|
|
|
func TestIFFT(t *testing.T) {
|
|
// Test that IFFT(FFT(data)) ≈ data
|
|
data := []complex128{
|
|
complex(1, 0),
|
|
complex(2, 0),
|
|
complex(3, 0),
|
|
complex(4, 0),
|
|
}
|
|
|
|
fftResult := FFT(data)
|
|
ifftResult := IFFT(fftResult)
|
|
|
|
// Check that IFFT recovers original data (within numerical precision)
|
|
tolerance := 1e-10
|
|
for i, original := range data {
|
|
recovered := ifftResult[i]
|
|
diff := cmplx.Abs(original - recovered)
|
|
if diff > tolerance {
|
|
t.Errorf("IFFT recovery failed at index %d: original=%v, recovered=%v, diff=%v",
|
|
i, original, recovered, diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFFTComplexData(t *testing.T) {
|
|
// Test with complex input data
|
|
data := []complex128{
|
|
complex(1, 1),
|
|
complex(2, -1),
|
|
complex(-3, 2),
|
|
complex(4, 0),
|
|
}
|
|
|
|
result := FFT(data)
|
|
|
|
// Check that result has same length
|
|
if len(result) != len(data) {
|
|
t.Errorf("FFT result length %d, expected %d", len(result), len(data))
|
|
}
|
|
|
|
// Check that result is not all zeros
|
|
allZero := true
|
|
for _, val := range result {
|
|
if cmplx.Abs(val) > 1e-10 {
|
|
allZero = false
|
|
break
|
|
}
|
|
}
|
|
if allZero {
|
|
t.Error("FFT result is all zeros")
|
|
}
|
|
}
|
|
|
|
func TestFFTEmpty(t *testing.T) {
|
|
// Test with empty slice
|
|
var data []complex128
|
|
result := FFT(data)
|
|
|
|
if len(result) != 0 {
|
|
t.Errorf("FFT of empty slice should return empty slice, got length %d", len(result))
|
|
}
|
|
}
|
|
|
|
func TestFFTSingle(t *testing.T) {
|
|
// Test with single element
|
|
data := []complex128{complex(5, 3)}
|
|
result := FFT(data)
|
|
|
|
if len(result) != 1 {
|
|
t.Errorf("FFT of single element should return single element, got length %d", len(result))
|
|
}
|
|
|
|
// Single element FFT should return the same value
|
|
if cmplx.Abs(result[0]-data[0]) > 1e-10 {
|
|
t.Errorf("FFT of single element should return same value, got %v, expected %v",
|
|
result[0], data[0])
|
|
}
|
|
}
|
|
|
|
func TestFFTMathematical(t *testing.T) {
|
|
// Test with mathematical properties of FFT
|
|
// FFT of [1, 0, 0, 0] should be [1, 1, 1, 1]
|
|
data := []complex128{
|
|
complex(1, 0),
|
|
complex(0, 0),
|
|
complex(0, 0),
|
|
complex(0, 0),
|
|
}
|
|
|
|
result := FFT(data)
|
|
|
|
// All elements should be approximately 1
|
|
tolerance := 1e-10
|
|
for i, val := range result {
|
|
if cmplx.Abs(val-complex(1, 0)) > tolerance {
|
|
t.Errorf("FFT of impulse should be all ones, got %v at index %d", val, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkFFT(b *testing.B) {
|
|
// Benchmark with power of 2 size
|
|
size := 1024
|
|
data := make([]complex128, size)
|
|
for i := range data {
|
|
data[i] = complex(float64(i), float64(i%10))
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
FFT(data)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFFTLarge(b *testing.B) {
|
|
// Benchmark with larger size
|
|
size := 4096
|
|
data := make([]complex128, size)
|
|
for i := range data {
|
|
data[i] = complex(float64(i), float64(i%10))
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
FFT(data)
|
|
}
|
|
}
|
|
|
|
func BenchmarkIFFT(b *testing.B) {
|
|
// Benchmark IFFT
|
|
size := 1024
|
|
data := make([]complex128, size)
|
|
for i := range data {
|
|
data[i] = complex(float64(i), float64(i%10))
|
|
}
|
|
|
|
fftResult := FFT(data)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
IFFT(fftResult)
|
|
}
|
|
} |