module Numeric.LAPACK.Matrix.Private where

import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Matrix.Shape.Box as Box
import qualified Numeric.LAPACK.Matrix.Extent as Extent

import qualified Data.Array.Comfort.Storable.Unchecked as Array
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Storable.Unchecked (Array(Array))

import Foreign.ForeignPtr (ForeignPtr)


type Full vert horiz height width =
         Array (MatrixShape.Full vert horiz height width)

type General height width = Array (MatrixShape.General height width)
type Tall height width = Array (MatrixShape.Tall height width)
type Wide height width = Array (MatrixShape.Wide height width)
type Square sh = Array (MatrixShape.Square sh)


argGeneral ::
   (MatrixShape.Order -> height -> width -> ForeignPtr a -> b) ->
   (General height width a -> b)
argGeneral f (Array (MatrixShape.Full order extent) a) =
   f order (Extent.height extent) (Extent.width extent) a

argSquare ::
   (MatrixShape.Order -> sh -> ForeignPtr a -> b) -> (Square sh a -> b)
argSquare f (Array (MatrixShape.Full order extent) a) =
   f order (Extent.squareSize extent) a


type ZeroInt = Shape.ZeroBased Int

zeroInt :: Int -> ZeroInt
zeroInt = Shape.ZeroBased


mapExtent ::
   (Extent.C vertA, Extent.C horizA) =>
   (Extent.C vertB, Extent.C horizB) =>
   Extent.Map vertA horizA vertB horizB height width ->
   Full vertA horizA height width a -> Full vertB horizB height width a
mapExtent f = Array.mapShape $ MatrixShape.fullMapExtent f

fromFull ::
   (Extent.C vert, Extent.C horiz) =>
   Full vert horiz height width a -> General height width a
fromFull = mapExtent Extent.toGeneral

generalizeTall ::
   (Extent.C vert, Extent.C horiz) =>
   Full vert Extent.Small height width a -> Full vert horiz height width a
generalizeTall = mapExtent Extent.generalizeTall

generalizeWide ::
   (Extent.C vert, Extent.C horiz) =>
   Full Extent.Small horiz height width a -> Full vert horiz height width a
generalizeWide = mapExtent Extent.generalizeWide


height :: (Box.Box shape) => Array shape a -> Box.HeightOf shape
height = Box.height . Array.shape

width :: (Box.Box shape) => Array shape a -> Box.WidthOf shape
width = Box.width . Array.shape


revealOrder ::
   (Extent.C horiz, Extent.C vert) =>
   Full vert horiz height width a ->
   Either (Array (height,width) a) (Array (width,height) a)
revealOrder (Array (MatrixShape.Full order extent) a) =
   let (h,w) = Extent.dimensions extent
   in case order of
         MatrixShape.RowMajor -> Left $ Array (h,w) a
         MatrixShape.ColumnMajor -> Right $ Array (w,h) a