/*
   matrix.c -- MATRIX routines.

   Copyright (C) 1994-97  K. Scott Hunziker.
   Copyright (C) 1990-94  The Boeing Company.

   See the file COPYING for license, warranty, and permission details.
 */

static char rcsid[] =
"$Id: matrix.c,v 1.5 1997/04/24 06:01:03 ksh Exp $";

#include "entity.h"
#include "scalar.h"
#include "vector.h"
#include "matrix.h"
#include "table.h"
#include "get.h"
#include "put.h"
#include "file_io.h"
#include "binop.h"
#include "sparse.h"
#include "dense.h"

/*
 * This array describes the members of the
 * MATRIX structure.  The fields are `name',
 * and `id'.  The entries must be in
 * alphabetical order, and there must be
 * exactly one entry for each member of the
 * MATRIX_MEMBER enumeration except END_Matrix.
 */

MEMBER_ID matrix_member_names[] =
{
  {"cid", MatrixCid},
  {"class", MatrixClass},
  {"density", MatrixDensity},
  {"nc", MatrixNc},
  {"nn", MatrixNn},
  {"nr", MatrixNr},
  {"order", MatrixOrder},
  {"rid", MatrixRid},
  {"symmetry", MatrixSymmetry},
  {"type", MatrixType},
};

MATRIX_MEMBER
matrix_member_search (s)
     char *s;
{
  MEMBER_ID *m;

  assert (s != NULL);

  m = (MEMBER_ID *) bsearch (s, matrix_member_names, END_Matrix, sizeof (MEMBER_ID), member_cmp);

  return ((m == NULL) ? END_Matrix : m->id);
}

ENTITY *
bi_matrix (n, p)
     int n;
     ENTITY *p;
{
  /*
   * Convert `p' to matrix.  NULL becomes a zero-by-zero real
   * matrix.  Scalars become one-by-one matrices, vectors become
   * matrices with one row, and matrices are unchanged.
   */

  return p ? matrix_entity (p) : make_matrix (0, 0, real, dense);

}

ENTITY *
matrix_entity (p)
     ENTITY *p;
{
  /* Convert `p' to a matrix. */

  EASSERT (p, 0, 0);

  switch (p->class)
    {
    case scalar:
      return (scalar_to_matrix ((SCALAR *) p));
    case vector:
      return (vector_to_matrix ((VECTOR *) p));
    case matrix:
      return (p);
    default:
      fail ("Can't convert a %s to a matrix.", class_string[p->class]);
      delete_entity (p);
      break;
    }
  raise_exception ();
}

ENTITY *
scalar_to_matrix (s)
     SCALAR *s;
{
  /* Convert `s' to matrix. */

  MATRIX *m;

  EASSERT (s, scalar, 0);

  m = (MATRIX *) form_matrix (1, 1, s->type, dense);
  m->symmetry = symmetric;

  switch (s->type)
    {
    case integer:
      m->a.integer[0] = s->v.integer;
      break;
    case real:
      m->a.real[0] = s->v.real;
      break;
    case complex:
      m->a.complex[0] = s->v.complex;
      break;
    case character:
      m->a.character[0] = dup_char (s->v.character);
      break;
    default:
      BAD_TYPE (s->type);
      delete_scalar (s);
      delete_matrix (m);
      raise_exception ();
    }

  if (s->stuff)
    m->stuff = (TABLE *) copy_table (s->stuff);

  delete_scalar (s);
  return (ENT (m));
}

ENTITY *
make_matrix (nr, nc, type, density)
     int nr;
     int nc;
     TYPE type;
     DENSITY density;
{
  /*
   * Makes a new matrix with `nr' rows and `nc' columns.  It's initialized
   * to zero (or "" if it's character) and marked as symmetric or hermitian
   * if it's square.
   *
   * NOTE: If the resulting matrix is square, then it is known to be
   * symmetric, even if you supply your own non-zero values on the
   * diagonal.  But if the matrix is complex, then it is hermitian
   * only by virtue of the fact that the diagonal values have zero
   * imaginary parts.  An easy way to screw up is to use this
   * function to form a complex array, then put in complex values.
   * That should be symmetric, not hermitian.
   */

  MATRIX *m;
  int i;

  m = (MATRIX *) form_matrix (nr, nc, type, density);

  m->symmetry = (nr == nc) ?
    ((type == complex) ? hermitian : symmetric) : general;

  switch (density)
    {
    case dense:
      if (m->nn)
	memset (m->a.ptr, 0, m->nn * type_size[type]);
      if (type == character)
	for (i = 0; i < m->nn; i++)
	  m->a.character[i] = NULL_string;
      break;
    case sparse:
    case sparse_upper:
      break;
    default:
      wipeout ("Bad density.");
    }

  if (debug_level > 1)
    inform ("Matrix created:  %x.", m);

  return (ENT (m));
}

