{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}
{- |
This module provides an infix operator and some data constructors
that shall resemble mathematical notation.
E.g. you can write @a#^T@ for @Matrix.transpose a@.
It is clearly abuse of the type system
because I suspect you will never write algorithms
that are generic in the superscript type.
-}
module Numeric.LAPACK.Matrix.Superscript where

import qualified Numeric.LAPACK.Singular as Singular
import qualified Numeric.LAPACK.Matrix.Shape.Omni as Omni
import qualified Numeric.LAPACK.Matrix.Layout as Layout
import qualified Numeric.LAPACK.Matrix.Extent as Extent
import qualified Numeric.LAPACK.Matrix.Array.Private as ArrMatrix
import qualified Numeric.LAPACK.Matrix as Matrix
import Numeric.LAPACK.Matrix.Layout.Private (Filled)
import Numeric.LAPACK.Matrix.Extent (Shape, Small)
import Numeric.LAPACK.Matrix (Matrix)

import qualified Data.Array.Comfort.Shape as Shape

import qualified Numeric.Netlib.Class as Class


{- |
left associative in contrast to ^, ^^ etc.
because there is no law analogous to power law @(x^a)^b = x^(a*b)@.
-}
infixl 8 #^

class Superscript sup where
   data Exponent sup
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a
   (#^) ::
      (Extent.Measure measA, Extent.C vertA, Extent.C horizA) =>
      (Shape.C widthA, Shape.C heightA) =>
      (Extent.Measure measB, Extent.C vertB, Extent.C horizB) =>
      (Shape.C widthB, Shape.C heightB) =>
      (Class.Floating a) =>
      Matrix typA xlA xuA lowerA upperA measA vertA horizA heightA widthA a ->
      Exponent sup
         typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
         typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a ->
      Matrix typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a


data None
instance Superscript None where
   data Exponent None
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         N ::
            Exponent None
               typ xl xu lower upper meas vert horiz height width
               typ xl xu lower upper meas vert horiz height width a
   a#^N = a


data Transpose
instance Superscript Transpose where
   data Exponent Transpose
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         T ::
            (Matrix.Transpose typ) =>
            Exponent Transpose
               typ xl xu lower upper meas vert horiz height width
               typ xu xl upper lower meas horiz vert width height a
   a#^T = Matrix.transpose a


data Adjoint
instance Superscript Adjoint where
   data Exponent Adjoint
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         A ::
            (Matrix.Transpose typ, Matrix.Complex typ) =>
            Exponent Adjoint
               typ xl xu lower upper meas vert horiz height width
               typ xu xl upper lower meas horiz vert width height a
   a#^A = Matrix.adjoint a


data Conjugate
instance Superscript Conjugate where
   data Exponent Conjugate
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         C ::
            (Matrix.Complex typ) =>
            Exponent Conjugate
               typ xl xu lower upper meas vert horiz height width
               typ xl xu lower upper meas vert horiz height width a
   a#^C = Matrix.conjugate a


data Inverse
instance Superscript Inverse where
   data Exponent Inverse
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         -- We could relax lowerB and upperB using Filled type function
         Inv ::
            (Matrix.Inverse typ xl xu,
             Omni.PowerStrip lower, Omni.PowerStrip upper) =>
            Exponent Inverse
               typ xl xu lower upper meas Small Small height width
               typ xl xu lower upper meas Small Small width height a
   a#^Inv = Matrix.inverse a


data PseudoInverse
instance Superscript PseudoInverse where
   data Exponent PseudoInverse
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         Pseudo ::
            (typ ~ ArrMatrix.Array Layout.Unpacked Omni.Arbitrary) =>
            Matrix.RealOf a ->
            Exponent PseudoInverse
               typ () () Filled Filled Shape Small Small sh sh
               typ () () Filled Filled Shape Small Small sh sh a
   a#^Pseudo rcond = snd $ Singular.pseudoInverseRCond rcond a


data Power
instance Superscript Power where
   data Exponent Power
      typA xlA xuA lowerA upperA measA vertA horizA heightA widthA
      typB xlB xuB lowerB upperB measB vertB horizB heightB widthB a where
         -- We could relax lowerB and upperB using Filled type function
         Exp ::
            (Matrix.Power typ xl xu,
             Omni.PowerStrip lower, Omni.PowerStrip upper) =>
            Integer ->
            Exponent Power
               typ xl xu lower upper Shape Small Small sh sh
               typ xl xu lower upper Shape Small Small sh sh a
   a#^Exp n = Matrix.power n a