which are implemented in Gandalf using the macros

Gan_Matrix34 m34A, m34D; Gan_Matrix33 m33B; Gan_Matrix44 m44C; /* ... set up m34A, m33B and m44C ... */ gan_mat34_lmultm33_q ( &m34A, &m33B, &m34D ); /* D = B*A */ gan_mat34_lmultm33T_q ( &m34A, &m33B, &m34D ); /* D = B*A^T */ gan_mat34_rmultm44_q ( &m34A, &m44C, &m34D ); /* D = A*C */ gan_mat34_rmultm44T_q ( &m34A, &m44C, &m34D ); /* D = A*C^T */Equivalent function calls are available:

m34D = gan_mat34_lmultm33_s ( &m34A, &m33B ); /* D = B*A */ m34D = gan_mat34_lmultm33T_s ( &m34A, &m33B ); /* D = B*A^T */ m34D = gan_mat34_rmultm44_s ( &m34A, &m44C ); /* D = A*C */ m34D = gan_mat34_rmultm44T_s ( &m34A, &m44C ); /* D = A*C^T */Note that although by and large the functions described here for matrices are repeated for square matrices, there is redundancy because in the case of matrices the routines

m33D = gan_mat33_lmultm33_s ( &m33A, &m33B ); /* D = B*A */ m33D = gan_mat33_rmultm33_s ( &m33B, &m33A ); /* D = B*A */would be equivalent, so in fact only the routines

The square matrix may be symmetric or triangular, for which cases there are specific Gandalf functions. Firstly for multiplying by symmetric matrices we have the routines

Gan_Matrix34 m34A, m34B; Gan_SquMatrix33 sm33S; Gan_SquMatrix44 sm44S; /* ... set up m34A, symmetric sm33S and sm44S ... */ gan_mat34_lmults33_q ( &m34A, &sm33S, &m34B ); /* B = S*A, macro */ gan_mat34_rmults44_q ( &m34A, &sm44S, &m34B ); /* B = A*S, macro */with equivalent function calls

m34B = gan_mat34_lmults33_q ( &m34A, &sm33S ); /* B = S*A, function call */ m34B = gan_mat34_rmults44_q ( &m34A, &sm44S ); /* B = A*S, function call */When multiplying by a triangular matrix, there are also options of implicit transpose and inverse, as described in the introduction to this chapter. Gandalf also supports in-place operations in this case. So there is a whole family of functions covering multiplication of a matrix by a triangular matrix. Mathematically the operations are

Gandalf macro routines to implement these operations are

Gan_Matrix34 m34A, m34B; Gan_SquMatrix33 sm33L; Gan_SquMatrix44 sm44L; /* ... set up m34A, lower triangular sm33L and sm44L ... */ gan_mat34_lmultl33_q ( &m34A, &sm33L, &m34B ); /* B = L*A, macro */ gan_mat34_lmultl33T_q ( &m34A, &sm33L, &m34B ); /* B = L^T*A, macro */ gan_mat34_lmultl33I_q ( &m34A, &sm33L, &m34B ); /* B = L^-1*A, macro */ gan_mat34_lmultl33IT_q ( &m34A, &sm33L, &m34B ); /* B = L^-T*A, macro */ gan_mat34_rmultl44_q ( &m34A, &sm44L, &m34B ); /* B = A*L, macro */ gan_mat34_rmultl44T_q ( &m34A, &sm44L, &m34B ); /* B = A*L^T, macro */ gan_mat34_rmultl44I_q ( &m34A, &sm44L, &m34B ); /* B = A*L^-1, macro */ gan_mat34_rmultl44IT_q ( &m34A, &sm44L, &m34B ); /* B = A*L^-T, macro */There are also function calls to implement the same operations:

m34B = gan_mat34_lmultl33_s ( &m34A, &sm33L ); /* B = L*A, function call */ m34B = gan_mat34_lmultl33T_s ( &m34A, &sm33L ); /* B = L^T*A, function call */ m34B = gan_mat34_lmultl33I_s ( &m34A, &sm33L ); /* B = L^-1*A, function call */ m34B = gan_mat34_lmultl33IT_s ( &m34A, &sm33L ); /* B = L^-T*A, function call */ m34B = gan_mat34_rmultl44_s ( &m34A, &sm44L ); /* B = A*L, function call */ m34B = gan_mat34_rmultl44T_s ( &m34A, &sm44L ); /* B = A*L^T, function call */ m34B = gan_mat34_rmultl44I_s ( &m34A, &sm44L ); /* B = A*L^-1, function call */ m34B = gan_mat34_rmultl44IT_s ( &m34A, &sm44L ); /* B = A*L^-T, function call */Finally there is a set of macros for writing the result in-place into the matrix .

