
/* Integer Multi-Dimensional Interpolation */

/*
 * Copyright 2000 - 2007 Graeme W. Gill
 * All rights reserved.
 *
 * This material is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */

/*
 * Top level kernel code generator
 *
 * This module is invoked from the make system,
 * and generates all the versions and configurations of 
 * the IMDI kernel code. It includes all the generated
 * files in imdi_k.h, which also contains a table
 * so that the run time code knows what kernels
 * are available.
*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
#include "copyright.h"
#include "aconfig.h"

#include "imdi.h"
#include "imdi_tab.h"

#ifndef MAXNAMEL
# define MAXNAMEL 512	/* Maximum command line filename lengths */
#endif

#undef VERBOSE
#undef TEST1		/* Generate one test case */

/*
	Ideal grid resolutions for 8 bit precision calculations.
	See imdi_gen.c for a more detailed list.

	Grid	Jumps	
	4		0
	6		0
	16		0
	18		0
	52		0
	86		0
	256		0

	3		1
	5		1
	9		1
	17		1
	33		1
	65		1
	128		1
	129		1
	255		1

 */

/* The following structure initialisations define what kernel routines should be built */
static
gendesc descs[] = {
#ifdef TEST1
	{
		{ 3, 0 },					/* * Input dimension combinations */
		{ 33, 0 },	 				/* + Interpolation table resolutions */
		{ 8, 0 },	 				/* + Min Simplex table resolutions */
		{ 4, 0 },					/* * Output dimension combinations */
		{OOPT(oopts_check,3), oopts_none},	/* + Output channel options */
		{pixint8, 0 },				/* * Input pixel representation */
		{prec_p16, 0},				/* + Internal precision */
		{pixint8, 0},				/* + Output pixel representation */
		{opts_sort_splx, opts_end}		/* * Direction & stride combinations */
//		{opts_splx_sort, opts_end}		/* * Direction & stride combinations */
	}
#else	/* !TEST1 */
	/* A reasonably full set of combinations */
								/* * means multiplies combination */
								/* + means lockstep with previous line */
	{
		{ 1,    3,  4,  5,  6,  7,  8, 9, 10, 0 },	/* * Input dimension combinations */
		{ 256, 33, 18, 16, 12,  8,  7, 6, 5,  0 }, 	/* + Min Interpolation table resolutions */
		{ 1,    8, 17,  1,  1,  1,  1, 1, 1,  0 }, 	/* + Min Simplex table resolutions */

		{ 1, 3, 4, 5, 6, 7, 8, 9, 10, 0 },				/* * Output dimension combinations */
		{oopts_none, oopts_none, oopts_none, oopts_none, oopts_none, oopts_none,
		 oopts_none, oopts_none, oopts_none, oopts_none, oopts_none, oopts_none},
														/* + Output channel options */

		{pixint8, pixint16, pixint8,  pixint16, pixint16, 0 },	/* * Input pixel representation */
		{prec_p8, prec_p8,  prec_p8,  prec_p16, prec_p16, 0 },	/* + Internal precision */
		{pixint8, pixint8,  pixint16, pixint16, pixint16, 0 },	/* + Output pixel representation */

		{
		 opts_splx_sort,						/* (both, but default to simple alg, no stride) */
		 opts_istride | opts_ostride,			/* + (Sort only with stride) */
		 opts_end }								/* * Direction & stride combinations */
	}
#endif	/* !TEST1 */
};

void set_architecture(mach_arch *ar, int use64);

struct _knamestr {
	char name[100];
	char desc[100];
	struct _knamestr *next;
}; typedef struct _knamestr knamestr;

knamestr *
new_knamestr(char *name, char *desc) {
	knamestr *kn;
	
	if ((kn = (knamestr *)malloc(sizeof(knamestr))) == NULL) {
		fprintf(stderr,"new_knamestr malloc failed\n");
		exit(-1);
	}
	strcpy(kn->name, name);
	strcpy(kn->desc, desc);
	kn->next = NULL;
	return kn;
}

void usage(void) {
	fprintf(stderr,"Make imdi kernel code Version %s\n",ARGYLL_VERSION_STR);
	fprintf(stderr,"usage: imdi_make [-i]\n");
	fprintf(stderr," -d dir        Directory to create them in (default .)\n");
	fprintf(stderr," -i            Individial Files\n");
	fprintf(stderr," -f            Force 64 bit\n");
	exit(1);
}

