```{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Matrix.Divide where

import qualified Numeric.LAPACK.Matrix.Plain.Divide as ArrDivide
import qualified Numeric.LAPACK.Matrix.Array.Basic as Basic
import qualified Numeric.LAPACK.Matrix.Array as ArrMatrix
import qualified Numeric.LAPACK.Matrix.Permutation as PermMatrix
import qualified Numeric.LAPACK.Matrix.Multiply as Multiply
import qualified Numeric.LAPACK.Matrix.Type as Type
import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Matrix.Extent.Private as Extent
import qualified Numeric.LAPACK.Vector as Vector
import Numeric.LAPACK.Matrix.Array (Full)
import Numeric.LAPACK.Matrix.Type (Matrix, scaleWithCheck)
import Numeric.LAPACK.Matrix.Modifier
(Transposition(NonTransposed,Transposed),
Inversion(Inverted))
import Numeric.LAPACK.Vector (Vector)

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Shape as Shape

import Data.Semigroup ((<>))

class
(Type.Box typ, Type.HeightOf typ ~ Type.WidthOf typ) =>
Determinant typ where
determinant :: (Class.Floating a) => Matrix typ a -> a

class (Type.Box typ, Type.HeightOf typ ~ Type.WidthOf typ) => Solve typ where
{-# MINIMAL solve | solveLeft,solveRight #-}
solve ::
(Type.HeightOf typ ~ height, Eq height, Shape.C width,
Extent.C horiz, Extent.C vert, Class.Floating a) =>
Transposition -> Matrix typ a ->
Full vert horiz height width a -> Full vert horiz height width a
solve NonTransposed a b = solveRight a b
solve Transposed a b = Basic.transpose \$ solveLeft (Basic.transpose b) a

solveRight ::
(Type.HeightOf typ ~ height, Eq height, Shape.C width,
Extent.C horiz, Extent.C vert, Class.Floating a) =>
Matrix typ a ->
Full vert horiz height width a -> Full vert horiz height width a
solveRight = solve NonTransposed

solveLeft ::
(Type.WidthOf typ ~ width, Eq width, Shape.C height,
Extent.C horiz, Extent.C vert, Class.Floating a) =>
Full vert horiz height width a ->
Matrix typ a -> Full vert horiz height width a
solveLeft = Basic.swapMultiply \$ solve Transposed

class (Solve typ, Multiply.Power typ) => Inverse typ where
inverse :: (Class.Floating a) => Matrix typ a -> Matrix typ a

infixl 7 ##/#
infixr 7 #\##

(#\##) ::
(Solve typ, Type.HeightOf typ ~ height, Eq height, Shape.C width,
Extent.C horiz, Extent.C vert, Class.Floating a) =>
Matrix typ a ->
Full vert horiz height width a -> Full vert horiz height width a
(#\##) = solveRight

(##/#) ::
(Solve typ, Type.WidthOf typ ~ width, Eq width, Shape.C height,
Extent.C horiz, Extent.C vert, Class.Floating a) =>
Full vert horiz height width a ->
Matrix typ a -> Full vert horiz height width a
(##/#) = solveLeft

solveVector ::
(Solve typ, Type.HeightOf typ ~ height, Eq height, Class.Floating a) =>
Transposition -> Matrix typ a -> Vector height a -> Vector height a
solveVector trans =
ArrMatrix.unliftColumn MatrixShape.ColumnMajor . solve trans

infixl 7 -/#
infixr 7 #\|

(#\|) ::
(Solve typ, Type.HeightOf typ ~ height, Eq height, Class.Floating a) =>
Matrix typ a -> Vector height a -> Vector height a
(#\|) = solveVector NonTransposed

(-/#) ::
(Solve typ, Type.HeightOf typ ~ height, Eq height, Class.Floating a) =>
Vector height a -> Matrix typ a -> Vector height a
(-/#) = flip \$ solveVector Transposed

instance (Shape.C shape, Eq shape) => Determinant (Type.Scale shape) where
determinant (Type.Scale sh a) = a ^ Shape.size sh

instance (Shape.C shape, Eq shape) => Solve (Type.Scale shape) where
solve _trans =
scaleWithCheck "Matrix.Scale.solve" Type.height \$
ArrMatrix.lift1 . Vector.scale . recip

instance (Shape.C shape, Eq shape) => Inverse (Type.Scale shape) where
inverse (Type.Scale shape a) = Type.Scale shape \$ recip a

instance (Shape.C shape) => Determinant (PermMatrix.Permutation shape) where
determinant = PermMatrix.determinant

instance (Shape.C shape) => Solve (PermMatrix.Permutation shape) where
solve trans =
PermMatrix.multiplyFull
(Inverted <> PermMatrix.inversionFromTransposition trans)

instance (Shape.C shape) => Inverse (PermMatrix.Permutation shape) where
inverse = PermMatrix.transpose

instance
(ArrDivide.Determinant shape) => Determinant (ArrMatrix.Array shape) where
determinant = ArrDivide.determinant . ArrMatrix.toVector

instance (ArrDivide.Solve shape) => Solve (ArrMatrix.Array shape) where
solve = ArrMatrix.lift2 . ArrDivide.solve
solveLeft = ArrMatrix.lift2 ArrDivide.solveLeft
solveRight = ArrMatrix.lift2 ArrDivide.solveRight

instance (ArrDivide.Inverse shape) => Inverse (ArrMatrix.Array shape) where
inverse = ArrMatrix.lift1 ArrDivide.inverse
```