#ifndef lint
static char *RCSid = "$Id: binary.c,v 1.4.2.1 2002/01/31 21:18:21 lhecking Exp $";
#endif

/*
 * The addition of gnubin and binary, along with a small patch
 * to command.c, will permit gnuplot to plot binary files.
 * gnubin  - contains the code that relies on gnuplot include files
 *                     and other definitions
 * binary      - contains those things that are independent of those 
 *                     definitions and files
 *
 * With these routines, hidden line removal of your binary data is possible!
 *
 * Last update:  3/29/92 memory allocation bugs fixed. jvdwoude@hut.nl
 *               3/09/92 spelling errors, general cleanup, use alloc with no
 *                       nasty fatal errors
 *               3/03/92 for Gnuplot 3.24.
 * Created from code for written by RKC for gnuplot 2.0b.
 *
 * Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
 *
 */

#include "plot.h"		/* We have to get TRUE and FALSE */
#include "stdfn.h"
#include "binary.h"

/* 
 * This routine scans the first block of the file to see if the file is a 
 * binary file.  A file is considered binary if 10% of the characters in it 
 * are not in the ascii character set. (values < 128), or if a NUL is found.
 * I hope this doesn't break when used on the bizzare PC's.
 */
int is_binary_file(fp)
register FILE *fp;
{
    register int i, len;
    register int odd;		/* Contains a count of the odd characters */
    long where;
    register unsigned char *c;
    unsigned char buffer[512];

    if ((where = ftell(fp)) == -1) {	/* Find out where we start */
	fprintf(stderr, "Notice: Assuming unseekable data is not binary\n");
	return (FALSE);
    } else {
	rewind(fp);

	len = fread(buffer, sizeof(char), 512, fp);
	if (len <= 0)		/* Empty file is declared ascii */
	    return (FALSE);

	c = buffer;

	/* now scan buffer to look for odd characters */
	odd = 0;
	for (i = 0; i < len; i++, c++) {
	    if (!*c) {		/* NUL _never_ allowed in text */
		odd += len;
		break;
	    } else if ((*c & 128) ||	/* Meta-characters--we hope it's not formatting */
		       (*c == 127) ||	/* DEL */
		       (*c < 32 &&
			*c != '\n' && *c != '\r' && *c != '\b' &&
			*c != '\t' && *c != '\f' && *c != 27 /*ESC */ ))
		odd++;
	}

	fseek(fp, where, 0);	/* Go back to where we started */

	if (odd * 10 > len)	/* allow 10% of the characters to be odd */
	    return (TRUE);
	else
	    return (FALSE);
    }
}


/*========================= I/O Routines ================================
  These may be useful for situations other than just gnuplot.  Note that I 
  have included the reading _and_ the writing routines, so others can create 
  the file as well as read the file.
*/

/*
   This function reads a matrix from a stream

   This routine never returns anything other than vectors and arrays
   that range from 0 to some number.  

 */
#define START_ROWS 100		/* Each of these must be at least 1 */
#define ADD_ROWS 50
int fread_matrix(fin, ret_matrix, nr, nc, row_title, column_title)
FILE *fin;
float GPFAR *GPFAR * GPFAR * ret_matrix, GPFAR * GPFAR * row_title,
 GPFAR * GPFAR * column_title;
int *nr, *nc;
{
    float GPFAR *GPFAR * m, GPFAR * rt, GPFAR * ct;
    register int num_rows = START_ROWS;
    register int num_cols;
    register int current_row = 0;
    register float GPFAR *GPFAR * temp_array;
    float fdummy;

    if (fread(&fdummy, sizeof(fdummy), 1, fin) != 1)
	return FALSE;

    num_cols = (int) fdummy;

    /* 
       Choose a reasonable number of rows,
       allocate space for it and continue until this space
       runs out, then extend the matrix as necessary.
     */
    ct = vector(0, num_cols - 1);
    fread(ct, sizeof(*ct), num_cols, fin);

    rt = vector(0, num_rows - 1);
    m = matrix(0, num_rows - 1, 0, num_cols - 1);

    while (fread(&rt[current_row], sizeof(rt[current_row]), 1, fin) == 1) {
	/* We've got another row */
	if (fread(m[current_row], sizeof(*(m[current_row])), num_cols, fin) != num_cols)
	    return (FALSE);	/* Not a True matrix */

	current_row++;
	if (current_row >= num_rows) {	/* We've got to make a bigger rowsize */
	    temp_array = extend_matrix(m, 0, num_rows - 1, 0, num_cols - 1,
				  num_rows + ADD_ROWS - 1, num_cols - 1);
	    rt = extend_vector(rt, 0, num_rows - 1, num_rows + ADD_ROWS - 1);

	    num_rows += ADD_ROWS;
	    m = temp_array;
	}
    }
    /*  finally we force the matrix to be the correct row size */
    /*  bug fixed. procedure called with incorrect 6th argument.
     *   jvdwoude@hut.nl */
    temp_array = retract_matrix(m, 0, num_rows - 1, 0, num_cols - 1, current_row - 1, num_cols - 1);
    /* Now save the things that change */
    *ret_matrix = temp_array;
    *row_title = retract_vector(rt, 0, num_rows - 1, current_row - 1);
    *column_title = ct;
    *nr = current_row;		/* Really the total number of rows */
    *nc = num_cols;
    return (TRUE);
}