int
main(int argc, char *argv[]) {
	int fa,nfa;				/* argument we're looking at */
	int indiv = 0;			/* Individual files */
	int rv;
	int dn, tnd;
	genspec gs, ogs;
	tabspec ts, ots;
	mach_arch ar;
	int ix = 1;				/* kernel index */
	knamestr *list = NULL, *lp = NULL;
#if defined(ALLOW64) && defined(USE64)
	int use64 = 1;
#else
	int use64 = 0;
#endif
	char dirname[MAXNAMEL+1+1] = "";   /* Output directory name */
	char temp[MAXNAMEL+100+1];		/* Buffer to compose filenames in */
	FILE *kcode = NULL;	/* Kernel routine code file */
	FILE *kheader;		/* Kernel routine header file */

	/* Zero out the gen and tabspecs, to give diff a place to start */
	memset((void *)&ogs, 0, sizeof(genspec));
	memset((void *)&gs, 0, sizeof(genspec));
	memset((void *)&ots, 0, sizeof(tabspec));
	memset((void *)&ts, 0, sizeof(tabspec));

	/* Process the arguments */
	for(fa = 1;fa < argc;fa++) {
		nfa = fa;					/* skip to nfa if next argument is used */
		if (argv[fa][0] == '-')	{	/* Look for any flags */
			char *na = NULL;		/* next argument after flag, null if none */

			if (argv[fa][2] != '\000')
				na = &argv[fa][2];		/* next is directly after flag */
			else {
				if ((fa+1) < argc) {
					if (argv[fa+1][0] != '-') {
						nfa = fa + 1;
						na = argv[nfa];		/* next is seperate non-flag argument */
					}
				}
			}

			if (argv[fa][1] == '?') {
				usage();

			}
			/* Destination directory */
			else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
				int len;
				fa = nfa;
				if (na == NULL) usage();
				strncpy(dirname,na,MAXNAMEL); dirname[MAXNAMEL] = '\000';
				len = strlen(dirname);
				if (len > 0) {
					if (dirname[len-1] != '/' && dirname[len-1] != '\\')
						strcat(dirname, "/");
				}
			}
			/* Individual files */
			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
				indiv = 1;
			}
			/* Force 64 bit */
			else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') {
#ifdef ALLOW64
				use64 = 1;
#else
				fprintf(stderr,"ALLOW64 bits is undefined\n");
				usage();
#endif
			}
			else { 
				usage();
			}
		} else
			break;
	}

	set_architecture(&ar, use64);

	/* Open the file for kernel routine declaration header */
	sprintf(temp, "%simdi_k.h",dirname);
	if ((kheader = fopen(temp, "w")) == NULL) {
		fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp);
		exit(-1);
	}

	if (!indiv) {
		sprintf(temp, "%simdi_k.c",dirname);
		if ((kcode = fopen(temp, "w")) == NULL) {
			fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp);
			exit(-1);
		}
	}

	tnd = sizeof(descs)/sizeof(gendesc);	/* Total number of descriptions */
#ifdef VERBOSE
	printf("Number of descriptions = %d\n",tnd);
