Files
mercury/extras/complex_numbers/samples/fft.m
Julien Fischer 8c7beeccdc Update programming style in extras/complex_numbers.
extras/complex_numbers/*/*.m:
    As above.
2022-01-18 16:23:55 +11:00

154 lines
4.4 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
%
% File: fft.m.
% Main author: conway.
% Date: August 1996.
%
% This source file is hereby placed in the public domain.
% -conway (the author).
%
% This module provides a predicate for performing the Fast Fourier Transform
% (fft) on a list of complex numbers. The list must be a power of 2 in length
% (ie 4, 8, 16, ... elements).
%
% The algorithm used here is derived from a few sources:
% - "Numerical Recipes in C": Press, et al
% - "Elements of Computer Music": Moore
% - Lecture notes from 433-325 "Mathematical Software B": Dr Rex Harris
%
% It actually took the combination of these sources for me to uncover the
% algorithm for combining the component transforms. This code is not maximally
% efficient, but rather is intended as a clear presentation of the FFT
% algorithm.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module fft.
:- interface.
:- import_module io.
%-----------------------------------------------------------------------------%
:- pred main(io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module complex_numbers.
:- import_module complex_numbers.complex.
:- import_module complex_numbers.float_imag.
:- import_module complex_numbers.imag.
:- import_module float.
:- import_module int.
:- import_module list.
:- import_module math.
:- import_module require.
%-----------------------------------------------------------------------------%
main(!IO) :-
Zero = 0.0 + 0.0 * i,
One = 1.0 + 0.0 * i,
fft([One, Zero, One, Zero], T),
io.write_line(T, !IO).
:- pred fft(list(complex)::in, list(complex)::out) is det.
fft(Ins, Outs) :-
% First put the list into bit-reversed order.
bit_rev(Ins, Shuffle),
list.length(Shuffle, NInt),
int.log2(NInt, R),
N = float(NInt),
% Now recombine the component transforms.
combine(N, 1.0, R, Shuffle, Outs).
:- pred bit_rev(list(T)::in, list(T)::out) is det.
bit_rev([], []).
bit_rev([X], [X]).
bit_rev([X1, X2 | Xs], List) :-
split([X1, X2 | Xs], List1, List2),
bit_rev(List1, List3),
bit_rev(List2, List4),
list.append(List3, List4, List).
:- pred split(list(T)::in, list(T)::out, list(T)::out) is det.
split([], [], []).
split([_], _, _) :-
error("Input sequence not a power of two in length").
split([X1, X2|Xs], [X1|Ys], [X2|Zs]) :-
split(Xs, Ys, Zs).
:- pred combine(float::in, float::in, int::in,
list(complex)::in, list(complex)::out) is det.
combine(N, K, R, Ins, Outs) :-
( if
R = 0
then
Outs = Ins
else
R1 = R - 1,
L = 1 << R1,
% Split the list in half.
divide(L, Ins, Fs0, Ss0),
K2 = K * 2.0,
% Now combine the transforms in the respective halves.
combine(N, K2, R1, Fs0, Fs),
combine(N, K2, R1, Ss0, Ss),
% Now perform the 'butterfly'.
xform(Fs, Ss, complex(1.0), w(K, N), Rs0, Rs1),
list.append(Rs0, Rs1, Outs)
).
:- pred xform(list(complex)::in, list(complex)::in, complex::in, complex::in,
list(complex)::out, list(complex)::out) is det.
xform([], [_ | _], _WJN, _WKN, _, _) :-
error("ran out of Di's").
xform([_ | _], [], _WJN, _WKN, _, _) :-
error("ran out of Dj's").
xform([], [], _WJN, _WKN, [], []).
xform([Di0 | Dis0], [Dj0 | Djs0], WJN, WKN, [Di | Dis], [Dj | Djs]) :-
Tmp = WJN * Dj0,
Di = Di0 + Tmp,
Dj = Di0 - Tmp,
xform(Dis0, Djs0, WJN * WKN, WKN, Dis, Djs).
:- pred divide(int::in, list(T)::in, list(T)::out, list(T)::out) is det.
divide(R, Ins, Fs, Ss) :-
( if
R =< 0
then
Fs = [],
Ss = Ins
else
(
Ins = [],
error("divide error!")
;
Ins = [F | Ins1],
divide(R - 1, Ins1, Fs1, Ss),
Fs = [F | Fs1]
)
).
:- func w(float, float) = complex.
w(J, N) = cis(Theta) :-
Theta = J * 2.0 * pi / N.
%-----------------------------------------------------------------------------%
:- end_module fft.
%-----------------------------------------------------------------------------%