# [System] Matrices

#### looking_for_help

Level 14
Matrix operations are a very basic concept in Mathematics and many problems can be solved easy and elegant with them. Unfortunatly Warcraft 3 does not provide any of them, so this system aims to close this gap and provide various methods to deal with matrices.

Enclosed is the system code:

JASS:
``````library Matrices /* v 1.4.0.0
**********************************************************************************
*
*   Matrices
*   ¯¯¯¯¯¯¯¯
*   By looking_for_help aka eey
*
*   This system provides advanced methods for handling Matrices in Warcraft 3.
*   Features like Matrix multiplication, calculation of norms or trace as well as
*   solving a system of linear equations with Gauss-Elimination are implemented.
*   The system also provides a big variety of functions to initialize, copy or
*   reshape Matrices.
*
*   Credits go to Magtheridon for helping with the Systems API.
*
**********************************************************************************
*
*   Requirements
*   ¯¯¯¯¯¯¯¯¯¯¯¯
*   */  uses Maths   /*  hiveworkshop.com/forums/spells-569/advanced-maths-ingame-calculator-234024/?prev=r%3D20%26page%3D5
*
**********************************************************************************
*
*   Implementation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   To use this system just copy it into an empty trigger in your map. As this
*   system uses the Math library you should install it first to get the system
*   to work. The Math library can be found under the link above.
*
**********************************************************************************
*
*   System API
*   ¯¯¯¯¯¯¯¯¯¯
*
*   Struct usage
*   -----------------------------
*
*       struct Matrix
*           - This struct provides the methods and logic to use Matrices in
*             Warcraft 3 with this system. Use the create method to allocate a
*             new instance of a Matrix of your desired size. The Matrix can then
*             be used like a 2D-Array (example displayed above) and provides
*             various methods to perform advanced Matrix operations.
*
*                                                          0  1  2
*                                                       0 [0][0][0]
*             local Matrix mat = Matrix.create(3, 3) =  1 [0][0][0]
*                                                       2 [0][0][0]
*
*
*   Operators
*   -----------------------------
*
*       [] operators
*           - Use the [] operators to access directly to the values of a given
*             Matrix. For example mat[2][1] will return the element in the third
*             row, second column. Note that the indices are, like Wc3 arrays,
*             zero-based.
*
*
*   Fields
*   -----------------------------
*
*           - Specifies the number of rows of a given Matrix.
*
*           - Specifies the number of columns of a given Matrix.
*
*           - An invalid Matrix of the size 0 x 0. You can't create such a Matrix,
*             so if you need one, use this field. Some algorithms, such as solveSLE
*             return such a Matrix if no solution for a given System of Linear
*             Equations could be found. You can use the method isValid() to
*             determine wether a Matrix is valid or not.
*
*       static constant integer ONE_NORM
*           - Use this as a parameter for the norm method to specify that the method
*             should calculate the one norm (maximum of column sum absolute values).
*
*       static constant integer EUCLIDEAN_NORM
*           - Use this as a parameter for the norm method to specify that the method
*             should calculate the euclidean norm (square root of sum of squares).
*
*       static constant integer INFINITY_NORM
*           - Use this as a parameter for the norm method to specify that the method
*             should calculate the infinity norm (maximum of row sum absolute values).
*
*       static constant integer METHOD_ROW_WISE
*           - Use this as a parameter for methods like initStepWise or reShape to
*             specify whether the method should be applied row-wise.
*
*       static constant integer METHOD_COL_WISE
*           - Use this as a parameter for methods like initStepWise or reShape to
*             specify whether the method should be applied col-wise.
*
*
*   Methods
*   -----------------------------
*
*       static method create takes integer nDim, integer mDim returns Matrix
*           - Creates a new n x m Matrix with the maximum size of MAX_MATRIX_SIZE
*             (specified in the globals block). The Matrix is initialized with
*             zeros.
*
*       method destroy takes nothing returns nothing
*           - Destroys a given Matrix to free its memory usage.
*
*       method display takes nothing returns nothing
*           - Use this method to display a Matrix ingame. This is especially
*             meant for debugging issues.
*
*       method isValid takes nothing returns boolean
*           - Checks whether a given Matrix is valid (means: not empty) or not.
*
*       method isEqual takes Matrix mat returns boolean
*           - Checks whether two Matrices are equal or not. If A == B then
*             A.isEqual(B) returns true.
*
*       method init takes real value returns nothing
*           - Use this method to initialize a Matrix with a desired real value.
*             Note that this method does not create a new instance of the Matrix
*             object, as well as all methods that only initialize the values of a
*             Matrix.
*
*       method eye takes nothing returns nothing
*           - Use this method to initialize an Identity Matrix.
*
*       method diag takes real value, integer whichDiagonal returns nothing
*           - Use this method to initialize a Diagonal Matrix with a desired real
*             value. Use the second argument to specify which diagonal you want
*             to set (zero is the main diagonal, negative values address the
*             upper, positive values the lower diagonals).
*
*       method rand takes real lowerBound, real upperBound returns nothing
*           - Use this method to initialize a Matrix with random real values.
*
*       method initStepWise takes real startValue, real steps returns nothing
*           - Use this method to initialize a matrix from a given start value in
*             ascending or descending order specified by the steps parameter.
*
*       method assign takes nothing returns Matrix
*           - Use this method to assign a Matrix to another Matrix. For example
*             set B = A.assign() will copy A to B. Note that A and B must be of
*             equal size for this operation to work.
*
*       method addScalar takes real value returns Matrix
*           - This method performs a element-wise addition of a given real value
*             value 2.0 to all elements of the Matrix A.
*
*       method subScalar takes real value returns Matrix
*           - This method performs a element-wise subtraction of a given real
*             value.
*
*       method multScalar takes real value returns Matrix
*           - This method performs a element-wise multiplication by a given real
*             value.
*
*       method divScalar takes real value returns Matrix
*           - This method performs a element-wise division by a given real value.
*
*       method abs takes nothing returns Matrix
*           - This method performs the abs function on all Matrix elements.
*
*       method add takes Matrix whichMatrix returns Matrix
*           - Matrix addition. The expression A.add(B) where both A and B are
*             Matrices returns the resulting Matrix A + B. Note that the Matrices
*             must follow common Matrix calculation rules, like here that A and B
*             have the same size.
*
*       method sub takes Matrix whichMatrix returns Matrix
*           - Matrix substraction. Same as the add method, A.sub(B) performs
*             A - B.
*
*       method multiply takes Matrix whichMatrix returns Matrix
*           - Matrix multiplication. The expression A.multiply(B) performs the
*             operation A*B. Note that A's number of columns must equal B's
*             number of rows for this operation to be well-defined.
*
*       method transpose takes nothing returns Matrix
*           - Matrix transposition. The expression A.transpose() computes A^T.
*
*       method invert takes nothing returns Matrix
*           - Matrix inversion. Use A.invert() to calculate A^-1, the inverse
*             of the Matrix A. Be aware of the fact that not every Matrix is
*             invertable.
*
*       method gauss takes nothing returns Matrix
*           - Use this method to perform a Gauss-Elimination with pivotising.
*             The result is a upper triangular Matrix.
*
*       method solveSLE takes Matrix b returns Matrix
*           - Use this method to solve a System of Linear Equations following
*             the common notation A*x = b. To calculate x use A.solveSLE(b)
*             where A is the system Matrix and b is the solution vector. If the
*             SLE has no unique solution, an invalid vector of size 0 x 0 is
*             returned.
*
*       method dotProduct takes Matrix b returns real
*           - Use this method to calculate the dot product of two Vectors. Use
*             it like a.dotProduct(b), which results in a^T*b. Calling this
*             method is faster than performing the transposition manualy and
*             should therefore be used if possible. To check whether two Vectors
*             are orthogonal, the dot product must be zero.
*
*       method crossProduct takes Matrix b returns Matrix
*           - Use this method to calculate the cross product of two Vectors.
*             This implementation only supports calculation of cross products
*             for Vectors in R^3.
*
*       method trace takes nothing returns real
*           - Returns the trace of a given Matrix. Which is defined as the sum
*             over its diagonal elements.
*
*       method det takes nothing returns real
*           - Returns the determinant of a given Matrix. From A.det() == 0
*             follows for example that A is not invertable.
*
*       method rank takes nothing returns integer
*           - Returns the rank of a given Matrix. A square Matrix must have full
*             rank to be invertable, which means that A.rank() == A.n must return
*             true.
*
*       method norm takes integer whichNorm returns real
*           - Computes the norm of a given Matrix. You can choose between different
*             norms by using the constants defined in the Matrix struct. Valid
*             values for the whichNorm parameter are ONE_NORM, EUCLIDEAN_NORM
*             and the INFINITY_NORM.
*
*       method cond takes integer whichNorm returns real
*           - Computes the condition of a Matrix. You can specify which Norm you
*             want to use for that purpose by the parameter whichNorm (see method
*             norm). Note that the Matrix must be invertable otherwise the
*             condition will be infinity.
*
*       method kron takes Matrix mat returns Matrix
*           - Computes the Kronecker Product of two Matrices. The result of for
*             example A.kron(B) where A is a n x m and B is a p x q Matrix is
*             a n*p x m*q Matrix. As you see this operation potentially produces
*             very big Matrices so use it with care.
*
*       method subMatrix takes integer startRow, integer startCol, integer endRow,
*                        integer endCol returns Matrix
*           - This method can be used to get a sub Matrix out of another Matrix.
*             With the parameters startRow and startCol you can specify where
*             the submatrix should begin and the parameters endRow as well as
*             endCol define where to end the sub Matrix. If A is for example
*             a 3 x 3 Matrix, A.subMatrix(0, 0, 2, 0) will return the first
*             column Vector of A, while A.subMatrix(0, 0, 1, 1) will return
*             the first 2 x 2 sub Matrix of A and so on.
*
*       method embed takes Matrix subMat, integer startRow, integer startCol
*                    returns Matrix
*           - This method embeds one Matrix into another. If you have for example
*             the 3 x 3 Matrix A and the 2 x 2 Matrix B then A.embed(B, 0, 0)
*             will assign the upper left sub matrix of A to the values of B. The
*             parameters startRow and startCol specify where the embeding should
*             start. Of course the sub matrix must fit into the Matrix you want to
*             embed it into, otherwise an error will be thrown.
*
*       method concatH takes Matrix mat returns Matrix
*           - Use this method to concatenate two Matrices. The Matrices must
*             have the same amount of rows for this operation to work. The Matrices
*             will get concatenated horizontal, resulting for  two n x m
*             Matrices in a n x 2*m Matrix. Example: A.concatH(B) will concate-
*             nate A to B (from the left side).
*
*       method concatV takes Matrix mat returns Matrix
*           - Use this method to concatenate two Matrices. The Matrices must
*             have the same amount of columns for this operation to work. The
*             Matrices will get concatenated vertically, resulting for two n x m
*             Matrices in a 2*n x m Matrix. Example: A.concatV(B) will stack
*             the Matrix B on A.
*
*       method reShape takes integer newN, integer newM, integer whichMethod
*                      returns Matrix
*           - Use this method to reshape a Matrix. If you have for example a
*             3 x 2 Matrix A, by performing A.reShape(2, 3, METHOD_ROW_WISE)
*             the Matrix will get a 2 x 3 Matrix. The third parameter whichMethod
*             determines whether the operation should be done row-wise or
*             column-wise. METHOD_ROW_WISE and METHOD_COL_WISE are valid
*             parameters. This can also be used to make a Vektor of a Matrix
*             (or vice versa): A.reShape(6, 1, METHOD_COL_WISE) will stack
*             all column Vektors of A to one 6 x 1 Vektor.
*
*       method switchRow takes integer whichRow, integer newRow returns Matrix
*           - Use this method to switch two different rows of a Matrix. The
*             expression A.switchRow(0, 2) will switch the first with the third
*             row. Note that both parameters whichRow and newRow must not exceed
*             Matrix dimensions.
*
*       method switchCol takes integer whichCol, integer newCol returns Matrix
*           - Use this method to switch two different columns of a Matrix. Same
*             rules as for switchRow apply here.
*
*********************************************************************************/

