package odometry

import "math"

// fft is an in-place iterative radix-2 Cooley-Tukey FFT.
// len(a) must be a power of two. If inverse is true the result is normalized
// by 1/n (so ifft(fft(x)) == x).
func fft(a []complex128, inverse bool) {
	n := len(a)
	if n <= 1 {
		return
	}

	// Bit-reversal permutation.
	for i, j := 1, 0; i < n; i++ {
		bit := n >> 1
		for ; j&bit != 0; bit >>= 1 {
			j ^= bit
		}
		j ^= bit
		if i < j {
			a[i], a[j] = a[j], a[i]
		}
	}

	// Butterfly stages.
	for length := 2; length <= n; length <<= 1 {
		// Standard forward DFT uses exp(-i*2π/length); inverse negates.
		angle := -2 * math.Pi / float64(length)
		if inverse {
			angle = -angle
		}
		wlen := complex(math.Cos(angle), math.Sin(angle))
		half := length / 2
		for i := 0; i < n; i += length {
			var w complex128 = 1
			for k := 0; k < half; k++ {
				u := a[i+k]
				v := a[i+k+half] * w
				a[i+k] = u + v
				a[i+k+half] = u - v
				w *= wlen
			}
		}
	}

	if inverse {
		invN := complex(1/float64(n), 0)
		for i := range a {
			a[i] *= invN
		}
	}
}

// fftRows applies a 1-D FFT to every row of m (in place).
func fftRows(m [][]complex128, inverse bool) {
	for i := range m {
		fft(m[i], inverse)
	}
}

// fftCols applies a 1-D FFT to every column of m (in place).
func fftCols(m [][]complex128, inverse bool) {
	rows := len(m)
	if rows == 0 {
		return
	}
	cols := len(m[0])
	col := make([]complex128, rows)
	for j := 0; j < cols; j++ {
		for i := 0; i < rows; i++ {
			col[i] = m[i][j]
		}
		fft(col, inverse)
		for i := 0; i < rows; i++ {
			m[i][j] = col[i]
		}
	}
}

// fft2 performs a forward 2-D FFT on the rows×cols complex matrix m.
func fft2(m [][]complex128, inverse bool) {
	fftRows(m, inverse)
	fftCols(m, inverse)
}
