#ifndef lint
static char *RCSid = "$Id: binary.c%v 3.38.2.100 1993/03/22 03:23:10 woo Exp woo $";
#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 <stdio.h>
#if !defined(apollo) && !defined(sequent) && !defined(u3b2)
#include <stdlib.h> /* realloc() */
#else
#include <sys/types.h> /* typedef long size_t; */
extern char *realloc();
#endif
#include <math.h>

#if defined(MSDOS) && defined(__TURBOC__) && !defined(DOSX286)
#include <alloc.h>		/* for farmalloc, farrealloc */
#define SMALLMALLOC
#endif
#if defined(_Windows) && !defined(WIN32)
#include <windows.h>
#include <windowsx.h>
#define farmalloc(s) GlobalAllocPtr(GHND,s)
#define farrealloc(p,s) GlobalReAllocPtr(p,s,GHND)
#define SMALLMALLOC
#endif
#ifdef sequent
#include <sys/types.h>      /* unsigned long size_t; */
#endif

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

float GPFAR *vector();
float GPFAR *extend_vector();
float GPFAR *retract_vector();
float GPFAR * GPFAR *matrix();
float GPFAR * GPFAR *extend_matrix();
float GPFAR * GPFAR *retract_matrix();
void free_matrix();
void free_vector();

/* versions of alloc, realloc and free that work with segmented
   architectures (yuk!) */
char GPFAR *
gpfaralloc(size, message)
	unsigned long size;		/* # of bytes */
	char *message;			/* description of what is being allocated */
{
#ifdef SMALLMALLOC
	char GPFAR *p;				/* the new allocation */
	char errbuf[100];		/* error message string */
	p = farmalloc(size);
	if (p == (char *)NULL) {
		/* really out of memory */
		if (message != NULL) {
			(void) sprintf(errbuf, "out of memory for %s", message);
			int_error(errbuf, NO_CARET);
			/* NOTREACHED */
		}
		/* else we return NULL */
	}
	return(p);
#else
	return alloc(size, message);
#endif
}

char GPFAR *
gpfarrealloc(p, size)
	char GPFAR *p;			/* old pointer */
	unsigned long size;		/* # of bytes */
{
#ifdef SMALLMALLOC
	return farrealloc(p, size);
#else
	return realloc(p, (size_t)size);
#endif
}

void
gpfarfree(p)
char GPFAR *p;
{
#ifdef SMALLMALLOC
#ifdef _Windows
HGLOBAL hGlobal = GlobalHandle(SELECTOROF(p));
	GlobalUnlock(hGlobal);
	GlobalFree(hGlobal);
#else
	farfree(p);
#endif
#else
	free(p);
#endif
}


/* 
 * 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;
  
  fread(&fdummy,sizeof(fdummy),1,fin);
  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 = 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] = 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] = 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 *)gpfaralloc((unsigned long) (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;
{
  gpfarfree((char GPFAR *)(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;
  if(!(new_v = (float GPFAR *)gpfarrealloc((void*)(vec+old_nl),
                       (unsigned long)(new_nh-old_nl+1)*sizeof(float)) )){
    int_error("not enough memory to extend vector",NO_CARET);
    return NULL;
  } 
  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;
  if(!(new_v = (float GPFAR *)gpfarrealloc((void*)(v+old_nl),
                               (unsigned long)(new_nh-old_nl+1)*sizeof(float)))){
    int_error("not enough memory to retract vector",NO_CARET);
    return NULL;
  }
  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;

  if (!(m = (float GPFAR * GPFAR *)gpfaralloc((unsigned long)(nrh-nrl+1)*sizeof(float GPFAR *),NULL))){
    int_error("not enough memory to create matrix",NO_CARET);
    return NULL;
  }
  m -= nrl;

  for (i=nrl; i<=nrh; i++)
    {
      if (!(m[i] = (float GPFAR *) gpfaralloc((unsigned long)(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 int i;

  for (i=nrl; i<=nrh; i++) 
    gpfarfree((char GPFAR *) (m[i]+ncl));
  gpfarfree((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;

  /*  bug fixed. realloc() called with incorrect 2nd argument. jvdwoude@hut.nl */
  if(!(m = (float GPFAR * GPFAR *)gpfarrealloc((void*)(a+nrl),(unsigned long)(srh-nrl+1)*sizeof(float GPFAR *)) )){
    int_error("not enough memory to extend matrix",NO_CARET);
    return NULL;
  }

  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 *) gpfaralloc((unsigned long) (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);
  }

  /*  bug fixed. realloc() called with incorrect 2nd argument. jvdwoude@hut.nl */
  if(!(m = (float GPFAR * GPFAR *)gpfarrealloc((void*)(a+nrl), (unsigned long)(srh-nrl+1)*sizeof(float GPFAR *)) )){
    int_error("not enough memory to retract matrix",NO_CARET);
    return NULL;
  }

  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;
}