globals
/*************************************************************************
*   Configurable globals
*************************************************************************/

// Accuracy for considering a Matrix too close to singularity
private constant real EPSILON = 0.01

// Biggest possible n x n-Matrix
private constant integer MAX_MATRIX_SIZE = 30

/*************************************************************************
*   End of configurable globals
*************************************************************************/
endglobals

private struct MatrixRow
real array values[MAX_MATRIX_SIZE]
integer maxCols

method operator [] takes integer column returns real
debug call ThrowError(this == 0, "Matrices", "[]", "MatrixRow", this, "Attempt to access null reference!")
return this.values[column]
endmethod

method operator []= takes integer column, real value returns nothing
debug call ThrowError(this == 0, "Matrices", "[]=", "MatrixRow", this, "Attempt to access null reference!")
debug call ThrowError(column < 0 or column >= maxCols, "Matrices", "[]=", "MatrixRow", this, "Can't access Matrix! Column index "+I2S(column)+" exceeds Matrix dimensions!")
set this.values[column] = value
endmethod

static method create takes integer cols returns thistype
local thistype this = MatrixRow.allocate()
set this.maxCols = cols
return this
endmethod
endstruct

private module Inits
private static method onInit takes nothing returns nothing
set Matrix.Invalid_Matrix = Matrix.createInvalid()
endmethod
endmodule

private module GETTER_MODULE
private integer _n
private integer _m

method operator n takes nothing returns integer
debug call ThrowError(this == 0, "Matrices", "n", "Matrix", this, "Attempt to access null reference!")
return _n
endmethod
method operator n= takes integer value returns nothing
debug call ThrowError(this == 0, "Matrices", "n=", "Matrix", this, "Attempt to access null reference!")
set _n = value
endmethod

method operator m takes nothing returns integer
debug call ThrowError(this == 0, "Matrices", "m", "Matrix", this, "Attempt to access null reference!")
return _m
endmethod
method operator m= takes integer value returns nothing
debug call ThrowError(this == 0, "Matrices", "m=", "Matrix", this, "Attempt to access null reference!")
set _m = value
endmethod
endmodule

struct Matrix
private MatrixRow array matRow[MAX_MATRIX_SIZE]

static constant integer ONE_NORM = 1
static constant integer EUCLIDEAN_NORM = 2
static constant integer INFINITY_NORM = 3

static constant integer METHOD_ROW_WISE = 0
static constant integer METHOD_COL_WISE = 1

implement GETTER_MODULE

method operator [] takes integer row returns MatrixRow
debug call ThrowError(this == 0, "Matrices", "[]", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(row < 0 or row >= n, "Matrices", "[]", "Matrix", this, "Can't access Matrix! Row index "+I2S(row)+" exceeds Matrix dimensions!")
return this.matRow[row]
endmethod

static method create takes integer nDim, integer mDim returns thistype
local integer i = 0
local thistype this

debug call ThrowError(nDim < 1, "Matrices", "create", "Matrix", 0, "Can't create Matrix with "+I2S(nDim)+" rows! Number of rows must be larger than zero!")
debug call ThrowError(mDim < 1, "Matrices", "create", "Matrix", 0, "Can't create Matrix with "+I2S(mDim)+" columns! Number of columns must be larger than zero!")
debug call ThrowError(nDim > MAX_MATRIX_SIZE, "Matrices", "create", "Matrix", 0, "Can't create Matrix with "+I2S(nDim)+" rows! Number of rows exceeds maximum row count of "+I2S(MAX_MATRIX_SIZE)+"!")
debug call ThrowError(mDim > MAX_MATRIX_SIZE, "Matrices", "create", "Matrix", 0, "Can't create Matrix with "+I2S(mDim)+" columns! Number of columns exceeds maximum column count "+I2S(MAX_MATRIX_SIZE)+"!")

set this = Matrix.allocate()
loop
exitwhen i > nDim - 1
set this.matRow[i] = MatrixRow.create(mDim)
set i = i + 1
endloop
set this.n = nDim
set this.m = mDim

return this
endmethod

private static method createInvalid takes nothing returns thistype
local thistype this = Matrix.allocate()

set this.n = 0
set this.m = 0

return this
endmethod

method isValid takes nothing returns boolean
debug call ThrowError(this == 0, "Matrices", "isValid", "Matrix", this, "Attempt to access null reference!")
return this.n == 0
endmethod

method isEqual takes Matrix mat returns boolean
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "isEqual", "Matrix", this, "Attempt to access null reference!")

