/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd and Michael C. Wrinn.	    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/


/*=~=~=~=~ McGill-Computer-Vision-and-Robotics-Laboratory--C-Module ~=~=~=~=
 *		
 * NAME:	maketable
 *
 * USAGE:	maketable [-h] <  specification-file
 *
 * DESCRIPTION: The purpose of this program is to build lookup tables
 *		for math functions. Each table is generated as an initialized
 *		array in a '.c' file.
 *
 *		Each table is an array of C structures of the form:
 *
 *			struct _tabent {
 *				float b;
 *				float a;
 *			}
 *
 *		Field 'b' contains the value associated with index i.
 *		Field 'a' is the difference between 'b_i' and 'b_(i+1)';
 *		this is used in linearly interpolating between indices (this
 *		field is included simply to save the table driven function
 *		from having to do a subtraction). Hence given a integer i
 *		and a fractional value x (between 0 and 1), a full 
 *		approximation for y is computed as
 *
 *				y = table[i].a x + table[i].b
 *
 *		Each table is generated as an initialized array in a C file.
 *		Once compiled down, of course, the table can be used from
 *		C or assembly.
 *
 *		To build a table, it necessary to specifiy an interval 
 *		[X1, X2] over which the table is defined. This interval
 *		is in tern associated with a pair of indices (I1, I2).
 *		Typically, these indices will be (0, TABLESIZE-1),
 *		but not necessarily: there are instances where the defining
 *		interval only spans a subsection of the table, and the
 *		interval is extended appropriately for those indices which
 *		lie outside the section. 
 *
 *		IMPORTANT: Regardless of how the defining interval is
 *		specified, the function must have defined values over the
 *		whole table.
 *
 *		At the endpoint n, 'a_n' is taken to be 'a_(n-1)'.
 *
 *		The program build tables using a fixed format specification 
 *		given on stdin. (The specification format can be made 
 *		sexier if anyone feels bored). The format looks like:
 * 
 *		(1) (string, 64 characters max.): The name of the table. 
 *		    This is also the name given to the '.c' file, EXCEPT
 *		    '$' are replaced with '_' (because UNIX hates '$' in
 *		    files names, but JL loves them in official variable names
 *		    -- ughh!)
 *
 *		(2) (string, 64 characters max.): The prefix for the 
 *		    definitions created in the '.h' and '.sed' output files 
 *		    (see below).
 *
 *		(3) (string): The name of the generating function. The 
 *		    availiable functions are hardwired into this program: 
 *		    Check the array funNames (below) to see what is necessary;
 *		    add entries to this array and re-compile if necessary.
 *
 *		(4) (decimal integer): The size of the table 
 *		    (number of entries).
 *
 *		(5) (decimal integer): The 'origin' of the table. This takes 
 *		    a word of explantion: For all indices >= the origin, 'a' 
 *		    is computed as 'b_(i+1)' - 'b_i' (as described above).
 *		    For indices < the origin, 'a' is computed as 
 *		    'b_i' - 'b_(i-1)', and 'a_0' is set equal to 'a_1'.
 *
 *		    NOTE: This feature has been included because it is useful 
 *		    for some lookup methodologies when the function in 
 *		    question is symmetric about x = 0.0. If this is 
 *		    not needed, then set origin to 0.
 *
 *		(6) (decimal integer): The index I1 corresponding to the low 
 *		    value of x on the defining interval.
 *
 *		(7) (double): The low value of x on the defining interval.
 *
 *		(8) (decimal integer): The index I2 corresponding to the high
 *		    value of x on the defining interval.
 *
 *		(9) (double): The high value of x on the defining interval.
 *
 *		Generating auxillary files:
 *
 *		The program also generates 3 definition files, intended for
 *		use by code which uses the tables:
 *
 *			<fxn>Cdefs.h -- definitions for inclusion in a C
 *				        program.
 *
 *			<fxn>Adefs.h -- definitions for inclusion in an
 *				        assembly program.
 *
 *			<fxn>Adefs.sed -- VAX assembly definitions in 'sed' 
 *				        format (used for inline subsitution).
 *			
 *		<func>Cdefs.h contains the following definitions for each
 *		function <FXN>:
 *
 *		   <FXN>_TBSIZE  -- table size (decimal)
 *		   <FXN>_TORIGIN -- the origin index (decimal)
 *		   <FXN>_TINDEX1 -- low index of interval (decimal)
 *		   <FXN>_TINDEX2 -- high index of interval (decimal)
 *		   <FXN>_TLOWER  -- low value of interval (double)
 *		   <FXN>_TUPPER  -- high value of interval (double)
 *		   <FXN>_TSCALE  -- (index2-index1) / (upper-lower) (double)
 *
 *		<fxn>Adefs.h and <fxn>Adefs.sed contain the following 
 *		definitions for each function <FXN>.
 *
 *		   <FXN>_TBSIZE  -- table size (decimal, number of entries)
 *		   <FXN>_TESIZE  -- table entry size (bytes)
 *		   <FXN>_TORIGIN_O -- the origin index (byte offset)
 *		   <FXN>_TINDEX1_O -- low index of interval (byte offset)
 *		   <FXN>_TINDEX2_O -- high index of interval (byte offset)
 *		   <FXN>_TLOWER_F  -- low value of interval (float)
 *		   <FXN>_TUPPER_F  -- high value of interval (float)
 *		   <FXN>_TSCALE_F  -- (index2-index1) / (upper-lower) (float)
 *		   <FXN>_TLOWER_D  -- low value of interval (double)
 *		   <FXN>_TUPPER_D  -- high value of interval (double)
 *		   <FXN>_TSCALE_D  -- (index2-index1) / (upper-lower) (double)
 *		
 * ARGUMENTS:	The argument '-h' causes only the .h and .sed files to be 
 *		created.
 *
 * AUTHOR/DATE: John Lloyd, Fall, 1986.
 *
 * UPDATES:
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include <stdio.h>
#include <math.h>

#define MAXTSIZE 0x1000

struct _tabent { 
	float b, a;
};

#define MAXTABNAME	64
#define MAXTABDEFS	64

struct _funTab {
	char *functionName;
	double (*entry)();
	char defined;
	char create;
	double X1, X2;
	int I1, I2;	
	int tsize;
	int origin;
	double XtoTab;
	char tableName[MAXTABNAME+1];
	char prefix[MAXTABNAME+1];
};

struct _funNames {
	char *name;
	double (*entry)(); } funNames [] =

	{ "sin", sin,
	  "cos", cos,
	  "atan", atan,
	  "sqrt", sqrt,
	  "acos", acos,
	};

struct _funTab funTab [MAXTABDEFS];

#define ARRAY_SIZE(a)	(sizeof(a)/sizeof(a[0]))

static char ibuf1[MAXTABNAME+1];
static char ibuf2[MAXTABNAME+1];
static char obuf[MAXTABNAME+1];
static char nbuf[MAXTABNAME+1];
static char ofileName[MAXTABNAME+3];

#include <ctype.h>

char *pnum (str, number)
char *str;
double number;
{
	char *p;

	sprintf (str, "%-14.12lg", number);
	
	p = str;
	while (!isspace(*p) && *p != '\0')
	 { p++;
	 }
	*p = '\0';
	if (index (str, '.') == 0)
	 { strcat (str, ".0");
	 }
	return (str);
}

main(argc, argv)
int argc;
char **argv;
{
	char defFileName[256];
	FILE *of;

	int findex, tabsize;
	int i, j, k;
	double XtoTab;
	double mat[3][3], kernel[3][3];

	char hflag = 0;

	argv++; argc--;
	if (argc != 1 && argc != 0)
	 { fprintf (stderr, "USAGE: maketable [-h] < configuartion\n");
	   exit (1);
	 }
	if (argc == 1)
	 { if (strcmp (*argv, "-h") == 0)
	    { hflag = 1;
	    }
	   else
	    { fprintf (stderr, "USAGE: maketable [-h] < configuration\n");
	      exit (1);
	    }
	 }

	for (i=0; i<ARRAY_SIZE(funTab); i++)
	 { funTab[i].defined = 0;
	   funTab[i].create = 0;
	 }

	/* Scan the input to determine table configuration */

	k = 0;
	while (scanf("%64s", ibuf1) == 1)
	 { int size, origin;
	   double X1, X2, temp;
	   int I1, I2;

	   if (k >= MAXTABDEFS)
	    { fatalMsg (-1, "number of table definitions exceeds %d\n", 
		     MAXTABDEFS);
	    }
	   strcpy (funTab[k].tableName, ibuf1);

	   if (scanf ("%64s", ibuf2) != 1)
	    { fatalMsg (-1,"premature end of input after %s\n", ibuf1);
	    }
	   strcpy (funTab[k].prefix, ibuf2);
	   
	   if (scanf ("%64s", ibuf2) != 1)
	    { fatalMsg (-1,"premature end of input after %s\n", ibuf2);
	    }
	   for (i=0; i<ARRAY_SIZE(funNames); i++)
	    { if (strcmp (funNames[i].name, ibuf2) == 0)
	       { break;
	       }
	    }
	   if (i == ARRAY_SIZE(funNames))
	    { fatalMsg (-1,"function %s not known by program\n", 
		     ibuf2);
	    }

	   funTab[k].defined = 1;
	   funTab[k].functionName = funNames[i].name;
	   funTab[k].entry = funNames[i].entry;

	   if (scanf ("%d%d%d%lf%d%lf", 
		      &size, &origin, &I1, &X1, &I2, &X2) != 6)
	    { fatalMsg (-1,"cannot read entries in config file for function %s\n",
		     ibuf2);
	    }
	   if (X1 == X2)
	    { 
fatalMsg (-1,"0 size interval defined for function %s\n", ibuf2);
	    }
	   if (X1 > X2)
	    { 
fatalMsg (-1,"upper interval bound less than lower bound for function %s\n", ibuf2);
	    }
	   if (size < 1)
	    { 
fatalMsg (-1,"minimun table size is 1 (function %s)\n", ibuf2);
	    }
	   if (I1 < 0 || I1 >= size)
	    { 
fatalMsg (-1,"lower table index (%d) not between 0 and %d (function %s)\n", 
	      I1, size, ibuf2);
	    }
	   if (I2 < 0 || I2 >= size)
	    { 
fatalMsg (-1,"upper table index (%d) not between 0 and %d (function %s)\n",
	      I2, size, ibuf2);
	    }
	   if (origin < 0 || origin >= size)
	    { 
fatalMsg (-1,"origin (%d) not between 0 and %d (function %s)\n", 
	      origin, size, ibuf2);
	    }
	   if (I1 >= I2)
	    { 
fatalMsg (-1,"upper table index (%d) <= lower table index (%d) (function %s)\n",
	      I2, I1, ibuf2);
	    }

	   funTab[k].tsize = size;
	   funTab[k].X2 = X2;
	   funTab[k].X1 = X1;
	   funTab[k].I2 = I2;
	   funTab[k].I1 = I1;
	   funTab[k].origin = origin;
	   funTab[k].XtoTab = (I2 - I1) / (X2 - X1);
	   k++;
	 }

	/* Get the function described by argv */

	if (hflag)
	 { /* do nothing */
	 }
	else
	 { for (i=0; i<ARRAY_SIZE(funTab); i++)
	    { if (funTab[i].defined)
	       { funTab[i].create = 1;
	       }
	    }
	 }

	/* Now go ahead and create all the tables */

	for (k=0; k<ARRAY_SIZE(funTab); k++)
	 { if (funTab[k].create)
	    { 
	      double lastY, nextY, thisY, XtoTab, X1;
	      int i, l, tsize, I1, origin;

	      strcpy (ofileName, funTab[k].tableName);
	      l = strlen (ofileName);
	      for (i=0; i<l; i++)	/* Hack to replace '$' with '_' */
	       { if (ofileName[i] == '$')
	          { ofileName[i] = '_';
		  }
	       }
	      strcat (ofileName, ".c");
	      if ((of = fopen (ofileName, "w")) == NULL)
	       { fatalMsg (-1,"cannot open output file %s\n", ofileName);
	       }
	      fprintf (of, "struct tabent {\n");
	      fprintf (of, "\tfloat b, a;\n");
	      fprintf (of, "};\n\n");
	      fprintf (of, "struct tabent %s[0x%x] = {\n", 
		       funTab[k].tableName, funTab[k].tsize);

	      tsize = funTab[k].tsize;
	      XtoTab = funTab[k].XtoTab;
	      X1 = funTab[k].X1;
	      I1 = funTab[k].I1;
	      origin = funTab[k].origin;

	      nextY = (*funTab[k].entry)(X1 + (1-I1)/XtoTab);
	      for (i=0; i<origin; i++)
	       { 
	         thisY = (*funTab[k].entry)(X1 + (i-I1)/XtoTab);
		 if (i==0)
	          { fprintf (of, "%14.12lg, %14.12lg,\n", thisY, nextY-thisY);
	          }
	         else
	          { fprintf (of, "%14.12lg, %14.12lg,\n", thisY, thisY-lastY);
	          }
		 lastY = thisY;
	       }

	      nextY = (*funTab[k].entry)(X1 + (origin-I1)/XtoTab);
	      for (i=origin; i<tsize; i++)
	       { thisY = nextY;

		 if (i<tsize-1)
		  { nextY = (*funTab[k].entry)(X1 + (i+1-I1)/XtoTab);
		    fprintf (of, "%14.12lg, %14.12lg,\n", thisY, nextY-thisY);
		  }
		 else
		  { lastY = (*funTab[k].entry)(X1 + (i-1-I1)/XtoTab);
		    fprintf (of, "%14.12lg, %14.12lg,\n", thisY, thisY-lastY);
		  }
	       }

	      fprintf (of, "};\n");
	      fclose (of);
	    }
	 }

	/* Print out C include files */

	for (k=0; k<ARRAY_SIZE(funTab); k++)
	 { if (funTab[k].defined)
	    {
	      sprintf (defFileName, "%s_Cdefs.h", funTab[k].prefix);
	      if ((of = fopen (defFileName, "w")) == NULL)
	       { fatalMsg (-1,"cannot open output file %s\n", defFileName);
	       }

	      fprintf (of, "struct _tabent {\n");
	      fprintf (of, "\tfloat b, a;\n");
	      fprintf (of, "};\n\n");

	      strcpy (ibuf1, funTab[k].prefix);
	
	      fprintf (of, "#define %s_TBSIZE %d\n", 
		       ibuf1, funTab[k].tsize);
	      fprintf (of, "#define %s_TORIGIN %d\n", 
		       ibuf1, funTab[k].origin);
	      fprintf (of, "#define %s_TINDEX1 %d\n", 
		       ibuf1, funTab[k].I1);
	      fprintf (of, "#define %s_TINDEX2 %d\n", 
		       ibuf1, funTab[k].I2);
	      fprintf (of, "#define %s_TLOWER (double)(%-14.12lg)\n", 
		       ibuf1, funTab[k].X1);
	      fprintf (of, "#define %s_TUPPER (double)(%-14.12lg)\n", 
		       ibuf1, funTab[k].X2);
	      fprintf (of, "#define %s_TSCALE (double)(%-14.12lg)\n\n", 
		       ibuf1, funTab[k].XtoTab);

	      fprintf (of, "extern struct _tabent %s [];\n", 
			   funTab[k].tableName);

	      fclose (of);
	    }
	 }

