{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Numeric.LAPACK.Matrix (
   Type.Matrix,
   Full,
   General, Tall, Wide, Square.Square,
   Triangular.Triangular, Triangular.Upper, Triangular.Lower,
   Triangular.Diagonal, Triangular.Symmetric,
   Hermitian.Hermitian,
   PermMatrix.Permutation,

   ZeroInt, zeroInt,
   transpose, adjoint,
   Type.height, Type.width,
   Type.HeightOf, Type.WidthOf,
   Type.Box, Type.indices,
   ArrMatrix.reshape,
   ArrMatrix.mapShape,
   caseTallWide,
   fromScalar, toScalar,
   fromList,
   mapExtent, fromFull,
   tallFromGeneral, wideFromGeneral,
   generalizeTall, generalizeWide,
   mapHeight, mapWidth,
   identity,
   diagonal,
   fromRowsNonEmpty,    fromRowArray,    fromRows,
   fromColumnsNonEmpty, fromColumnArray, fromColumns,
   singleRow,   singleColumn,
   flattenRow,  flattenColumn,
   liftRow,     liftColumn,
   unliftRow,   unliftColumn,
   toRows, toColumns,
   toRowArray, toColumnArray,
   takeRow, takeColumn,
   takeRows, takeColumns, takeEqually,
   dropRows, dropColumns, dropEqually,
   takeTop, takeBottom,
   takeLeft, takeRight,
   takeRowArray, takeColumnArray,
   swapRows, swapColumns,
   reverseRows, reverseColumns,
   fromRowMajor, toRowMajor, flatten,
   ArrMatrix.forceOrder, ArrMatrix.adaptOrder,
   Plain.OrderBias, leftBias, rightBias, contiguousBias,
   (|||), beside,
   (===), above,

   (|*-),
   tensorProduct,
   outer,
   kronecker,
   sumRank1,

   map,
   Type.Complex, Type.conjugate, Type.fromReal, Type.toComplex,

   RealOf,
   rowSums, columnSums,
   rowArgAbsMaximums, columnArgAbsMaximums,
   scaleRows, scaleColumns,
   scaleRowsReal, scaleColumnsReal,
   (\*#), (#*\),
   multiply,
   multiplyVector,

   ArrMatrix.zero, ArrMatrix.negate,
   ArrMatrix.scale, ArrMatrix.scaleReal, ArrMatrix.scaleRealReal,
   (ArrMatrix..*#),
   ArrMatrix.add, ArrMatrix.sub,
   (ArrMatrix.#+#), (ArrMatrix.#-#),
   Multiply.Multiply, (Multiply.#*#),
   Multiply.MultiplyLeft, (Multiply.-*#),
   Multiply.MultiplyRight, (Multiply.#*|),
   Multiply.MultiplySquare, multiplySquare,
   (Multiply.##*#), (Multiply.#*##),
   Indexed.Indexed, (Indexed.#!),

   Divide.Solve, Divide.solve, Divide.solveLeft, Divide.solveRight,
   (Divide.##/#), (Divide.#\##),
   Divide.solveVector, (Divide.-/#), (Divide.#\|),
   Divide.Inverse, Divide.inverse,
   Mod.Transposition(..),
   ) where

import qualified Numeric.LAPACK.Matrix.Permutation as PermMatrix
import qualified Numeric.LAPACK.Matrix.Triangular as Triangular
import qualified Numeric.LAPACK.Matrix.Hermitian as Hermitian
import qualified Numeric.LAPACK.Matrix.Square as Square

import qualified Numeric.LAPACK.Matrix.Extent as Extent
import qualified Numeric.LAPACK.Matrix.Basic as Basic
import qualified Numeric.LAPACK.Matrix.Array as ArrMatrix
import qualified Numeric.LAPACK.Matrix.Type as Type
import qualified Numeric.LAPACK.Matrix.Plain as Plain
import qualified Numeric.LAPACK.Matrix.Modifier as Mod
import qualified Numeric.LAPACK.Matrix.Divide as Divide
import qualified Numeric.LAPACK.Matrix.Multiply as Multiply
import qualified Numeric.LAPACK.Matrix.Indexed as Indexed
import Numeric.LAPACK.Matrix.Shape.Private (Order)
import Numeric.LAPACK.Matrix.Array (Full, General, Tall, Wide)
import Numeric.LAPACK.Matrix.Private (ZeroInt, zeroInt)
import Numeric.LAPACK.Vector (Vector)
import Numeric.LAPACK.Scalar (RealOf)

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Storable as Array
import qualified Data.Array.Comfort.Boxed as BoxedArray
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Storable.Unchecked (Array)
import Data.Array.Comfort.Shape ((:+:))

import Foreign.Storable (Storable)

import qualified Data.NonEmpty as NonEmpty
import qualified Data.Either.HT as EitherHT

import Prelude hiding (map)


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 = ArrMatrix.lift1 . Plain.mapExtent

fromFull ::
   (Extent.C vert, Extent.C horiz) =>
   Full vert horiz height width a -> General height width a
fromFull = ArrMatrix.lift1 Plain.fromFull

tallFromGeneral ::
   (Shape.C height, Shape.C width, Storable a) =>
   General height width a -> Tall height width a
tallFromGeneral = ArrMatrix.lift1 Plain.tallFromGeneral

wideFromGeneral ::
   (Shape.C height, Shape.C width, Storable a) =>
   General height width a -> Wide height width a
wideFromGeneral = ArrMatrix.lift1 Plain.wideFromGeneral

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



fromScalar :: (Storable a) => a -> General () () a
fromScalar = ArrMatrix.lift0 . Plain.fromScalar

toScalar :: (Storable a) => General () () a -> a
toScalar = Plain.toScalar . ArrMatrix.toVector

fromList ::
   (Shape.C height, Shape.C width, Storable a) =>
   height -> width -> [a] -> General height width a
fromList height width = ArrMatrix.lift0 . Plain.fromList height width


identity ::
   (Shape.C sh, Class.Floating a) =>
   sh -> General sh sh a
identity = ArrMatrix.lift0 . Plain.identity

diagonal ::
   (Shape.C sh, Class.Floating a) =>
   Vector sh a -> General sh sh a
diagonal = ArrMatrix.lift0 . Plain.diagonal


{- |
Square matrices will be classified as 'Tall'.
-}
caseTallWide ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width) =>
   Full vert horiz height width a ->
   Either (Tall height width a) (Wide height width a)
caseTallWide =
   EitherHT.mapBoth ArrMatrix.lift0 ArrMatrix.lift0 .
   Basic.caseTallWide . ArrMatrix.toVector


transpose ::
   (Extent.C vert, Extent.C horiz) =>
   Full vert horiz height width a -> Full horiz vert width height a
transpose = ArrMatrix.lift1 Basic.transpose

{- |
conjugate transpose

Problem: @adjoint a \<\> a@ is always square,
but how to convince the type checker to choose the Square type?
Anser: Use @Hermitian.toSquare $ Hermitian.gramian a@ instead.
-}
adjoint ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width,
    Class.Floating a) =>
   Full vert horiz height width a -> Full horiz vert width height a
adjoint = ArrMatrix.lift1 Basic.adjoint


{- |
The number of rows must be maintained by the height mapping function.
-}
mapHeight ::
   (Shape.C heightA, Shape.C heightB,
    Extent.GeneralTallWide vert horiz,
    Extent.GeneralTallWide horiz vert) =>
   (heightA -> heightB) ->
   Full vert horiz heightA width a ->
   Full vert horiz heightB width a
mapHeight = ArrMatrix.lift1 . Plain.mapHeight

{- |
The number of columns must be maintained by the width mapping function.
-}
mapWidth ::
   (Shape.C widthA, Shape.C widthB,
    Extent.GeneralTallWide vert horiz,
    Extent.GeneralTallWide horiz vert) =>
   (widthA -> widthB) ->
   Full vert horiz height widthA a ->
   Full vert horiz height widthB a
mapWidth = ArrMatrix.lift1 . Plain.mapWidth


singleRow :: Order -> Vector width a -> General () width a
singleRow order = ArrMatrix.lift0 . Basic.singleRow order

singleColumn :: Order -> Vector height a -> General height () a
singleColumn order = ArrMatrix.lift0 . Basic.singleColumn order

flattenRow :: General () width a -> Vector width a
flattenRow = Basic.flattenRow . ArrMatrix.toVector

flattenColumn :: General height () a -> Vector height a
flattenColumn = Basic.flattenColumn . ArrMatrix.toVector

liftRow ::
   Order ->
   (Vector height0 a -> Vector height1 b) ->
   General () height0 a -> General () height1 b
liftRow order = ArrMatrix.lift1 . Basic.liftRow order

liftColumn ::
   Order ->
   (Vector height0 a -> Vector height1 b) ->
   General height0 () a -> General height1 () b
liftColumn order = ArrMatrix.lift1 . Basic.liftColumn order

unliftRow ::
   Order ->
   (General () height0 a -> General () height1 b) ->
   Vector height0 a -> Vector height1 b
unliftRow order = Basic.unliftRow order .  ArrMatrix.unlift1

unliftColumn ::
   Order ->
   (General height0 () a -> General height1 () b) ->
   Vector height0 a -> Vector height1 b
unliftColumn order = Basic.unliftColumn order . ArrMatrix.unlift1


fromRowsNonEmpty ::
   (Shape.C width, Eq width, Storable a) =>
   NonEmpty.T [] (Vector width a) -> General ZeroInt width a
fromRowsNonEmpty = ArrMatrix.lift0 . Plain.fromRowsNonEmpty

fromRowArray ::
   (Shape.C height, Shape.C width, Eq width, Storable a) =>
   width -> BoxedArray.Array height (Vector width a) -> General height width a
fromRowArray width = ArrMatrix.lift0 . Plain.fromRowArray width

fromRows ::
   (Shape.C width, Eq width, Storable a) =>
   width -> [Vector width a] -> General ZeroInt width a
fromRows width = ArrMatrix.lift0 . Plain.fromRows width

fromColumnsNonEmpty ::
   (Shape.C height, Eq height, Storable a) =>
   NonEmpty.T [] (Vector height a) -> General height ZeroInt a
fromColumnsNonEmpty = ArrMatrix.lift0 . Plain.fromColumnsNonEmpty

fromColumnArray ::
   (Shape.C height, Eq height, Shape.C width, Storable a) =>
   height -> BoxedArray.Array width (Vector height a) -> General height width a
fromColumnArray height = ArrMatrix.lift0 . Plain.fromColumnArray height

fromColumns ::
   (Shape.C height, Eq height, Storable a) =>
   height -> [Vector height a] -> General height ZeroInt a
fromColumns height = ArrMatrix.lift0 . Plain.fromColumns height


toRows ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> [Vector width a]
toRows = Plain.toRows . ArrMatrix.toVector

toColumns ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> [Vector height a]
toColumns = Plain.toColumns . ArrMatrix.toVector

toRowArray ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> BoxedArray.Array height (Vector width a)
toRowArray = Plain.toRowArray . ArrMatrix.toVector

toColumnArray ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> BoxedArray.Array width (Vector height a)
toColumnArray = Plain.toColumnArray . ArrMatrix.toVector


takeRow ::
   (Extent.C vert, Extent.C horiz,
    Shape.Indexed height, Shape.C width, Shape.Index height ~ ix,
    Class.Floating a) =>
   Full vert horiz height width a -> ix -> Vector width a
takeRow = Plain.takeRow . ArrMatrix.toVector

takeColumn ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.Indexed width, Shape.Index width ~ ix,
    Class.Floating a) =>
   Full vert horiz height width a -> ix -> Vector height a
takeColumn = Plain.takeColumn . ArrMatrix.toVector


takeTop ::
   (Extent.C vert, Shape.C height0, Shape.C height1, Shape.C width,
    Class.Floating a) =>
   Full vert Extent.Big (height0:+:height1) width a ->
   Full vert Extent.Big height0 width a
takeTop = ArrMatrix.lift1 Basic.takeTop

takeBottom ::
   (Extent.C vert, Shape.C height0, Shape.C height1, Shape.C width,
    Class.Floating a) =>
   Full vert Extent.Big (height0:+:height1) width a ->
   Full vert Extent.Big height1 width a
takeBottom = ArrMatrix.lift1 Basic.takeBottom

takeLeft ::
   (Extent.C vert, Shape.C height, Shape.C width0, Shape.C width1,
    Class.Floating a) =>
   Full Extent.Big vert height (width0:+:width1) a ->
   Full Extent.Big vert height width0 a
takeLeft = ArrMatrix.lift1 Basic.takeLeft

takeRight ::
   (Extent.C vert, Shape.C height, Shape.C width0, Shape.C width1,
    Class.Floating a) =>
   Full Extent.Big vert height (width0:+:width1) a ->
   Full Extent.Big vert height width1 a
takeRight = ArrMatrix.lift1 Basic.takeRight

takeRows, dropRows ::
   (Extent.C vert, Shape.C width, Class.Floating a) =>
   Int ->
   Full vert Extent.Big ZeroInt width a ->
   Full vert Extent.Big ZeroInt width a
takeRows = ArrMatrix.lift1 . Basic.takeRows
dropRows = ArrMatrix.lift1 . Basic.dropRows

takeColumns, dropColumns ::
   (Extent.C horiz, Shape.C height, Class.Floating a) =>
   Int ->
   Full Extent.Big horiz height ZeroInt a ->
   Full Extent.Big horiz height ZeroInt a
takeColumns = ArrMatrix.lift1 . Basic.takeColumns
dropColumns = ArrMatrix.lift1 . Basic.dropColumns


{- |
Take a left-top aligned square or as much as possible of it.
The advantange of this function is that it maintains the matrix size relation,
e.g. Square remains Square, Tall remains Tall.
-}
takeEqually ::
   (Extent.C vert, Extent.C horiz, Class.Floating a) =>
   Int ->
   Full vert horiz ZeroInt ZeroInt a ->
   Full vert horiz ZeroInt ZeroInt a
takeEqually = ArrMatrix.lift1 . Plain.takeEqually

{- |
Drop the same number of top-most rows and left-most columns.
The advantange of this function is that it maintains the matrix size relation,
e.g. Square remains Square, Tall remains Tall.
-}
dropEqually ::
   (Extent.C vert, Extent.C horiz, Class.Floating a) =>
   Int ->
   Full vert horiz ZeroInt ZeroInt a ->
   Full vert horiz ZeroInt ZeroInt a
dropEqually = ArrMatrix.lift1 . Plain.dropEqually


swapRows ::
   (Extent.C vert, Extent.C horiz,
    Shape.Indexed height, Shape.C width, Class.Floating a) =>
   Shape.Index height -> Shape.Index height ->
   Full vert horiz height width a -> Full vert horiz height width a
swapRows i j = ArrMatrix.lift1 $ Plain.swapRows i j

swapColumns ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.Indexed width, Class.Floating a) =>
   Shape.Index width -> Shape.Index width ->
   Full vert horiz height width a -> Full vert horiz height width a
swapColumns i j =  ArrMatrix.lift1 $ Plain.swapColumns i j


reverseRows ::
   (Extent.C vert, Extent.C horiz, Shape.C width, Class.Floating a) =>
   Full vert horiz ZeroInt width a -> Full vert horiz ZeroInt width a
reverseRows = ArrMatrix.lift1 Plain.reverseRows

reverseColumns ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Class.Floating a) =>
   Full vert horiz height ZeroInt a -> Full vert horiz height ZeroInt a
reverseColumns = ArrMatrix.lift1 Plain.reverseColumns


{- |
The function is optimized for blocks of consecutive rows.
For scattered rows in column major order
the function has quite ugly memory access patterns.
-}
takeRowArray ::
   (Shape.Indexed height, Shape.C width, Shape.C sh, Class.Floating a) =>
   BoxedArray.Array sh (Shape.Index height) ->
   General height width a -> General sh width a
takeRowArray = ArrMatrix.lift1 . Plain.takeRowArray

takeColumnArray ::
   (Shape.C height, Shape.Indexed width, Shape.C sh, Class.Floating a) =>
   BoxedArray.Array sh (Shape.Index width) ->
   General height width a -> General height sh a
takeColumnArray = ArrMatrix.lift1 . Plain.takeColumnArray



fromRowMajor ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   Array (height,width) a -> General height width a
fromRowMajor = ArrMatrix.lift0 . Plain.fromRowMajor

toRowMajor ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> Array (height,width) a
toRowMajor = Plain.toRowMajor . ArrMatrix.toVector

flatten ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> Vector ZeroInt a
flatten = Plain.flatten . ArrMatrix.toVector


infixr 3 |||
infixr 2 ===

(|||) ::
   (Extent.C vertA, Extent.C vertB, Extent.C vertC,
    Extent.Append vertA vertB ~ vertC,
    Shape.C height, Eq height, Shape.C widthA, Shape.C widthB,
    Class.Floating a) =>
   Full vertA Extent.Big height widthA a ->
   Full vertB Extent.Big height widthB a ->
   Full vertC Extent.Big height (widthA:+:widthB) a
(|||) = beside rightBias Extent.appendAny

(===) ::
   (Extent.C horizA, Extent.C horizB, Extent.C horizC,
    Extent.Append horizA horizB ~ horizC,
    Shape.C width, Eq width, Shape.C heightA, Shape.C heightB,
    Class.Floating a) =>
   Full Extent.Big horizA heightA width a ->
   Full Extent.Big horizB heightB width a ->
   Full Extent.Big horizC (heightA:+:heightB) width a
(===) = above rightBias Extent.appendAny

beside ::
   (Extent.C vertA, Extent.C vertB, Extent.C vertC,
    Shape.C height, Eq height, Shape.C widthA, Shape.C widthB,
    Class.Floating a) =>
   Plain.OrderBias ->
   Extent.AppendMode vertA vertB vertC height widthA widthB ->
   Full vertA Extent.Big height widthA a ->
   Full vertB Extent.Big height widthB a ->
   Full vertC Extent.Big height (widthA:+:widthB) a
beside orderBias = ArrMatrix.lift2 . Plain.beside orderBias

above ::
   (Extent.C horizA, Extent.C horizB, Extent.C horizC,
    Shape.C width, Eq width, Shape.C heightA, Shape.C heightB,
    Class.Floating a) =>
   Plain.OrderBias ->
   Extent.AppendMode horizA horizB horizC width heightA heightB ->
   Full Extent.Big horizA heightA width a ->
   Full Extent.Big horizB heightB width a ->
   Full Extent.Big horizC (heightA:+:heightB) width a
above orderBias = ArrMatrix.lift2 . Plain.above orderBias

{- |
Use the element order of the first operand.
-}
leftBias :: Plain.OrderBias
leftBias = Plain.LeftBias

{- |
Use the element order of the second operand.
-}
rightBias :: Plain.OrderBias
rightBias = Plain.RightBias

{- |
Choose element order such that, if possible,
one part can be copied as one block.
For 'above' this means that 'RowMajor' is chosen
whenever at least one operand is 'RowMajor'
and 'ColumnMajor' is chosen when both operands are 'ColumnMajor'.
-}
contiguousBias :: Plain.OrderBias
contiguousBias = Plain.ContiguousBias


rowSums ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> Vector height a
rowSums = Plain.rowSums . ArrMatrix.toVector

columnSums ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Class.Floating a) =>
   Full vert horiz height width a -> Vector width a
columnSums = Plain.columnSums . ArrMatrix.toVector


rowArgAbsMaximums ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.InvIndexed width, Shape.Index width ~ ix, Storable ix,
    Class.Floating a) =>
   Full vert horiz height width a -> (Vector height ix, Vector height a)
rowArgAbsMaximums = Plain.rowArgAbsMaximums . ArrMatrix.toVector

columnArgAbsMaximums ::
   (Extent.C vert, Extent.C horiz,
    Shape.InvIndexed height, Shape.C width,
    Shape.Index height ~ ix, Storable ix,
    Class.Floating a) =>
   Full vert horiz height width a -> (Vector width ix, Vector width a)
columnArgAbsMaximums = Plain.columnArgAbsMaximums . ArrMatrix.toVector


map ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width,
    Storable a, Storable b) =>
   (a -> b) ->
   Full vert horiz height width a -> Full vert horiz height width b
map = ArrMatrix.lift1 . Array.map


infixl 7 |*-

(|*-) ::
   (Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
   Vector height a -> Vector width a -> General height width a
x|*-y = ArrMatrix.lift0 $ x Plain.|*- y

{- |
> tensorProduct order x y = singleColumn order x #*# singleRow order y
-}
tensorProduct ::
   (Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
   Order -> Vector height a -> Vector width a -> General height width a
tensorProduct order x y = ArrMatrix.lift0 $ Plain.tensorProduct order x y

{- |
> outer order x y = tensorProduct order x (Vector.conjugate y)
-}
outer ::
   (Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
   Order -> Vector height a -> Vector width a -> General height width a
outer order x y = ArrMatrix.lift0 $ Plain.outer order x y

kronecker ::
   (Extent.C vert, Extent.C horiz,
    Shape.C heightA, Shape.C widthA, Shape.C heightB, Shape.C widthB,
    Class.Floating a) =>
   Full vert horiz heightA widthA a ->
   Full vert horiz heightB widthB a ->
   Full vert horiz (heightA,heightB) (widthA,widthB) a
kronecker = ArrMatrix.lift2 Plain.kronecker

sumRank1 ::
   (Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
   (height,width) ->
   [(a, (Vector height a, Vector width a))] -> General height width a
sumRank1 dims = ArrMatrix.lift0 . Plain.sumRank1 dims


infixl 7 #*\
infixr 7 \*#

scaleRows, (\*#) ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Eq height, Shape.C width, Class.Floating a) =>
   Vector height a ->
   Full vert horiz height width a ->
   Full vert horiz height width a
scaleRows = ArrMatrix.lift1 . Basic.scaleRows
(\*#) = scaleRows

scaleColumns ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
   Vector width a ->
   Full vert horiz height width a ->
   Full vert horiz height width a
scaleColumns = ArrMatrix.lift1 . Basic.scaleColumns

(#*\) ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
   Full vert horiz height width a ->
   Vector width a ->
   Full vert horiz height width a
(#*\) = flip scaleColumns

scaleRowsReal ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Eq height, Shape.C width,
    Class.Floating a) =>
   Vector height (RealOf a) ->
   Full vert horiz height width a ->
   Full vert horiz height width a
scaleRowsReal = ArrMatrix.lift1 . Basic.scaleRowsReal

scaleColumnsReal ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
   Vector width (RealOf a) ->
   Full vert horiz height width a ->
   Full vert horiz height width a
scaleColumnsReal = ArrMatrix.lift1 . Basic.scaleColumnsReal


multiplySquare ::
   (Multiply.MultiplySquare typ,
    Type.HeightOf typ ~ height, Eq height, Shape.C width,
    Extent.C horiz, Extent.C vert, Class.Floating a) =>
   Mod.Transposition -> Type.Matrix typ a ->
   Full vert horiz height width a -> Full vert horiz height width a
multiplySquare = Multiply.transposableSquare

multiplyVector ::
   (Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width, Eq width,
    Class.Floating a) =>
   Full vert horiz height width a -> Vector width a -> Vector height a
multiplyVector = Basic.multiplyVector . ArrMatrix.toVector

multiply ::
   (Extent.C vert, Extent.C horiz,
    Shape.C height,
    Shape.C fuse, Eq fuse,
    Shape.C width,
    Class.Floating a) =>
   Full vert horiz height fuse a ->
   Full vert horiz fuse width a ->
   Full vert horiz height width a
multiply = ArrMatrix.lift2 Basic.multiply