{-# LANGUAGE TypeOperators #-}
module Numeric.LAPACK.Matrix.Array.Unpacked where

import qualified Numeric.LAPACK.Matrix.Array.Private as ArrMatrix
import qualified Numeric.LAPACK.Matrix.Square.Basic as Square
import qualified Numeric.LAPACK.Matrix.Plain as Plain
import qualified Numeric.LAPACK.Matrix.Basic as Basic
import qualified Numeric.LAPACK.Matrix.Private as Matrix
import qualified Numeric.LAPACK.Matrix.Layout.Private as Layout
import qualified Numeric.LAPACK.Matrix.Shape.Omni as Omni
import qualified Numeric.LAPACK.Matrix.Extent.Strict as ExtentStrict
import qualified Numeric.LAPACK.Matrix.Extent as Extent
import Numeric.LAPACK.Vector (Vector)
import Numeric.LAPACK.Shape.Private (Unchecked)

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Shape ((::+))



type Unpacked property lower upper meas vert horiz height width =
         ArrMatrix.ArrayMatrix Layout.Unpacked
            property lower upper meas vert horiz height width
type Quadratic property lower upper sh =
         Unpacked property lower upper
            Extent.Shape Extent.Small Extent.Small sh sh
{- |
We do not support unit diagonal tag for trapezoids,
because unit diagonal is not preserved by multiplication of trapezoids.
-}
type LowerTrapezoid meas vert horiz height width =
         Unpacked Omni.Arbitrary Layout.Filled Layout.Empty
            meas vert horiz height width
type UpperTrapezoid meas vert horiz height width =
         Unpacked Omni.Arbitrary Layout.Empty Layout.Filled
            meas vert horiz height width


recheck ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   (Extent.Measure meas, Extent.C vert, Extent.C horiz) =>
   Unpacked property lower upper
      meas vert horiz (Unchecked height) (Unchecked width) a ->
   Unpacked property lower upper meas vert horiz height width a
recheck = ArrMatrix.liftUnpacked1 Basic.recheck

uncheck ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   (Extent.Measure meas, Extent.C vert, Extent.C horiz) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Unpacked property lower upper
      meas vert horiz (Unchecked height) (Unchecked width) a
uncheck = ArrMatrix.liftUnpacked1 Basic.uncheck


fillLower ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Unpacked property Layout.Filled upper meas vert horiz height width a
fillLower = ArrMatrix.liftUnpacked1 id

fillUpper ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Unpacked property lower Layout.Filled meas vert horiz height width a
fillUpper = ArrMatrix.liftUnpacked1 id

fillBoth ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Unpacked property Layout.Filled Layout.Filled meas vert horiz height width a
fillBoth = ArrMatrix.liftUnpacked1 id


mapExtent ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   (Extent.Measure measA, Extent.C vertA, Extent.C horizA) =>
   (Extent.Measure measB, Extent.C vertB, Extent.C horizB) =>
   Extent.Map measA vertA horizA measB vertB horizB height width ->
   Unpacked property lower upper measA vertA horizA height width a ->
   Unpacked property lower upper measB vertB horizB height width a
mapExtent = ArrMatrix.liftUnpacked1 . Plain.mapExtent . ExtentStrict.apply

transpose ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   (Extent.Measure meas, Extent.C vert, Extent.C horiz) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Unpacked property upper lower meas horiz vert width height a
transpose = ArrMatrix.liftUnpacked1 Basic.transpose

swapMultiply ::
   (Omni.Property propertyA, Omni.Strip lowerA, Omni.Strip upperA) =>
   (Omni.Property propertyB, Omni.Strip lowerB, Omni.Strip upperB) =>
   (Extent.Measure measA, Extent.C vertA, Extent.C horizA,
    Extent.Measure measB, Extent.C vertB, Extent.C horizB) =>
   (matrix ->
    Unpacked propertyA upperA lowerA measA horizA vertA widthA heightA a ->
    Unpacked propertyB upperB lowerB measB horizB vertB widthB heightB a) ->
   Unpacked propertyA lowerA upperA measA vertA horizA heightA widthA a ->
   matrix ->
   Unpacked propertyB lowerB upperB measB vertB horizB heightB widthB a
swapMultiply multiplyTrans a b = transpose $ multiplyTrans b $ transpose a


takeTopLeft ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper,
    Shape.C sh0, Shape.C sh1, Class.Floating a) =>
   Quadratic property lower upper (sh0::+sh1) a ->
   Quadratic property lower upper sh0 a
takeTopLeft =
   ArrMatrix.liftUnpacked1 $
      Basic.recheck . Square.fromFull . Basic.uncheck .
      Basic.takeTop . Basic.takeLeft . Matrix.fromFull

takeTopRight ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper,
    Shape.C sh0, Shape.C sh1, Class.Floating a) =>
   Quadratic property lower upper (sh0::+sh1) a -> ArrMatrix.General sh0 sh1 a
takeTopRight =
   ArrMatrix.liftUnpacked1 $
      Basic.takeTop . Basic.takeRight . Matrix.fromFull

takeBottomLeft ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper,
    Shape.C sh0, Shape.C sh1, Class.Floating a) =>
   Quadratic property lower upper (sh0::+sh1) a -> ArrMatrix.General sh1 sh0 a
takeBottomLeft =
   ArrMatrix.liftUnpacked1 $
      Basic.takeBottom . Basic.takeLeft . Matrix.fromFull

takeBottomRight ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper,
    Shape.C sh0, Shape.C sh1, Class.Floating a) =>
   Quadratic property lower upper (sh0::+sh1) a ->
   Quadratic property lower upper sh1 a
takeBottomRight =
   ArrMatrix.liftUnpacked1 $
      Basic.recheck . Square.fromFull . Basic.uncheck .
      Basic.takeBottom . Basic.takeRight . Matrix.fromFull


multiplyVector ::
   (Omni.Property property, Omni.Strip lower, Omni.Strip upper) =>
   (Extent.Measure meas, Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
   Unpacked property lower upper meas vert horiz height width a ->
   Vector width a -> Vector height a
multiplyVector = Basic.multiplyVector . ArrMatrix.unpackedToVector

multiply ::
   (Omni.Property propertyA, Omni.Strip lowerA, Omni.Strip upperA) =>
   (Omni.Property propertyB, Omni.Strip lowerB, Omni.Strip upperB) =>
   (Omni.Property propertyC, Omni.Strip lowerC, Omni.Strip upperC) =>
   (Extent.Measure meas, Extent.C vert, Extent.C horiz,
    Shape.C height,
    Shape.C fuse, Eq fuse,
    Shape.C width,
    Class.Floating a) =>
   Unpacked propertyA lowerA upperA meas vert horiz height fuse a ->
   Unpacked propertyB upperB lowerB meas vert horiz fuse width a ->
   Unpacked propertyC upperC lowerC meas vert horiz height width a
multiply = ArrMatrix.liftUnpacked2 Basic.multiply