Skip to content

Commit 485ce02

Browse files
committed
Merge pull request #19 from capn-freako/master
First successful implementation of FFT as a class.
2 parents 7f461be + fa7590a commit 485ce02

File tree

1 file changed

+34
-20
lines changed

1 file changed

+34
-20
lines changed

test/fft_test.hs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
{-# LANGUAGE GADTs #-}
1313
{-# LANGUAGE TemplateHaskell #-}
14+
{-# LANGUAGE TypeSynonymInstances #-}
1415

1516
module Main where
1617

@@ -20,35 +21,48 @@ import Control.Applicative
2021
import Control.Arrow
2122
import Control.Monad (forM_, unless)
2223
import Data.Complex
23-
import Data.Foldable (sum)
24+
import Data.Foldable (Foldable, sum, foldl')
2425
import Data.Newtypes.PrettyDouble
2526
import System.Exit (exitFailure)
26-
import TypeUnary.Nat (IsNat, natToZ, Nat(..), nat, N2, N3, N4, N5) -- , N6)
27+
import TypeUnary.Nat (IsNat, Nat(..), nat, N2, N3, N4, N5) -- , N6)
2728

2829
-- import Test.QuickCheck (choose, vectorOf, elements, collect)
2930
import Test.QuickCheck (choose, vectorOf)
3031
import Test.QuickCheck.Arbitrary
3132
import Test.QuickCheck.All (quickCheckAll)
3233

33-
import Circat.Scan (scanlTEx)
34+
import Circat.Scan (lproducts, LScan)
3435
import qualified Circat.Pair as P
3536
import qualified Circat.RTree as RT
3637
import Circat.RTree (bottomSplit)
3738

3839
type RTree = RT.Tree
3940

40-
-- Phasor, as a function of tree depth.
41-
phasor :: (IsNat n, RealFloat a, Enum a) => Nat n -> RTree n (Complex a)
42-
phasor n = scanlTEx (*) 1 (pure phaseDelta)
43-
where phaseDelta = cis ((-pi) / 2 ** natToZ n)
44-
45-
-- Radix-2, DIT FFT
46-
fft_r2_dit :: (IsNat n, RealFloat a, Enum a) => RTree n (Complex a) -> RTree n (Complex a)
47-
fft_r2_dit = fft_r2_dit' nat
48-
49-
fft_r2_dit' :: (RealFloat a, Enum a) => Nat n -> RTree n (Complex a) -> RTree n (Complex a)
50-
fft_r2_dit' Zero = id
51-
fft_r2_dit' (Succ n) = RT.toB . P.inP (uncurry (+) &&& uncurry (-)) . P.secondP (liftA2 (*) (phasor n)) . fmap (fft_r2_dit' n) . bottomSplit
41+
-- FFT, as a class
42+
-- The LScan constraint comes from the use of 'lproducts', in 'phasor'.
43+
class (LScan f) => FFT f where
44+
fft :: (RealFloat a) => f (Complex a) -> f (Complex a)
45+
46+
instance IsNat n => FFT (RTree n) where
47+
fft = fft' nat
48+
where fft' :: (RealFloat a) => Nat n -> RTree n (Complex a) -> RTree n (Complex a)
49+
fft' Zero = id
50+
fft' (Succ n) = inDIT $ fftP . P.secondP addPhase . fmap (fft' n)
51+
where inDIT g = RT.toB . g . bottomSplit
52+
fftP = P.inP (uncurry (+) &&& uncurry (-))
53+
addPhase = liftA2 (*) id phasor
54+
55+
-- Phasor, as a general function on LScans.
56+
-- Gives the "length" (i.e. - number of elements in) of a Foldable.
57+
-- (Soon, to be provided by the Foldable class, as "length".)
58+
flen :: (Foldable f) => f a -> Int
59+
flen = foldl' (flip ((+) . const 1)) 0
60+
61+
-- Given a Foldable Applicative LScan, construct its matching phasor.
62+
phasor :: (Applicative f, Foldable f, LScan f, RealFloat b) => f a -> f (Complex b)
63+
phasor f = fst $ lproducts (pure phaseDelta)
64+
where phaseDelta = cis ((-pi) / (fromIntegral n))
65+
n = flen f
5266

5367
-- Test config.
5468
realData :: [[PrettyDouble]]
@@ -112,19 +126,19 @@ instance Arbitrary FFTTestVal where
112126
return $ FFTTestVal zs
113127

114128
prop_fft_test_N2 :: FFTTestVal -> Bool
115-
prop_fft_test_N2 testVal = fft_r2_dit (myTree2 zs) == (RT.fromList $ dft zs)
129+
prop_fft_test_N2 testVal = fft (myTree2 zs) == (RT.fromList $ dft zs)
116130
where zs = take 4 $ getVal testVal
117131

118132
prop_fft_test_N3 :: FFTTestVal -> Bool
119-
prop_fft_test_N3 testVal = fft_r2_dit (myTree3 zs) == (RT.fromList $ dft zs)
133+
prop_fft_test_N3 testVal = fft (myTree3 zs) == (RT.fromList $ dft zs)
120134
where zs = take 8 $ getVal testVal
121135

122136
prop_fft_test_N4 :: FFTTestVal -> Bool
123-
prop_fft_test_N4 testVal = fft_r2_dit (myTree4 zs) == (RT.fromList $ dft zs)
137+
prop_fft_test_N4 testVal = fft (myTree4 zs) == (RT.fromList $ dft zs)
124138
where zs = take 16 $ getVal testVal
125139

126140
prop_fft_test_N5 :: FFTTestVal -> Bool
127-
prop_fft_test_N5 testVal = fft_r2_dit (myTree5 zs) == (RT.fromList $ dft zs)
141+
prop_fft_test_N5 testVal = fft (myTree5 zs) == (RT.fromList $ dft zs)
128142
where zs = take 32 $ getVal testVal
129143

130144
-- Test definitions & choice
@@ -136,7 +150,7 @@ basicTest = do
136150
putStr "Expected output: "
137151
print $ dft x
138152
putStr "Actual output: "
139-
print $ fft_r2_dit $ myTree2 x
153+
print $ fft $ myTree2 x
140154
)
141155

142156
-- This weirdness is required, as of GHC 7.8.

0 commit comments

Comments
 (0)