if this.n != mat.n or this.m != mat.m then
return false
endif

loop
exitwhen i >= n
loop
exitwhen j >= m
if this.matRow[i][j] != mat[i][j] then
return false
endif
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return true
endmethod

method addScalar takes real value returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat

debug call ThrowError(this == 0, "Matrices", "addScalar", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "addScalar", "Matrix", this, "Cannot add "+R2S(value)+" to Matrix elements of and Invalid Matrix!")

set mat = Matrix.create(n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[i][j] = this.matRow[i][j] + value
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method subScalar takes real value returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat

debug call ThrowError(this == 0, "Matrices", "subScalar", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "subScalar", "Matrix", this, "Cannot subtract "+R2S(value)+" to Matrix elements of and Invalid Matrix!")

set mat = Matrix.create(n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[i][j] = this.matRow[i][j] - value
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method multScalar takes real value returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat

debug call ThrowError(this == 0, "Matrices", "multScalar", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "multScalar", "Matrix", this, "Cannot multiply "+R2S(value)+" to Matrix elements of and Invalid Matrix!")

set mat = Matrix.create(n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[i][j] = this.matRow[i][j]*value
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method divScalar takes real value returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat

debug call ThrowError(this == 0, "Matrices", "divScalar", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "divScalar", "Matrix", this, "Cannot divide Matrix elements of Invalid Matrix by "+R2S(value)+"!")

set mat = Matrix.create(n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[i][j] = this.matRow[i][j]/value
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method abs takes nothing returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat

debug call ThrowError(this == 0, "Matrices", "abs", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "abs", "Matrix", this, "Cannot perform absolute value to Matrix elements of and Invalid Matrix!")

set mat = Matrix.create(n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
if this.matRow[i][j] >= 0 then
set mat[i][j] = this.matRow[i][j]
else
set mat[i][j] = -this.matRow[i][j]
endif
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method eye takes real value returns nothing
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "eye", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "eye", "Matrix", this, "Invalid Matrix!")

loop
exitwhen j >= n
loop
exitwhen i >= m
if i != j then
set this.matRow[j][i] = 0.0
else
set this.matRow[j][i] = 1.0
endif
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
endmethod

method diag takes real value, integer whichDiagonal returns nothing
local integer i = 0
local integer j = 0
local integer minDim

debug call ThrowError(this == 0, "Matrices", "diag", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "diag", "Matrix", this, "Invalid Matrix!")

if n <= m then
set minDim = n - 1
else
set minDim = m - 1
endif

debug call ThrowError(whichDiagonal > minDim, "Matrices", "diag", "Matrix", this, "Diagonal Index "+I2S(whichDiagonal)+" exceeds Matrix dimensions!")

loop
exitwhen j >= n
loop
exitwhen i >= m
if i != j - whichDiagonal then
set this.matRow[j][i] = 0.0
else
set this.matRow[j][i] = value
endif
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
endmethod

method init takes real value returns nothing
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "init", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "init", "Matrix", this, "Invalid Matrix!")

loop
exitwhen j >= n
loop
exitwhen i >= m
set this.matRow[j][i] = value
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
endmethod

method rand takes real lowerBound, real upperBound returns nothing
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "rand", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "rand", "Matrix", this, "Invalid Matrix!")

loop
exitwhen j >= n
loop
exitwhen i >= m
set this.matRow[j][i] = GetRandomReal(lowerBound, upperBound)
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
endmethod

method initStepWise takes real startValue, real step, integer whichMethod returns nothing
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "initStepWise", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "initStepWise", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(whichMethod != METHOD_ROW_WISE and whichMethod != METHOD_COL_WISE, "Matrices", "initStepWise", "Matrix", this, "Invalid method of number "+I2S(whichMethod)+" for initializing!")

if whichMethod == METHOD_ROW_WISE then
loop
exitwhen j >= n
loop
exitwhen i >= m
set this.matRow[j][i] = startValue
set startValue = startValue + step
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
elseif whichMethod == METHOD_COL_WISE then
loop
exitwhen j >= m
loop
exitwhen i >= n
set this.matRow[i][j] = startValue
set startValue = startValue + step
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop
endif
endmethod

method add takes Matrix mat returns Matrix
local Matrix result
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "add", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "add", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(mat.n != this.n or mat.m != this.m, "Matrices", "add", "Matrix", this, "Cannot perform addition of a "+I2S(n)+" x "+I2S(m)+" with a "+I2S(mat.n)+" x "+I2S(mat.m)+" Matrix!")

set result = Matrix.create(n, m)
loop
exitwhen j >= n
loop
exitwhen i >= m
set result[j][i] = this.matRow[j][i] + mat[j][i]
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop

return result
endmethod

method sub takes Matrix mat returns Matrix
local Matrix result
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "sub", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "sub", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(mat.n != this.n or mat.m != this.m, "Matrices", "sub", "Matrix", this, "Cannot perform subtraction of a "+I2S(n)+" x "+I2S(m)+" with a "+I2S(mat.n)+" x "+I2S(mat.m)+" Matrix!")

set result = Matrix.create(n, m)
loop
exitwhen j >= n
loop
exitwhen i >= m
set result[j][i] = this.matRow[j][i] - mat[j][i]
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop

return result
endmethod

method multiply takes Matrix mat returns Matrix
local Matrix result
local integer i = 0
local integer j = 0
local integer k = 0
local real temp = 0.0

debug call ThrowError(this == 0, "Matrices", "multiply", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "multiply", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(this.m != mat.n, "Matrices", "multiply", "Matrix", this, "Cannot perform multiplication of a "+I2S(n)+" x "+I2S(m)+" with a "+I2S(mat.n)+" x "+I2S(mat.m)+" Matrix!")

set result = Matrix.create(n, mat.m)
loop
exitwhen j >= result.n
loop
exitwhen k >= result.m
loop
exitwhen i >= this.m
set temp = temp + this.matRow[j][i]*mat[i][k]
set i = i + 1
endloop
set result[j][k] = temp
set i = 0
set temp = 0.0
set k = k + 1
endloop
set k = 0
set j = j + 1
endloop

return result
endmethod

method transpose takes nothing returns Matrix
local Matrix mat = Matrix.create(m, n)
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "transpose", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "transpose", "Matrix", this, "Invalid Matrix!")

loop
exitwhen j >= m
loop
exitwhen i >= n
set mat[j][i] = this.matRow[i][j]
set i = i + 1
endloop
set i = 0
set j = j + 1
endloop

return mat
endmethod

method gauss takes nothing returns Matrix
local Matrix mat
local integer i = 0
local integer j = 0
local integer k = 0
local integer row
local real maxVal = -Math.Inf
local real pivot

debug call ThrowError(this == 0, "Matrices", "gauss", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "gauss", "Matrix", this, "Invalid Matrix!")

set mat = this.assign()
loop
exitwhen i >= n - 1
set j = i
set row = i
loop
exitwhen j >= m
set pivot = Math.abs(mat[j][i])
if pivot > maxVal then
set maxVal = pivot
set row = j
endif
set j = j + 1
endloop

if Math.abs(pivot) < EPSILON then
debug call ThrowWarning(true, "Matrices", "gauss", "Matrix", this, "Can't perform Gauss Elimination, Matrix too close to singularity!")
endif

if row != i then
set j = 0
loop
exitwhen j >= m
set pivot = mat[i][j]
set mat[i][j] = mat[row][j]
set mat[row][j] = pivot
set j = j + 1
endloop
endif

if mat[i][i] >= EPSILON then
set j = i + 1
loop
exitwhen j >= n
set pivot = mat[j][i]/mat[i][i]
set k = i
loop
exitwhen k >= n
set mat[j][k] = mat[j][k] - pivot*mat[i][k]
set k = k + 1
endloop
set j = j + 1
endloop
endif
set i = i + 1
endloop

return mat
endmethod

method invert takes nothing returns Matrix
local Matrix mat
local Matrix inv
local integer i = 0
local integer j = 0
local integer k = 0
local integer row
local real maxVal = -Math.Inf
local real pivot
local real temp_inv

debug call ThrowError(this == 0, "Matrices", "invert", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "invert", "Matrix", this, "Invalid Matrix!")

set mat = this.assign()
set inv = Matrix.create(n, n)
call inv.eye(n)
loop
exitwhen i >= n - 1
set j = i + 1
set row = i
loop
exitwhen j >= n
set pivot = Math.abs(mat[j][i])
if pivot > maxVal then
set maxVal = pivot
set row = j
endif
set j = j + 1
endloop

if Math.abs(pivot) < EPSILON then
debug call ThrowWarning(true, "Matrices", "invert", "Matrix", this, "Can't invert Matrix, too close to singularity!")
return Matrix.Invalid_Matrix
endif

if row != i then
set j = 0
loop
exitwhen j >= m
set pivot = mat[i][j]
set mat[i][j] = mat[row][j]
set mat[row][j] = pivot
set temp_inv = inv[i][j]
set inv[i][j] = inv[row][j]
set inv[row][j] = temp_inv
set j = j + 1
endloop
endif

set j = i + 1
loop
exitwhen j >= n
set pivot = mat[j][i]/mat[i][i]
set k = 0
loop
exitwhen k >= n
set mat[j][k] = mat[j][k] - pivot*mat[i][k]
set inv[j][k] = inv[j][k] - pivot*inv[i][k]
set k = k + 1
endloop
set j = j + 1
endloop
set i = i + 1
endloop

set i = n - 1
loop
exitwhen i < 0
set j = i - 1
loop
exitwhen j < 0
set pivot = mat[j][i]/mat[i][i]
set mat[j][i] = mat[j][i] - pivot*mat[i][i]
set k = 0
loop
exitwhen k >= m
set inv[j][k] = inv[j][k] - pivot*inv[i][k]
set k = k + 1
endloop
set j = j - 1
endloop
set i = i - 1
endloop

set i = 0
loop
exitwhen i >= n
set j = 0
loop
exitwhen j >= m
set inv[i][j] = inv[i][j]/mat[i][i]
set j = j + 1
endloop
set i = i + 1
endloop
call mat.destroy()

return inv
endmethod

method trace takes nothing returns real
local integer i = 0
local integer minDim
local real result = 0.0

debug call ThrowError(this == 0, "Matrices", "trace", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "trace", "Matrix", this, "Invalid Matrix!")

if n <= m then
set minDim = n - 1
else
set minDim = m - 1
endif

loop
exitwhen i > minDim
set result = result + this.matRow[i][i]
set i = i + 1
endloop

return result
endmethod

method assign takes nothing returns Matrix
local integer i = 0
local integer j = 0
local Matrix mat = Matrix.create(n, m)

debug call ThrowError(this == 0, "Matrices", "assign", "Matrix", this, "Attempt to access null reference!")

if n < 1 then
set mat = Invalid_Matrix
return mat
endif

loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[i][j] = this.matRow[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method rank takes nothing returns integer
local Matrix mat = this.assign()
local integer minDim
local integer i
local integer rank

debug call ThrowError(this == 0, "Matrices", "rank", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "rank", "Matrix", this, "Invalid Matrix!")

if n <= m then
set minDim = n - 1
else
set minDim = m - 1
endif

set i = minDim
set rank = minDim + 1
set mat = this.gauss()

loop
exitwhen i < 1
if Math.abs(mat[i][i]) < EPSILON then
set rank = rank - 1
endif
set i = i - 1
endloop

return rank
endmethod

method cond takes integer whichNorm returns real
local Matrix mat
local real result

debug call ThrowError(this == 0, "Matrices", "cond", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "cond", "Matrix", this, "Invalid Matrix!")

set mat = this.assign()
set mat = mat.invert()

if not mat.isValid() then
debug call ThrowError(true, "Matrices", "cond", "Matrix", this, "Matrix has infinite condition!")
return Math.Inf
endif

set mat = this.multiply(mat)
set result = mat.norm(whichNorm)
call mat.destroy()
return result
endmethod

method det takes nothing returns real
local Matrix mat
local integer i = 0
local real result = 1.0

debug call ThrowError(this == 0, "Matrices", "det", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "det", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(n != m, "Matrices", "det", "Matrix", this, "Matrix "+I2S(n)+" x "+I2S(m)+" isn't square! Matrix must be square!")

set mat = this.assign()
set mat = mat.gauss()

loop
exitwhen i >= n
set result = result*mat[i][i]
set i = i + 1
endloop
if Math.abs(result) < EPSILON then
set result = 0.0
endif

call mat.destroy()
return result
endmethod

method solveSLE takes Matrix b returns Matrix
local Matrix mat
local Matrix sol
local Matrix x
local integer i = 0
local integer j = 0
local integer k = 0
local integer row
local real maxVal = -Math.Inf
local real pivot

debug call ThrowError(this == 0, "Matrices", "solveSLE", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "solveSLE", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(this.n != this.m or b.n != this.n or b.m > 1, "Matrices", "solveSLE", "Matrix", this, "Invalid Matrices used! Can't solve SLE!")

set mat = this.assign()
set sol = b.assign()
set x = Matrix.create(b.n, 1)
loop
exitwhen i >= n - 1
set j = i + 1
set row = i
loop
exitwhen j >= n
set pivot = Math.abs(mat[j][i])
if pivot > maxVal then
set maxVal = pivot
set row = j
endif
set j = j + 1
endloop
set pivot = maxVal

if Math.abs(pivot) < EPSILON then
return Matrix.Invalid_Matrix
endif

if row != i then
set j = 0
loop
exitwhen j >= m
set pivot = mat[i][j]
set mat[i][j] = mat[row][j]
set mat[row][j] = pivot
set j = j + 1
endloop
set pivot = sol[i][0]
set sol[i][0] = sol[row][0]
set sol[row][0] = pivot
endif

set j = i + 1
loop
exitwhen j >= n
set pivot = mat[j][i]/mat[i][i]
set k = i
loop
exitwhen k >= n
set mat[j][k] = mat[j][k] - pivot*mat[i][k]
set k = k + 1
endloop
set sol[j][0] = sol[j][0] - pivot*sol[i][0]
set j = j + 1
endloop
set i = i + 1
endloop

set x[x.n - 1][0] = sol[x.n - 1][0]/mat[n - 1][n - 1]
set i = x.n - 2
loop
exitwhen i < 0
set pivot = sol[i][0]
set j = i + 1
loop
exitwhen j >= n
set pivot = pivot - mat[i][j]*x[j][0]
set j = j + 1
endloop
set x[i][0] = pivot/mat[i][i]
set i = i - 1
endloop
call sol.destroy()
call mat.destroy()

return x
endmethod

method norm takes integer whichNorm returns real
local integer i = 0
local integer j = 0
local real result
local real maxVal = -Math.Inf

debug call ThrowError(this == 0, "Matrices", "norm", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "norm", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(whichNorm != ONE_NORM and whichNorm != EUCLIDEAN_NORM and whichNorm != INFINITY_NORM, "Matrices", "[]", "Matrix", this, "Invalid Norm with number "+I2S(whichNorm)+" used!")

if whichNorm == ONE_NORM then
set result = 0.0
loop
exitwhen i >= m
loop
exitwhen j >= n
set result = result + Math.abs(this.matRow[j][i])
set j = j + 1
endloop
if result > maxVal then
set maxVal = result
endif
set result = 0.0
set j = 0
set i = i + 1
endloop
return maxVal
elseif whichNorm == EUCLIDEAN_NORM then
set result = 0.0
loop
exitwhen i >= n
loop
exitwhen j >= m
set result = result + this.matRow[i][j]*this.matRow[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop
return SquareRoot(result)
else
set result = 0.0
loop
exitwhen i >= n
loop
exitwhen j >= m
set result = result + Math.abs(this.matRow[i][j])
set j = j + 1
endloop
if result > maxVal then
set maxVal = result
endif
set result = 0.0
set j = 0
set i = i + 1
endloop
return maxVal
endif
endmethod

method kron takes Matrix mat returns Matrix
local Matrix result = Matrix.create(n*mat.n, m*mat.m)
local integer i = 0
local integer j = 0
local integer k = 0
local integer l = 0

debug call ThrowError(this == 0, "Matrices", "kron", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1 or mat.n < 1, "Matrices", "kron", "Matrix", this, "Can't compute Kronecker Product of Invalid Matrix!")

loop
exitwhen i >= n
loop
exitwhen j >= mat.n
loop
exitwhen k >= m
loop
exitwhen l >= mat.m
set result[i*n + k][j*m + l] = this.matRow[i][j]*mat[k][l]
set l = l + 1
endloop
set l = 0
set k = k + 1
endloop
set k = 0
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return result
endmethod

method dotProduct takes Matrix mat returns real
local real temp = 0.0
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "dotProduct", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1 or mat.n < 1, "Matrices", "dotProduct", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(mat.m != 1 or this.m != 1, "Matrices", "dotProduct", "Matrix", this, "Dot-Product is only defined for Vectors!")
debug call ThrowError(mat.n != this.n, "Matrices", "dotProduct", "Matrix", this, "Dot-Product is only defined for Vectors of same length!")

loop
exitwhen i > n
set temp = temp + this.matRow[i][1]*mat[i][1]
set i = i + 1
endloop

return temp
endmethod

method crossProduct takes Matrix mat returns Matrix
local Matrix result

debug call ThrowError(this == 0, "Matrices", "crossProduct", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1 or mat.n < 1, "Matrices", "crossProduct", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(mat.m != 1 or this.m != 1, "Matrices", "crossProduct", "Matrix", this, "Cross-Product is only defined for Vectors!")
debug call ThrowError(mat.n != this.n, "Matrices", "crossProduct", "Matrix", this, "Cross-Product is only defined for Vectors of same length!")
debug call ThrowError(mat.n != 3 or this.n != 3, "Matrices", "crossProduct", "Matrix", this, "This implementation only supports Cross-Products for Vectors in R^3")

set result = Matrix.create(3, 1)
set result[0][0] = this.matRow[1][0]*mat[2][0] - this.matRow[2][0]*mat[1][0]
set result[1][0] = this.matRow[2][0]*mat[0][0] - this.matRow[0][0]*mat[2][0]
set result[2][0] = this.matRow[0][0]*mat[1][0] - this.matRow[1][0]*mat[0][0]

return result
endmethod

method reShape takes integer newN, integer newM, integer whichMethod returns Matrix
local Matrix mat
local integer i = 0
local integer j = 0
local integer row = 0
local integer col = 0

debug call ThrowError(this == 0, "Matrices", "reShape", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "reShape", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(newN*newM != n*m, "Matrices", "reShape", "Matrix", this, "Can't reshape a "+I2S(n)+" x "+I2S(m)+" Matrix to a "+I2S(newN)+" x "+I2S(newM)+" Matrix! Dimension missmatch!")
debug call ThrowError(newN < 1 or newM < 1, "Matrices", "reShape", "Matrix", this, "Reshape not possible! Index must be greater than zero!")
debug call ThrowError(whichMethod != METHOD_ROW_WISE and whichMethod != METHOD_COL_WISE, "Matrices", "reShape", "Matrix", this, "Invalid method with number "+I2S(whichMethod)+" for reshaping!")

set mat = Matrix.create(newN, newM)

if whichMethod == METHOD_ROW_WISE then
loop
exitwhen i >= n
loop
exitwhen j >= m
set mat[row][col] = this.matRow[i][j]
set col = col + 1
if col >= mat.m then
set row = row + 1
set col = 0
endif
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop
elseif whichMethod == METHOD_COL_WISE then
loop
exitwhen i >= m
loop
exitwhen j >= n
set mat[row][col] = this.matRow[j][i]
set col = col + 1
if col >= mat.m then
set row = row + 1
set col = 0
endif
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop
endif

return mat
endmethod

method embed takes Matrix subMat, integer startRow, integer startCol returns Matrix
local Matrix mat
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "embed", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n == 0 or subMat.n == 0, "Matrices", "embed", "Matrix", this, "Can't embed sub Matrix! This is an invalid Matrix!")
debug call ThrowError(startRow < 0 or startCol < 0, "Matrices", "embed", "Matrix", this, "Can't merge Matrices to start row "+I2S(startRow)+" and start column "+I2S(startCol)+"! Index must be greater than 0!")
debug call ThrowError(startRow + subMat.n - 1 > n or startCol + subMat.m - 1 > m, "Matrices", "embed", "Matrix", this, "Can't merge Matrices to start row "+I2S(startRow)+" and start column "+I2S(startCol)+"! Matrices don't fit!")

set mat = this.assign()
loop
exitwhen i >= subMat.n
loop
exitwhen j >= subMat.m
set mat[i + startRow][j + startCol] = subMat[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method subMatrix takes integer startRow, integer startCol, integer endRow, integer endCol returns Matrix
local Matrix mat
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "subMatrix", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n == 0, "Matrices", "subMatrix", "Matrix", this, "Can't determine sub Matrix! This is an invalid Matrix!")
debug call ThrowError(endRow < startRow or endCol < startCol, "Matrices", "subMatrix", "Matrix", this, "Can't determine sub Matrix! Size of sub Matrix smaller 1 x 1!")
debug call ThrowError(endRow > n - 1 or endCol > m - 1, "Matrices", "subMatrix", "Matrix", this, "Sub Matrix of size "+I2S(endRow - startRow + 1)+" x "+I2S(endCol - startCol + 1)+" exceeds Matrix dimensions!")

set mat = Matrix.create(endRow - startRow + 1, endCol - startCol + 1)
loop
exitwhen i >= mat.n
loop
exitwhen j >= mat.m
set mat[i][j] = this.matRow[i + startRow][j + startCol]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return mat
endmethod

method concatV takes Matrix mat returns Matrix
local Matrix result
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "concatV", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n == 0 or m == 0, "Matrices", "concatV", "Matrix", this, "Can't concat Invalid Matrices!")
debug call ThrowError(m != mat.m, "Matrices", "concatV", "Matrix", this, "Can't concat Matrices! Matrix column dimensions must be equal!")

set result = Matrix.create(n + mat.n, m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set result[i][j] = mat[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop
set i = 0
set j = 0
loop
exitwhen i >= n
loop
exitwhen j >= m
set result[i + n][j] = this.matRow[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return result
endmethod

method concatH takes Matrix mat returns Matrix
local Matrix result
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "concatH", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n == 0 or m == 0, "Matrices", "concatH", "Matrix", this, "Can't concat Invalid Matrices!")
debug call ThrowError(n != mat.n, "Matrices", "concatH", "Matrix", this, "Can't concat Matrices! Matrix row dimensions must be equal!")

set result = Matrix.create(n, m + mat.m)
loop
exitwhen i >= n
loop
exitwhen j >= m
set result[i][j] = this.matRow[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop
set i = 0
set j = 0
loop
exitwhen i >= n
loop
exitwhen j >= m
set result[i][j + m] = mat[i][j]
set j = j + 1
endloop
set j = 0
set i = i + 1
endloop

return result
endmethod

method switchRow takes integer whichRow, integer newRow returns nothing
local real temp
local integer i = 0

debug call ThrowError(this == 0, "Matrices", "switchRow", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "switchRow", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(whichRow < 0 or whichRow >= n or newRow < 0 or newRow >= n, "Matrices", "switchRow", "Matrix", this, "Can't switch row "+I2S(whichRow)+" with "+I2S(newRow)+". Index exceeds Matrix dimensions!")

loop
exitwhen i >= this.m
set temp = this.matRow[newRow][i]
set this.matRow[newRow][i] = this.matRow[whichRow][i]
set this.matRow[whichRow][i] = temp
set i = i + 1
endloop
endmethod

method switchCol takes integer whichCol, integer newCol returns nothing
local real temp
local integer i = 0

debug call ThrowError(this == 0, "Matrices", "switchCol", "Matrix", this, "Attempt to access null reference!")
debug call ThrowError(n < 1, "Matrices", "switchCol", "Matrix", this, "Invalid Matrix!")
debug call ThrowError(whichCol < 0 or whichCol >= n or newCol < 0 or newCol >= n, "Matrices", "switchCol", "Matrix", this, "Can't switch column "+I2S(whichCol)+" with "+I2S(newCol)+". Index exceeds Matrix dimensions!")

loop
exitwhen i >= this.n
set temp = this.matRow[i][newCol]
set this.matRow[i][newCol] = this.matRow[i][whichCol]
set this.matRow[i][whichCol] = temp
set i = i + 1
endloop
endmethod

method display takes nothing returns nothing
local string s = ""
local integer i = 0
local integer j = 0

debug call ThrowError(this == 0, "Matrices", "display", "Matrix", this, "Attempt to access null reference!")
debug call ThrowWarning(n < 1, "Matrices", "display", "Matrix", this, "Invalid Matrix can't be displayed!")

loop
exitwhen j >= this.n
loop
exitwhen i >= this.m
set s = s + " " + R2S(this.matRow[j][i])
set i = i + 1
endloop
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 60.0, s)
set s = ""
set i = 0
set j = j + 1
endloop
endmethod

implement Inits
endstruct
endlibrary``````

And here a little example that demonstrates the solution of a System of Linear Equations (SLE).

JASS:
``````scope Example initializer Init
private function Init takes nothing returns nothing
/**********************************************************************************
*
*   Example Code
*   ¯¯¯¯¯¯¯¯¯¯¯¯
*
*   Solves the Linear System of Equations A*x = b for
*
*    [1][4][7]   [x1]   [1]                          [ 0]
*   [17][5][8] * [x2] = [2]    with the solution x = [ 2] approximately.
*    [3][6][9]   [x3]   [3]                          [-1]
*
*   Feel free to try out more.
*
**********************************************************************************/

// Create a 3 x 3 system Matrix A
local Matrix A = Matrix.create(3, 3)

// Create a 3 x 1 solution Vector b
local Matrix b = Matrix.create(3, 1)

// Instanciate the unknown Vector x
local Matrix x

// Initialize Matrix A in ascending order (col-wise)
call A.initStepWise(1.0, 1.0, Matrix.METHOD_COL_WISE)

// Set the element in the second row, first column of A to 17.0
set A[1][0] = 17.0

// Initialize Vector b (row-wise)
call b.initStepWise(1.0, 1.0, Matrix.METHOD_ROW_WISE)

// Solve SLE
set x = A.solveSLE(b)

// Display A, b and x
call A.display()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 60.0, " ")
call b.display()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 60.0, " ")
call x.display()
endfunction
endscope``````

Even more detailed documentation will be included as a sub-chapter to the Math library soon.

#### Attachments

• Matrices.w3x
60.2 KB · Views: 168
Last edited:

Level 36
Looks pretty nice though personally I think its quite too much to have these for wc3... unless someone will make a map that uses these kinds of computations...

anyway, nice job on this...

Last edited:

#### Nestharus

Level 31
I'm going to have to look through this.

I recommend ErrorMessage over your own custom errors.

Also keep in mind that plain vJASS structs do generate some extra garbage code.

I don't see dot or cross product and it's missing magnitude. Getting a unit vector from a vector would also be helpful.

You also do know that this will do lots of multiplication and addition whenever you read an element right?

`real array values[MAX_MATRIX_SIZE]`

All error checking should be debug only as well. I know if I'm doing AES, I want to minimize the operation count to minimize how much the thing freezes during encrypting/decrypting. Every little operation counts and can make a pretty big difference ;o.

edit
Please also create a testing suite to ensure that this is absolutely bugless =). Yea, I know right now everything's chaotic, but trying to bring in some order. You'll notice that I wrote a collections testing suite ^)^.

What I can say is that you have gotten much better at coding. You have advanced leaps and bounds from your physical damage detection. Very good ; ). I expect great things from you in the future.

#### looking_for_help

Level 14
I don't see dot or cross product and it's missing magnitude. Getting a unit vector from a vector would also be helpful.

Right, I will add those. If I missed more functions, please tell me.

You also do know that this will do lots of multiplication and addition whenever you read an element right?

When I read an element? You mean `local real r = mat[1][2]` will cause additions and multiplications? How's that?

All error checking should be debug only as well. I know if I'm doing AES, I want to minimize the operation count to minimize how much the thing freezes during encrypting/decrypting. Every little operation counts and can make a pretty big difference ;o.

edit
Please also create a testing suite to ensure that this is absolutely bugless =). Yea, I know right now everything's chaotic, but trying to bring in some order. You'll notice that I wrote a collections testing suite ^)^.

Will do.

What I can say is that you have gotten much better at coding. You have advanced leaps and bounds from your physical damage detection. Very good ; ). I expect great things from you in the future.

Thanks

EDIT:
Magnitute is already there, the euclidean norm returns a vectors magnitude, for a Matrix its the frobenius norm.

Last edited:

#### Nestharus

Level 31
`local real r = mat[1][2]`

is

`local real r = mat[1*n + 2]`

Because you are reading from a constant row, it could just be

row = whatever

local real r = mat[row + index]

or you could iterate over elements of the row

#### looking_for_help

Level 14
`local real r = mat[1][2]`

is

`local real r = mat[1*n + 2]`

Oh, I see.

Because you are reading from a constant row, it could just be

row = whatever

local real r = mat[row + index]

or you could iterate over elements of the row

Hm I don't understand... So it is possible to avoid these extra calculations?

EDIT:
Oh I see, if one access to a constant index... Well that doesn't help much unfortunatly because all functions use variant indices...
And for manual setting I think the API `set mat[1][2] = someValue` is the best.

Last edited:

#### Almia

Level 33
Someone beat me
I thought my implementation of LuaMatrix to vJASS be better

#### Nestharus

Level 31
Btw, why are you using your Math lib in this?

There is an Abs BJ function :\, and I don't see using the library for a constant.

edit
I don't really agree with a lot of the stuff in your Math lib. For example, you can check if something is an integer with R2I(number) == number. You do if statements and weird stuff for rounding instead.

It has string parsing... which really doesn't belong in a math lib, lol.

You redo things like RAbsBJ.

I just don't agree with a lot of stuff in your Math lib. I'm pretty averse to anything that uses it.

If it had been submitted to the JASS Section, I don't think that it would have been approved. As this uses such a resource, you should probably just submit this to the Spells section . You'd be better off then, the Spells section is way less strict. When a moderator doesn't think that the resource is particularly good, it'll get a low rating, but still be approved.

#### looking_for_help

Level 14
Update v 1.1.0.0
- Error Statements are now debug only
- initStepWise can now also be used column-wise
- New methods included:
- isEqual, checks for equality of two matrices
- dotProduct
- crossProduct
- subMatrix, gets a sub matrix out a matrix
- embed, embeds a matrix into another
- concatH, concatenates two matrices horizontaly
- concatV, concatenates two matrices vertically
- reShape, changes a matrix dimensions

Someone beat me
I thought my implementation of LuaMatrix to vJASS be better

You make something like that already? Where?

Btw, why are you using your Math lib in this?

There is an Abs BJ function :\, and I don't see using the library for a constant.

Well, but that constant requires an additional `Pow` call, so why should I do that in every resource that requires that constant if I can just have one collection with them all inside?

After the Matrices library I'm going to do more math stuff. Actually I need at the moment some CurveFitting and Optimization Stuff. Each of these libraries will require various things of the Math lib. User defined constants are also more accurate than the native ones, so it does make sense to collect them all together to rely on one basic framework.

That the Math library itself is not perfect is another thing, I can still remove the redundant functions. I asked back then what the mods think about but they didn't say anything so I thought its fine.

But that suggestion is just useless and arrogant:

If it had been submitted to the JASS Section, I don't think that it would have been approved. As this uses such a resource, you should probably just submit this to the Spells section . You'd be better off then, the Spells section is way less strict. When a moderator doesn't think that the resource is particularly good, it'll get a low rating, but still be approved.

Because the resource is good. There isn't really any much better way to implement matrices to Wc3 so, wouldn't it make much more sense to base future resources that are in need of matrices on such a resource?

#### Nestharus

Level 31
Use ErrorMessage so that you get normal behavior and standard output. Your error messages are missing a lot of information.

1. You need to say the library name (good)
2. You need to say the object name (not so good)
3. You need to say the instance (not good)
4. You need to output all arguments (def not good)
5. You need to disable the system so that errors don't propagate (not so good)
6. You need to stop the thread so that errors don't propagate (not so good)

You are only meeting the first requirement. Put in the other 5.

Because the resource is good. There isn't really any much better way to implement matrices to Wc3 so, wouldn't it make much more sense to base future resources that are in need of matrices on such a resource?

Take out all of the string stuff from Math.

I want to know how your abs method is better than the RABsBJ/IAbsBJ functions.

If you improve the math lib, then I have no problems using it for constants, but right now it's filled with extraneous clutter ; ).

edit
Missing augment matrix

#### looking_for_help

Level 14
Use ErrorMessage so that you get normal behavior and standard output. Your error messages are missing a lot of information.

1. You need to say the library name (good)
2. You need to say the object name (not so good)
3. You need to say the instance (not good)
4. You need to output all arguments (def not good)
5. You need to disable the system so that errors don't propagate (not so good)
6. You need to stop the thread so that errors don't propagate (not so good)

You are only meeting the first requirement. Put in the other 5.

Hm Ok, I will do that with the next update.

Take out all of the string stuff from Math.

Well I can make it an extension, just like Matrices. Not implementing it to Math but make it use its own struct. It basically only changes the API a bit, so I have no problem with doing so.

I want to know how your abs method is better than the RABsBJ/IAbsBJ functions.

It looks nicer, nothing more. Red functions are ugly, don't you think?

If you improve the math lib, then I have no problems using it for constants, but right now it's filled with extraneous clutter ; ).

K, I will remove all the trigonometry wrappers and exclude the string stuff. But rounding functions etc are useful as well as Ln, gamma or hyperbolics.

edit
Missing augment matrix

`set A = A.concatH(B)`

#### Almia

Level 33
Nah it is just a WIP im making. It just uses only one struct and Alloc library.

#### looking_for_help

Level 14
Use ErrorMessage so that you get normal behavior and standard output. Your error messages are missing a lot of information.
...
If you improve the math lib, then I have no problems using it for constants, but right now it's filled with extraneous clutter ; ).

Done.

Update to Version 1.2.0.0
- Now using ErrorMessage
- Fixed some minor bugs
- Reworked the Maths library (see changelog here), removed redundant functions as well as the String Parsing.

#### Nestharus

Level 31

`debug call ThrowError(true, "Matrices", "[]", "Matrix", this, "Can't access Matrix! Index exceeds Matrix dimensions!")`

The [] takes an argument row, yet you're not displaying it.

Example
JASS:
``````method enqueue takes thistype node returns nothing
debug call ThrowError(this == 0,            "UniqueQueue", "enqueue", "thistype", this, "Attempted To Enqueue (" + I2S(node) + ") On To Null Queue.")
debug call ThrowError(recycler[this] != -1, "UniqueQueue", "enqueue", "thistype", this, "Attempted To Enqueue (" + I2S(node) + ") On To Invalid Queue.")
debug call ThrowError(node == 0,            "UniqueQueue", "enqueue", "thistype", this, "Attempted To Enqueue Null Node.")
debug call ThrowError(node.isNode,          "UniqueQueue", "enqueue", "thistype", this, "Attempted To Enqueue Owned Node (" + I2S(node) + ").")``````

edit
Also put your expressions inside of the function call ;p

JASS:
``````            debug if  then
debug call ThrowError(true, "Matrices", "[]=", "MatrixRow", this, "Can't access Matrix! Index exceeds Matrix dimensions!")
debug endif``````

To

`debug call ThrowError(column < 0 or column >= maxCols, "Matrices", "[]=", "MatrixRow", this, "Attempt To Set Out Of Bounds Matrix Column (" + I2S(column) + ").")`

Always be very explicit with your errors. If [] takes a row, don't go with a generic index, state a row .

Also, it doesn't necessarily exceed the matrix because it may be < 0, it's really out of bounds.

edit
Under standard 4, public fields aren't allowed ;p

JASS:
``````        readonly static Matrix Invalid_Matrix

Those should be private with method operators, that way you can do error checking. Field names should be _n and _m. For now, put your struct inside of a module and implement that module into the struct, that way you can use this. Wait until a new jass parser comes out to get around the lame _n,_m syntax errors outside of a module.

edit
Put this below library reqs and above API listing please. It's hidden away down there.

JASS:
``````    globals
/*************************************************************************
*   Configurable globals
*************************************************************************/

// Accuracy for considering a Matrix too close to singularity
private constant real EPSILON = 0.01

// Do you want the system to display debug error messages?
private constant boolean DISPLAY_MATRIX_ERRORS = true

// Biggest possible n x n-Matrix
private constant integer MAX_MATRIX_SIZE = 50

/*************************************************************************
*   End of configurable globals
*************************************************************************/
endglobals``````

edit
While I know what m,n stand for because they are standard dimensions for a matrix, other people may not. You might want to change them to rows/columns. I know ur being cool with the math lingo, but still ;p.

#### looking_for_help

Level 14

`debug call ThrowError(true, "Matrices", "[]", "Matrix", this, "Can't access Matrix! Index exceeds Matrix dimensions!")`

edit
Also put your expressions inside of the function call ;p

Hm kk. Though this will be quite a lot of work -.-

edit
Under standard 4, public fields aren't allowed ;p

JASS:
``````        readonly static Matrix Invalid_Matrix

Whats wrong with public fields?

Those should be private with method operators, that way you can do error checking. Field names should be _n and _m. For now, put your struct inside of a module and implement that module into the struct, that way you can use this. Wait until a new jass parser comes out to get around the lame _n,_m syntax errors outside of a module.

What kind of error checking? They are readonly so you can't make anything wrong with them.

Also I don't think it makes sense to build (and enforce) a new standard with features that are (not yet) supported by jasshelper and very likely will never be.
Who should make such an update and when?

Also `Matrix._n` looks super stupid, don't you think?

#### Nestharus

Level 31
Also I don't think it makes sense to build (and enforce) a new standard with features that are (not yet) supported by jasshelper and very likely will never be.
Who should make such an update and when?

I'm working on it right now : P

The user wouldn't use matrix._n, they would use matrix.n

Let's consider that a user uses 0 as a matrix by an error, or they accidentally use a deallocated matrix. If they did matrix.n and n was just a field, the error wouldn't be caught. If you made it a method operator, you could then check for errors, making sure that the matrix is both not null and not invalid.

JASS:
``````private integer _n
method operator n takes nothing returns integer
debug call ThrowError(this == 0, ...)
debug call ThrowError(this.isAllocated, ...)

return _n
endmethod``````

This way you catch any errors as they occur. If you don't catch the error and then another error happens later, the user won't know where it originated from (the first occurance of the error). You should check for errors in everything that the user can interact with in your resource =).

You can also name it n_p or w/e if you'd prefer not to use the module workaround to get _n working.

#### looking_for_help

Level 14
The user wouldn't use matrix._n, they would use matrix.n

Let's consider that a user uses 0 as a matrix by an error, or they accidentally use a deallocated matrix. If they did matrix.n and n was just a field, the error wouldn't be caught. If you made it a method operator, you could then check for errors, making sure that the matrix is both not null and not invalid.

Ok convinced, that makes sense.

I'm working on it right now : P

Really? Will you implement more stuff or only fix this Bug? Because I have a wish list here

#### Nestharus

Level 31
I will implement a lot of stuff, yar

You could always help me you know, I could get you set up : )

