/* nl-matrix.c --- matrix functions for newLISP
Copyright (C) 2007 Lutz Mueller
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Since version 8.9.9 all matrix operations work on lists and
arrays. Previously only lists where supported.
*/
#include "newlisp.h"
#include "protos.h"
#define TINY 1.0e-20
double * * multiply(double ** A, double ** B, int m, int n, int k, int l);
double * * invert(double * * A, int n, int * err);
int ludcmp(double * * a, int n, int * indx, double * d);
void lubksb(double * * a, int n, int * indx, double * b);
double * * getMatrix(CELL * params, int * type, int * n, int * m, int * err, int evalFlag);
double * * makeMatrix(CELL * number, int n, int m);
int getDimensions(CELL * mat, int * n, int * m);
double * * data2matrix(CELL * list, int n, int m);
CELL * matrix2data(double * * matrix, int type, int n, int m);
double * * allocateMatrix(int rows, int cols);
void freeMatrix(double * * m, int rows);
/* since version 7.4.8 'transpose' tramspose any matrix not only
matrices containing numbers.
*/
CELL * p_matTranspose(CELL * params)
{
CELL * A;
CELL * B;
CELL * rowA = NULL;
CELL * cell = NULL;
CELL * conCell;
CELL * new;
int n, m, i, j;
CELL * * Brows;
A = evaluateExpression(params);
if(A->type == CELL_ARRAY)
return(arrayTranspose(A));
if(A->type != CELL_EXPRESSION)
return(errorProcExt(ERR_NOT_MATRIX, params));
if(getDimensions(A, &n, &m) == FALSE)
return(errorProcExt(ERR_NOT_MATRIX, params));
if(n == 0 || m == 0)
return(errorProcExt(ERR_WRONG_DIMENSIONS, params));
Brows = allocMemory(sizeof(CELL *) * m);
for(j = 0; j < m; j++)
{
Brows[j] = getCell(CELL_EXPRESSION);
if(j > 0) Brows[j-1]->next = Brows[j];
}
B = getCell(CELL_EXPRESSION);
B->contents = (UINT)Brows[0];
for(i = 0; i < n; i++)
{
if(i == 0)
rowA = (CELL*)A->contents;
else
rowA = rowA->next;
if(rowA->type != CELL_EXPRESSION)
conCell = rowA;
else
conCell = NULL;
for(j = 0; j < m; j++)
{
if(conCell == NULL)
{
if(j == 0)
cell = (CELL*)rowA->contents;
else
cell = cell->next;
new = copyCell(cell);
}
else
new = copyCell(conCell);
if( i == 0)
Brows[j]->contents = (UINT)new;
else
Brows[j]->next = new;
Brows[j] = new;
}
}
free(Brows);
return(B);
}
CELL * p_matMultiply(CELL * params)
{
CELL * C;
double * * X = NULL;
double * * Y = NULL;
int typeX, typeY, typeM;
double * * M = NULL;
int n, m, k, l;
int err = 0;
if((X = getMatrix(params, &typeX, &n, &m, &err, TRUE)) == NULL)
return(errorProcExt(err, params));
if((Y = getMatrix(params->next, &typeY, &k, &l, &err, TRUE)) == NULL)
{
freeMatrix(X, n);
return(errorProcExt(err, params->next));
}
typeM = (typeX == CELL_ARRAY || typeY == CELL_ARRAY) ? CELL_ARRAY : CELL_EXPRESSION;
if(m != k)
err = ERR_WRONG_DIMENSIONS;
else if((M = multiply(X, Y, n, m, k, l)) == NULL)
err = ERR_NOT_ENOUGH_MEMORY;
freeMatrix(X, n);
freeMatrix(Y, k);
if(err) return(errorProc(err));
C = matrix2data(M, typeM, n, l);
freeMatrix(M, n);
return(C);
}
CELL * p_matInvert(CELL * params)
{
CELL * dataY;
int n, m, err = 0;
int typeA;
double * * A;
double * * Y;
if((A = getMatrix(params, &typeA, &n, &m, &err, TRUE)) == NULL)
return(errorProcExt(err, params));
if(n != m)
{
freeMatrix(A, n);
return(errorProcExt(ERR_WRONG_DIMENSIONS, params));
}
if( (Y = invert(A, n, &err)) == NULL)
{
freeMatrix(A, n);
if(err) return(errorProc(err));
return(nilCell);
}
dataY = matrix2data(Y, typeA, n, n);
freeMatrix(A, n);
freeMatrix(Y, n);
return(dataY);
}
CELL * p_determinant(CELL * params)
{
double * * M;
double d;
int typeM, n, m, i, err;
int * indx;
if( (M = getMatrix(params, &typeM, &m, &n, &err, TRUE)) == NULL)
return(errorProcExt(err, params));
if(n != m)
{
freeMatrix(M, n);
return(errorProcExt(ERR_WRONG_DIMENSIONS, params));
}
indx = (int *)calloc((n + 1), sizeof(int));
if(ludcmp(M, n, indx, &d) == FALSE)
return(nilCell);
for(i = 1; i <= n; i++)
d *= M[i][i];
return(stuffFloat(&d));
}
CELL * p_matScalar(CELL * params)
{
CELL * param;
double * * A = NULL;
double * * B = NULL;
double * * M = NULL;
int typeA, typeB, typeC = 0;
int type;
int n, m, k, l, err = 0;
CELL * result;
param = params;
param = evaluateExpression(param);
if(param->type == CELL_SYMBOL)
type = *((SYMBOL *)param->contents)->name;
else if(param->type == CELL_PRIMITIVE)
type = *(char *)param->aux;
else
return(errorProcExt(ERR_ILLEGAL_TYPE, params));
params = params->next;
if( (A = getMatrix(params, &typeA, &n, &m, &err, TRUE)) == NULL)
return(errorProcExt(err, params));
result = evaluateExpression(params->next);
if(isNumber(result->type))
{
k = n, l = m;
if((B = makeMatrix(result, k, l)) == NULL)
{
err = ERR_NOT_ENOUGH_MEMORY;
goto MAT_SCALAR_FIN;
}
typeB = CELL_EXPRESSION;
}
else if( (B = getMatrix(result, &typeB, &k, &l, &err, FALSE)) == NULL)
{
freeMatrix(A, n);
return(errorProc(err));
}
typeC = (typeA == CELL_ARRAY || typeB == CELL_ARRAY) ? CELL_ARRAY : CELL_EXPRESSION;
if(n != k || m != l)
err = ERR_WRONG_DIMENSIONS;
else if((M = allocateMatrix(n, m)) == NULL)
err = ERR_NOT_ENOUGH_MEMORY;
else
{
for(k = 1; k <= n; k++)
for(l = 1; l <= m; l++)
{
switch(type)
{
case '+': M[k][l] = A[k][l] + B[k][l]; break;
case '-': M[k][l] = A[k][l] - B[k][l]; break;
case '*': M[k][l] = A[k][l] * B[k][l]; break;
case '/':
{
if(B[k][l] == 0)
return(errorProc(ERR_MATH));
else
M[k][l] = A[k][l] / B[k][l];
}
break;
default:
return(errorProcExt(ERR_ILLEGAL_TYPE, params));
}
}
}
MAT_SCALAR_FIN:
freeMatrix(A, n);
freeMatrix(B, n);
if(err) return (errorProc(err));
result = matrix2data(M, typeC, n, m);
freeMatrix(M, n);
return(result);
}
/* ------------------- C = A*B, A and B unchanged --------------------- */
double * * multiply(double ** A, double ** B, int n, int m, int k, int l)
{
double * * C;
int i, j, s;
double sum;
if((C = allocateMatrix(n, l)) == NULL)
return(NULL);
if(m != k)
{
errorProc(ERR_WRONG_DIMENSIONS);
return(NULL);
}
for(i = 1; i <= n; i++)
{
for(j = 1; j <= l; j++)
{
sum = 0.0;
for(s = 1; s <= m; s++)
sum += A[i][s] * B[s][j];
C[i][j] = sum;
}
}
return(C);
}
/* ----- return inverse of A in Y, A will contain LU decomposition --- */
double * * invert(double * * A, int n, int * err)
{
double * * Y = NULL;
double * col;
double d;
int i, j;
int * indx;
col = (double *)calloc(n + 4, sizeof(double));
indx = (int *)calloc((n + 1), sizeof(int));
if(ludcmp(A, n, indx, &d) == FALSE)
goto INVERT_FIN;
if((Y = allocateMatrix(n, n)) == NULL)
{
*err = ERR_NOT_ENOUGH_MEMORY;
goto INVERT_FIN;
}
for(j = 1; j <= n; j++)
{
for(i = 1; i <= n; i++) col[i] = 0.0;
col[j] = 1.0;
lubksb(A, n, indx, col);
for(i = 1; i <= n; i++) Y[i][j] = col[i];
}
INVERT_FIN:
free(col);
free(indx);
return(Y);
}
/* ------------------------- LU decomposition ---------------------------
// algorithms ludcmp() and lubskb() adapted from:
// Numerical Recipes in 'C', 2nd Edition
// W.H. Press, S.A. Teukolsky
// W.T. Vettering, B.P. Flannery
*/
int ludcmp(double * * a, int n, int * indx, double * d)
{
int i, imax = 0, j, k;
double big, dum, sum, temp;
double * vv;
vv = (double *)calloc(n + 4, sizeof(double));
*d = 1.0;
/* find abs biggest number in each row and put 1/big in vector */
for (i = 1; i <= n; i++)
{
big = 0.0;
for(j = 1;j <= n; j++)
if ((temp = fabs(a[i][j])) > big) big = temp;
if(big == 0.0)
{
free(vv);
return(FALSE);
}
vv[i] = 1.0 / big;
}
for (j = 1; j <= n; j++)
{
for (i = 1; i < j; i++)
{
sum = a[i][j];
for(k = 1; k < i; k++) sum -= a[i][k] * a[k][j];
a[i][j] = sum;
}
big = 0.0;
for (i = j; i <= n; i++)
{
sum = a[i][j];
for(k = 1; k < j; k++)
sum -= a[i][k] * a[k][j];
a[i][j] = sum;
if ( (dum = vv[i] * fabs(sum)) >= big)
{
big = dum;
imax = i;
}
}
if (j != imax)
{
for (k = 1; k <= n; k++)
{
dum = a[imax][k];
a[imax][k] = a[j][k];
a[j][k] = dum;
}
*d = -(*d);
vv[imax] = vv[j];
}
indx[j] = imax;
if (a[j][j] == 0.0) a[j][j] = TINY;
if (j != n)
{
dum = 1.0 / (a[j][j]);
for(i = j+1; i <= n; i++) a[i][j] *= dum;
}
}
free(vv);
return(TRUE);
}
void lubksb(double * * a, int n, int * indx, double * b)
{
int i, ii = 0, ip, j;
double sum;
for (i = 1; i <= n; i++)
{
ip = indx[i];
sum = b[ip];
b[ip] = b[i];
if (ii)
for(j = ii; j <= i-1; j++) sum -= a[i][j] * b[j];
else
if(sum) ii = i;
b[i] = sum;
}
for (i = n; i >= 1; i--)
{
sum = b[i];
for(j = i+1; j <= n; j++) sum -= a[i][j] * b[j];
b[i] = sum / a[i][i];
}
}
/* ------------ make a matrix from list or array expr ----------- */
double * * getMatrix(CELL * params, int * type, int * n, int * m, int *err, int evalFlag)
{
CELL * data;
double * * M;
if(evalFlag)
data = evaluateExpression(params);
else
data = params;
if(data->type != CELL_EXPRESSION && data->type != CELL_ARRAY)
{
*err = ERR_NOT_MATRIX;
return(NULL);
}
*type = data->type;
if(getDimensions(data, n, m) == FALSE)
{
*err = ERR_NOT_MATRIX;
return(NULL);
}
if( (M = data2matrix(data, *n, *m)) == NULL)
{
*err = ERR_NOT_ENOUGH_MEMORY;
return(NULL);
}
return(M);
}
double * * makeMatrix(CELL * number, int n, int m)
{
double s;
double * * matrix;
int i, j;
#ifndef NEWLISP64
if(number->type == CELL_FLOAT)
s = *(double *)&number->aux;
else if(number->type == CELL_INT64)
s = *(INT64 *)&number->aux;
#else
if(number->type == CELL_FLOAT)
s = *(double *)&number->contents;
#endif
else
s = number->contents;
if((matrix = allocateMatrix(n, m)) == NULL)
return(NULL);
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
matrix[i][j] = s;
return(matrix);
}
/* --------------------------- get dimensons or a matrix --------------- */
int getDimensions(CELL * mat, int * n, int * m)
{
CELL * row;
CELL * cell;
int rows, cols;
CELL * * addr;
if(mat->type == CELL_ARRAY)
{
addr = (CELL * *)mat->contents;
*n = (mat->aux - 1) / sizeof(CELL *);
cell = *addr;
if(cell->type != CELL_ARRAY)
{
errorProcExt(ERR_WRONG_DIMENSIONS, mat);
return(FALSE);
}
*m = (cell->aux - 1) / sizeof(CELL *);
return(TRUE);
}
/* mat->type is CELL_EXPRESSSION */
row = (CELL *)mat->contents;
if(row->type != CELL_EXPRESSION)
{
errorProcExt(ERR_NOT_MATRIX, mat);
return(FALSE);
}
cell = (CELL *)row->contents;
rows = cols = 0;
while(row != nilCell)
{
++rows;
row = row->next;
}
while(cell != nilCell)
{
++cols;
cell = cell->next;
}
if(rows == 0 || cols == 0)
{
errorProcExt(ERR_WRONG_DIMENSIONS, mat);
return(FALSE);
}
*n = rows;
*m = cols;
return(TRUE);
}
/* ------------ copy array/list into a matrix of doubles -------- */
/* xlate lists or arrays into C arrays with 1-offest for 1st element
wrong row type result and missing row elelements result on 0.0 */
double * * data2matrix(CELL * list, int n, int m)
{
double ** matrix;
CELL * row;
CELL * cell;
int i, j, size;
CELL * * addr;
CELL * * rowAddr;
if((matrix = allocateMatrix(n, m)) == NULL)
return(NULL);
if(list->type == CELL_ARRAY)
{
addr = (CELL * *)list->contents;
for(i = 1; i <= n; i++)
{
row = *(addr + i - 1);
if(row->type != CELL_ARRAY)
{
for(j = 1; j <= m; j++) matrix[i][j] = 0.0;
continue;
}
rowAddr = (CELL * *)row->contents;
size = (row->aux - 1)/sizeof(CELL *);
for(j = 1; j <= m; j++)
{
if(j <= size)
matrix[i][j] = getDirectFloat((CELL *)*(rowAddr + j - 1));
else matrix[i][j] = 0.0;
}
}
return(matrix);
}
/* list->type == CELL_EXPRESSION */
row = (CELL *)list->contents;
for(i = 1; i <= n; i++)
{
if(row->type != CELL_EXPRESSION)
for(j = 1; j <= m; j++) matrix[i][j] = 0.0;
else
{
cell = (CELL *)row->contents;
for(j = 1; j <= m; j++)
{
matrix[i][j] = getDirectFloat(cell);
cell = cell->next;
}
}
row = row->next;
}
return(matrix);
}
/* ------- allocate an array/list and copy matrix into it ---- */
CELL * matrix2data(double ** matrix, int type, int n, int m)
{
CELL * list;
CELL * row;
CELL * cell;
int i, j;
double fnum;
CELL * * addr;
CELL * * rowAddr;
list = getCell(type);
if(type == CELL_EXPRESSION)
{
row = getCell(CELL_EXPRESSION);
list->contents = (UINT)row;
for(i = 1; i <= n; i++)
{
cell = getCell(CELL_FLOAT);
row->contents = (UINT)cell;
for(j = 1; j <= m; j++)
{
fnum = matrix[i][j];
#ifndef NEWLISP64
*(double *)&cell->aux = fnum;
#else
*(double *)&cell->contents = fnum;
#endif
if(j == m) break;
cell->next = getCell(CELL_FLOAT);
cell = cell->next;
}
if(i == n) break;
row->next = getCell(CELL_EXPRESSION);
row = row->next;
}
}
else /* type is CELL_ARRAY */
{
list->aux = n * sizeof(CELL *) + 1;
addr = (CELL * *)allocMemory(list->aux);
list->contents = (UINT)addr;
for(i = 1; i <= n; i++)
{
cell = getCell(CELL_ARRAY);
cell->aux = m * sizeof(CELL *) + 1;
rowAddr = (CELL * *)allocMemory(cell->aux);
cell->contents = (UINT)rowAddr;
*(addr + i -1) = cell;
for(j = 1; j <= m; j++)
{
fnum = matrix[i][j];
*(rowAddr + j - 1) = stuffFloat(&fnum);
}
}
}
return(list);
}
/* ------------------------ allocate/free a matrix ----------------- */
double * * allocateMatrix(int rows, int cols)
{
int i;
double * * m;
m = (double **) calloc(rows + 1, sizeof(double *));
if (!m)
return(NULL);
for(i = 1; i <= rows; i++)
{
m[i] = (double *)calloc(cols + 1, sizeof(double));
if (!m[i])
return(NULL);
}
return(m);
}
void freeMatrix(double * * m, int rows)
{
int i;
if(m == NULL) return;
for(i = 1; i <= rows; i++)
free(m[i]);
free(m);
}
/* end of file */
syntax highlighted by Code2HTML, v. 0.9.1