gan_mat34_lmultl33_i ( &m34A, &sm33L ); /* A = L*A, macro */ gan_mat34_lmultl33T_i ( &m34A, &sm33L ); /* A = L^T*A, macro */ gan_mat34_lmultl33I_i ( &m34A, &sm33L ); /* A = L^-1*A, macro */ gan_mat34_lmultl33IT_i ( &m34A, &sm33L ); /* A = L^-T*A, macro */ gan_mat34_rmultl44_i ( &m34A, &sm44L ); /* A = A*L, macro */ gan_mat34_rmultl44T_i ( &m34A, &sm44L ); /* A = A*L^T, macro */ gan_mat34_rmultl44I_i ( &m34A, &sm44L ); /* A = A*L^-1, macro */ gan_mat34_rmultl44IT_i ( &m34A, &sm44L ); /* A = A*L^-T, macro */

The next set of functions deals with multiplying a matrix by itself in
transpose, resulting in a symmetric matrix. These operations have the form

The Gandalf macro routines to implement these operations are

Gan_Matrix34 m34A; Gan_SquMatrix33 sm33S; Gan_SquMatrix44 sm44S; /* ... set up m34A using e.g. gan_mat34_fill_q() ... */ gan_mat34_slmultT_q ( &m34A, &sm44S ); /* S = A^T*A */ gan_mat34_srmultT_q ( &m34A, &sm33S ); /* S = A*A^T */with equivalent function calls

sm44S = gan_mat34_slmultT_s ( &m34A ); /* S = A^T*A */ sm33S = gan_mat34_srmultT_s ( &m34A ); /* S = A*A^T */There are similar routines for general matrices. For triangular matrices the functions are

Gan_SquMatrix33 sm33L, sm33S; /* ... set up sm33L using e.g. gan_ltmat33_fill_q()... */ gan_ltmat33_slmultT_q ( &sm33L, &sm33S ); /* S = L^T*L, macro */ sm33S = gan_ltmat33_slmultT_s ( &sm33L ); /* S = L^T*L, function call */ gan_ltmat33_srmultT_q ( &sm33L, &sm33S ); /* S = L*L^T, macro */ sm33S = gan_ltmat33_srmultT_s ( &sm33L ); /* S = L*L^T, function call */In the case of triangular matrices the operation ``multiply by transposed self'' can also be done in-place, so we also have the macro routines

Gan_SquMatrix33 sm33A; /* ... set up sm33A as triangular using e.g. gan_ltmat33_fill_q()... */ gan_ltmat33_slmultT_i ( &sm33A ); /* A = A^T*A, macro */ gan_ltmat33_srmultT_i ( &sm33A ); /* A = A*A^T, macro */

There are also routines to multiply a matrix by the transpose
of another matrix of the same size, where the result is *assumed* to be
a symmetric matrix. So mathematically the operations have the form

The Gandalf macro routines to implement these operations are

Gan_Matrix34 m34A, m34B; Gan_SquMatrix33 sm33S; Gan_SquMatrix44 sm44S; /* ... set up m34A, m34B using e.g. gan_mat34_fill_q() ... */ gan_mat34_rmultm34T_sym_q ( &m34A, &m34B, &sm33S ); /* S = A*B^T */ gan_mat34_lmultm34T_sym_q ( &m34B, &m34A, &sm44S ); /* S = A^T*B */with equivalent function calls

sm33S = gan_mat34_rmultm34T_sym_s ( &m34A, &m34B ); /* S = A*B^T */ sm44S = gan_mat34_lmultm34T_sym_s ( &m34B, &m34A ); /* S = A^T*B */

A common operation is to multiply a symmetric matrix on left and right by a matrix and its transpose, producing another symmetric matrix. Gandalf supports all combinations of these operations. Those involving matrices are

Gan_SquMatrix33 sm33Sa; Gan_SquMatrix44 sm44Sb; Gan_Matrix34 m34A; gan_symmat33_lrmultm34T_q ( &sm33Sa, &m34A, &sm44Sb ); /* Sb = A^T*Sa*A, macro */ sm44Sb = gan_symmat33_lrmultm34T_s ( &sm33Sa, &m34A ); /* Sb = A^T*Sa*A, function call */ gan_symmat44_lrmultm34_q ( &sm44Sb, &m34A, &sm33Sa ); /* Sa = A*Sb*A^T, macro */ sm33Sa = gan_symmat44_lrmultm34_s ( &sm44Sb, &m34A ); /* Sa = A*Sb*A^T, function call */

**Error detection:** If implicit inverse is used (e.g. the
`...multl33I_[qsi]()` or `...multl33IT_[qsi]()` routines),
the matrix must be non-singular, which for triangular matrices
means that none of the diagonal elements should be zero.
If the matrix was produced
by successful Cholesky factorisation of a symmetric matrix
(see Section 3.1.2.12) the matrix is guaranteed to be
non-singular. This is the normal method of creating a triangular matrix,
and Gandalf uses `assert()` to check for the singularity of the matrix.