#### looking_for_help

Level 14
This way you catch any errors as they occur. If you don't catch the error and then another error happens later, the user won't know where it originated from (the first occurance of the error). You should check for errors in everything that the user can interact with in your resource =).

You can also name it n_p or w/e if you'd prefer not to use the module workaround to get _n working.

Hm, I still have doubts about this practise to replace all fields with operators. While a field is just a variable (read out very fast) a method operator gets compiled into a function call which is much slower.

If I change for example the fields n and m to getter operators, performance drops by ~9% for gaussian elimination which is quite a lot. Especially when you consider that this is just for catching the case where a user uses a deallocated object I don't think its worth the performance lose as this is a really trivial error.

#### Nestharus

Level 31
Hm, I still have doubts about this practise to replace all fields with operators. While a field is just a variable (read out very fast) a method operator gets compiled into a function call which is much slower.

If I change for example the fields n and m to getter operators, performance drops by ~9% for gaussian elimination which is quite a lot. Especially when you consider that this is just for catching the case where a user uses a deallocated object I don't think its worth the performance lose as this is a really trivial error.

It gets inlined out of debug mode

#### looking_for_help

Level 14
Hm ok, then its fine. Debug mode is extremly slow anyway. Some of my functions have double performance when running in standard mode, even if I through away all the error checking stuff.

