packages feed

arrayfire-0.9.0.0: src/ArrayFire/Internal/Types.hsc

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE ViewPatterns        #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE CPP                 #-}
module ArrayFire.Internal.Types where

#include "af/seq.h"
#include "af/complex.h"
#include "af/graphics.h"
#include "af/index.h"

import ArrayFire.Internal.Defines

import Data.Complex
import Data.Proxy
import Data.Word
import Foreign.C.String
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (Ptr)
import Foreign.Storable
import GHC.Int

data AFSeq
  = AFSeq
  { afSeqBegin :: {-# UNPACK #-} !Double
  , afSeqEnd   :: {-# UNPACK #-} !Double
  , afSeqStep  :: {-# UNPACK #-} !Double
  } deriving (Show, Eq)

instance Storable AFSeq where
  sizeOf _ = #{size af_seq}
  alignment _ = #{alignment af_seq}
  peek ptr = do
    afSeqBegin <- #{peek af_seq, begin} ptr
    afSeqEnd <- #{peek af_seq, end} ptr
    afSeqStep <- #{peek af_seq, step} ptr
    pure AFSeq {..}
  poke ptr AFSeq{..} = do
    #{poke af_seq, begin} ptr afSeqBegin
    #{poke af_seq, end} ptr afSeqEnd
    #{poke af_seq, step} ptr afSeqStep

-- | Type used for indexing into 'Array'
data AFIndex
  = AFIndex
  { afIdx :: !(Either AFArray AFSeq)
  , afIsSeq :: !Bool
  , afIsBatch :: !Bool
  }
instance Storable AFIndex where
  sizeOf _ = #{size af_index_t}
  alignment _ = #{alignment af_index_t}
  peek ptr = do
    afIsSeq <- #{peek af_index_t, isSeq} ptr
    afIsBatch <- #{peek af_index_t, isBatch} ptr
    afIdx <-
      if afIsSeq
        then Right <$> #{peek af_index_t, idx.seq} ptr
        else Left <$> #{peek af_index_t, idx.arr} ptr
    pure AFIndex{..}
  poke ptr AFIndex{..} = do
    case afIdx of
      Left afarr -> #{poke af_index_t, idx.arr} ptr afarr
      Right afseq -> #{poke af_index_t, idx.seq} ptr afseq
    #{poke af_index_t, isSeq} ptr afIsSeq
    #{poke af_index_t, isBatch} ptr afIsBatch

data AFCFloat
  = AFCFloat
  { afcReal :: {-# UNPACK #-} !Float
  , afcImag :: {-# UNPACK #-} !Float
  } deriving (Eq, Show)

instance Storable AFCFloat where
  sizeOf _ = #{size af_cfloat}
  alignment _ = #{alignment af_cfloat}
  peek ptr = do
    afcReal <- #{peek af_cfloat, real} ptr
    afcImag <- #{peek af_cfloat, imag} ptr
    pure AFCFloat{..}
  poke ptr AFCFloat{..} = do
    #{poke af_cfloat, real} ptr afcReal
    #{poke af_cfloat, imag} ptr afcImag

data AFCell
  = AFCell
  { afCellRow :: {-# UNPACK #-} !Int
  , afCellCol :: {-# UNPACK #-} !Int
  , afCellTitle :: {-# UNPACK #-} !CString
  , afCellColorMap :: {-# UNPACK #-} !AFColorMap
  } deriving (Show, Eq)

instance Storable AFCell where
  sizeOf _ = #{size af_cell}
  alignment _ = #{alignment af_cell}
  peek ptr = do
    afCellRow <- #{peek af_cell, row} ptr
    afCellCol <- #{peek af_cell, col} ptr
    afCellTitle <- #{peek af_cell, title} ptr
    afCellColorMap <- #{peek af_cell, cmap} ptr
    pure AFCell{..}
  poke ptr AFCell{..} = do
    #{poke af_cell, row} ptr afCellRow
    #{poke af_cell, col} ptr afCellCol
    #{poke af_cell, title} ptr afCellTitle
    #{poke af_cell, cmap} ptr afCellColorMap

-- | ArrayFire 'Array'
newtype Array a = Array (ForeignPtr ())

-- | ArrayFire 'Features'
newtype Features = Features (ForeignPtr ())

-- | ArrayFire 'RandomEngine'
newtype RandomEngine = RandomEngine (ForeignPtr ())

-- | ArrayFire 'Window'
newtype Window = Window (ForeignPtr ())

-- | Mapping of Haskell types to ArrayFire types
class Storable a => AFType a where
  afType :: Proxy a -> AFDtype

instance AFType Double where
  afType Proxy = f64

instance AFType Float where
  afType Proxy = f32

instance AFType (Complex Double) where
  afType Proxy = c64

instance AFType (Complex Float) where
  afType Proxy = c32

instance AFType CBool where
  afType Proxy = b8

instance AFType Int32 where
  afType Proxy = s32

instance AFType Word32 where
  afType Proxy = u32

instance AFType Word8 where
  afType Proxy = u8

instance AFType Int64 where
  afType Proxy = s64

instance AFType Int where
  afType Proxy = s64

instance AFType Int16 where
  afType Proxy = s16

instance AFType Word16 where
  afType Proxy = u16

instance AFType Word64 where
  afType Proxy = u64

instance AFType Word where
  afType Proxy = u64

-- | Maps an ArrayFire element type to the scalar type returned by whole-array
-- reductions (e.g. 'meanAll', 'det').  Real and integral element types yield
-- 'Double'; complex element types yield 'Complex Double'.
class AFType a => AFResult a where
  type Scalar a
  -- | Convert the raw @(real, imag)@ pair returned by the C API to the
  -- appropriate Haskell scalar.
  toAFResult :: (Double, Double) -> Scalar a

instance AFResult Double where
  type Scalar Double = Double
  toAFResult (r, _) = r

instance AFResult Float where
  type Scalar Float = Double
  toAFResult (r, _) = r

instance AFResult (Complex Double) where
  type Scalar (Complex Double) = Complex Double
  toAFResult (r, i) = r :+ i

instance AFResult (Complex Float) where
  type Scalar (Complex Float) = Complex Double
  toAFResult (r, i) = r :+ i

instance AFResult CBool where
  type Scalar CBool = Double
  toAFResult (r, _) = r

instance AFResult Int32 where
  type Scalar Int32 = Double
  toAFResult (r, _) = r

instance AFResult Word32 where
  type Scalar Word32 = Double
  toAFResult (r, _) = r

instance AFResult Word8 where
  type Scalar Word8 = Double
  toAFResult (r, _) = r

instance AFResult Int64 where
  type Scalar Int64 = Double
  toAFResult (r, _) = r

instance AFResult Int where
  type Scalar Int = Double
  toAFResult (r, _) = r

instance AFResult Int16 where
  type Scalar Int16 = Double
  toAFResult (r, _) = r

instance AFResult Word16 where
  type Scalar Word16 = Double
  toAFResult (r, _) = r

instance AFResult Word64 where
  type Scalar Word64 = Double
  toAFResult (r, _) = r

instance AFResult Word where
  type Scalar Word = Double
  toAFResult (r, _) = r

-- | ArrayFire backends
data Backend
  = Default
  -- ^ Use the default backend (determined by ArrayFire)
  | CPU
  -- ^ CPU backend (always available)
  | CUDA
  -- ^ NVIDIA CUDA GPU backend
  | OpenCL
  -- ^ OpenCL backend (AMD, Intel, NVIDIA)
  deriving (Show, Eq, Ord)

-- | Low-level to high-level Backend conversion
toBackend :: AFBackend -> Backend
toBackend (AFBackend 0) = Default
toBackend (AFBackend 1) = CPU
toBackend (AFBackend 2) = CUDA
toBackend (AFBackend 4) = OpenCL
toBackend (AFBackend x) = error $ "Invalid backend: " <> show x

-- | High-level to low-level Backend conversion
toAFBackend :: Backend -> AFBackend
toAFBackend Default = (AFBackend 0)
toAFBackend CPU = (AFBackend 1)
toAFBackend CUDA = (AFBackend 2)
toAFBackend OpenCL = (AFBackend 4)

-- | Read multiple backends
toBackends :: Int -> [Backend]
toBackends 1 = [CPU]
toBackends 2 = [CUDA]
toBackends 3 = [CPU,CUDA]
toBackends 4 = [OpenCL]
toBackends 5 = [CPU,OpenCL]
toBackends 6 = [CUDA,OpenCL]
toBackends 7 = [CPU,CUDA,OpenCL]
toBackends _ = []

-- | Matrix properties
data MatProp
  = None
  -- ^ No property
  | Trans
  -- ^ Data needs to be transposed
  | CTrans
  -- ^ Data needs to be conjugate transposed
  | Conj
  -- ^ Data needs to be conjugated
  | Upper
  -- ^ Matrix is upper triangular
  | Lower
  -- ^ Matrix is lower triangular
  | DiagUnit
  -- ^ Diagonal contains units; used with triangular solvers
  | Sym
  -- ^ Matrix is symmetric
  | PosDef
  -- ^ Matrix is positive definite
  | Orthog
  -- ^ Matrix is orthogonal
  | TriDiag
  -- ^ Matrix is tri-diagonal
  | BlockDiag
  -- ^ Matrix is block diagonal
  deriving (Show, Eq, Ord)

-- | Low-level to High-level 'MatProp' conversion
fromMatProp
  :: AFMatProp
  -> MatProp
fromMatProp (AFMatProp 0) = None
fromMatProp (AFMatProp 1) = Trans
fromMatProp (AFMatProp 2) = CTrans
fromMatProp (AFMatProp 4) = Conj
fromMatProp (AFMatProp 32) = Upper
fromMatProp (AFMatProp 64) = Lower
fromMatProp (AFMatProp 128) = DiagUnit
fromMatProp (AFMatProp 512) = Sym
fromMatProp (AFMatProp 1024) = PosDef
fromMatProp (AFMatProp 2048) = Orthog
fromMatProp (AFMatProp 4096) = TriDiag
fromMatProp (AFMatProp 8192) = BlockDiag
fromMatProp x = error $ "Invalid AFMatProp value: " <> show x

-- | High-level to low-level 'MatProp' conversion
toMatProp
  :: MatProp
  -> AFMatProp
toMatProp None = (AFMatProp 0)
toMatProp Trans = (AFMatProp 1)
toMatProp CTrans = (AFMatProp 2)
toMatProp Conj = (AFMatProp 4)
toMatProp Upper = (AFMatProp 32)
toMatProp Lower = (AFMatProp 64)
toMatProp DiagUnit = (AFMatProp 128)
toMatProp Sym = (AFMatProp 512)
toMatProp PosDef = (AFMatProp 1024)
toMatProp Orthog = (AFMatProp 2048)
toMatProp TriDiag = (AFMatProp 4096)
toMatProp BlockDiag = (AFMatProp 8192)

-- | Binary operation support (used with scan-by-key and similar operations)
data BinaryOp
  = Add
  -- ^ Addition
  | Mul
  -- ^ Multiplication
  | Min
  -- ^ Minimum
  | Max
  -- ^ Maximum
  deriving (Show, Eq, Ord)

-- | High-level to low-level 'MatProp' conversion
toBinaryOp :: BinaryOp -> AFBinaryOp
toBinaryOp Add = AFBinaryOp 0
toBinaryOp Mul = AFBinaryOp 1
toBinaryOp Min = AFBinaryOp 2
toBinaryOp Max = AFBinaryOp 3

-- | 'BinaryOp' conversion helper
fromBinaryOp :: AFBinaryOp -> BinaryOp
fromBinaryOp (AFBinaryOp 0) = Add
fromBinaryOp (AFBinaryOp 1) = Mul
fromBinaryOp (AFBinaryOp 2) = Min
fromBinaryOp (AFBinaryOp 3) = Max
fromBinaryOp x = error ("Invalid Binary Op: " <> show x)

-- | Storage type used for Sparse arrays
data Storage
  = Dense
  -- ^ Dense storage (not sparse)
  | CSR
  -- ^ Compressed Sparse Row format
  | CSC
  -- ^ Compressed Sparse Column format
  | COO
  -- ^ Coordinate list (COO) format
  deriving (Show, Eq, Ord, Enum)

toStorage :: Storage -> AFStorage
toStorage = AFStorage . fromIntegral . fromEnum

fromStorage :: AFStorage -> Storage
fromStorage (AFStorage (fromIntegral -> x))
  | x `elem` [0..3] = toEnum x
  | otherwise = error $ "Invalid Storage " <> (show x)

-- | Type for different RandomEngines
data RandomEngineType
  = Philox
  | ThreeFry
  | Mersenne
  deriving (Eq, Show)

toRandomEngine :: AFRandomEngineType -> RandomEngineType
toRandomEngine (AFRandomEngineType 100) = Philox
toRandomEngine (AFRandomEngineType 200) = ThreeFry
toRandomEngine (AFRandomEngineType 300) = Mersenne
toRandomEngine (AFRandomEngineType x) =
  error ("Invalid random engine: " <> show x)

fromRandomEngine :: RandomEngineType ->  AFRandomEngineType
fromRandomEngine Philox = (AFRandomEngineType 100)
fromRandomEngine ThreeFry = (AFRandomEngineType 200)
fromRandomEngine Mersenne = (AFRandomEngineType 300)

-- | Interpolation type
data InterpType
  = Nearest
  -- ^ Nearest-neighbor interpolation
  | Linear
  -- ^ Linear interpolation
  | Bilinear
  -- ^ Bilinear interpolation
  | Cubic
  -- ^ Cubic interpolation
  | LowerInterp
  -- ^ Floor interpolation (rounds down to nearest integer)
  | LinearCosine
  -- ^ Cosine-windowed linear interpolation
  | BilinearCosine
  -- ^ Cosine-windowed bilinear interpolation
  | Bicubic
  -- ^ Bicubic interpolation
  | CubicSpline
  -- ^ Cubic spline interpolation
  | BicubicSpline
  -- ^ Bicubic spline interpolation
  deriving (Show, Eq, Ord, Enum)

toInterpType :: AFInterpType -> InterpType
toInterpType (AFInterpType (fromIntegral -> x)) = toEnum x

fromInterpType :: InterpType -> AFInterpType
fromInterpType = AFInterpType . fromIntegral . fromEnum

-- | Border Type
data BorderType
  = PadZero
  | PadSym
  deriving (Show, Ord, Enum, Eq)

toBorderType :: AFBorderType -> BorderType
toBorderType (AFBorderType (fromIntegral -> x)) = toEnum x

fromBorderType :: BorderType -> AFBorderType
fromBorderType = AFBorderType . fromIntegral . fromEnum

-- | Connectivity Type
data Connectivity
  = Conn4
  | Conn8
  deriving (Show, Ord, Enum, Eq)

toConnectivity :: AFConnectivity -> Connectivity
toConnectivity (AFConnectivity 4) = Conn4
toConnectivity (AFConnectivity 8) = Conn8
toConnectivity (AFConnectivity x) = error ("Unknown connectivity option: " <> show x)

fromConnectivity :: Connectivity -> AFConnectivity
fromConnectivity Conn4 = AFConnectivity 4
fromConnectivity Conn8 = AFConnectivity 8

-- | Color Space type
data CSpace
  = Gray
  -- ^ Grayscale
  | RGB
  -- ^ Red-Green-Blue
  | HSV
  -- ^ Hue-Saturation-Value
  | YCBCR
  -- ^ Luminance + chroma (blue-difference, red-difference)
  deriving (Show, Eq, Ord, Enum)

toCSpace :: AFCSpace -> CSpace
toCSpace (AFCSpace (fromIntegral -> x)) = toEnum x

fromCSpace :: CSpace -> AFCSpace
fromCSpace = AFCSpace . fromIntegral . fromEnum

-- | YCbCr standard
data YccStd
  = Ycc601
  -- ^ ITU-R BT.601 (standard definition)
  | Ycc709
  -- ^ ITU-R BT.709 (high definition)
  | Ycc2020
  -- ^ ITU-R BT.2020 (ultra high definition)
  deriving (Show, Eq, Ord)

toAFYccStd :: AFYccStd -> YccStd
toAFYccStd (AFYccStd 601) = Ycc601
toAFYccStd (AFYccStd 709) = Ycc709
toAFYccStd (AFYccStd 2020) = Ycc2020
toAFYccStd (AFYccStd x) = error ("Unknown AFYccStd option: " <> show x)

fromAFYccStd :: YccStd -> AFYccStd
fromAFYccStd Ycc601 = afYcc601
fromAFYccStd Ycc709 = afYcc709
fromAFYccStd Ycc2020 = afYcc2020

-- | Image moment types
data MomentType
  = M00
  -- ^ Zeroth-order moment (image area / mass)
  | M01
  -- ^ First-order moment about x-axis
  | M10
  -- ^ First-order moment about y-axis
  | M11
  -- ^ Mixed first-order moment
  | FirstOrder
  -- ^ All first-order moments (M00, M01, M10, M11)
  deriving (Show, Eq, Ord)

toMomentType :: AFMomentType -> MomentType
toMomentType x
  | x == afMomentM00 = M00
  | x == afMomentM01 = M01
  | x == afMomentM10 = M10
  | x == afMomentM11 = M11
  | x == afMomentFirstOrder = FirstOrder
  | otherwise = error ("Unknown moment type: " <> show x)

fromMomentType :: MomentType -> AFMomentType
fromMomentType M00 = afMomentM00
fromMomentType M01 = afMomentM01
fromMomentType M10 = afMomentM10
fromMomentType M11 = afMomentM11
fromMomentType FirstOrder = afMomentFirstOrder

-- | Threshold mode for Canny edge detection
data CannyThreshold
  = Manual
  -- ^ User-supplied low and high threshold values
  | AutoOtsu
  -- ^ Thresholds computed automatically via Otsu's method
  deriving (Show, Eq, Ord, Enum)

toCannyThreshold :: AFCannyThreshold -> CannyThreshold
toCannyThreshold (AFCannyThreshold (fromIntegral -> x)) = toEnum x

fromCannyThreshold :: CannyThreshold -> AFCannyThreshold
fromCannyThreshold = AFCannyThreshold . fromIntegral . fromEnum

-- | Flux function for anisotropic diffusion
data FluxFunction
  = FluxDefault
  -- ^ Default flux function (same as 'FluxQuadratic')
  | FluxQuadratic
  -- ^ Quadratic flux function (Perona-Malik)
  | FluxExponential
  -- ^ Exponential flux function (Perona-Malik)
  deriving (Show, Eq, Ord, Enum)

toFluxFunction :: AFFluxFunction -> FluxFunction
toFluxFunction (AFFluxFunction (fromIntegral -> x)) = toEnum x

fromFluxFunction :: FluxFunction -> AFFluxFunction
fromFluxFunction = AFFluxFunction . fromIntegral . fromEnum

-- | Diffusion equation type for anisotropic smoothing
data DiffusionEq
  = DiffusionDefault
  -- ^ Default (same as 'DiffusionGrad')
  | DiffusionGrad
  -- ^ Gradient-based diffusion (Perona-Malik)
  | DiffusionMCDE
  -- ^ Mean curvature diffusion equation
  deriving (Show, Eq, Ord, Enum)

toDiffusionEq :: AFDiffusionEq -> DiffusionEq
toDiffusionEq (AFDiffusionEq (fromIntegral -> x)) = toEnum x

fromDiffusionEq :: DiffusionEq -> AFDiffusionEq
fromDiffusionEq = AFDiffusionEq . fromIntegral . fromEnum

-- | Iterative deconvolution algorithm
data IterativeDeconvAlgo
  = DeconvDefault
  -- ^ Default algorithm (same as 'DeconvLandweber')
  | DeconvLandweber
  -- ^ Landweber iteration (gradient descent on least squares)
  | DeconvRichardsonLucy
  -- ^ Richardson-Lucy algorithm (maximum likelihood for Poisson noise)
  deriving (Show, Eq, Ord, Enum)

toIterativeDeconvAlgo :: AFIterativeDeconvAlgo -> IterativeDeconvAlgo
toIterativeDeconvAlgo (AFIterativeDeconvAlgo (fromIntegral -> x)) = toEnum x

fromIterativeDeconvAlgo :: IterativeDeconvAlgo -> AFIterativeDeconvAlgo
fromIterativeDeconvAlgo = AFIterativeDeconvAlgo . fromIntegral . fromEnum

-- | Inverse (non-iterative) deconvolution algorithm
data InverseDeconvAlgo
  = InverseDeconvDefault
  -- ^ Default algorithm (same as 'InverseDeconvTikhonov')
  | InverseDeconvTikhonov
  -- ^ Tikhonov regularized Wiener filter
  deriving (Show, Eq, Ord, Enum)

toInverseDeconvAlgo :: AFInverseDeconvAlgo -> InverseDeconvAlgo
toInverseDeconvAlgo (AFInverseDeconvAlgo (fromIntegral -> x)) = toEnum x

fromInverseDeconvAlgo :: InverseDeconvAlgo -> AFInverseDeconvAlgo
fromInverseDeconvAlgo = AFInverseDeconvAlgo . fromIntegral . fromEnum

-- | Cell type, used in Graphics module to describe a subplot position
data Cell
  = Cell
  { cellRow :: Int
  -- ^ Row index of the subplot (0-based)
  , cellCol :: Int
  -- ^ Column index of the subplot (0-based)
  , cellTitle :: String
  -- ^ Title string displayed above the plot
  , cellColorMap :: ColorMap
  -- ^ Color map used for rendering
  } deriving (Show, Eq)

-- | Marshals a 'Cell' into a temporary 'AFCell' and hands a pointer to it to
-- the continuation. The title 'CString' is only valid for the duration of the
-- continuation, so the C call consuming the cell must happen inside it —
-- returning the 'AFCell' from under 'withCString' would leave a dangling
-- title pointer.
withAFCell :: Cell -> (Ptr AFCell -> IO a) -> IO a
withAFCell Cell {..} f =
  withCString cellTitle $ \cstr ->
    alloca $ \cellPtr -> do
      poke cellPtr AFCell { afCellRow = cellRow
                          , afCellCol = cellCol
                          , afCellTitle = cstr
                          , afCellColorMap = fromColorMap cellColorMap
                          }
      f cellPtr

-- | Color map for rendering
data ColorMap
  = ColorMapDefault
  -- ^ Default grayscale color map
  | ColorMapSpectrum
  -- ^ Rainbow spectrum (violet to red)
  | ColorMapColors
  -- ^ Distinct colors
  | ColorMapRed
  -- ^ Red gradient
  | ColorMapMood
  -- ^ Mood color map (cool tones)
  | ColorMapHeat
  -- ^ Heat map (black to red to yellow to white)
  | ColorMapBlue
  -- ^ Blue gradient
  | ColorMapInferno
  -- ^ Perceptually uniform: black-purple-orange-yellow
  | ColorMapMagma
  -- ^ Perceptually uniform: black-purple-pink-white
  | ColorMapPlasma
  -- ^ Perceptually uniform: blue-purple-yellow
  | ColorMapViridis
  -- ^ Perceptually uniform: purple-teal-yellow
  deriving (Show, Eq, Ord, Enum)

fromColorMap :: ColorMap -> AFColorMap
fromColorMap = AFColorMap . fromIntegral . fromEnum

toColorMap :: AFColorMap -> ColorMap
toColorMap (AFColorMap (fromIntegral -> x)) = toEnum x

-- | Marker shape for scatter plots
data MarkerType
  = MarkerTypeNone
  -- ^ No marker
  | MarkerTypePoint
  -- ^ Single pixel point
  | MarkerTypeCircle
  -- ^ Circle
  | MarkerTypeSquare
  -- ^ Square
  | MarkerTypeTriangle
  -- ^ Triangle
  | MarkerTypeCross
  -- ^ X cross
  | MarkerTypePlus
  -- ^ Plus sign
  | MarkerTypeStar
  -- ^ Star
  deriving (Show, Eq, Ord, Enum)

fromMarkerType :: MarkerType -> AFMarkerType
fromMarkerType = AFMarkerType . fromIntegral . fromEnum

toMarkerType :: AFMarkerType -> MarkerType
toMarkerType (AFMarkerType (fromIntegral -> x)) = toEnum x

-- | Template matching metric type
data MatchType
  = MatchTypeSAD
  -- ^ Sum of Absolute Differences
  | MatchTypeZSAD
  -- ^ Zero-mean Sum of Absolute Differences
  | MatchTypeLSAD
  -- ^ Locally scaled Sum of Absolute Differences
  | MatchTypeSSD
  -- ^ Sum of Squared Differences
  | MatchTypeZSSD
  -- ^ Zero-mean Sum of Squared Differences
  | MatchTypeLSSD
  -- ^ Locally scaled Sum of Squared Differences
  | MatchTypeNCC
  -- ^ Normalized Cross Correlation
  | MatchTypeZNCC
  -- ^ Zero-mean Normalized Cross Correlation
  | MatchTypeSHD
  -- ^ Sum of Hamming Distances
  deriving (Show, Eq, Ord, Enum)

fromMatchType :: MatchType -> AFMatchType
fromMatchType = AFMatchType . fromIntegral . fromEnum

toMatchType :: AFMatchType -> MatchType
toMatchType (AFMatchType (fromIntegral -> x)) = toEnum x

-- | Order for @topk@ results
data TopK
  = TopKDefault
  -- ^ Default order (same as 'TopKMax')
  | TopKMin
  -- ^ Return the k smallest values
  | TopKMax
  -- ^ Return the k largest values
  deriving (Show, Eq, Ord, Enum)

fromTopK :: TopK -> AFTopkFunction
fromTopK = AFTopkFunction . fromIntegral . fromEnum

toTopK :: AFTopkFunction -> TopK
toTopK (AFTopkFunction (fromIntegral -> x)) = toEnum x

-- | Variance bias correction method
data VarBias
  = VarianceDefault
  -- ^ Default (same as 'VariancePopulation')
  | VarianceSample
  -- ^ Sample variance (divides by N-1; Bessel's correction)
  | VariancePopulation
  -- ^ Population variance (divides by N)
  deriving (Show, Eq, Ord, Enum)

fromVarBias :: VarBias -> AFVarBias
fromVarBias = AFVarBias . fromIntegral . fromEnum

-- | Homography estimation method
data HomographyType
  = RANSAC
  -- ^ Random Sample Consensus — robust to outliers
  | LMEDS
  -- ^ Least Median of Squares — robust to up to 50% outliers
  deriving (Show, Eq, Ord, Enum)

fromHomographyType :: HomographyType -> AFHomographyType
fromHomographyType = AFHomographyType . fromIntegral . fromEnum

toHomographyType :: AFHomographyType -> HomographyType
toHomographyType (AFHomographyType (fromIntegral -> x)) = toEnum x

-- | Sequence Type
data Seq
  = Seq
  { seqBegin :: !Double
  , seqEnd :: !Double
  , seqStep :: !Double
  } deriving (Show, Eq, Ord)

toAFSeq :: Seq -> AFSeq
toAFSeq (Seq x y z) = (AFSeq x y z)

-- | Index Type
data Index
  = SeqIndex Bool Seq
  | ArrIndex Bool (Array Int)

seqIdx :: Seq -> Bool -> Index
seqIdx s batch = SeqIndex batch s

arrIdx :: Array Int -> Bool -> Index
arrIdx a batch = ArrIndex batch a

-- | Index a contiguous range [begin..end] with step 1.
range :: Int -> Int -> Index
range b e = SeqIndex False (Seq (fromIntegral b) (fromIntegral e) 1)

-- | Index a range [begin..end] with an explicit step.
rangeStep :: Int -> Int -> Int -> Index
rangeStep b e s = SeqIndex False (Seq (fromIntegral b) (fromIntegral e) (fromIntegral s))

-- | Index a single element.
at :: Int -> Index
at n = let d = fromIntegral n in SeqIndex False (Seq d d 1)

toAFIndex :: Index -> IO AFIndex
toAFIndex (SeqIndex batch s) =
  pure $ AFIndex (Right (toAFSeq s)) True batch
toAFIndex (ArrIndex batch (Array fptr)) =
  pure $ AFIndex (Left (unsafeForeignPtrToPtr fptr)) False batch


-- | Type alias for ArrayFire API version
type Version = (Int,Int,Int)

-- | Norm Type
data NormType
  = NormVectorOne
  -- ^ treats the input as a vector and returns the sum of absolute values
  | NormVectorInf
  -- ^ treats the input as a vector and returns the max of absolute values
  | NormVector2
  -- ^ treats the input as a vector and returns euclidean norm
  | NormVectorP
  -- ^ treats the input as a vector and returns the p-norm
  | NormMatrix1
  -- ^ return the max of column sums
  | NormMatrixInf
  -- ^ return the max of row sums
  | NormMatrix2
  -- ^ returns the max singular value). Currently NOT SUPPORTED
  | NormMatrixLPQ
  -- ^ returns Lpq-norm
  | NormEuclid
  -- ^ The default. Same as AF_NORM_VECTOR_2
  deriving (Show, Eq, Enum)

-- | Note: this cannot be derived via 'fromEnum' because in @af\/defines.h@
-- @AF_NORM_EUCLID@ is an alias for @AF_NORM_VECTOR_2@ (value 2), not a
-- distinct enum value following @AF_NORM_MATRIX_L_PQ@.
fromNormType :: NormType -> AFNormType
fromNormType NormVectorOne  = AFNormType 0
fromNormType NormVectorInf  = AFNormType 1
fromNormType NormVector2    = AFNormType 2
fromNormType NormVectorP    = AFNormType 3
fromNormType NormMatrix1    = AFNormType 4
fromNormType NormMatrixInf  = AFNormType 5
fromNormType NormMatrix2    = AFNormType 6
fromNormType NormMatrixLPQ  = AFNormType 7
fromNormType NormEuclid     = AFNormType 2

toNormType :: AFNormType -> NormType
toNormType (AFNormType (fromIntegral -> x))
  | x >= 0 && x <= 7 = toEnum x
  | otherwise = error ("Invalid AFNormType value: " <> show x)

-- | Convolution Domain
data ConvDomain
  = ConvDomainAuto
  -- ^ ArrayFire automatically picks the right convolution algorithm
  | ConvDomainSpatial
  -- ^ Perform convolution in spatial domain
  | ConvDomainFreq
  -- ^ Perform convolution in frequency domain
  deriving (Show, Eq, Enum)

-- | Convolution Mode
data ConvMode
  = ConvDefault
  -- ^ Output of the convolution is the same size as input
  | ConvExpand
  -- ^ Output of the convolution is signal_len + filter_len - 1
  deriving (Show, Eq, Enum)

fromConvDomain :: ConvDomain -> AFConvDomain
fromConvDomain = AFConvDomain . fromIntegral . fromEnum

toConvDomain :: AFConvDomain -> ConvDomain
toConvDomain (AFConvDomain (fromIntegral -> x)) = toEnum x

fromConvMode :: AFConvMode -> ConvMode
fromConvMode (AFConvMode (fromIntegral -> x)) = toEnum x

toConvMode :: ConvMode -> AFConvMode
toConvMode = AFConvMode . fromIntegral . fromEnum

-- | ArrayFire element types (mirrors @af_dtype@)
data AFDType
  = F32
  -- ^ 32-bit IEEE 754 float
  | C32
  -- ^ Complex number of two 32-bit floats
  | F64
  -- ^ 64-bit IEEE 754 double
  | C64
  -- ^ Complex number of two 64-bit doubles
  | B8
  -- ^ 8-bit boolean
  | S32
  -- ^ 32-bit signed integer
  | U32
  -- ^ 32-bit unsigned integer
  | U8
  -- ^ 8-bit unsigned integer
  | S64
  -- ^ 64-bit signed integer
  | U64
  -- ^ 64-bit unsigned integer
  | S16
  -- ^ 16-bit signed integer
  | U16
  -- ^ 16-bit unsigned integer
  deriving (Show, Eq, Enum)

fromAFType :: AFDtype -> AFDType
fromAFType (AFDtype (fromIntegral -> x)) = toEnum x

toAFType :: AFDType -> AFDtype
toAFType = AFDtype . fromIntegral . fromEnum