ENTITY *
form_matrix (nr, nc, type, density)
     int nr;
     int nc;
     TYPE type;
     DENSITY density;
{
  /*
   * Forms a new matrix with `nr' rows and `nc' columns.  If dense,
   * memory for its data is allocated but not initialized.  (No memory
   * is allocated for data if it's sparse.)  Its symmetry is general.
   */

  MATRIX *m;

  assert (nr >= 0);
  assert (nc >= 0);

  m = (MATRIX *) CALLOC (1, sizeof (MATRIX));

  m->entity.ref_count = 1;
  m->entity.class = matrix;

  m->symmetry = general;

  m->type = type;
  m->order = ordered;
  m->density = density;

  m->nr = nr;
  m->nc = nc;

  switch (density)
    {

    case dense:

      if ((double) nr * (double) nc * type_size[type] > INT_MAX)
        {
          fail ("Out of memory.");
          raise_exception ();
        }
      m->nn = nr * nc;
      m->a.ptr = (m->nn) ? E_MALLOC (m->nn, type) : NULL;

      /*
       * For character type, set the first pointer to NULL so that
       * `free_matrix' knows there's junk there and it shouldn't
       * call FREE_CHAR on it.
       */

      if (m->nn && type == character)
	m->a.character[0] = NULL;

      break;

    case sparse:
    case sparse_upper:
      break;
    default:
      wipeout ("Bad density.");
    }

  if (debug_level > 1)
    inform ("Matrix created:  %x.", m);

  return (ENT (m));
}

ENTITY *
matrix_to_scalar (m)
     MATRIX *m;
{
  /* Convert `m' to scalar (only if it's one by one). */

  ENTITY *s;

  EASSERT (m, matrix, 0);

  /*  The matrix must have only 1 row and 1 column  */

  if (m->nr != 1 || m->nc != 1)
    {
      fail ("Can't convert to scalar a matrix with %d row%s and %d column%s.",
	    m->nr, PLURAL (m->nr), m->nc, PLURAL (m->nc));
      delete_matrix (m);
      raise_exception ();
    }
  else
    {
      switch (m->density)
	{
	case dense:
	  switch (m->type)
	    {
	    case integer:
	      s = int_to_scalar (m->a.integer[0]);
	      break;
	    case real:
	      s = real_to_scalar (m->a.real[0]);
	      break;
	    case complex:
	      s = complex_to_scalar (m->a.complex[0]);
	      break;
	    case character:
	      s = char_to_scalar (dup_char (m->a.character[0]));
	      break;
	    default:
	      BAD_TYPE (m->type);
	      delete_matrix (m);
	      raise_exception ();
	    }
	  break;
	case sparse:
	  /*
	   * It's a one-by-one matrix, so it has either 0 or 1 elements.  If
	   * it has 1 element, then that's the one we want.
	   */
	  if (m->nn > 0)
	    {
	      switch (m->type)
		{
		case integer:
		  s = int_to_scalar (m->a.integer[0]);
		  break;
		case real:
		  s = real_to_scalar (m->a.real[0]);
		  break;
		case complex:
		  s = complex_to_scalar (m->a.complex[0]);
		  break;
		case character:
		  s = char_to_scalar (dup_char (m->a.character[0]));
		  break;
		default:
		  BAD_TYPE (m->type);
		  delete_matrix (m);
		  raise_exception ();
		}
	    }
	  else
	    {
	      s = make_scalar (m->type);
	    }
	  break;
	case sparse_upper:
	  if (m->d.integer != NULL)
	    {
	      switch (m->type)
		{
		case integer:
		  s = int_to_scalar (m->d.integer[0]);
		  break;
		case real:
		  s = real_to_scalar (m->d.real[0]);
		  break;
		case complex:
		  s = complex_to_scalar (m->d.complex[0]);
		  break;
		case character:
		  s = char_to_scalar (dup_char (m->d.character[0]));
		  break;
		default:
		  BAD_TYPE (m->type);
		  delete_matrix (m);
		  raise_exception ();
		}
	    }
	  else
	    {
	      s = make_scalar (m->type);
	    }
	  break;
	default:
	  BAD_DENSITY (m->density);
	  delete_matrix (m);
	  raise_exception ();
	}
    }

  if (m->stuff)
    ((SCALAR *) s)->stuff = (TABLE *) copy_table (m->stuff);

  delete_matrix (m);
  return (s);
}