#endif /* VERBOSE */

	fprintf(kheader,"/* Integer Multi-Dimensional Interpolation */\n");
	fprintf(kheader,"/* Declarations for all the generated kernel functions */\n");
	fprintf(kheader,"/* This file is generated by imdi_make */\n\n");
	fprintf(kheader,"/* Copyright 2000 - 2007 Graeme W. Gill */\n");
	fprintf(kheader,"/* All rights reserved. */\n");
	fprintf(kheader,"/* This material is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- */\n");
	fprintf(kheader,"/* see the License.txt file for licensing details.*/\n");

	fprintf(kheader,"\n");

	/* For all the descriptions */
	for (dn = 0; dn < tnd; dn++) {
		int cb, ncb;

		/* Do all combinations for that description */
		for (cb = 0, ncb = 1; cb < ncb; cb++) {
			int nalg, alg;
			char ofname[100];

			/* Compute generate spec. and number of combinations */
			ncb = set_genspec(&gs, &descs[dn], cb, &ar);

			if (indiv) {
				sprintf(temp, "%s%s.c",dirname,ofname);
				if ((kcode = fopen(temp, "w")) == NULL) {
					fprintf(stderr,"imdi_make: unable to open file '%s'\n",temp);
					exit(-1);
				}
			}

			nalg = 2;		/* By default generate just sort algorithm */
			alg = 1;

			if ((gs.opt & opts_splx_sort)
			 || (gs.opt & opts_sort_splx)) {
				alg = 0;	/* Generate both simplex and sort algorithms */			
			}
			if (gs.opt & opts_splx) {
				nalg = 1;	/* Generate just simplex algorithm */
				alg = 0;
			}

			for (; alg < nalg; alg++) {

				if (alg == 0)
					gs.opt |=  opts_splx;
				else
					gs.opt &=  ~opts_splx;

				/* Generate it */
				rv = gen_c_kernel(&gs, &ts, &ar, kcode, ix, &ogs, &ots);
				if (rv != 0 && rv != 1) {
					fprintf(stderr,"imdi_make: gen_c_kernel returned a err %d\n",rv);
					exit(-1);
				}

				/* Add the name to the list */
				if (list == NULL)
					lp = list = new_knamestr(gs.kname, gs.kdesc);
				else {
					lp->next = new_knamestr(gs.kname, gs.kdesc);
					lp = lp->next;
				}
				if (indiv) {
					if (fclose(kcode) != 0) {
						fprintf(stderr,"imdi_make: unable to close file '%s'\n",ofname);
						exit(-1);
					}
				}
				ogs = gs;	/* Structure copy */
				ots = ts;
				ix++;

				if (rv == 0)
					break;		/* there was only sort available */
			}
		}
	}

	/* Include the kernel functions in the header file */
	if (indiv) {
		for(lp = list; lp != NULL; lp = lp->next) {
			fprintf(kheader,"#include \"%s_%s.c\"\n",lp->name,lp->desc);
		}
	} else {
		fprintf(kheader,"#include \"imdi_k.c\"	/* All the kernel code */\n");
	}
	fprintf(kheader,"\n");

	/* Output function table */
	
	fprintf(kheader,
		"struct {\n"
		"	void (*interp)(imdi *s, void **outp, int ostride, void **inp, int  istride, unsigned int npix);\n"
		"	void (*gentab)(genspec *g, tabspec *t);\n"
		"} ktable[%d] = {\n",ix-1);

	for(lp = list; lp != NULL; lp = lp->next) {
		fprintf(kheader,"\t{ %s, %s_gentab }%s\n", lp->name, lp->name, 
		lp->next != NULL ? "," : "");
	}
	fprintf(kheader,"};\n");
	fprintf(kheader,"\n");
	fprintf(kheader,"int no_kfuncs = %d;\n",ix-1);
	fprintf(kheader,"\n");

	if (!indiv) {
		if (fclose(kcode) != 0) {
			fprintf(stderr,"imdi_make: unable to close file 'imdi_k.c'\n");
			exit(-1);
		}
	}

	if (fclose(kheader) != 0) {
		fprintf(stderr,"imdi_make: unable to close file 'imdi_k.h'\n");
		exit(-1);
	}

	/* Free the kname list */
	for(lp = list; lp != NULL;) {
		char *p = (char *)lp;
		lp = lp->next;
		free(p);
	}

	return 0;
}


