* Copyright (c) 1994 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
* Extended precision rational arithmetic matrix functions.
* Matrices can contain arbitrary types of elements.
static void matswaprow
MATH_PROTO((MATRIX
*m
, long r1
, long r2
));
static void matsubrow
MATH_PROTO((MATRIX
*m
, long oprow
, long baserow
,
static void matmulrow
MATH_PROTO((MATRIX
*m
, long row
, VALUE
*mulval
));
static MATRIX
*matident
MATH_PROTO((MATRIX
*m
));
* Add two compatible matrices.
long min1
, min2
, max1
, max2
, index
;
if (m1
->m_dim
!= m2
->m_dim
)
math_error("Incompatible matrix dimensions for add");
for (dim
= 0; dim
< m1
->m_dim
; dim
++) {
if ((min1
&& min2
&& (min1
!= min2
)) || ((max1
-min1
) != (max2
-min2
)))
math_error("Incompatible matrix bounds for add");
tmp
.m_min
[dim
] = (min1
? min1
: min2
);
tmp
.m_max
[dim
] = tmp
.m_min
[dim
] + (max1
- min1
);
res
= matalloc(m1
->m_size
);
for (index
= m1
->m_size
; index
> 0; index
--)
addvalue(v1
++, v2
++, vres
++);
* Subtract two compatible matrices.
long min1
, min2
, max1
, max2
, index
;
if (m1
->m_dim
!= m2
->m_dim
)
math_error("Incompatible matrix dimensions for sub");
for (dim
= 0; dim
< m1
->m_dim
; dim
++) {
if ((min1
&& min2
&& (min1
!= min2
)) || ((max1
-min1
) != (max2
-min2
)))
math_error("Incompatible matrix bounds for sub");
tmp
.m_min
[dim
] = (min1
? min1
: min2
);
tmp
.m_max
[dim
] = tmp
.m_min
[dim
] + (max1
- min1
);
res
= matalloc(m1
->m_size
);
for (index
= m1
->m_size
; index
> 0; index
--)
subvalue(v1
++, v2
++, vres
++);
* Produce the negative of a matrix.
register VALUE
*val
, *vres
;
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
* Multiply two compatible matrices.
long i1
, i2
, max1
, max2
, index
, maxindex
;
if ((m1
->m_dim
!= 2) || (m2
->m_dim
!= 2))
math_error("Matrix dimension must be two for mul");
if ((m1
->m_max
[1] - m1
->m_min
[1]) != (m2
->m_max
[0] - m2
->m_min
[0]))
math_error("Incompatible bounds for matrix mul");
max1
= (m1
->m_max
[0] - m1
->m_min
[0] + 1);
max2
= (m2
->m_max
[1] - m2
->m_min
[1] + 1);
maxindex
= (m1
->m_max
[1] - m1
->m_min
[1] + 1);
res
= matalloc(max1
* max2
);
res
->m_min
[0] = m1
->m_min
[0];
res
->m_max
[0] = m1
->m_max
[0];
res
->m_min
[1] = m2
->m_min
[1];
res
->m_max
[1] = m2
->m_max
[1];
for (i1
= 0; i1
< max1
; i1
++) {
for (i2
= 0; i2
< max2
; i2
++) {
sum
.v_num
= qlink(&_qzero_
);
v1
= &m1
->m_table
[i1
* maxindex
];
for (index
= 0; index
< maxindex
; index
++) {
addvalue(&sum
, &tmp1
, &tmp2
);
index
= (i1
* max2
) + i2
;
res
->m_table
[index
] = sum
;
math_error("Matrix dimension must be two for square");
if ((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))
math_error("Squaring non-square matrix");
max
= (m
->m_max
[0] - m
->m_min
[0] + 1);
res
= matalloc(max
* max
);
res
->m_min
[0] = m
->m_min
[0];
res
->m_max
[0] = m
->m_max
[0];
res
->m_min
[1] = m
->m_min
[1];
res
->m_max
[1] = m
->m_max
[1];
for (i1
= 0; i1
< max
; i1
++) {
for (i2
= 0; i2
< max
; i2
++) {
sum
.v_num
= qlink(&_qzero_
);
v1
= &m
->m_table
[i1
* max
];
for (index
= 0; index
< max
; index
++) {
addvalue(&sum
, &tmp1
, &tmp2
);
res
->m_table
[index
] = sum
;
* Compute the result of raising a square matrix to an integer power.
* Negative powers mean the positive power of the inverse.
* Note: This calculation could someday be improved for large powers
* by using the characteristic polynomial of the matrix.
MATRIX
*m
; /* matrix to be raised */
NUMBER
*q
; /* power to raise it to */
long power
; /* power to raise to */
unsigned long bit
; /* current bit value */
math_error("Matrix dimension must be two for power");
if ((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))
math_error("Raising non-square matrix to a power");
math_error("Raising matrix to non-integral power");
math_error("Raising matrix to very large power");
power
= (zistiny(q
->num
) ? z1tol(q
->num
) : z2tol(q
->num
));
* Handle some low powers specially
if ((power
<= 4) && (power
>= -2)) {
* Compute the power by squaring and multiplying.
* This uses the left to right method of power raising.
while ((bit
& power
) == 0)
* Calculate the cross product of two one dimensional matrices each
if ((m1
->m_dim
!= 1) || (m2
->m_dim
!= 1))
math_error("Matrix not 1d for cross product");
if ((m1
->m_size
!= 3) || (m2
->m_size
!= 3))
math_error("Matrix not size 3 for cross product");
mulvalue(v1
+ 1, v2
+ 2, &tmp1
);
mulvalue(v1
+ 2, v2
+ 1, &tmp2
);
subvalue(&tmp1
, &tmp2
, vr
+ 0);
mulvalue(v1
+ 2, v2
+ 0, &tmp1
);
mulvalue(v1
+ 0, v2
+ 2, &tmp2
);
subvalue(&tmp1
, &tmp2
, vr
+ 1);
mulvalue(v1
+ 0, v2
+ 1, &tmp1
);
mulvalue(v1
+ 1, v2
+ 0, &tmp2
);
subvalue(&tmp1
, &tmp2
, vr
+ 2);
* Return the dot product of two matrices.
* result = matdot(m1, m2);
VALUE result
, tmp1
, tmp2
;
if ((m1
->m_dim
!= 1) || (m2
->m_dim
!= 1))
math_error("Matrix not 1d for dot product");
if (m1
->m_size
!= m2
->m_size
)
math_error("Incompatible matrix sizes for dot product");
mulvalue(v1
, v2
, &result
);
mulvalue(++v1
, ++v2
, &tmp1
);
addvalue(&result
, &tmp1
, &tmp2
);
* Scale the elements of a matrix by a specified power of two.
MATRIX
*m
; /* matrix to be scaled */
register VALUE
*val
, *vres
;
MATRIX
*res
; /* resulting matrix */
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
scalevalue(val
++, &num
, vres
++);
* Shift the elements of a matrix by the specified number of bits.
* Positive shift means leftwards, negative shift rightwards.
MATRIX
*m
; /* matrix to be scaled */
register VALUE
*val
, *vres
;
MATRIX
*res
; /* resulting matrix */
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
shiftvalue(val
++, &num
, FALSE
, vres
++);
* Multiply the elements of a matrix by a specified value.
MATRIX
*m
; /* matrix to be multiplied */
VALUE
*vp
; /* value to multiply by */
register VALUE
*val
, *vres
;
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
mulvalue(val
++, vp
, vres
++);
* Divide the elements of a matrix by a specified value, keeping
* only the integer quotient.
MATRIX
*m
; /* matrix to be divided */
VALUE
*vp
; /* value to divide by */
register VALUE
*val
, *vres
;
if ((vp
->v_type
== V_NUM
) && qiszero(vp
->v_num
))
math_error("Division by zero");
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
quovalue(val
++, vp
, vres
++);
* Divide the elements of a matrix by a specified value, keeping
* only the remainder of the division.
MATRIX
*m
; /* matrix to be divided */
VALUE
*vp
; /* value to divide by */
register VALUE
*val
, *vres
;
if ((vp
->v_type
== V_NUM
) && qiszero(vp
->v_num
))
math_error("Division by zero");
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
modvalue(val
++, vp
, vres
++);
MATRIX
*m
; /* matrix to be transposed */
register VALUE
*v1
, *v2
; /* current values */
long rows
, cols
; /* rows and columns in new matrix */
long row
, col
; /* current row and column */
math_error("Matrix dimension must be two for transpose");
res
= matalloc(m
->m_size
);
res
->m_min
[0] = m
->m_min
[1];
res
->m_max
[0] = m
->m_max
[1];
res
->m_min
[1] = m
->m_min
[0];
res
->m_max
[1] = m
->m_max
[0];
rows
= (m
->m_max
[1] - m
->m_min
[1] + 1);
cols
= (m
->m_max
[0] - m
->m_min
[0] + 1);
for (row
= 0; row
< rows
; row
++) {
for (col
= 0; col
< cols
; col
++) {
* Produce a matrix with values all of which are conjugated.
register VALUE
*val
, *vres
;
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
conjvalue(val
++, vres
++);
* Produce a matrix with values all of which have been rounded to the
* specified number of decimal places.
register VALUE
*val
, *vres
;
math_error("Negative number of places for matround");
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
roundvalue(val
++, &tmp
, vres
++);
* Produce a matrix with values all of which have been rounded to the
* specified number of binary places.
register VALUE
*val
, *vres
;
math_error("Negative number of places for matbround");
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
broundvalue(val
++, &tmp
, vres
++);
* Produce a matrix with values all of which have been truncated to integers.
register VALUE
*val
, *vres
;
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
* Produce a matrix with values all of which have only the fraction part left.
register VALUE
*val
, *vres
;
res
= matalloc(m
->m_size
);
for (index
= m
->m_size
; index
> 0; index
--)
fracvalue(val
++, vres
++);
* Index a matrix normally by the specified set of index values.
* Returns the address of the matrix element if it is valid, or generates
* an error if the index values are out of range. The create flag is TRUE
* if the element is to be written, but this is ignored here.
matindex(mp
, create
, dim
, indices
)
long dim
; /* dimension of the indexing */
VALUE
*indices
; /* table of values being indexed by */
NUMBER
*q
; /* index value */
long index
; /* index value as an integer */
long offset
; /* current offset into array */
int i
; /* loop counter */
if ((dim
<= 0) || (dim
> MAXDIM
))
math_error("Bad dimension %ld for matrix", dim
);
math_error("Indexing a %ldd matrix as a %ldd matrix", mp
->m_dim
, dim
);
for (i
= 0; i
< dim
; i
++) {
if (indices
->v_type
!= V_NUM
)
math_error("Non-numeric index for matrix");
math_error("Non-integral index for matrix");
if (zisbig(q
->num
) || (index
< mp
->m_min
[i
]) || (index
> mp
->m_max
[i
]))
math_error("Index out of bounds for matrix");
offset
*= (mp
->m_max
[i
] - mp
->m_min
[i
] + 1);
offset
+= (index
- mp
->m_min
[i
]);
return mp
->m_table
+ offset
;
* Search a matrix for the specified value, starting with the specified index.
* Returns the index of the found value, or -1 if the value was not found.
val
= &m
->m_table
[index
];
while (index
< m
->m_size
) {
if (!comparevalue(vp
, val
))
* Search a matrix backwards for the specified value, starting with the
* specified index. Returns the index of the found value, or -1 if the
val
= &m
->m_table
[index
];
if (!comparevalue(vp
, val
))
* Fill all of the elements of a matrix with one of two specified values.
* All entries are filled with the first specified value, except that if
* the matrix is square and the second value pointer is non-NULL, then
* all diagonal entries are filled with the second value. This routine
* affects the supplied matrix directly, and doesn't return a copy.
MATRIX
*m
; /* matrix to be filled */
VALUE
*v1
; /* value to fill most of matrix with */
VALUE
*v2
; /* value for diagonal entries (or NULL) */
if (v2
&& ((m
->m_dim
!= 2) ||
((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))))
math_error("Filling diagonals of non-square matrix");
for (index
= m
->m_size
; index
> 0; index
--)
for (index
= m
->m_size
; index
> 0; index
--)
rows
= m
->m_max
[0] - m
->m_min
[0] + 1;
for (row
= 0; row
< rows
; row
++) {
for (col
= 0; col
< rows
; col
++) {
copyvalue(((row
!= col
) ? v1
: v2
), val
++);
* Set a copy of a square matrix to the identity matrix.
register VALUE
*val
; /* current value */
long row
, col
; /* current row and column */
long rows
; /* number of rows */
MATRIX
*res
; /* resulting matrix */
math_error("Matrix dimension must be two for setting to identity");
if ((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))
math_error("Matrix must be square for setting to identity");
res
= matalloc(m
->m_size
);
rows
= (res
->m_max
[0] - res
->m_min
[0] + 1);
for (row
= 0; row
< rows
; row
++) {
for (col
= 0; col
< rows
; col
++) {
val
->v_num
= ((row
== col
) ? qlink(&_qone_
) : qlink(&_qzero_
));
* Calculate the inverse of a matrix if it exists.
* This is done by using transformations on the supplied matrix to convert
* it to the identity matrix, and simultaneously applying the same set of
* transformations to the identity matrix.
MATRIX
*res
; /* matrix to become the inverse */
long rows
; /* number of rows */
long cur
; /* current row being worked on */
long row
, col
; /* temp row and column values */
VALUE
*val
; /* current value in matrix*/
VALUE mulval
; /* value to multiply rows by */
VALUE tmpval
; /* temporary value */
math_error("Matrix dimension must be two for inverse");
if ((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))
math_error("Inverting non-square matrix");
* Begin by creating the identity matrix with the same attributes.
res
= matalloc(m
->m_size
);
rows
= (m
->m_max
[0] - m
->m_min
[0] + 1);
for (row
= 0; row
< rows
; row
++) {
for (col
= 0; col
< rows
; col
++) {
val
->v_num
= qlink(&_qone_
);
val
->v_num
= qlink(&_qzero_
);
* Now loop over each row, and eliminate all entries in the
* corresponding column by using row operations. Do the same
* operations on the resulting matrix. Copy the original matrix
* so that we don't destroy it.
for (cur
= 0; cur
< rows
; cur
++) {
* Find the first nonzero value in the rest of the column
* downwards from [cur,cur]. If there is no such value, then
* the matrix is not invertible. If the first nonzero entry
* is not the current row, then swap the two rows to make the
val
= &m
->m_table
[(row
* rows
) + row
];
while (testvalue(val
) == 0) {
math_error("Matrix is not invertible");
invertvalue(val
, &mulval
);
matswaprow(res
, row
, cur
);
* Now for every other nonzero entry in the current column, subtract
* the appropriate multiple of the current row to force that entry
/* ignore Saber-C warning about bad pointer val */
for (row
= 0; row
< rows
; row
++, val
+= rows
) {
if ((row
== cur
) || (testvalue(val
) == 0))
mulvalue(val
, &mulval
, &tmpval
);
matsubrow(m
, row
, cur
, &tmpval
);
matsubrow(res
, row
, cur
, &tmpval
);
* Now the original matrix has nonzero entries only on its main diagonal.
* Scale the rows of the result matrix by the inverse of those entries.
for (row
= 0; row
< rows
; row
++) {
if ((val
->v_type
!= V_NUM
) || !qisone(val
->v_num
)) {
invertvalue(val
, &mulval
);
matmulrow(res
, row
, &mulval
);
/* ignore Saber-C warning about bad pointer val */
* Calculate the determinant of a square matrix.
* This is done using row operations to create an upper-diagonal matrix.
long rows
; /* number of rows */
long cur
; /* current row being worked on */
long row
; /* temp row values */
int neg
; /* whether to negate determinant */
VALUE
*val
; /* current value */
VALUE mulval
, tmpval
; /* other values */
math_error("Matrix dimension must be two for determinant");
if ((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1]))
math_error("Non-square matrix for determinant");
* Loop over each row, and eliminate all lower entries in the
* corresponding column by using row operations. Copy the original
* matrix so that we don't destroy it.
rows
= (m
->m_max
[0] - m
->m_min
[0] + 1);
for (cur
= 0; cur
< rows
; cur
++) {
* Find the first nonzero value in the rest of the column
* downwards from [cur,cur]. If there is no such value, then
* the determinant is zero. If the first nonzero entry is not
* the current row, then swap the two rows to make the current
* one nonzero, and remember that the determinant changes sign.
val
= &m
->m_table
[(row
* rows
) + row
];
while (testvalue(val
) == 0) {
mulval
.v_num
= qlink(&_qzero_
);
invertvalue(val
, &mulval
);
* Now for every other nonzero entry lower down in the current column,
* subtract the appropriate multiple of the current row to force that
/* ignore Saber-C warning about bad pointer into val */
val
= &m
->m_table
[(row
* rows
) + cur
];
/* ignore Saber-C warning about bad pointer into val */
for (; row
< rows
; row
++, val
+= rows
) {
mulvalue(val
, &mulval
, &tmpval
);
matsubrow(m
, row
, cur
, &tmpval
);
* Now the matrix is upper-diagonal, and the determinant is the
* product of the main diagonal entries, and is possibly negated.
mulval
.v_num
= qlink(&_qone_
);
for (row
= 0; row
< rows
; row
++) {
mulvalue(&mulval
, val
, &tmpval
);
/* ignore Saber-C warning about bad pointer into val */
negvalue(&mulval
, &tmpval
);
* Local utility routine to swap two rows of a square matrix.
* No checks are made to verify the legality of the arguments.
rows
= (m
->m_max
[0] - m
->m_min
[0] + 1);
v1
= &m
->m_table
[r1
* rows
];
v2
= &m
->m_table
[r2
* rows
];
* Local utility routine to subtract a multiple of one row to another one.
* The row to be changed is oprow, the row to be subtracted is baserow.
* No checks are made to verify the legality of the arguments.
matsubrow(m
, oprow
, baserow
, mulval
)
register VALUE
*vop
, *vbase
;
entries
= (m
->m_max
[0] - m
->m_min
[0] + 1);
vop
= &m
->m_table
[oprow
* entries
];
vbase
= &m
->m_table
[baserow
* entries
];
mulvalue(vbase
, mulval
, &tmp1
);
subvalue(vop
, &tmp1
, &tmp2
);
* Local utility routine to multiply a row by a specified number.
* No checks are made to verify the legality of the arguments.
matmulrow(m
, row
, mulval
)
rows
= (m
->m_max
[0] - m
->m_min
[0] + 1);
val
= &m
->m_table
[row
* rows
];
mulvalue(val
, mulval
, &tmp
);
* Make a full copy of a matrix.
res
= matalloc(m
->m_size
);
if (v1
->v_type
== V_NUM
) {
v2
->v_num
= qlink(v1
->v_num
);
* Allocate a matrix with the specified number of elements.
m
= (MATRIX
*) malloc(matsize(size
));
math_error("Cannot get memory to allocate matrix of size %d", size
);
* Free a matrix, along with all of its element values.
if (vp
->v_type
== V_NUM
) {
* Test whether a matrix has any nonzero values.
if ((vp
->v_type
!= V_NUM
) || (!qiszero(vp
->v_num
)))
* Test whether or not two matrices are equal.
* Equality is determined by the shape and values of the matrices,
* but not by their index bounds. Returns TRUE if they differ.
if ((m1
->m_dim
!= m2
->m_dim
) || (m1
->m_size
!= m2
->m_size
))
for (i
= 0; i
< m1
->m_dim
; i
++) {
if ((m1
->m_max
[i
] - m1
->m_min
[i
]) != (m2
->m_max
[i
] - m2
->m_min
[i
]))
if (comparevalue(v1
++, v2
++))
* Test whether or not a matrix is the identity matrix.
register VALUE
*val
; /* current value */
long row
, col
; /* row and column numbers */
((m
->m_max
[0] - m
->m_min
[0]) != (m
->m_max
[1] - m
->m_min
[1])))
for (row
= 0; row
< m
->m_size
; row
++) {
for (col
= 0; col
< m
->m_size
; col
++) {
if (val
->v_type
!= V_NUM
)
if (!qiszero(val
->v_num
))
* Return a trivial hash value for a matrix.
hash
= m
->m_dim
* 500009;
for (i
= m
->m_dim
- 1; i
>= 0; i
--) {
hash
= hash
* 500029 + m
->m_max
[i
];
fullsize
*= (m
->m_max
[i
] - m
->m_min
[i
] + 1);
hash
= hash
* 500041 + fullsize
;
for (i
= 0; ((i
< fullsize
) && (i
< 16)); i
++)
hash
= hash
* 500057 + hashvalue(vp
++);
skip
= (fullsize
/ 11) + 1;
hash
= hash
* 500069 + hashvalue(vp
);
* Print a matrix and possibly few of its elements.
* The argument supplied specifies how many elements to allow printing.
* If any elements are printed, they are printed in short form.
long fullsize
, count
, index
, num
;
for (i
= dim
- 1; i
>= 0; i
--) {
fullsize
*= (m
->m_max
[i
] - m
->m_min
[i
] + 1);
msg
= ((max_print
> 0) ? "\nmat [" : "mat [");
for (i
= 0; i
< dim
; i
++) {
math_fmt("%s%ld:%ld", msg
, m
->m_min
[i
], m
->m_max
[i
]);
math_fmt("%s%ld", msg
, m
->m_max
[i
] + 1);
if (max_print
> fullsize
)
for (index
= 0; index
< fullsize
; index
++) {
if ((vp
->v_type
!= V_NUM
) || !qiszero(vp
->v_num
))
math_fmt("] (%ld element%s, %ld nonzero)",
fullsize
, (fullsize
== 1) ? "" : "s", count
);
* Now print the first few elements of the matrix in short
for (index
= 0; index
< max_print
; index
++) {
for (i
= 0; i
< dim
; i
++) {
math_fmt("%s%ld", msg
, m
->m_min
[i
] + (num
/ sizes
[i
]));
printvalue(vp
++, PRINT_SHORT
| PRINT_UNAMBIG
);
if (max_print
< fullsize
)