ENTITY *
dup_matrix (old)
     MATRIX *old;
{
  /*
   * In Algae, duplicating an entity means that we really make another
   * copy of it in memory (not just incrementing its reference count).
   */

  MATRIX *new;
  int i;

  EASSERT (old, matrix, 0);

  if (old->entity.ref_count == 1)
    return (ENT (old));

  new = (MATRIX *) form_matrix (old->nr, old->nc, old->type, old->density);

  new->symmetry = old->symmetry;

  if (old->rid)
    new->rid = copy_entity (old->rid);
  if (old->cid)
    new->cid = copy_entity (old->cid);

  new->nn = old->nn;

  if (old->ia)
    new->ia = (int *) dup_mem (old->ia, (old->nr + 1) * sizeof (int));

  if (old->ja)
    new->ja = (int *) dup_mem (old->ja, old->nn * sizeof (int));

  if (old->a.integer)
    {
      if (new->a.integer == NULL)
	new->a.integer = (int *) E_MALLOC (old->nn, old->type);
      memcpy (new->a.integer, old->a.integer,
	      old->nn * type_size[old->type]);
      if (old->type == character)
	for (i = 0; i < old->nn; i++)
	  new->a.character[i] = dup_char (new->a.character[i]);
    }

  if (old->d.integer)
    {
      new->d.integer = (int *) dup_mem (old->d.integer,
					old->nr * type_size[old->type]);
      if (old->type == character)
	for (i = 0; i < old->nr; i++)
	  new->d.character[i] = dup_char (new->d.character[i]);
    }

  if (debug_level > 1)
    inform ("Matrix created:  %x.", new);

  delete_matrix (old);
  return (ENT (new));
}

