{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Numeric.LAPACK.Matrix.Hermitian.Linear (
   determinant,
   ) where

import qualified Numeric.LAPACK.Matrix.Symmetric.Unified as Symmetric
import qualified Numeric.LAPACK.Matrix.Layout.Private as Layout
import Numeric.LAPACK.Matrix.Hermitian.Basic (HermitianP)
import Numeric.LAPACK.Matrix.Hermitian.Private (Determinant(..))
import Numeric.LAPACK.Scalar (RealOf, absoluteSquared)
import Numeric.LAPACK.Private (realPtr)

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Shape as Shape

import Foreign.Ptr (Ptr)
import Foreign.Storable (peek)


determinant ::
   (Layout.Packing pack, Shape.C sh, Class.Floating a) =>
   HermitianP pack sh a -> RealOf a
determinant =
   getDeterminant $
   Class.switchFloating
      (Determinant $ Symmetric.determinant peekBlockDeterminant)
      (Determinant $ Symmetric.determinant peekBlockDeterminant)
      (Determinant $ Symmetric.determinant peekBlockDeterminant)
      (Determinant $ Symmetric.determinant peekBlockDeterminant)

peekBlockDeterminant ::
   (Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
   (Ptr a, Maybe (Ptr a, Ptr a)) -> IO ar
peekBlockDeterminant (a0Ptr,ext) = do
   let peekReal = peek . realPtr
   a0 <- peekReal a0Ptr
   case ext of
      Nothing -> return a0
      Just (a1Ptr,bPtr) -> do
         a1 <- peekReal a1Ptr
         b <- peek bPtr
         return (a0*a1 - absoluteSquared b)