#### looking_for_help

Level 14
Update Version 1.3.0.0
- Reworked the error handling
- Now displaying relevant arguments on errors
- Changed readonly instance fields to method operators
- Added new method kron: Computes the Kronecker Product of two Matrices

#### Magtheridon96

Level 35
JASS:
``````*       method assign takes nothing returns Matrix
*           - Use this method to assign a Matrix to another Matrix. For example
*             set B = A.assign() will copy A to B. Note that A and B must be of
*             equal size for this operation to work.``````

If that's what it does, wouldn't .copy() be better?

#### Nestharus

Level 31
no, copy means you create a new matrix and then assign the old matrix to the new matrix and return the new matrix

assign means

matrix a elements = matrix b elements

#### looking_for_help

Level 14
Update Version 1.4.0.0
- Fixed a bug with solution of system of linear equations
- Gauss elimination now works for all kind of matrices
- Replaced some Errors with warnings and added more Error-Checking

#### edo494

Level 23
you are using nes's ThrowError, yet you dont provide a link for the library to download and I didnt find the function anywhere in your code

#### looking_for_help

Level 14
Huh?

The link is in the Math library (which itself is linked in the system code) and in the testmap you can find it too, so I don't understand?

Basically the testmap provides everthing you need.

#### edo494

Level 23
sorry, didnt check if it isnt in the Math Im such a derp, should've checked before