ENTITY *
matrix_to_vector (m)
     MATRIX *m;
{
  /* Convert matrix `m' to a vector (if it's a single row or column). */

  VECTOR *v;
  int i, j;

  EASSERT (m, matrix, 0);

  /* Make sure it's a single row or column. */

  if (m->nr != 1 && m->nc != 1)
    {
      fail ("Can't convert a %d by %d matrix into a vector.",
            m->nr, m->nc);
      delete_matrix (m);
      raise_exception ();
    }

  if (m->density == sparse_upper)
    {

      /* If it's sparse_upper, then it has to be a 1x1 matrix. */

      assert (m->nr == 1 && m->nc == 1);
      v = (VECTOR *) make_vector (1, m->type, dense);
      if (m->d.ptr != NULL)
	memcpy (v->a.ptr, m->d.ptr,
		type_size[m->type]);
      if (v->type == character)
	v->a.character[0] = dup_char (v->a.character[0]);

    }
  else if (m->entity.ref_count == 1)
    {

      /* If it has a ref_count of 1, we'll take a shortcut. */

      v = (VECTOR *) CALLOC (1, sizeof (VECTOR));
      if (debug_level > 1)
	inform ("Vector created:  %x.", v);
      v->entity.ref_count = 1;
      v->entity.class = vector;
      v->type = m->type;
      v->order = m->order;
      v->density = m->density;
      v->nn = m->nn;

      /*
       * For sparse, if `m' was a row, then `ja' is just what we want.
       * Otherwise, we have to fill in `ja' with the element numbers.
       */

      v->ja = m->ja;
      if (v->ja && m->nr != 1)
	{
	  for (i = j = 0; i < m->nr; i++)
	    {
	      if (m->ia[i + 1] > m->ia[i])
		v->ja[j++] = i + 1;
	    }
	}
      m->ja = NULL;

      /*
       * We don't have to worry about cleaning up `m->ia'---it's done
       * in `delete_matrix' below.
       */

      v->a = m->a;
      m->a.ptr = NULL;

    }
  else
    {

      /* Here's the general case. */

      v = (VECTOR *) form_vector ((m->nr != 1) ? m->nr :
				  m->nc, m->type, m->density);
      if (m->nn > 0)
	{
	  v->nn = m->nn;
	  if (m->density == sparse)
	    {
	      if (m->nr == 1)
		{
		  v->ja = (int *) dup_mem (m->ja, m->nn * sizeof (int));
		}
	      else
		{
		  v->ja = (int *) MALLOC (v->nn * sizeof (int));
		  for (i = j = 0; i < m->nr; i++)
		    {
		      if (m->ia[i + 1] > m->ia[i])
			v->ja[j++] = i + 1;
		    }
		}
	      v->a.ptr = dup_mem (m->a.ptr, m->nn * type_size[m->type]);
	    }
	  else
	    {
	      assert (m->density == dense);
	      memcpy (v->a.ptr, m->a.ptr, m->nn * type_size[m->type]);
	    }
	  if (v->type == character)
	    {
	      for (i = 0; i < v->nn; i++)
		v->a.character[i] = dup_char (v->a.character[i]);
	    }
	}
    }

  if (m->nr != 1)
    {
      v->ne = m->nr;
      if (m->rid != NULL)
	v->eid = copy_entity (m->rid);
    }
  else
    {
      v->ne = m->nc;
      if (m->cid != NULL)
	v->eid = copy_entity (m->cid);
    }

  if (m->stuff)
    v->stuff = (TABLE *) copy_table (m->stuff);

  delete_matrix (m);
  return (apt_vector (v));
}

ENTITY *
vector_to_matrix (v)
     VECTOR *v;
{
  /* Convert vector `v' to a matrix (a single row). */

  MATRIX *m;
  int i;

  EASSERT (v, vector, 0);

  if (v->entity.ref_count == 1)
    {
      m = (MATRIX *) CALLOC (1, sizeof (MATRIX));
      if (debug_level > 1)
	inform ("Matrix created:  %x.", m);
      m->entity.ref_count = 1;
      m->entity.class = matrix;
      m->symmetry = (v->ne == 1) ? symmetric : general;
      m->type = v->type;
      m->order = v->order;
      m->density = v->density;
      if (v->eid != NULL)
	m->cid = copy_entity (v->eid);
      m->nr = 1;
      m->nc = v->ne;
      m->nn = v->nn;
      if (v->density == sparse && v->nn > 0)
	{
	  m->ia = (int *) CALLOC (2, sizeof (int));
	  m->ia[0] = 1;
	  m->ia[1] = 1 + m->nn;
	}
      m->ja = v->ja;
      v->ja = NULL;
      m->a = v->a;
      v->a.ptr = NULL;
    }
  else
    {
      m = (MATRIX *) make_matrix (1, v->ne, v->type, v->density);
      m->symmetry = (v->ne == 1) ? symmetric : general;
      if (v->eid != NULL)
	m->cid = copy_entity (v->eid);
      if (v->nn > 0)
	{
	  m->nn = v->nn;
	  if (v->density == sparse)
	    {
	      m->ia = (int *) CALLOC (2, sizeof (int));
	      m->ia[0] = 1;
	      m->ia[1] = 1 + m->nn;
	      m->ja = (int *) dup_mem (v->ja, m->nn * sizeof (int));
	      m->a.ptr = dup_mem (v->a.ptr, m->nn * type_size[v->type]);
	    }
	  else
	    {
	      memcpy (m->a.ptr, v->a.ptr, m->nn * type_size[v->type]);
	    }
	  if (m->type == character)
	    {
	      for (i = 0; i < m->nn; i++)
		m->a.character[i] = dup_char (m->a.character[i]);
	    }
	}
    }

  if (v->stuff)
    m->stuff = (TABLE *) copy_table (v->stuff);

  delete_vector (v);
  return (apt_matrix (m));
}

