{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Shape where

import qualified Data.Array.Comfort.Shape as Shape
import Data.Tagged (Tagged)
import Data.Ix (Ix)

import Control.DeepSeq (NFData, rnf)


{- |
Class of shapes where indices still make sense if we permute elements.
We use this for all matrix factorisations
involving permutations or more generally orthogonal transformations,
e.g. eigenvalue and singular value, LU and QR decompositions.
E.g. say, we have a square matrix with dimension 'Shape.Enumeration Ordering'.
Its vector of eigenvalues has the same dimension,
but it does not make sense to access the eigenvalues
with indices like 'LT' or 'EQ'.
Thus 'Shape.Enumeration' is no instance of 'Permutable'
(and you should not add an orphan instance).

If you want to factor a matrix with a non-permutable shape,
you should convert it temporarily to a permutable one,
like 'Shape.ZeroBased' (i.e. 'Matrix.ShapeInt') or 'IntIndexed'.

The 'Permutable' class has no method, so you could add any shape to it.
However, you should use good taste when adding an instance.
There is no strict criterion which shape type to add.

We tried to use 'ShapeInt' for eigenvalue vectors
and 'LiberalSquare's as transformation matrices of eigenvalue decompositions.
However, this way, the type checker cannot infer
that the product of the factorisation is a strict square.

We also tried to use 'Shape.IntIndexed' for eigenvalue vectors
with according 'LiberalSquare's transformations.
This has also the problem of inferring squares.
Additionally,
more such transformations lead to nested 'Shape.IntIndexed' wrappers
and for 'Matrix.ShapeInt' even the first wrapper is unnecessary.
-}
class (Shape.C shape) => Permutable shape where
instance Permutable Shape.Zero where
instance Permutable () where
instance (Ix n) => Permutable (Shape.Range n) where
instance (Integral n) => Permutable (Shape.Shifted n) where
instance (Integral n) => Permutable (Shape.ZeroBased n) where
instance (Integral n) => Permutable (Shape.OneBased n) where
instance (Integral n) => Permutable (Shape.Cyclic n) where
instance (Permutable sh) => Permutable (Shape.Deferred sh) where
instance (Permutable sh) => Permutable (Tagged s sh) where


{- |
This shape type wraps any other array shape type.
However, its 'Shape.Indexed' instance just uses zero-based 'Int' indices.
Thus it can turn any shape type into a 'Shape.Indexed' one.
The main usage is to make an arbitrary shape 'Permutable'.
-}
newtype IntIndexed sh = IntIndexed {IntIndexed sh -> sh
deconsIntIndexed :: sh}
   deriving (IntIndexed sh -> IntIndexed sh -> Bool
(IntIndexed sh -> IntIndexed sh -> Bool)
-> (IntIndexed sh -> IntIndexed sh -> Bool) -> Eq (IntIndexed sh)
forall sh. Eq sh => IntIndexed sh -> IntIndexed sh -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: IntIndexed sh -> IntIndexed sh -> Bool
$c/= :: forall sh. Eq sh => IntIndexed sh -> IntIndexed sh -> Bool
== :: IntIndexed sh -> IntIndexed sh -> Bool
$c== :: forall sh. Eq sh => IntIndexed sh -> IntIndexed sh -> Bool
Eq, Int -> IntIndexed sh -> ShowS
[IntIndexed sh] -> ShowS
IntIndexed sh -> String
(Int -> IntIndexed sh -> ShowS)
-> (IntIndexed sh -> String)
-> ([IntIndexed sh] -> ShowS)
-> Show (IntIndexed sh)
forall sh. Show sh => Int -> IntIndexed sh -> ShowS
forall sh. Show sh => [IntIndexed sh] -> ShowS
forall sh. Show sh => IntIndexed sh -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [IntIndexed sh] -> ShowS
$cshowList :: forall sh. Show sh => [IntIndexed sh] -> ShowS
show :: IntIndexed sh -> String
$cshow :: forall sh. Show sh => IntIndexed sh -> String
showsPrec :: Int -> IntIndexed sh -> ShowS
$cshowsPrec :: forall sh. Show sh => Int -> IntIndexed sh -> ShowS
Show)


instance (NFData sh) => NFData (IntIndexed sh) where
   rnf :: IntIndexed sh -> ()
rnf (IntIndexed sh
sh) = sh -> ()
forall a. NFData a => a -> ()
rnf sh
sh

instance (Shape.C sh) => Shape.C (IntIndexed sh) where
   size :: IntIndexed sh -> Int
size (IntIndexed sh
sh) = sh -> Int
forall sh. C sh => sh -> Int
Shape.size sh
sh

shapeInt :: (Shape.C sh) => sh -> Shape.ZeroBased Int
shapeInt :: sh -> ZeroBased Int
shapeInt = Int -> ZeroBased Int
forall n. n -> ZeroBased n
Shape.ZeroBased (Int -> ZeroBased Int) -> (sh -> Int) -> sh -> ZeroBased Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. sh -> Int
forall sh. C sh => sh -> Int
Shape.size

instance (Shape.C sh) => Shape.Indexed (IntIndexed sh) where
   type Index (IntIndexed sh) = Int
   indices :: IntIndexed sh -> [Index (IntIndexed sh)]
indices (IntIndexed sh
sh) = Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take (sh -> Int
forall sh. C sh => sh -> Int
Shape.size sh
sh) [Int
0 ..]
   unifiedOffset :: IntIndexed sh -> Index (IntIndexed sh) -> Result check Int
unifiedOffset (IntIndexed sh
sh) = ZeroBased Int -> Index (ZeroBased Int) -> Result check Int
forall sh check.
(Indexed sh, Checking check) =>
sh -> Index sh -> Result check Int
Shape.unifiedOffset (sh -> ZeroBased Int
forall sh. C sh => sh -> ZeroBased Int
shapeInt sh
sh)
   unifiedSizeOffset :: IntIndexed sh -> (Int, Index (IntIndexed sh) -> Result check Int)
unifiedSizeOffset (IntIndexed sh
sh) =
      ZeroBased Int -> (Int, Index (ZeroBased Int) -> Result check Int)
forall sh check.
(Indexed sh, Checking check) =>
sh -> (Int, Index sh -> Result check Int)
Shape.unifiedSizeOffset (sh -> ZeroBased Int
forall sh. C sh => sh -> ZeroBased Int
shapeInt sh
sh)
   inBounds :: IntIndexed sh -> Index (IntIndexed sh) -> Bool
inBounds (IntIndexed sh
sh) Index (IntIndexed sh)
k =
      ZeroBased Int -> Index (ZeroBased Int) -> Bool
forall sh. Indexed sh => sh -> Index sh -> Bool
Shape.inBounds (Int -> ZeroBased Int
forall n. n -> ZeroBased n
Shape.ZeroBased (Int -> ZeroBased Int) -> Int -> ZeroBased Int
forall a b. (a -> b) -> a -> b
$ sh -> Int
forall sh. C sh => sh -> Int
Shape.size sh
sh) Index (ZeroBased Int)
Index (IntIndexed sh)
k

instance (Shape.C sh) => Shape.InvIndexed (IntIndexed sh) where
   unifiedIndexFromOffset :: IntIndexed sh -> Int -> Result check (Index (IntIndexed sh))
unifiedIndexFromOffset (IntIndexed sh
sh) =
      ZeroBased Int -> Int -> Result check (Index (ZeroBased Int))
forall sh check.
(InvIndexed sh, Checking check) =>
sh -> Int -> Result check (Index sh)
Shape.unifiedIndexFromOffset (sh -> ZeroBased Int
forall sh. C sh => sh -> ZeroBased Int
shapeInt sh
sh)

instance (Shape.C sh) => Permutable (IntIndexed sh) where