/***********************************************************

      SAVEMAT.C

      A routine for saving a matrix as a MATfile, the
      native format of Matlab.
      The routine can handle matrices in double precision,
      floating point, long integer, and signed and unsigned
      short integer.
      It will also convert a C nul-terminated string into
      a Matlab string, which is stored as a double precision
      array.

      This is a major modification of a function written by
      J. N. Little, 86-11-03, for MATLAB.

            Eric Firing
            88-02-12

      Mon  03-13-1989  Modified to put #defines into the header.

      Tue  09-19-89 JR Modified to store arrays in either column-wise
                       or row-wise fashion, depending on the
                       setting of the tens digit of the imagf flag:
                       choose from REAL_COLS, REAL_ROWS, IMAG_COLS,
                       IMAG_ROWS.
           93/02/22 JR Modified to actually transpose any row-wise
		       arrays to suit Matlab 4.0, which requires
		       all arrays to be column-wise, regardless of
		       the orientation bit in the file header.

**************************************************************/

#include "common.h"
#include "matfile.h"


/**** Convert a nul-terminated string to a Matlab string.
      Allocate heap space for the Matlab string.
****/
#if PROTOTYPE_ALLOWED
double *matstring(char *s, int *n)
#else
double *matstring(s, n)
char *s;
int *n;      /* used to return ncol for the Matlab string */
#endif
{
   int i;
   double *mstring;

   *n = strlen(s);
   mstring = (double *) calloc(*n, sizeof(double));
   if (mstring != NULL)  {
      for (i=0; i<*n; i++)
         mstring[i] = (double) s[i];
   }
   return(mstring);  /* will be NULL if there is an error */
}                       /* matstring() */

#if PROTOTYPE_ALLOWED
void savemat(FILE *fp,
             char type, char *pname,
             int mrows, int ncols, int imagf,
             char *preal, char *pimag)
#else
void savemat(fp, type, pname, mrows, ncols, imagf, preal, pimag)
   FILE *fp;       /* File pointer */
   char type;      /* d, f, l, i, u, t:
                        double, float, long, short, ushort, text */
   int mrows;      /* row dimension (not required for string) */
   int ncols;      /* column dimension (not required for string) */
   int imagf;      /* flag: REAL_COLS, IMAG_COLS, REAL_ROWS, or IMAG_ROWS */
   char *pname;    /* pointer to matrix name */
   char *preal;    /* pointer to real data, or to nul-terminated string */
   char *pimag;    /* pointer to imag data */
#endif
{                        /* savemat() */
   Fmatrix x;
   int mn;
   int nbytes, free_preal = 0, free_pimag = 0;
   char *mstring;  /* temporary pointer */

   x.type = 0;
   switch (type) {
      case 't':
         /* Note that preal is changed to point to a new
            block of memory allocated on the heap.
         */
         mstring = (char *) matstring(preal, &ncols);
         if (mstring == NULL)  {
            printf("\nSAVEMAT error, not enough memory for MAT string.\n");
            exit(-1);
         }
         preal = mstring;
	 free_preal = 1;
         mrows = 1;
         nbytes = 8;
         x.type += 1;
         break;
      case 'd':
         nbytes = 8;
         x.type += 0;
         break;
      case 'f':
         nbytes = 4;
         x.type += 10;
         break;
      case 'l':
         nbytes = 4;
         x.type += 20;
         break;
      case 'i':
#if (HOST_BYTES_PER_WORD == 2)
         nbytes = 2;
         x.type += 30;
#else
         nbytes = 4;
         x.type += 20;
#endif
         break;
      case 's':
         nbytes = 2;
         x.type += 30;
         break;
      case 'u':
         nbytes = 2;
         x.type += 40;
         break;
      default: { printf("\nSAVEMAT error, invalid type: %c\n ", type);
         exit(-1);
      }
   }
   x.mrows = mrows;
   x.ncols = ncols;
   x.imagf = imagf % 10;            /* LS Bit is imaginary flag */
   x.namlen = strlen(pname) + 1;
   mn = x.mrows * x.ncols * nbytes;

   x.type += COLUMN_WISE;  /* Matlab 4.0 supports only column-wise! */
   if ( (imagf == REAL_ROWS || imagf == IMAG_ROWS) && (x.mrows > 1 || x.ncols > 1) )
   {
      if ( (preal = transpose(preal, mrows, ncols, nbytes)) == NULL ) exit(-1);
      free_preal = 1;
      if (x.imagf)
      {
         if ( (pimag = transpose(pimag, mrows, ncols, nbytes)) == NULL ) exit(-1);
	 free_pimag = 1;
      }
   }

   x.type += MACHINE_TYPE;

   fwrite(&x, sizeof(Fmatrix), 1, fp);
   fwrite(pname, sizeof(char), (int)x.namlen, fp);
   fwrite(preal, 1, mn, fp);
   if (free_preal) free(preal);
   if (x.imagf)
      fwrite(pimag, 1, mn, fp);
   if (free_pimag) free(pimag);
}                       /* savemat() */

#define Mrows Ccols
#define Mcols Crows

#if PROTOTYPE_ALLOWED
char *transpose(char pdata[], int Crows, int Ccols, int nbytes)
#else
char *transpose(pdata, Crows, Ccols, nbytes)
char pdata[];         
int  Crows, Ccols, nbytes;
#endif
{
   int i, j, k, Cbytes_per_row, Mbytes_per_row;
   char *buff;

   if ( (buff = malloc(Crows * Ccols * nbytes)) == NULL )
   {
      printf("\n savemat transpose error: insufficient memory\n\n");
      return(NULL);
   }

   Cbytes_per_row = nbytes * Ccols;
   Mbytes_per_row = nbytes * Mcols;

   for (i = 0; i < Mrows; i++)
      for (j = 0; j < Mcols; j++)
	 for (k = 0; k < nbytes; k++)
	    buff[i * Mbytes_per_row + j * nbytes + k] = 
           pdata[j * Cbytes_per_row + i * nbytes + k];

   return(buff);
}
