/* GENIUS Calculator
* Copyright (C) 1997-2007 Jiri (George) Lebl
*
* Author: Jiri (George) Lebl
*
* This file is part of Genius.
*
* Genius 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 .
*/
#include "config.h"
#include
#include
#include
#include "calc.h"
#include "mpwrap.h"
#include "eval.h"
#include "dict.h"
#include "util.h"
#include "funclib.h"
#include "matrix.h"
#include "matrixw.h"
#include "matop.h"
extern calcstate_t calcstate;
gboolean
gel_is_matrix_value_only (GelMatrixW *m)
{
int i, j, w, h;
if (m->cached_value_only)
return m->value_only;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index(m,i,j);
if(n && n->type != VALUE_NODE) {
m->cached_value_only = 1;
m->value_only = 0;
m->cached_value_only_real = 1;
m->value_only_real = 0;
m->cached_value_only_rational = 1;
m->value_only_rational = 0;
m->cached_value_only_integer = 1;
m->value_only_integer = 0;
return FALSE;
}
}
}
m->cached_value_only = 1;
m->value_only = 1;
return TRUE;
}
gboolean
gel_is_matrix_value_or_bool_only (GelMatrixW *m)
{
int i, j, w, h;
gboolean got_bools = FALSE;
if (m->cached_value_or_bool_only)
return m->value_or_bool_only;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index(m,i,j);
if ( ! n)
continue;
if (n->type == BOOL_NODE) {
got_bools = TRUE;
continue;
}
if (n->type != VALUE_NODE) {
m->cached_value_or_bool_only = 1;
m->value_or_bool_only = 0;
m->cached_value_only = 1;
m->value_only = 0;
m->cached_value_only_real = 1;
m->value_only_real = 0;
m->cached_value_only_rational = 1;
m->value_only_rational = 0;
m->cached_value_only_integer = 1;
m->value_only_integer = 0;
return FALSE;
}
}
}
m->cached_value_or_bool_only = 1;
m->value_or_bool_only = 1;
m->cached_value_only = 1;
if (got_bools)
m->value_only = 0;
else
m->value_only = 1;
return TRUE;
}
gboolean
gel_is_matrix_value_only_real (GelMatrixW *m)
{
int i, j, w, h;
if (m->cached_value_only_real)
return m->value_only_real;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index(m,i,j);
if (n != NULL &&
(n->type != VALUE_NODE ||
mpw_is_complex (n->val.value))) {
m->cached_value_only_real = 1;
m->value_only_real = 0;
return FALSE;
}
}
}
m->cached_value_only = 1;
m->value_only = 1;
m->cached_value_only_real = 1;
m->value_only_real = 1;
return TRUE;
}
gboolean
gel_is_matrix_value_only_rational (GelMatrixW *m)
{
int i, j, w, h;
if (m->cached_value_only_rational)
return m->value_only_rational;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index(m,i,j);
if (n != NULL &&
(n->type != VALUE_NODE ||
mpw_is_complex (n->val.value) ||
mpw_is_float (n->val.value))) {
m->cached_value_only_rational = 1;
m->value_only_rational = 0;
return FALSE;
}
}
}
m->cached_value_only = 1;
m->value_only = 1;
m->cached_value_only_rational = 1;
m->value_only_rational = 1;
return TRUE;
}
gboolean
gel_is_matrix_value_only_integer (GelMatrixW *m)
{
int i, j, w, h;
if (m->cached_value_only_integer)
return m->value_only_integer;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index(m,i,j);
if (n != NULL &&
(n->type != VALUE_NODE ||
mpw_is_complex (n->val.value) ||
! mpw_is_integer (n->val.value))) {
m->cached_value_only_integer = 1;
m->value_only_integer = 0;
return FALSE;
}
}
}
m->cached_value_only = 1;
m->value_only = 1;
m->cached_value_only_rational = 1;
m->value_only_rational = 1;
m->cached_value_only_integer = 1;
m->value_only_integer = 1;
return TRUE;
}
void
gel_matrix_conjugate_transpose (GelMatrixW *m)
{
int i, j, w, h;
gel_matrixw_make_private (m);
m->tr = !m->tr;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
GelETree *n = gel_matrixw_get_index (m, i, j);
if (n == NULL)
continue;
if (n->type == VALUE_NODE) {
if (mpw_is_complex (n->val.value))
mpw_conj (n->val.value, n->val.value);
} else {
GelETree *nn;
GET_NEW_NODE (nn);
nn->type = OPERATOR_NODE;
nn->op.oper = E_DIRECTCALL;
GET_NEW_NODE (nn->op.args);
nn->op.args->type = IDENTIFIER_NODE;
nn->op.args->id.id = d_intern ("conj");
nn->op.args->any.next = n;
n->any.next = NULL;
gel_matrixw_set_index (m, i, j) = nn;
}
}
}
}
void
gel_value_matrix_multiply (GelMatrixW *res, GelMatrixW *m1, GelMatrixW *m2,
mpw_ptr modulo)
{
int i, j, k, w, h, m1w;
mpw_t tmp;
mpw_init(tmp);
gel_matrixw_make_private(res);
w = gel_matrixw_width (res);
h = gel_matrixw_height (res);
m1w = gel_matrixw_width (m1);
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
gboolean got_something = FALSE;
mpw_t accu;
mpw_init(accu);
for (k = 0; k < m1w; k++) {
GelETree *l = gel_matrixw_get_index(m1,k,j);
GelETree *r = gel_matrixw_get_index(m2,i,k);
/* if both zero add nothing */
if (l == NULL || r == NULL)
continue;
got_something = TRUE;
mpw_mul(tmp,l->val.value,r->val.value);
mpw_add(accu,accu,tmp);
if (modulo != NULL) {
mpw_mod (accu, accu, modulo);
if (error_num != 0) { /*FIXME: for now ignore errors in moding*/
error_num = 0;
}
if (mpw_sgn (accu) < 0)
mpw_add (accu, modulo, accu);
}
/*XXX: are there any problems that could occur
here? ... I don't seem to see any, if there
are catch them here*/
}
if (got_something) {
gel_matrixw_set_index(res,i,j) = gel_makenum_use(accu);
} else {
gel_matrixw_set_index(res,i,j) = NULL;
mpw_clear (accu);
}
}
}
mpw_clear(tmp);
}
static void
swap_rows(GelMatrixW *m, int row, int row2)
{
int i, w;
if(row==row2) return;
gel_matrixw_make_private(m);
w = gel_matrixw_width (m);
for (i = 0; i < w; i++) {
GelETree *t = gel_matrixw_get_index(m,i,row);
gel_matrixw_set_index(m,i,row) = gel_matrixw_get_index(m,i,row2);
gel_matrixw_set_index(m,i,row2) = t;
}
}
static gboolean
div_row (GelCtx *ctx, GelMatrixW *m, int row, mpw_t div)
{
int i, w;
gboolean ret = TRUE;
if (mpw_eql_ui (div, 1))
return TRUE;
gel_matrixw_make_private(m);
w = gel_matrixw_width (m);
for (i = 0; i < w; i++) {
GelETree *t = gel_matrixw_get_index(m,i,row);
if(t) {
mpw_div(t->val.value,t->val.value,div);
if (ctx->modulo != NULL) {
gel_mod_node (ctx, t);
/* can't mod so we have a singular matrix / system */
if (t != NULL && t->type != VALUE_NODE)
ret = FALSE;
}
}
}
return ret;
}
static gboolean
mul_sub_row (GelCtx *ctx, GelMatrixW *m, int row, mpw_t mul, int row2)
{
int i, w;
mpw_t tmp;
gboolean ret = TRUE;
gel_matrixw_make_private(m);
mpw_init(tmp);
w = gel_matrixw_width(m);
for (i = 0; i < w; i++) {
GelETree *t = gel_matrixw_get_index(m,i,row);
if (t && ! mpw_zero_p (t->val.value)) {
GelETree *t2 = gel_matrixw_get_index(m,i,row2);
mpw_mul(tmp,t->val.value,mul);
if(!t2) {
mpw_neg(tmp,tmp);
gel_matrixw_set_index(m,i,row2) = t2 = gel_makenum_use(tmp);
mpw_init(tmp);
} else {
mpw_sub(t2->val.value,t2->val.value,tmp);
if (mpw_exact_zero_p (t2->val.value)) {
gel_freetree (t2);
gel_matrixw_set_index(m,i,row2) = NULL;
}
}
if (ctx->modulo != NULL) {
gel_mod_node (ctx, t2);
/* can't mod so we have a singular matrix / system */
if (t2 != NULL && t2->type != VALUE_NODE)
ret = FALSE;
}
}
}
mpw_clear(tmp);
return ret;
}
/*NOTE: if simul is passed then we assume that it's the same size as m*/
/* return FALSE if singular */
/* FIXME: if modular arithmetic is on, work over the modulo properly!!!! */
gboolean
gel_value_matrix_gauss (GelCtx *ctx, GelMatrixW *m, gboolean reduce, gboolean
uppertriang, gboolean stopsing, mpw_ptr detop,
GelMatrixW *simul)
{
int i, j, d, ii, w, h;
GelETree *piv;
mpw_t tmp;
gboolean ret = TRUE;
gboolean made_private = FALSE;
if(detop)
mpw_set_ui(detop,1);
mpw_init(tmp);
d = 0;
w = gel_matrixw_width (m);
h = gel_matrixw_height (m);
for (i = 0; i < w && d < h; i++) {
for (j = d; j < h; j++) {
GelETree *t = gel_matrixw_get_index(m,i,j);
if (t != NULL &&
! mpw_zero_p (t->val.value))
break;
}
if (j == h) {
ret = FALSE;
if(stopsing) {
mpw_clear(tmp);
return FALSE;
}
continue;
}
if ( ! made_private) {
gel_matrixw_make_private(m);
made_private = TRUE;
}
if (j > d) {
swap_rows(m,j,d);
if(simul)
swap_rows(simul,j,d);
if(detop)
mpw_neg(detop,detop);
}
piv = gel_matrixw_index(m,i,d);
for (j = d+1; j < h; j++) {
GelETree *t = gel_matrixw_get_index(m,i,j);
/* Assume t is already reduced mod ctx->modulo
* if appropriate */
if (t != NULL &&
! mpw_zero_p (t->val.value)) {
mpw_div(tmp,t->val.value,piv->val.value);
if ( ! mul_sub_row (ctx, m, d, tmp, j) &&
stopsing) {
mpw_clear(tmp);
return FALSE;
}
if(simul) {
if ( ! mul_sub_row (ctx, simul, d, tmp, j) &&
stopsing) {
mpw_clear(tmp);
return FALSE;
}
}
}
}
/*we just want to do an upper triangular matrix*/
if(uppertriang) {
d++;
continue;
}
if(reduce) {
for(j=0;jval.value)) {
mpw_div(tmp,t->val.value,piv->val.value);
if ( ! mul_sub_row (ctx, m, d, tmp, j) &&
stopsing) {
mpw_clear(tmp);
return FALSE;
}
if(simul) {
if ( ! mul_sub_row (ctx, simul, d, tmp, j) &&
stopsing) {
mpw_clear(tmp);
return FALSE;
}
}
}
}
}
for (ii = i+1; ii < w; ii++) {
GelETree *t = gel_matrixw_get_index(m,ii,d);
if(t) {
mpw_div(t->val.value,
t->val.value,
piv->val.value);
if (ctx->modulo != NULL) {
gel_mod_node (ctx, t);
if (stopsing &&
t != NULL &&
t->type != VALUE_NODE) {
mpw_clear(tmp);
return FALSE;
}
}
}
}
if(detop)
mpw_div (detop, detop, piv->val.value);
if(simul) {
if ( ! div_row (ctx, simul, d, piv->val.value) &&
stopsing) {
mpw_clear(tmp);
return FALSE;
}
}
mpw_set_ui(piv->val.value,1);
d++;
}
if (detop && ctx->modulo != NULL) {
/* FIXME: this may fail!!! */
gel_mod_integer_rational (detop, ctx->modulo);
}
mpw_clear(tmp);
return ret;
}
static void
det2(mpw_t rop, GelMatrixW *m)
{
mpw_t tmp;
mpw_init(tmp);
mpw_mul(rop,gel_matrixw_index(m,0,0)->val.value,
gel_matrixw_index(m,1,1)->val.value);
mpw_mul(tmp,gel_matrixw_index(m,1,0)->val.value,
gel_matrixw_index(m,0,1)->val.value);
mpw_sub(rop,rop,tmp);
mpw_clear(tmp);
}
static void
det3(mpw_t rop, GelMatrixW *m)
{
mpw_t tmp;
mpw_init(tmp);
mpw_mul(rop,gel_matrixw_index(m,0,0)->val.value,
gel_matrixw_index(m,1,1)->val.value);
mpw_mul(rop,rop,
gel_matrixw_index(m,2,2)->val.value);
mpw_mul(tmp,gel_matrixw_index(m,1,0)->val.value,
gel_matrixw_index(m,2,1)->val.value);
mpw_mul(tmp,tmp,
gel_matrixw_index(m,0,2)->val.value);
mpw_add(rop,rop,tmp);
mpw_mul(tmp,gel_matrixw_index(m,2,0)->val.value,
gel_matrixw_index(m,0,1)->val.value);
mpw_mul(tmp,tmp,
gel_matrixw_index(m,1,2)->val.value);
mpw_add(rop,rop,tmp);
mpw_mul(tmp,gel_matrixw_index(m,2,0)->val.value,
gel_matrixw_index(m,1,1)->val.value);
mpw_mul(tmp,tmp,
gel_matrixw_index(m,0,2)->val.value);
mpw_sub(rop,rop,tmp);
mpw_mul(tmp,gel_matrixw_index(m,1,0)->val.value,
gel_matrixw_index(m,0,1)->val.value);
mpw_mul(tmp,tmp,
gel_matrixw_index(m,2,2)->val.value);
mpw_sub(rop,rop,tmp);
mpw_mul(tmp,gel_matrixw_index(m,0,0)->val.value,
gel_matrixw_index(m,2,1)->val.value);
mpw_mul(tmp,tmp,
gel_matrixw_index(m,1,2)->val.value);
mpw_sub(rop,rop,tmp);
mpw_clear(tmp);
}
gboolean
gel_value_matrix_det (GelCtx *ctx, mpw_t rop, GelMatrixW *m)
{
int w = gel_matrixw_width(m);
int h = gel_matrixw_height(m);
GelMatrixW *mm;
mpw_t tmp;
int i;
/* only works properly without modulo, but modulo is taken
* care of after the det function is executed */
g_assert (ctx->modulo == NULL);
if(w != h) {
gel_errorout (_("Determinant of a non-square matrix is undefined"));
return FALSE;
}
/* If we already are in rref form just compute determinant */
if (m->rref) {
mpw_set_ui (rop, 1);
for (i = 0; i < w; i++) {
GelETree *t = gel_matrixw_get_index (m, i, i);
if (t == NULL ||
mpw_zero_p (t->val.value)) {
mpw_set_ui (rop, 0);
return TRUE;
}
/* row reduced form has 1's on the diagonal! */
/*mpw_mul(rop,rop,t->val.value);*/
}
return TRUE;
}
switch(w) {
case 1:
mpw_set(rop,gel_matrixw_index(m,0,0)->val.value);
break;
case 2:
det2(rop,m);
break;
case 3:
det3(rop,m);
break;
default:
mpw_init(tmp);
mm = gel_matrixw_copy(m);
gel_value_matrix_gauss(ctx,mm,FALSE,TRUE,FALSE,tmp,NULL);
mpw_mul(rop,tmp,gel_matrixw_index(mm,0,0)->val.value);
mpw_clear(tmp);
for (i = 1; i < w; i++) {
GelETree *t = gel_matrixw_get_index(mm,i,i);
if (t == NULL) {
gel_matrixw_free(mm);
mpw_set_ui(rop,0);
return TRUE;
}
mpw_mul(rop,rop,t->val.value);
}
gel_matrixw_free(mm);
break;
}
return TRUE;
}