//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : math_matrix.C
// Author      : Patrick Theobald (PT)
// Last change : PT, Oct 28 1995, initial version 
//

/*
$Id: math_matrix.c,v 1.10 1996/12/18 15:11:13 theobald Exp $
*/

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:math_matrix.h>
#include <LiDIA:arith.inl>
#include <LiDIA:xerror.h>
#else
#include <LiDIA/math_matrix.h>
#include <LiDIA/arith.inl>
#include <LiDIA/xerror.h>
#endif

/**
 ** debug defines / error defines
 **/

extern const char *PRT;
extern const char *matrix_error_msg[];

#define DV_MM LDBL_MATRIX + 10   /* Debug value   */
#define DM_MM "math_matrix"      /* Debug message / Error message */
#define LMM_ERROR matrix_error_msg

/**
 ** debug level
 **
 **   0 : addtion
 **   1 : subtraction
 **   2 : multiplication
 **   3 : division
 **   4 : negation
 **   5 : comparison
 **   6 : trace
 **/

/**
 ** BEGIN: arithmetic procedures
 **/

/**
 ** addition
 **/

template < class T >
void math_matrix < T >:: 
add(const math_matrix < T > &M, const math_matrix < T > &N)
{
  /**
   ** DESCRIPTION: RES.add(M,N)
   **              => RES = M + N,
   ** ERROR: M.rows != N.rows or 
   **        M.columns != N.columns 
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "add(const math_matrix < T > &, const math_matrix < T > &)", DV_MM);
  
  register lidia_size_t i, j;
  
  if (M.rows != N.rows || M.columns != N.columns)
    lidia_error_handler_para(M.rows, "M.rows", "M.rows == N.rows",
			     N.rows, "N.rows", "M.rows == N.rows",
			     M.columns, "M.columns", "M.columns == N.columns",
			     N.columns, "N.columns", "M.columns == N.columns",
			     "void math_matrix < T >::"
			     "add(const math_matrix < T > &M, const math_matrix < T > &N)",
			     DM_MM, LMM_ERROR[4]);
  
  if (rows != N.rows)
    set_no_of_rows(N.rows);
  if (columns != N.columns)
    set_no_of_columns(N.columns);
  
  T *Mtmp, *Ntmp, *REStmp;
  for (i = 0; i < N.rows; i++)
    {
      Ntmp = N.value[i];
      Mtmp = M.value[i];
      REStmp = value[i];
      for (j = 0; j < N.columns; j++)
	::add(REStmp[j], Ntmp[j], Mtmp[j]);
    }
}

template < class T >
void math_matrix < T >::
add(const math_matrix < T > &M, const T &a)
{
  /**
   ** DESCRIPTION: RES.add(M,a);
   **              => RES.value[x][y] = M.value[x][y] + a,
   **                 x=0,...,M.rows-1, y=0,...,M.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "add(const math_matrix < T > &, const T &)", DV_MM);
  
  if (rows != M.rows)
    set_no_of_rows(M.rows);
  if (columns != M.columns)
    set_no_of_columns(M.columns);
  
  register lidia_size_t i, j;
  T *REStmp, *Mtmp;
  
  for (i = 0; i < rows; i++)
    {
      REStmp = value[i];
      Mtmp = M.value[i];
      for (j = 0; j < columns; j++)
	::add(REStmp[j], Mtmp[j], a);
    }
}

template < class T >
void math_matrix < T >::
add(const T &a, const math_matrix < T > &M)
{
  /**
   ** DESCRIPTION: RES.add(a,M);
   **              => RES.value[x][y] = a + M.value[x][y],
   **                 x=0,...,M.rows-1, y=0,...,M.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "add(const T &, const math_matrix < T > &)", DV_MM);
  
  if (rows != M.rows)
    set_no_of_rows(M.rows);
  if (columns != M.columns)
    set_no_of_columns(M.columns);
  
  register lidia_size_t i, j;
  T *REStmp, *Mtmp;
  
  for (i = 0; i < rows; i++)
    {
      REStmp = value[i];
      Mtmp = M.value[i];
      for (j = 0; j < columns; j++)
	::add(REStmp[j], Mtmp[j], a);
    }
}

/**
 ** subtraction
 **/

template < class T >
void math_matrix < T >::
subtract(const math_matrix < T > &M, const math_matrix < T > &N)
{
  /**
   ** DESCRIPTION: RES.subtract(M, N)
   **              => RES = M - N
   ** ERROR: M.rows != N.rows or 
   **        M.columns != N.columns
   ** VERSION: 2.0
   **/

  debug_handler_l(DM_MM, "in member - function "
		  "subtract(const math_matrix < T > &, const math_matrix < T > &)", DV_MM + 1);

  register lidia_size_t i, j;

  if (M.rows != N.rows || M.columns != N.columns)
    lidia_error_handler_para(M.rows, "M.rows", "M.rows == N.rows",
			     N.rows, "N.rows", "M.rows == N.rows",
			     M.columns, "M.columns", "M.columns == N.columns",
			     N.columns, "N.columns", "M.columns == N.columns",
			     "void math_matrix < T >::"
			     "subtract(const math_matrix < T > &M, const math_matrix < T > &N)",
			     DM_MM, LMM_ERROR[4]);
  if (rows != N.rows)
    set_no_of_rows(N.rows);
  if (columns != N.columns)
    set_no_of_columns(N.columns);
  
  T *REStmp, *Mtmp, *Ntmp;

  for (i = 0; i < N.rows; i++)
    {
      REStmp = value[i];
      Mtmp = M.value[i];
      Ntmp = N.value[i];
      for (j = 0; j < N.columns; j++)
	::subtract(REStmp[j], Mtmp[j], Ntmp[j]);
    }
}

template < class T >
void math_matrix < T >::
subtract(const math_matrix < T > &M, const T &a)
{
  /**
   ** DESCRIPTION: RES.subtract(M, a)
   **              => RES.value[x][y] = M.value[x][y] - a,
   **                 x=0,...,M.rows-1, y=0,...,M.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "subtract(const math_matrix < T > &, const T &)", DV_MM + 1);

  if (rows != M.rows)
    set_no_of_rows(M.rows);
  if (columns != M.columns)
    set_no_of_columns(M.columns);
  
  register lidia_size_t i, j;
  T *REStmp, *Mtmp;

  for (i = 0; i < rows; i++)
    {
      REStmp = value[i];
      Mtmp = M.value[i];
      for (j = 0; j < columns; j++)
	::subtract(REStmp[j], Mtmp[j], a);
    }
}

template < class T >
void math_matrix < T >::
subtract(const T &a, const math_matrix < T > &M)
{
  /**
   ** DESCRIPTION: RES.subtract(a, M)
   **              => RES.value[x][y] = a - M.value[x][y],
   **                 x=0,...,M.rows-1, y=0,...,M.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "subtract(const T &, const math_matrix < T > &)", DV_MM + 1);

  if (rows != M.rows)
    set_no_of_rows(M.rows);
  if (columns != M.columns)
    set_no_of_columns(M.columns);
  
  register lidia_size_t i, j;
  T *REStmp, *Mtmp;

  for (i = 0; i < rows; i++)
    {
      REStmp = value[i];
      Mtmp = M.value[i];
      for (j = 0; j < columns; j++)
	::subtract(REStmp[j], a, Mtmp[j]);
    }
}

/**
 ** multiplication
 **/

template < class T >
void math_matrix < T >::
multiply(const math_matrix < T > &A, const math_matrix < T > &B)
{
  /**
   ** DESCRIPTION: RES.multiply(A, B)
   **              => RES = A * B
   ** ERROR: A.columns != B.rows 
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply(const math_matrix < T > &, const math_matrix < T > &)", DV_MM + 2);

  if (A.columns != B.rows)
    lidia_error_handler_para(A.columns, "A.columns", "A.columns == B.rows",
			     B.rows, "B.rows", "A.columns == B.rows",
			     "void math_matrix < T >::"
			     "multiply(const math_matrix < T > &A, const math_matrix < T > &B)",
			     DM_MM, LMM_ERROR[4]);

  register lidia_size_t j, i, z;
  T TMP, TMP1, *Atmp, *REStmp;
  
  if (A.value != value && B.value != value)
    {
      if (rows != A.rows)
	set_no_of_rows(A.rows);
      if (columns != B.columns)
	set_no_of_columns(B.columns);
      
      for (j = 0; j < A.rows; j++)
	{
	  Atmp = A.value[j];
	  REStmp = value[j];
	  for (i = 0; i < B.columns; i++)
	    {
	      TMP = 0; /* TMP.assign_zero(); */
	      for (z = 0; z < B.rows; z++)
		{
		  ::multiply(TMP1, Atmp[z], B.value[z][i]);
		  ::add(TMP, TMP, TMP1);
		}
	      REStmp[i]=TMP;
	    }
	}
    }
  else
    {
      math_matrix < T > RES1(A.rows, B.columns);
      for (j = 0; j < A.rows; j++)
	{
	  Atmp = A.value[j];
	  REStmp = RES1.value[j];
	  for (i = 0; i < B.columns; i++)
	    {
	      TMP = 0; /* TMP.assign_zero(); */
	      for (z = 0; z < B.rows; z++)
		{
		  ::multiply(TMP1, Atmp[z], B.value[z][i]);
		  ::add(TMP, TMP, TMP1);
		}
	      REStmp[i]=TMP;
	    }
	}
      assign(RES1);
    }
}