/* - - - - - - - - - - - - - - - - - - - - - - - */
/* Initialse the architecture structure properly. */
/* We're doing this purely at run time, on the assumption */
/* that the target machine is the one we're running on. */
/* We would have to do this differently in a cross development */
/* environment. */
void
set_architecture(
mach_arch *ar,
int use64
) {
	unsigned long etest = 0xff;
	char *machtype;		/* Environment value */

	if (*((unsigned char *)&etest) == 0xff) {
		ar->bigend = 0;		/* Little endian */
	} else {
		ar->bigend = 1;		/* Big endian endian */
	}

	machtype = getenv("MACHTYPE");

	/* Unfortunetaly many environments don't export MACHTYPE :-( */
	/* so we implement a fall back */
	if (machtype == NULL) {
#ifdef __ppc__
		machtype = "powerpc";
#endif
	}

	if (machtype != NULL && strcmp(machtype, "powerpc") == 0) {
	
		/* Section tunable for PowerPC */

		ar->uwa    = 0;		/* Use wide memory access */
		ar->shfm   = 0;		/* Use shifts to mask values */
		ar->oscale = 8;		/* Has scaled indexing up to * 8 */
		ar->smmul  = 0;		/* Doesn't have fast small multiply for index scaling */
		if (use64) {
			ar->nords  = 4;		/* Number of ord types */
			ar->nints  = 4;		/* Number of int types */
		} else {
			ar->nords  = 3;		/* Number of ord types */
			ar->nints  = 3;		/* Number of int types */
		}
		ar->natord = 2;		/* Most natural type (assume unsigned int) */
		ar->natint = 2;		/* Most natural type (assume int) */

		ar->pbits = sizeof(void *) * 8;		/* Number of bits in a pointer */

		ar->ords[0].bits = 8 * sizeof(unsigned char);
		ar->ords[0].name = "unsigned char";
		ar->ords[0].align = 1;

		ar->ords[1].bits = 8 * sizeof(unsigned short);
		ar->ords[1].name = "unsigned short";
		ar->ords[1].align = 1;

		ar->ords[2].bits = 8 * sizeof(unsigned int);
		ar->ords[2].name = "unsigned int";
		ar->ords[2].align = 1;

#ifdef ALLOW64
		ar->ords[3].bits = 8 * sizeof(unsigned longlong);
		ar->ords[3].name = "unsigned " str_longlong ;
		ar->ords[3].align = 0;
#endif /* ALLOW64 */

		ar->ints[0].bits = 8 * sizeof(signed char);
		ar->ints[0].name = "signed char";
		ar->ints[0].align = 1;

		ar->ints[1].bits = 8 * sizeof(short);
		ar->ints[1].name = "short";
		ar->ints[1].align = 1;

		ar->ints[2].bits = 8 * sizeof(int);
		ar->ints[2].name = "int";
		ar->ints[2].align = 1;

#ifdef ALLOW64
		ar->ints[3].bits = 8 * sizeof(longlong);
		ar->ints[3].name = str_longlong ;
		ar->ints[3].align = 0;
#endif /* ALLOW64 */

	} else {

		/* Currently assume x86 type */

		ar->uwa    = 0;		/* Use wide memory access */
		ar->shfm   = 0;		/* Use shifts to mask values */
		ar->oscale = 8;		/* Has scaled indexing up to * 8 */
		ar->smmul  = 0;		/* Doesn't have fast small multiply for index scaling */
		if (use64) {
			ar->nords  = 4;		/* Number of ord types */
			ar->nints  = 4;		/* Number of int types */
		} else {
			ar->nords  = 3;		/* Number of ord types */
			ar->nints  = 3;		/* Number of int types */
		}
		ar->natord = 2;		/* Most natural type (assume unsigned int) */
		ar->natint = 2;		/* Most natural type (assume int) */

		ar->pbits = sizeof(void *) * 8;		/* Number of bits in a pointer */

		ar->ords[0].bits = 8 * sizeof(unsigned char);
		ar->ords[0].name = "unsigned char";
		ar->ords[0].align = 1;

		ar->ords[1].bits = 8 * sizeof(unsigned short);
		ar->ords[1].name = "unsigned short";
		ar->ords[1].align = 1;

		ar->ords[2].bits = 8 * sizeof(unsigned int);
		ar->ords[2].name = "unsigned int";
		ar->ords[2].align = 1;

#ifdef ALLOW64
		ar->ords[3].bits = 8 * sizeof(unsigned longlong);
		ar->ords[3].name = "unsigned " str_longlong ;
		ar->ords[3].align = 0;
#endif /* ALLOW64 */

		ar->ints[0].bits = 8 * sizeof(signed char);
		ar->ints[0].name = "signed char";
		ar->ints[0].align = 1;

		ar->ints[1].bits = 8 * sizeof(short);
		ar->ints[1].name = "short";
		ar->ints[1].align = 1;

		ar->ints[2].bits = 8 * sizeof(int);
		ar->ints[2].name = "int";
		ar->ints[2].align = 1;

#ifdef ALLOW64
		ar->ints[3].bits = 8 * sizeof(longlong);
		ar->ints[3].name = str_longlong ;
		ar->ints[3].align = 0;
#endif /* ALLOW64 */
	}
}