#ifndef LINUX_CFG
	/* MS This appears to be a bug. */
	fclose (of);
#endif
	/* Print out Assembly include files */

	for (k=0; k<ARRAY_SIZE(funTab); k++)
	 { if (funTab[k].defined)
	    {
	      sprintf (defFileName, "%s_Adefs.h", funTab[k].prefix);
	      if ((of = fopen (defFileName, "w")) == NULL)
	       { fatalMsg (-1,"cannot open output file %s\n", defFileName);
	       }

	      strcpy (ibuf1, funTab[k].prefix);
	
	      fprintf (of, "#define %s_TBSIZE %d\n", 
		       ibuf1, funTab[k].tsize);
	      fprintf (of, "#define %s_TESIZE %d\n", 
		       ibuf1, sizeof(struct _tabent));
	      fprintf (of, "#define %s_TORIGIN_O %d\n", 
		       ibuf1, funTab[k].origin * sizeof(struct _tabent));
	      fprintf (of, "#define %s_TINDEX1_O %d\n", 
		       ibuf1, funTab[k].I1 * sizeof(struct _tabent));
	      fprintf (of, "#define %s_TINDEX2_O %d\n", 
		       ibuf1, funTab[k].I2 * sizeof(struct _tabent));
	      fprintf (of, "#define %s_TLOWER_D 0d%s\n",
		       ibuf1, pnum (nbuf, funTab[k].X1));
	      fprintf (of, "#define %s_TUPPER_D 0d%s\n", 
		       ibuf1, pnum (nbuf, funTab[k].X2));
	      fprintf (of, "#define %s_TSCALE_D 0d%s\n",
		       ibuf1, pnum (nbuf, funTab[k].XtoTab));
	      fprintf (of, "#define %s_TLOWER_F 0f%s\n",
		       ibuf1, pnum (nbuf, funTab[k].X1));
	      fprintf (of, "#define %s_TUPPER_F 0f%s\n",
		       ibuf1, pnum (nbuf, funTab[k].X2));
	      fprintf (of, "#define %s_TSCALE_F 0f%s\n\n",
		       ibuf1, pnum (nbuf, funTab[k].XtoTab));

	      fclose (of);

	    }
	 }

	/* Print out SED definition files */

	for (k=0; k<ARRAY_SIZE(funTab); k++)
	 { if (funTab[k].defined)
	    {
	      sprintf (defFileName, "%s_Adefs.sed", funTab[k].prefix);
	      if ((of = fopen (defFileName, "w")) == NULL)
	       { fatalMsg (-1,"cannot open output file %s\n", defFileName);
	       }

	      strcpy (ibuf1, funTab[k].prefix);
	
	      fprintf (of, "s/%s_TBSIZE/%d/\n", 
		       ibuf1, funTab[k].tsize);

	      fprintf (of, "s/%s_TESIZE/%d/\n", 
		       ibuf1, sizeof(struct _tabent));
	      fprintf (of, "s/%s_TORIGIN_O/%d/\n", 
		       ibuf1, funTab[k].origin * sizeof(struct _tabent));
	      fprintf (of, "s/%s_TINDEX1_O/%d/\n", 
		       ibuf1, funTab[k].I1 * sizeof(struct _tabent));
	      fprintf (of, "s/%s_TINDEX2_O/%d/\n", 
		       ibuf1, funTab[k].I2 * sizeof(struct _tabent));
	      fprintf (of, "s/%s_TLOWER_D/0d%s/\n",
		       ibuf1, pnum (nbuf, funTab[k].X1));
	      fprintf (of, "s/%s_TUPPER_D/0d%s/\n", 
		       ibuf1, pnum (nbuf, funTab[k].X2));
	      fprintf (of, "s/%s_TSCALE_D/0d%s/\n",
		       ibuf1, pnum (nbuf, funTab[k].XtoTab));
	      fprintf (of, "s/%s_TLOWER_F/0f%s/\n",
		       ibuf1, pnum (nbuf, funTab[k].X1));
	      fprintf (of, "s/%s_TUPPER_F/0f%s/\n",
		       ibuf1, pnum (nbuf, funTab[k].X2));
	      fprintf (of, "s/%s_TSCALE_F/0f%s/\n\n",
		       ibuf1, pnum (nbuf, funTab[k].XtoTab));

	      fclose (of);
	    }
	 }
	return (0);
}
 
upper_case (s1, s2)
char *s1, *s2;
{
	while (*s2 != '\0')
	 { if (islower (*s2))
	    { *s1++ = *s2++ + ('A' - 'a');
	    }
	   else
	    { *s1++ = *s2++;
	    }
	 }
	*s1 = '\0';
}