Level 14
No problem

#### muzzel

Level 14
I tihnk you should add support for sparse matrices. This could be done by using triples of (row, col, value):
Code:
``````[ 15  7  0  0  0 ]
[  0  8 34  0  0 ]
[  0  0 57  0  0 ]
[  0  0  0 81  0 ]
[  0  0  0 10  0 ]

== ((1, 1, 15), (1, 2, 7), (2, 2, 8), (2, 3, 34), (3, 3, 57), (4, 4, 81), (5, 4, 10))``````

A similar approach is the yale sparse matrix format.

Anyway il have to agree with Adiktuz, nobody really uses this stuff in wc3...

#### looking_for_help

Level 14
Well, that would be a seperate resource.

But the application of those data storage formats are large Matrices (really large ones), like 100000 x 100000 where its a problem to store those in the RAM of a common PC.

As you can't come even close to such huge Matrices anyway, it doesn't really make sense to implement something like this IMO. But you can of course make such a library and use this one as a base if you want

#### Dr Super Good

Spell Reviewer
Level 59
Does this contain element wise matrix operations such as the ".*" from matlab? This operation uses standard mathematical multiplication between each element of the two matrices to produce a new matrix. It is similar to scalar multiplication except to do that using this operation you need a matrix consisting of the same size and all elements being the scalar.