void
free_matrix (p)
     MATRIX *p;
{
  /* Destroy the matrix, and free up the space it occupied. */

  int i;

  assert (p->entity.ref_count == 0);

  delete_table (p->stuff);
  delete_entity (p->rid);
  delete_entity (p->cid);

  if (p->ia)
    FREE (p->ia);
  if (p->ja)
    FREE (p->ja);

  if (p->a.ptr)
    {

      /*
       * For character type, the understanding is that the pointers are
       * all valid (and we can call FREE_CHAR on them, provided that
       * p->a.character[0] is not NULL.
       */

      if (p->type == character && p->a.character[0])
	{
	  for (i = 0; i < p->nn; i++)
	    FREE_CHAR (p->a.character[i]);
	}

      FREE (p->a.ptr);
    }

  if (p->d.ptr)
    {

      /* Same as above for character type, but with p->d.character[0]. */

      if (p->type == character && p->d.character[0])
	{
	  for (i = 0; i < p->nr; i++)
	    FREE_CHAR (p->d.character[i]);
	}

      FREE (p->d.integer);
    }

  /* Just to make it harder to use it again inadvertently. */

  p->entity.class = undefined_class;
  FREE (p);
}

void
DB_delete_matrix (p, file, line)
     MATRIX *p;
     char *file;
     int line;
{
  /*
   * This is the DEBUG version of `delete_matrix'.  It decrements
   * the matrix's reference count, and frees it if it is unreferenced.
   */

  if (p)
    {

      if (--p->entity.ref_count < 0)
	{
	  wipeout ("A matrix's \"ref_count\" went below zero:  %s, %d.",
		   file, line);
	}

      if (p->entity.ref_count >= 1000 || debug_level > 1)
	{
	  inform ("matrix \"ref_count\" decrement:  %x, %d, %s, %d.",
		  p, p->entity.ref_count, file, line);
	}

      if (p->entity.ref_count == 0)
	free_matrix (p);

    }
}

int
put_matrix (s, stream, ent_tree)
     MATRIX *s;
     FILE *stream;
     struct ent_node *ent_tree;
{
  /* Write matrix `s' out in binary form to file `stream'. */

  int size, i;

  EASSERT (s, matrix, 0);

  assert (s->order == ordered);	/* unordered is obsolete */

  if (!WRITE_INT (&s->type, stream) ||
      !WRITE_INT (&s->density, stream) ||
      !WRITE_INT (&s->symmetry, stream) ||
      !WRITE_INT (&s->nr, stream) ||
      !WRITE_INT (&s->nc, stream) ||
      !WRITE_INT (&s->nn, stream))
    goto err;

  if (s->a.ptr)
    {

      i = 1;			/* values follow */
      if (!WRITE_INT (&i, stream))
	goto err;

      switch (s->type)
	{

	case integer:
	  if (!WRITE_INTS (s->a.integer, s->nn, stream))
	    goto err;
	  break;

	case real:
	  if (!WRITE_DOUBLES (s->a.real, s->nn, stream))
	    goto err;
	  break;

	case complex:
	  if (!WRITE_DOUBLES (s->a.real, 2 * s->nn, stream))
	    goto err;
	  break;

	case character:
	  for (i = 0; i < s->nn; i++)
	    {
	      size = strlen (s->a.character[i]);
	      if (!WRITE_INT (&size, stream))
		goto err;
	      if (size > 0 &&
		  fwrite (s->a.character[i], 1, size, stream) <
		  (size_t) size)
		{
		  WRITE_WARN (stream);
		  goto err;
		}
	    }
	  break;

	default:
	  BAD_TYPE (s->type);
	  delete_matrix (s);
	  raise_exception ();
	}

    }
  else
    {

      i = 0;			/* no values */
      if (!WRITE_INT (&i, stream))
	goto err;

    }