/* This writes a matrix to a stream 
   Note that our ranges are inclusive ranges--and we can specify subsets.
   This behaves similarly to the xrange and yrange operators in gnuplot
   that we all are familiar with.
 */
int fwrite_matrix(fout, m, nrl, nrh, ncl, nch, row_title, column_title)
register FILE *fout;
register float GPFAR *GPFAR * m, GPFAR * row_title, GPFAR * column_title;
register int nrl, nrh, ncl, nch;
{
    register int j;
    float length;
    register int col_length;
    register int status;
    float GPFAR *title = NULL;

    length = (float) (col_length = nch - ncl + 1);

    if ((status = fwrite((char *) &length, sizeof(float), 1, fout)) != 1) {
	fprintf(stderr, "fwrite 1 returned %d\n", status);
	return (FALSE);
    }
    if (!column_title) {
	column_title = title = vector(ncl, nch);
	for (j = ncl; j <= nch; j++)
	    title[j] = (float) j;
    }
    fwrite((char *) column_title, sizeof(float), col_length, fout);
    if (title) {
	free_vector(title, ncl, nch);
	title = NULL;
    }
    if (!row_title) {
	row_title = title = vector(nrl, nrh);
	for (j = nrl; j <= nrh; j++)
	    title[j] = (float) j;
    }
    for (j = nrl; j <= nrh; j++) {
	fwrite((char *) &row_title[j], sizeof(float), 1, fout);
	fwrite((char *) (m[j] + ncl), sizeof(float), col_length, fout);
    }
    if (title)
	free_vector(title, nrl, nrh);

    return (TRUE);
}

/*===================== Support routines ==============================*/

/******************************** VECTOR *******************************
 *       The following routines interact with vectors.
 *
 *   If there is an error we don't really return - int_error breaks us out.
 *
 *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
 *   by Press, Flannery, Teukoilsky and Vetterling (1988).
 *
 */
float GPFAR *vector(nl, nh)
register int nl, nh;
{
    register float GPFAR *vec;

    if (!(vec = (float GPFAR *) gp_alloc((nh - nl + 1) * sizeof(float), NULL))) {
	int_error("not enough memory to create vector", NO_CARET);
	return NULL;		/* Not reached */
    }
    return (vec - nl);
}


/* 
 *  Free a vector allocated above
 *
 *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
 *   by Press, Flannery, Teukoilsky and Vetterling (1988).
 *
 */
void free_vector(vec, nl, nh)
float GPFAR *vec;
int nl, nh;
{
    free(vec + nl);
}

/************ Routines to modify the length of a vector ****************/
float GPFAR *
 extend_vector(vec, old_nl, old_nh, new_nh)
float GPFAR *vec;
register int old_nl, old_nh, new_nh;
{
    register float GPFAR *new_v;
    new_v = (float GPFAR *) gp_realloc((void *) (vec + old_nl),
		   (unsigned long) (new_nh - old_nl + 1) * sizeof(float),
				       "extend vector");
    return new_v - old_nl;
}

float GPFAR *
 retract_vector(v, old_nl, old_nh, new_nh)
float GPFAR *v;
register int old_nl, old_nh, new_nh;
{
    register float GPFAR *new_v;
    new_v = (float GPFAR *) gp_realloc((void *) (v + old_nl),
				       (unsigned long) (new_nh - old_nl + 1) * sizeof(float), "retract vector");
    return new_v - old_nl;
}


/***************************** MATRIX ************************
 *
 * 	  The following routines work with matricies
 *
 * 	 I always get confused with this, so here I write it down:
 * 			  for nrl<= nri <=nrh and
 * 			  for ncl<= ncj <=nch
 *  
 *   This matrix is accessed as:
 *   
 *     matrix[nri][ncj];
 *     where nri is the offset to the pointer to a vector where the
 *     ncjth element lies.
 * 
 *   If there is an error we don't really return - int_error breaks us out.
 *
 *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
 *   by Press, Flannery, Teukoilsky and Vetterling (1988).
 *
 */