Its main purpose is for mask matrices.

#### looking_for_help

Level 14
No, thats not supported at the moment. I will add it with the next update, thanks.

#### Menag

Level 6
Does this contain element wise matrix operations such as the ".*" from matlab? This operation uses standard mathematical multiplication between each element of the two matrices to produce a new matrix. It is similar to scalar multiplication except to do that using this operation you need a matrix consisting of the same size and all elements being the scalar.

You could also add an exponentiation and component-wise function-application like sine/cosine/...

Second:
How do you calculate the condition? If i see the code correctly you do it like
|A * A^-1| which is for submultiplicative norms always 1. It should actually be |A| * |A^-1|.
For gauss-algorithm. Didn't look into the code, but i hope you use at least pivoting. You could also give the posibility to obtain a LU-Decomposition in case someone wants to solve several linear equation with the same matrix.

#### looking_for_help

Level 14
You could also add an exponentiation and component-wise function-application like sine/cosine/...

No I won't do that because you could always extend the list further making this basically what is called a monolith and this library is already big enough. If the user wants to do that he can easily iterate on his own over the Matrix and apply the function call on each element.

Second:
How do you calculate the condition? If i see the code correctly you do it like
|A * A^-1| which is for submultiplicative norms always 1. It should actually be |A| * |A^-1|.