template < class T >
void math_matrix < T >::
multiply(const math_matrix < T > &A, const T &k)
{
  /**
   ** DESCRIPTION: RES.multiply(A, k)
   **              => RES.value[x][y] = A.value[x][y]*k,
   **                 x=0,...,A.rows-1, y=0,...,A.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply(const math_matrix < T > &, const T &)", DV_MM + 2);
  
  if (rows != A.rows)
    set_no_of_rows(A.rows);
  if (columns != A.columns)
    set_no_of_columns(A.columns);
  
  register lidia_size_t j, i;
  T *REStmp, *Atmp;
  
  for (j = 0; j < A.rows; j++)
    {
      REStmp = value[j];
      Atmp = A.value[j];
      for (i = 0; i < A.columns; i++)
	::multiply(REStmp[i], Atmp[i], k);
    }
}

template < class T >
void math_matrix < T >::
multiply(const T &k, const math_matrix < T > &A)
{
  /**
   ** DESCRIPTION: RES.multiply(k,A)
   **              => RES.value[x][y] = k*A.value[x][y],
   **                 x=0,...,A.rows-1, y=0,...,A.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply(const T &, const math_matrix < T > &)", DV_MM + 2);
  
  if (rows != A.rows)
    set_no_of_rows(A.rows);
  if (columns != A.columns)
    set_no_of_columns(A.columns);
  
  register lidia_size_t j, i;
  T *REStmp, *Atmp;
  for (j = 0; j < A.rows; j++)
    {
      REStmp = value[j];
      Atmp = A.value[j];
      for (i = 0; i < A.columns; i++)
	::multiply(REStmp[i], Atmp[i], k);
    }
}

template < class T >
void math_matrix < T >::
compwise_multiply(const math_matrix < T > &A, const math_matrix < T > &B)
{
  /**
   ** DESCRIPTION: RES.compwise_multiply(A, B)
   **              => RES.value[x][y] = A.value[x][y]*B.value[x][y],
   **                 x=0,...,A.rows-1, y=0,...,A.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "compwise_multiply(const math_matrix < T > &, const math_matrix < T > &)", DV_MM + 2);
  
  if (A.rows != B.rows || A.columns != B.columns)
    lidia_error_handler_para(A.rows, "A.rows", "A.rows == B.rows",
			     B.rows, "B.rows", "A.rows == B.rows",
			     A.columns, "A.columns", "A.columns == B.columns",
			     B.columns, "B.columns", "B.columns == B.columns",
			     "void math_matrix < T >::"
			     "compwise_multiply(const math_matrix < T > &A, const math_matrix < T > &B)",
			     DM_MM, LMM_ERROR[4]);
  
  if (rows != A.rows)
    set_no_of_rows(A.rows);
  if (columns != A.columns)
    set_no_of_columns(A.columns);
  
  register lidia_size_t j, i;
  T *REStmp, *Atmp, *Btmp;
  
  for (j = 0; j < rows; j++)
    {
      REStmp = value[j];
      Atmp = A.value[j];
      Btmp = B.value[j];
      for (i = 0; i < columns; i++)
	::multiply(REStmp[i], Atmp[i], Btmp[i]);
    }
}

template < class T >
void math_matrix < T >::
multiply_right(math_vector < T > &res, const math_vector < T > &v) const
{
  /**
   ** DESCRIPTION: c[x] = A.value[x][0]*v[0]+...
   **                              +A.value[x][A.columns-1]*v[A.columns-1],
   **              x=0,...,A.rows-1
   ** ERROR: v.size != A.columns or
   **        v.data = c.data
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply_right(math_vector < T > &, const math_vector < T > &)", DV_MM + 2);

  if (v.size() != columns)
    lidia_error_handler_para(columns, "columns", "columns == v.size",
			     v.size(), "v.size", "columns == v.size",
			     "void math_matrix < T >::"
			     "multiply_right(math_vector < T > &res, const math_vector < T > &v) const",
			     DM_MM, LMM_ERROR[1]);

  register lidia_size_t i, j, l = res.size();
  T TMP, TMP1, *tmp;
  
  T *tmp1 = res.get_data_address(), *tmp2 = v.get_data_address();
  
  if (tmp1 == tmp2)
    {
      math_vector < T > res2(rows, rows);
      tmp1 = res2.get_data_address();
      
      for (i = 0; i < rows; i++)
	{
	  tmp = value[i];
	  TMP = 0; 
	  for (j = 0; j < columns; j++)
	    {
	      ::multiply(TMP1, tmp[j], tmp2[j]);
	      ::add(TMP, TMP, TMP1);
	    }
	  tmp1[i] = TMP;
	}
      res = res2;
    }
  else
    {
      if (l != rows)
	{
	  if (res.capacity() < rows)
	    res.set_capacity(rows);
	  if (l != rows)
	    res.set_size(rows);	
	}
      tmp1 = res.get_data_address();
  
      for (i = 0; i < rows; i++)
	{
	  tmp = value[i];
	  TMP = 0; 
	  for (j = 0; j < columns; j++)
	    {
	      ::multiply(TMP1, tmp[j], tmp2[j]);
	      ::add(TMP, TMP, TMP1);
	    }
	  tmp1[i] = TMP;
	}
    }
}

template < class T >
void math_matrix < T >::
multiply_right(T *&c, const T *v) const
{
  /**
   ** DESCRIPTION: c[x] = A.value[x][0]*v[0]+...
   **                              +A.value[x][A.columns-1]*v[A.columns-1],
   **              x=0,...,A.rows-1
   ** ERROR: v == NULL
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply_right(T *, const T *)", DV_MM + 2);
  
  if (v == NULL)
    lidia_error_handler_para(PRT, "v", "v != NULL",
			     "void math_matrix < T >::"
			     "multiply_right(T *c, const T *v) const",
			     DM_MM, LMM_ERROR[6]);
  
  register lidia_size_t i, j;
  T TMP, TMP1, *tmp;
  
  if (c == v || c == NULL)
    {
      c = new T[rows];
      memory_handler(c, DM_MM, "multiply_right :: "
		     "Error in memory allocation (c)");
    }
  
  for (i = 0; i < rows; i++)
    {
      TMP = 0; 
      tmp = value[i];
      for (j = 0; j < columns; j++)
	{
	  ::multiply(TMP1, tmp[j], v[j]);
	  ::add(TMP, TMP, TMP1);
	}
      c[i] = TMP;
    }
}

template < class T >
void math_matrix < T >::
multiply_left(math_vector < T > &res, const math_vector < T > &v) const
{
  /**
   ** DESCRIPTION: c[x] = v[0]*A.value[0][x]+...
   **                              +v[A.rows-1]*A.value[A.rows-1][x],
   **              x=0,...,A.columns-1
   ** ERROR: v.length != A.rows 
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply_left(math_vector < T > &, const math_vector < T > &)", DV_MM + 2);
  
  if (v.size() != rows)
    lidia_error_handler_para(rows, "rows", "rows == v.size",
			     v.size(), "v.size", "rows == v.size", 
			     "void math_matrix < T >::"
			     "multiply_left(math_vector < T > &res, const math_vector < T > &v) const",
			     DM_MM, LMM_ERROR[6]);

  register lidia_size_t i, j, l = res.size();
  T TMP, TMP1;
  
  T *tmp1 = res.get_data_address(), *tmp2 = v.get_data_address(); 
  
  if (tmp1 == tmp2)
    {
      math_vector < T > res2(columns, columns);
      tmp1 = res2.get_data_address();
      for (i = 0; i < columns; i++)
	{
	  TMP = 0; 
	  for (j = 0; j < rows; j++)
	    {
	      ::multiply(TMP1, value[j][i], tmp2[j]);
	      ::add(TMP, TMP, TMP1);
	    }
	  tmp1[i] = TMP;
	}
      res.assign(res2);
    }
  else
    {
      if (l != columns)
	{
	  if (res.capacity() < columns)
	    res.set_capacity(columns);
	  if (l != columns)
	    res.set_size(columns);
	}
      tmp1 = res.get_data_address();
  
      for (i = 0; i < columns; i++)
	{
	  TMP = 0; 
	  for (j = 0; j < rows; j++)
	    {
	      ::multiply(TMP1, value[j][i], tmp2[j]);
	      ::add(TMP, TMP, TMP1);
	    }
	  tmp1[i] = TMP;
	}
    }
}

template < class T >
void math_matrix < T >::
multiply_left(T *&c, const T *v) const
{
  /**
   ** DESCRIPTION: c[x] = v[0]*A.value[0][x]+...
   **                              +v[A.rows-1]*A.value[A.rows-1][x],
   **              x=0,...,A.columns-1
   ** ERROR: v == NULL 
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "multiply_left(T *, const T *)", DV_MM + 2);
  
  if (v == NULL)
    lidia_error_handler_para(PRT, "v", "v != NULL",
			     "void math_matrix < T >::"
			     "multiply_left(T *c, const T *v) const",
			     DM_MM, LMM_ERROR[6]);

  register lidia_size_t i, j;
  T TMP, TMP1;
  
  if (c == v || c == NULL)
    {
      c = new T[columns];
      memory_handler(c, DM_MM, "multiply_left :: "
		     "Error in memory allocation (c)");
    }
  
  for (i = 0; i < columns; i++)
    {
      TMP = 0;
      for (j = 0; j < rows; j++)
	{
	  ::multiply(TMP1, value[j][i], v[j]);
	  ::add(TMP, TMP, TMP1);
	}
      c[i]=TMP;
    }
}

/**
 ** divide
 **/

template < class T >
void math_matrix < T >::
divide(const math_matrix < T > &A, const T &k)
{
  /**
   ** DESCRIPTION: RES.divide(A, k)
   **              => RES.value[x][y] = A.value[x][y] / k,
   **                 x=0,...,A.rows-1, y=0,...,A.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "divide(const math_matrix < T > &, const T &)", DV_MM + 2);
  
  if (rows != A.rows)
    set_no_of_rows(A.rows);
  if (columns != A.columns)
    set_no_of_columns(A.columns);
  
  register lidia_size_t j, i;
  T *REStmp, *Atmp;
  
  for (j = 0; j < A.rows; j++)
    {
      REStmp = value[j];
      Atmp = A.value[j];
      for (i = 0; i < A.columns; i++)
	::divide(REStmp[i], Atmp[i], k);
    }
}

template < class T >
void math_matrix < T >::
compwise_divide(const math_matrix < T > &A, const math_matrix < T > &B)
{
  /**
   ** DESCRIPTION: RES.compwise_divide(A, B)
   **              => RES.value[x][y] = A.value[x][y] / B.value[x][y],
   **                 x=0,...,A.rows-1, y=0,...,A.columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "compwise_divide(const math_matrix < T > &, const math_matrix < T > &)", DV_MM + 3);

  if (A.rows != B.rows || A.columns != B.columns)
    lidia_error_handler_para(A.rows, "A.rows", "A.rows == B.rows",
			     B.rows, "B.rows", "A.rows == B.rows",
			     A.columns, "A.columns", "A.columns == B.columns",
			     B.columns, "B.columns", "B.columns == B.columns",
			     "void math_matrix < T >::"
			     "compwise_divide(const math_matrix < T > &A, const math_matrix < T > &B)",
			     DM_MM, LMM_ERROR[4]);
  
  if (rows != A.rows)
    set_no_of_rows(A.rows);
  if (columns != A.columns)
    set_no_of_columns(A.columns);
  
  register lidia_size_t j, i;
  T *REStmp, *Atmp, *Btmp;
  
  for (j = 0; j < rows; j++)
    {
      REStmp = value[j];
      Atmp = A.value[j];
      Btmp = B.value[j];
      for (i = 0; i < columns; i++)
	::divide(REStmp[i], Atmp[i], Btmp[i]);
    }
}

/**
 ** negation
 **/

template < class T >
void math_matrix < T >::
negate(const math_matrix < T > &B)
{
  /**
   ** DESCRIPTION: A.negate(B)
   **              => A.value[x][y] = - B.value[x][y],
   **                 x=0,...,rows-1, y=0,...,columns-1
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "negate(const math_matrix < T > &)", DV_MM + 4);

  register lidia_size_t i, j;  

  if (rows != B.rows)
    set_no_of_rows(B.rows);
  if (columns != B.columns)
    set_no_of_columns(B.columns);
  
  T *Btmp, *Atmp;

  for (i = 0; i < B.rows; i++)
    {
      Btmp = B.value[i];
      Atmp = value[i];
      for (j = 0; j < B.columns; j++)
	::negate(Atmp[j],Btmp[j]);
    }
}

/**
 ** END: arithmetical procedures
 **/

/**
 ** comparisons
 **/

template < class T >
bool math_matrix < T >::
equal(const math_matrix < T > &N) const
{
  /**
   ** DESCRIPTION: A.equal(B) returns 1, if A.value[x][y] == B.value[x][y],
   **              x=0,...,A.rows-1, y=0,...,A.columns-1
   **              and A.columns == B.columns and A.rows == B.rows
   ** VERSION: 2.0
   **/
  
  debug_handler_l(DM_MM, "in member - function "
		  "equal(const math_matrix < T > &)", DV_MM + 5);
  
  register lidia_size_t i, j;  
  if (rows != N.rows || columns != N.columns) 
    return false;
  
  T *tmp, *Ntmp;
  
  for (i = 0; i < rows; i++)
    {
      tmp = value[i];
      Ntmp = N.value[i];
      for (j = 0; j < columns; j++)
	if (tmp[j] != Ntmp[j])
	  return false;
    }
  return true;
}

/** 
 ** trace
 **/

template < class T >
void math_matrix < T >::
trace(T &tr) const
{
  /**
   ** DESCRIPTION: A.trace(tr)
   **              => tr = trace of matrix A
   ** ERROR: columns != rows
   ** VERSION: 2.0
   **/

  debug_handler_l(DM_MM, "in member - function "
		  "trace(T &)", DV_MM + 6);

  if (rows != columns)
    lidia_error_handler_para(rows, "rows", "rows == columns",
			     columns, "columns", "rows == columns",
			     "void math_matrix < T >::"
			     "trace(T &tr) const",
			     DM_MM, LMM_ERROR[7]);

  register lidia_size_t i;
  tr = value[0][0];
  for (i = 1; i < rows; i++)
    ::add(tr,tr,value[i][i]);
}

#undef DV_MM 
#undef DM_MM 
#undef LMM_ERROR 


