  if (s->d.ptr)
    {

      i = 1;			/* diagonal values follow */
      if (!WRITE_INT (&i, stream))
	goto err;

      switch (s->type)
	{

	case integer:
	  if (!WRITE_INTS (s->d.integer, s->nr, stream))
	    goto err;
	  break;

	case real:
	  if (!WRITE_DOUBLES (s->d.real, s->nr, stream))
	    goto err;
	  break;

	case complex:
	  if (!WRITE_DOUBLES (s->d.real, 2 * s->nr, stream))
	    goto err;
	  break;

	case character:
	  for (i = 0; i < s->nr; i++)
	    {
	      size = strlen (s->d.character[i]);
	      if (!WRITE_INT (&size, stream))
		goto err;
	      if (size > 0 &&
		  fwrite (s->d.character[i], 1, size, stream) <
		  (size_t) size)
		{
		  WRITE_WARN (stream);
		  goto err;
		}
	    }
	  break;
	}

    }
  else
    {

      i = 0;			/* no diagonal values */
      if (!WRITE_INT (&i, stream))
	goto err;

    }

  if (s->ia)
    {
      i = 1;			/* ia follows */
      if (!WRITE_INT (&i, stream) ||
	  !WRITE_INTS (s->ia, s->nr + 1, stream))
	goto err;
    }
  else
    {
      i = 0;			/* no ia */
      if (!WRITE_INT (&i, stream))
	goto err;
    }

  if (s->ja)
    {
      i = 1;			/* ja follows */
      if (!WRITE_INT (&i, stream) ||
	  !WRITE_INTS (s->ja, s->nn, stream))
	goto err;
    }
  else
    {
      i = 0;			/* no ja */
      if (!WRITE_INT (&i, stream))
	goto err;
    }

  if (s->rid)
    {
      i = 1;			/* rid follows */
      if (!WRITE_INT (&i, stream) ||
	  !put_entity (copy_entity (s->rid), stream, ent_tree))
	goto err;
    }
  else
    {
      i = 0;			/* no rid */
      if (!WRITE_INT (&i, stream))
	goto err;
    }

  if (s->cid)
    {
      i = 1;			/* cid follows */
      if (!WRITE_INT (&i, stream) ||
	  !put_entity (copy_entity (s->cid), stream, ent_tree))
	goto err;
    }
  else
    {
      i = 0;			/* no cid */
      if (!WRITE_INT (&i, stream))
	goto err;
    }

  if (s->stuff)
    {
      i = 1;			/* stuff follows */
      if (!WRITE_INT (&i, stream) ||
	  !put_entity (copy_table (s->stuff), stream, ent_tree))
	goto err;
    }
  else
    {
      i = 0;			/* no stuff */
      if (!WRITE_INT (&i, stream))
	goto err;
    }

  delete_matrix (s);
  return 1;

err:
  delete_matrix (s);
  return 0;
}