float
GPFAR *GPFAR * matrix(nrl, nrh, ncl, nch)
register int nrl, nrh, ncl, nch;
{
    register int i;
    register float GPFAR *GPFAR * m;

    m = (float GPFAR * GPFAR *) gp_alloc((nrh - nrl + 1) * sizeof(float GPFAR *), "matrix");
    m -= nrl;

    for (i = nrl; i <= nrh; i++) {
	if (!(m[i] = (float GPFAR *) gp_alloc((nch - ncl + 1) * sizeof(float), NULL))) {
	    free_matrix(m, nrl, i - 1, ncl, nch);
	    int_error("not enough memory to create matrix", NO_CARET);
	    return NULL;
	}
	m[i] -= ncl;
    }
    return m;
}
/* 
 * Free a matrix allocated above
 *
 *
 *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
 *   by Press, Flannery, Teukoilsky and Vetterling (1988).
 *
 */
void free_matrix(m, nrl, nrh, ncl, nch)
float GPFAR *GPFAR * m;
unsigned nrl, nrh, ncl, nch;
{
    register unsigned int i;

    for (i = nrl; i <= nrh; i++)
	free((char GPFAR *) (m[i] + ncl));
    free((char GPFAR *) (m + nrl));
}

/*
   This routine takes a sub matrix and extends the number of rows and 
   columns for a new matrix
 */
float GPFAR *GPFAR * extend_matrix(a, nrl, nrh, ncl, nch, srh, sch)
register float GPFAR *GPFAR * a;
register int nrl, nrh, ncl, nch;
register int srh, sch;
{
    register int i;
    register float GPFAR *GPFAR * m;

    m = (float GPFAR * GPFAR *) gp_realloc((void *) (a + nrl), (unsigned long) (srh - nrl + 1) * sizeof(float GPFAR *), "extend matrix");

    m -= nrl;

    if (sch != nch) {
	for (i = nrl; i <= nrh; i++) {	/* Copy and extend rows */
	    if (!(m[i] = extend_vector(m[i], ncl, nch, sch))) {
		free_matrix(m, nrl, nrh, ncl, sch);
		int_error("not enough memory to extend matrix", NO_CARET);
		return NULL;
	    }
	}
    }
    for (i = nrh + 1; i <= srh; i++) {
	if (!(m[i] = (float GPFAR *) gp_alloc((nch - ncl + 1) * sizeof(float), NULL))) {
	    free_matrix(m, nrl, i - 1, nrl, sch);
	    int_error("not enough memory to extend matrix", NO_CARET);
	    return NULL;
	}
	m[i] -= ncl;
    }
    return m;
}
/*
   this routine carves a large matrix down to size
 */
float GPFAR *GPFAR * retract_matrix(a, nrl, nrh, ncl, nch, srh, sch)
register float GPFAR *GPFAR * a;
register int nrl, nrh, ncl, nch;
register int srh, sch;
{
    register int i;
    register float GPFAR *GPFAR * m;

    for (i = srh + 1; i <= nrh; i++) {
	free_vector(a[i], ncl, nch);
    }

    m = (float GPFAR * GPFAR *) gp_realloc((void *) (a + nrl), (unsigned long) (srh - nrl + 1) * sizeof(float GPFAR *), "retract matrix");

    m -= nrl;

    if (sch != nch) {
	for (i = nrl; i <= srh; i++)
	    if (!(m[i] = retract_vector(m[i], ncl, nch, sch))) { {	/* Shrink rows */
		    free_matrix(m, nrl, srh, ncl, sch);
		    int_error("not enough memory to retract matrix", NO_CARET);
		    return NULL;
	    }
	    }
    }
    return m;
}

float
GPFAR *GPFAR * convert_matrix(a, nrl, nrh, ncl, nch)
float GPFAR *a;
register int nrl, nrh, ncl, nch;

/* allocate a float matrix m[nrl...nrh][ncl...nch] that points to the
   matrix declared in the standard C manner as a[nrow][ncol], where 
   nrow=nrh-nrl+1, ncol=nch-ncl+1.  The routine should be called with
   the address &a[0][0] as the first argument.  This routine does
   not free the memory used by the original array a but merely assigns
   pointers to the rows. */

{
    register int i, j, ncol, nrow;
    register float GPFAR *GPFAR * m;

    nrow = nrh - nrl + 1;
    ncol = nch - ncl + 1;
    m = (float GPFAR * GPFAR *) gp_alloc((nrh - nrl + 1) * sizeof(float GPFAR *), "convert_matrix");
    m -= nrl;

    m[nrl] = a - ncl;
    for (i = 1, j = nrl + 1; i <= nrow - 1; i++, j++)
	m[j] = m[j - 1] + ncol;
    return m;
}


void free_convert_matrix(b, nrl, nrh, ncl, nch)
float GPFAR *GPFAR * b;
register int nrl, nrh, ncl, nch;
{
    free((char *) (b + nrl));
}