Yes thats right, will fix.

For gauss-algorithm. Didn't look into the code, but i hope you use at least pivoting.

Well, if you didn't look at the code its quite to useless to discuss things like this. But yes, it is using pivoting. The variable used there is even called "pivot" -.-

#### Menag

Level 6
No I won't do that because you could always extend the list further making this basically what is called a monolith and this library is already big enough. If the user wants to do that he can easily iterate on his own over the Matrix and apply the function call on each element.

It wasn't actually meant like this, since DSG wanted you to implement something like this. That's why i quoted him.

Edit: In matlab this has actually a use since it can parallelise all this stuff. In wc3 it doesn't.

Last edited:

#### Menag

Level 6

And I'd recommend doing the swaps in gauss only implicit. Explicitly swapping all values is useless overhead imo.

#### looking_for_help

Level 14

No, as already stated a user can perform such things by simply iterating over the matrix himself. A library doesn't get better by just adding more and more functions to it. I could also add sin, cos, tan, exp, sqrt and so on and so on. But that would just add aditional overhead to the library.

Actually I'm thinking of removing some methods as the library is already very big.

And I'd recommend doing the swaps in gauss only implicit. Explicitly swapping all values is useless overhead imo.

Sorry, but what are you talking about?

Of course I only swap the required values and not all, lol. The algorithm for performing gaussian elimination is standardized and this is a typical implementation of it.

#### Menag

Level 6
Sorry, but what are you talking about?

When pivoting, you swap rows. If you take an array which holds the rownumbers as a wrapper, you don't have to actually swap the numbers, instead you just have one more array lookup. So the int array first looks like this:

rows[0, 1, 2, 3, 4, 5]

swapping 0 and 3 yields

rows[3, 1, 2, 0, 4, 5]

accessing the 0th row now is like
matrixrow(rows[0])

#### looking_for_help

Level 14
Oh, now I see you mean the wrapper for the [] operators, not the swapping itself

Yes, thats actually a good idea, that would safe two function calls per row swap. Will apply that in the next update, thank you.

#### Menag

Level 6
No, as already stated a user can perform such things by simply iterating over the matrix himself.
[...]
Actually I'm thinking of removing some methods as the library is already very big.

I would still add the usual algebraic operations. Negate, invert, add, multiply, subtract. These should be in such a package including the operations which one always needs for the given algebraic structure, here det, transpone, trace, maybe rank and a norm.
And then there should be something like a LinearAlgebra lib which can solve systems of linear equations and some other stuff. Just my opinion.

#### looking_for_help

Level 14
I would still add the usual algebraic operations. Negate, invert, add, multiply, subtract.

Well, the usual algeraic operations are already implemented. I mean, you can easily negate the Matrix by doing `matrix.multScalar(-1.0)`, the rest is already there.

And then there should be something like a LinearAlgebra lib which can solve systems of linear equations and some other stuff. Just my opinion.

Hm, yes that sounds like a good idea. I don't really like how big the library is at the moment, so I think spliting it up a bit makes sense.

#### Almia

Level 33
Has one tried using Vectors as rows? I found a C library that uses Vector2 and Vector3 for Matrix2, Matrix3 and Matrix4

#### Bribe

Level 38
I don't know what to dew with this resource. I never studied Matrices. I feel so incompetent just looking at this.

#### Bannar

Level 24
You havent been working with Matlab or Mathematica?

#### Bribe

Level 38
No, for all I know you could be making those words up.

#### muzzel

Level 14
Has one tried using Vectors as rows? I found a C library that uses Vector2 and Vector3 for Matrix2, Matrix3 and Matrix4

Whats the benefit? Its slower and more complicated. The only benefit i could imagine is that very sparse matrices could, in some cases, be more efficiently represented (memory wise). But those are very unlikely situations. (And for sparse matrices there are even more efficient representations)

#### Bribe

Level 38
Going to approve this resource based on the satusfaction of the people here who are smart enough to understand how this works. It is well-documented and optimized.

#### Magtheridon96

Level 35
Here's something useful:

JASS:
``````function MatrixPow takes Matrix m, integer b returns Matrix
local Matrix r = Matrix.create(m.n, m.m)
local Matrix a = m.assign()
call r.eye()
loop
exitwhen b == 0
if b/2*2 != b then
set r = r.multiply(a)
endif
set a = a.multiply(a)
set b = b / 2
endloop
return r
endfunction``````

This does exponentiation with O(log b) matrix multiplications.
I use these kinds of algorithms all the time when I'm working with recurrence relations.

Of course, the above is usually more useful when you augment it with an extra parameter 'mod' so that you'd have a MatrixPowMod function.

You can compute the 10^500th Fibonacci number modulo 1 000 000 007 pretty easily
(Without using Pisano periods)

Replies
16
Views
22K
Replies
1
Views
492
Replies
33
Views
5K
[vJASS] BJObjectId
Replies
10
Views
2K
Replies
4
Views
650