ENTITY *
get_matrix (stream, ver)
     FILE *stream;
     int ver;
{
  /* Read a vector from binary file `stream'. */

  MATRIX *s;
  int size, i;
  static char *warn_msg = "Invalid matrix in file.";

  s = (MATRIX *) CALLOC (1, sizeof (MATRIX));
  s->entity.ref_count = 1;
  s->entity.class = matrix;
  s->order = ordered;

  if (!READ_INT (&s->type, stream) ||
      !READ_INT (&s->density, stream) ||
      !READ_INT (&s->symmetry, stream) ||
      !READ_INT (&s->nr, stream) ||
      !READ_INT (&s->nc, stream) ||
      !READ_INT (&s->nn, stream))
    {
      FREE (s);
      return NULL;
    }

  if (!READ_INT (&i, stream))
    {				/* values follow? */
      FREE (s);
      return NULL;
    }

  if (i)
    {

      if (s->nn < 1)
	{
	  warn (warn_msg);
	  FREE (s);
	  return NULL;
	}

      switch (s->type)
	{

	case integer:
	  s->a.integer = MALLOC (s->nn * sizeof (int));
	  if (!READ_INTS (s->a.integer, s->nn, stream))
	    goto err;
	  break;

	case real:
	  s->a.real = MALLOC (s->nn * sizeof (REAL));
	  if (!READ_DOUBLES (s->a.real, s->nn, stream))
	    goto err;
	  break;

	case complex:
	  s->a.real = MALLOC (2 * s->nn * sizeof (REAL));
	  if (!READ_DOUBLES (s->a.real, 2 * s->nn, stream))
	    goto err;
	  break;

	case character:
	  s->a.character = MALLOC (s->nn * sizeof (char *));
	  for (i = 0; i < s->nn; i++)
	    s->a.character[i] = NULL_string;
	  for (i = 0; i < s->nn; i++)
	    {
	      if (!READ_INT (&size, stream))
		goto err;
	      if (size > 0)
		{
		  s->a.character[i] = (char *) MALLOC (size + 1);
		  if (fread (s->a.character[i], 1, size, stream) <
		      (size_t) size)
		    {
		      READ_WARN (stream);
		      goto err;
		    }
		  s->a.character[i][size] = '\0';
		}
	    }
	  break;

	default:
	  warn (warn_msg);
	  goto err;

	}

    }
  else
    {
      s->a.ptr = NULL;
    }

  if (!READ_INT (&i, stream))
    goto err;			/* diagonal values follow? */

  if (i)
    {

      if (s->nr < 1)
	{
	  warn (warn_msg);
	  goto err;
	}

      switch (s->type)
	{

	case integer:
	  s->d.integer = MALLOC (s->nr * sizeof (int));
	  if (!READ_INTS (s->d.integer, s->nr, stream))
	    goto err;
	  break;

	case real:
	  s->d.real = MALLOC (s->nr * sizeof (REAL));
	  if (!READ_DOUBLES (s->d.real, s->nr, stream))
	    goto err;
	  break;

	case complex:
	  s->d.real = MALLOC (2 * s->nr * sizeof (REAL));
	  if (!READ_DOUBLES (s->d.real, 2 * s->nr, stream))
	    goto err;
	  break;

	case character:
	  s->d.character = MALLOC (s->nr * sizeof (char *));
	  for (i = 0; i < s->nr; i++)
	    s->d.character[i] = NULL_string;
	  for (i = 0; i < s->nr; i++)
	    {
	      if (!READ_INT (&size, stream))
		goto err;
	      if (size > 0)
		{
		  s->d.character[i] = (char *) MALLOC (size + 1);
		  if (fread (s->d.character[i], 1, size, stream) <
		      (size_t) size)
		    {
		      READ_WARN (stream);
		      goto err;
		    }
		  s->d.character[i][size] = '\0';
		}
	    }
	  break;

	}

    }
  else
    {
      s->d.ptr = NULL;
    }

  if (!READ_INT (&i, stream))
    goto err;			/* ia follows? */
  if (i)
    {
      s->ia = (int *) MALLOC ((s->nr + 1) * sizeof (int));
      if (!READ_INTS (s->ia, s->nr + 1, stream))
	goto err;
    }

  if (!READ_INT (&i, stream))
    goto err;			/* ja follows? */
  if (i)
    {
      s->ja = (int *) MALLOC (s->nn * sizeof (int));
      if (!READ_INTS (s->ja, s->nn, stream))
	goto err;
    }

  if (!READ_INT (&i, stream))
    goto err;			/* rid follows */
  if (i && !(s->rid = get_entity (stream)))
    goto err;

  if (!READ_INT (&i, stream))
    goto err;			/* cid follows */
  if (i && !(s->cid = get_entity (stream)))
    goto err;

  if (!READ_INT (&i, stream))
    goto err;			/* stuff follows */
  if (i && !(s->stuff = (TABLE *)
	     (ver ? get_entity (stream) : get_table (stream, ver))))
    goto err;

  if (!ok_entity (ENT (s)))
    goto err;

  return apt_matrix (s);

err:
  delete_matrix (s);
  return NULL;
}

ENTITY *
gift_wrap_matrix (nr, nc, type, data)
     int nr;
     int nc;
     TYPE type;
     void *data;
{
  /*
   * This function creates a dense MATRIX structure to point to
   * the given array.
   */

  MATRIX *m;

  assert (nr >= 0);
  assert (nc >= 0);

  m = (MATRIX *) CALLOC (1, sizeof (MATRIX));

  m->entity.ref_count = 1;
  m->entity.class = matrix;

  m->symmetry = general;

  m->type = type;
  m->order = ordered;
  m->density = dense;
  m->nr = nr;
  m->nc = nc;

  if ((double) nr * nc > INT_MAX)
    {
      fail ("Integer overflow.  Matrix dimensions too big.");
      raise_exception ();
    }
  m->nn = nr * nc;
  m->a.ptr = (m->nn) ? data : NULL;

  if (debug_level > 1)
    inform ("Matrix created:  %x.", m);

  return (ENT (m));
}
