packages feed

comfort-fftw-0.0.0.1: test/Test/Numeric/FFTW/Common.hs

module Test.Numeric.FFTW.Common where

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Storable as Array
import qualified Data.Array.Comfort.Shape as Shape
import qualified Data.Complex as Complex
import qualified Data.NonEmpty as NonEmpty
import Data.Array.Comfort.Storable (Array)
import Data.Complex (Complex((:+)))
import Data.NonEmpty ((!:))

import Foreign.Storable.Record.Tuple (Tuple(getTuple))
import Foreign.Storable (Storable)

import Control.DeepSeq (NFData, deepseq)
import Control.Applicative ((<*>))

import Text.Printf (printf)

import qualified Test.QuickCheck as QC


prefix :: String -> [(String, test)] -> [(String, test)]
prefix msg =
   map (\(str,test) -> (msg ++ "." ++ str, test))


arrayFloat :: array Float -> array Float
arrayFloat = id

arrayDouble :: array Double -> array Double
arrayDouble = id

arrayComplexFloat :: array (Complex Float) -> array (Complex Float)
arrayComplexFloat = id

arrayComplexDouble :: array (Complex Double) -> array (Complex Double)
arrayComplexDouble = id


floatTol :: Float
floatTol = 1e-4

doubleTol :: Double
doubleTol = 1e-10

normInf ::
   (Shape.C sh, Storable b, Class.Real a) => (b -> a) -> Array sh b -> a
normInf mag xs =
   NonEmpty.maximum $ 0 !: Array.toList (Array.map mag xs)

approx ::
   (Shape.C sh, Eq sh, Storable b, Num b, Class.Real a) =>
   (b -> a) -> a -> Array sh b -> Array sh b -> Bool
approx mag tol xs ys =
   let absTol = tol * (normInf mag xs + normInf mag ys) in
   all (<=absTol) $ Array.toList $ Array.map mag $ Array.zipWith (-) xs ys

approxReal ::
   (Shape.C sh, Eq sh, Class.Real a) =>
   a -> Array sh a -> Array sh a -> Bool
approxReal = approx abs

approxComplex ::
   (Shape.C sh, Eq sh, Class.Real a) =>
   a -> Array sh (Complex a) -> Array sh (Complex a) -> Bool
approxComplex = approx Complex.magnitude


adjust :: (Shape.C sh, Class.Floating a) => Array sh a -> Array sh a
adjust xs = Array.map (fromIntegral (Shape.size (Array.shape xs)) *) xs

split ::
   (Shape.C sh, Storable a, Storable b) =>
   Array sh (Tuple (a, b)) -> (Array sh a, Array sh b)
split xys = (Array.map (fst.getTuple) xys, Array.map (snd.getTuple) xys)

scalarProduct ::
   (Shape.C sh, Eq sh, Class.Real a) =>
   Array sh (Complex a) -> Array sh (Complex a) -> Complex a
scalarProduct xs ys =
   sum $ Array.toList $
   Array.zipWith (\x y -> Complex.conjugate x * y) xs ys


factors :: (Integral n) => n -> [(n,n)]
factors n =
   concatMap (\k ->
      case divMod n k of
         (q,0) -> if q==k then [(k,q)] else [(k,q),(q,k)]
         _ -> []) $
   takeWhile (\k -> k*k<=n) $ iterate (1+) 1

genCyclicArray1 ::
   (QC.Arbitrary a, Storable a) => QC.Gen (Array (Shape.Cyclic Int) a)
genCyclicArray1 = do
   xs <- fmap (take 1000) $ QC.arbitrary
   return $ Array.fromList (Shape.Cyclic $ length xs) xs

genCyclicArray2 ::
   (QC.Arbitrary a, Storable a) =>
   QC.Gen (Array (Shape.Cyclic Int, Shape.Cyclic Int) a)
genCyclicArray2 = do
   xys <- fmap (take 1000) $ QC.arbitrary
   (n0,n1) <-
      case xys of
         _:_ -> QC.elements (factors $ length xys)
         [] -> QC.elements [(,) 0, flip (,) 0] <*> QC.choose (0,10)
   return $ Array.fromList (Shape.Cyclic n0, Shape.Cyclic n1) xys

genCyclicArray3 ::
   (QC.Arbitrary a, Storable a) =>
   QC.Gen (Array (Shape.Cyclic Int, Shape.Cyclic Int, Shape.Cyclic Int) a)
genCyclicArray3 = do
   xyzs <- fmap (take 1000) $ QC.arbitrary
   (n0,n1,n2) <-
      case xyzs of
         _:_ -> QC.elements $ do
            (n0,m0) <- factors $ length xyzs
            (n1,n2) <- factors m0
            return (n0,n1,n2)
         [] ->
            QC.elements [(,,) 0, flip (,,) 0, \n0 n1 -> (n0,n1,0)]
               <*> QC.choose (0,10)
               <*> QC.choose (0,10)
   let sh = (Shape.Cyclic n0, Shape.Cyclic n1, Shape.Cyclic n2)
   return $ Array.fromList sh xyzs


immutable ::
   (Shape.C sh, Storable a, Eq sh, Eq a, NFData b) =>
   (Array sh a -> b) -> (Array sh a -> Bool)
immutable transform arr =
   let copy = Array.map id arr
   in deepseq (transform copy) (copy==arr)



newtype FixedFloat = FixedFloat Float

instance Show FixedFloat where
   show (FixedFloat a) = printf "%.2f" a

floatList :: (Shape.C sh) => Array sh Float -> [FixedFloat]
floatList = map FixedFloat . Array.toList


newtype FixedComplexFloat = FixedComplexFloat (Complex Float)

instance Show FixedComplexFloat where
   show (FixedComplexFloat (r:+i)) =
      let str :: Float -> String
          str x =
            if abs x < 0.005
               then "0.00" --avoid "-0.00"
               else
                  if x>=0 then printf "%.2f" x else printf "(%.2f)" x
      in printf "%s:+%s" (str r) (str i)

complexFloatList ::
   (Shape.C sh) => Array sh (Complex Float) -> [FixedComplexFloat]
complexFloatList = map FixedComplexFloat . Array.toList