/* 
 * This utility configures a NPB to be built for a specific class. 
 * It creates a file "npbparams.h" 
 * in the source directory. This file keeps state information about 
 * which size of benchmark is currently being built (so that nothing
 * if unnecessarily rebuilt) and defines (through PARAMETER statements)
 * the number of nodes and class for which a benchmark is being built. 

 * The utility takes 3 arguments: 
 *       setparams benchmark-name class
 *    benchmark-name is "sp", "bt", etc
 *    class is the size of the benchmark
 * These parameters are checked for the current benchmark. If they
 * are invalid, this program prints a message and aborts. 
 * If the parameters are ok, the current npbsize.h (actually just
 * the first line) is read in. If the new parameters are the same as 
 * the old, nothing is done, but an exit code is returned to force the
 * user to specify (otherwise the make procedure succeeds but builds a
 * binary of the wrong name).  Otherwise the file is rewritten. 
 * Errors write a message (to stdout) and abort. 
 * 
 * This program makes use of two extra benchmark "classes"
 * class "X" means an invalid specification. It is returned if
 * there is an error parsing the config file. 
 * class "U" is an external specification meaning "unknown class"
 * 
 * Unfortunately everything has to be case sensitive. This is
 * because we can always convert lower to upper or v.v. but
 * can't feed this information back to the makefile, so typing
 * make CLASS=a and make CLASS=A will produce different binaries.
 *
 * 
 */

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

/*
 * This is the master version number for this set of 
 * NPB benchmarks. It is in an obscure place so people
 * won't accidentally change it. 
 */

#define VERSION "3.3.1"

/* controls verbose output from setparams */
/* #define VERBOSE */

#define FILENAME "npbparams.h"
#define DESC_LINE "c CLASS = %c\n"
#define DEF_CLASS_LINE     "#define CLASS '%c'\n"
#define FINDENT  "        "
#define CONTINUE "     > "

void get_info(char *argv[], int *typep, char *classp);
void check_info(int type, char class);
void read_info(int type, char *classp);
void write_info(int type, char class);
void write_sp_info(FILE *fp, char class);
void write_bt_info(FILE *fp, char class);
void write_dc_info(FILE *fp, char class);
void write_lu_info(FILE *fp, char class);
void write_mg_info(FILE *fp, char class);
void write_cg_info(FILE *fp, char class);
void write_ft_info(FILE *fp, char class);
void write_ep_info(FILE *fp, char class);
void write_is_info(FILE *fp, char class);
void write_ua_info(FILE *fp, char class);
void write_compiler_info(int type, FILE *fp);
void write_convertdouble_info(int type, FILE *fp);
void check_line(char *line, char *label, char *val);
int  check_include_line(char *line, char *filename);
void put_string(FILE *fp, char *name, char *val);
void put_def_string(FILE *fp, char *name, char *val);
void put_def_variable(FILE *fp, char *name, char *val);
int ilog2(int i);
double power(double base, int i);

enum benchmark_types {SP, BT, LU, MG, FT, IS, EP, CG, UA, DC};

int main(int argc, char *argv[])
{
  int type;
  char class, class_old;
  
  if (argc != 3) {
    printf("Usage: %s benchmark-name class\n", argv[0]);
    exit(1);
  }

  /* Get command line arguments. Make sure they're ok. */
  get_info(argv, &type, &class);
  if (class != 'U') {
#ifdef VERBOSE
    printf("setparams: For benchmark %s: class = %c\n", 
	   argv[1], class); 
#endif
    check_info(type, class);
  }

  /* Get old information. */
  read_info(type, &class_old);
  if (class != 'U') {
    if (class_old != 'X') {
#ifdef VERBOSE
      printf("setparams:     old settings: class = %c\n", 
	     class_old); 
#endif
    }
  } else {
    printf("setparams:\n\
  *********************************************************************\n\
  * You must specify CLASS to build this benchmark                    *\n\
  * For example, to build a class A benchmark, type                   *\n\
  *       make {benchmark-name} CLASS=A                               *\n\
  *********************************************************************\n\n"); 

    if (class_old != 'X') {
#ifdef VERBOSE
      printf("setparams: Previous settings were CLASS=%c \n", class_old); 
#endif
    }
    exit(1); /* exit on class==U */
  }

  /* Write out new information if it's different. */
  if (class != class_old) {
#ifdef VERBOSE
    printf("setparams: Writing %s\n", FILENAME); 
#endif
    write_info(type, class);
  } else {
#ifdef VERBOSE
    printf("setparams: Settings unchanged. %s unmodified\n", FILENAME); 
#endif
  }

  return 0;
}


/*
 *  get_info(): Get parameters from command line 
 */

void get_info(char *argv[], int *typep, char *classp) 
{

  *classp = *argv[2];

  if      (!strcmp(argv[1], "sp") || !strcmp(argv[1], "SP")) *typep = SP;
  else if (!strcmp(argv[1], "bt") || !strcmp(argv[1], "BT")) *typep = BT;
  else if (!strcmp(argv[1], "ft") || !strcmp(argv[1], "FT")) *typep = FT;
  else if (!strcmp(argv[1], "lu") || !strcmp(argv[1], "LU")) *typep = LU;
  else if (!strcmp(argv[1], "mg") || !strcmp(argv[1], "MG")) *typep = MG;
  else if (!strcmp(argv[1], "is") || !strcmp(argv[1], "IS")) *typep = IS;
  else if (!strcmp(argv[1], "ep") || !strcmp(argv[1], "EP")) *typep = EP;
  else if (!strcmp(argv[1], "cg") || !strcmp(argv[1], "CG")) *typep = CG;
  else if (!strcmp(argv[1], "ua") || !strcmp(argv[1], "UA")) *typep = UA;
  else if (!strcmp(argv[1], "dc") || !strcmp(argv[1], "DC")) *typep = DC;
  else {
    printf("setparams: Error: unknown benchmark type %s\n", argv[1]);
    exit(1);
  }
}

/*
 *  check_info(): Make sure command line data is ok for this benchmark 
 */

void check_info(int type, char class) 
{

  /* check class */
  if (class != 'S' && 
      class != 'W' && 
      class != 'A' && 
      class != 'B' && 
      class != 'C' && 
      class != 'D' && 
      class != 'E') {
    printf("setparams: Unknown benchmark class %c\n", class); 
    printf("setparams: Allowed classes are \"S\", \"W\", and \"A\" through \"E\"\n");
    exit(1);
  }

  if (class == 'E' && (type == IS || type == UA || type == DC)) {
    printf("setparams: Benchmark class %c not defined for IS, UA, or DC\n", class);
    exit(1);
  }
  if ((class == 'C' || class == 'D') && type == DC) {
    printf("setparams: Benchmark class %c not defined for DC\n", class);
    exit(1);
  }
}


/* 
 * read_info(): Read previous information from file. 
 *              Not an error if file doesn't exist, because this
 *              may be the first time we're running. 
 *              Assumes the first line of the file is in a special
 *              format that we understand (since we wrote it). 
 */

void read_info(int type, char *classp)
{
  int nread;
  FILE *fp;
  fp = fopen(FILENAME, "r");
  if (fp == NULL) {
#ifdef VERBOSE
    printf("setparams: INFO: configuration file %s does not exist (yet)\n", FILENAME); 
#endif
    goto abort;
  }
  
  /* first line of file contains info (fortran), first two lines (C) */

  switch(type) {
      case SP:
      case BT:
      case FT:
      case MG:
      case LU:
      case EP:
      case CG:
      case UA:
          nread = fscanf(fp, DESC_LINE, classp);
          if (nread != 1) {
            printf("setparams: Error parsing config file %s. Ignoring previous settings\n", FILENAME);
            goto abort;
          }
          break;
      case IS:
      case DC:
          nread = fscanf(fp, DEF_CLASS_LINE, classp);
          if (nread != 1) {
            printf("setparams: Error parsing config file %s. Ignoring previous settings\n", FILENAME);
            goto abort;
          }
          break;
      default:
        /* never should have gotten this far with a bad name */
        printf("setparams: (Internal Error) Benchmark type %d unknown to this program\n", type); 
        exit(1);
  }

  fclose(fp);


  return;

 abort:
  *classp = 'X';
  return;
}


/* 
 * write_info(): Write new information to config file. 
 *               First line is in a special format so we can read
 *               it in again. Then comes a warning. The rest is all
 *               specific to a particular benchmark. 
 */

void write_info(int type, char class) 
{
  FILE *fp;
  fp = fopen(FILENAME, "w");
  if (fp == NULL) {
    printf("setparams: Can't open file %s for writing\n", FILENAME);
    exit(1);
  }

  switch(type) {
      case SP:
      case BT:
      case FT:
      case MG:
      case LU:
      case EP:
      case CG:
      case UA:
          /* Write out the header */
          fprintf(fp, DESC_LINE, class);
          /* Print out a warning so bozos don't mess with the file */
          fprintf(fp, "\
c  \n\
c  \n\
c  This file is generated automatically by the setparams utility.\n\
c  It sets the number of processors and the class of the NPB\n\
c  in this directory. Do not modify it by hand.\n\
c  \n");

          break;
      case IS:
          fprintf(fp, DEF_CLASS_LINE, class);
          fprintf(fp, "\
/*\n\
   This file is generated automatically by the setparams utility.\n\
   It sets the number of processors and the class of the NPB\n\
   in this directory. Do not modify it by hand.   */\n\
   \n");
          break;
      case DC:
          fprintf(fp, DEF_CLASS_LINE, class);
          fprintf(fp, "\
/*\n\
   This file is generated automatically by the setparams utility.\n\
   It sets the number of processors and the class of the NPB\n\
   in this directory. Do not modify it by hand.\n\
   This file provided for backward compatibility.\n\
   It is not used in DC benchmark.   */\n\
   \n");
          break;
      default:
          printf("setparams: (Internal error): Unknown benchmark type %d\n", 
                                                                         type);
          exit(1);
  }

  /* Now do benchmark-specific stuff */
  switch(type) {
  case SP:
    write_sp_info(fp, class);
    break;	      
  case BT:	      
    write_bt_info(fp, class);
    break;
 case DC:
    write_dc_info(fp, class);
    break;	      
  case LU:	      
    write_lu_info(fp, class);
    break;	      
  case MG:	      
    write_mg_info(fp, class);
    break;	      
  case IS:	      
    write_is_info(fp, class);  
    break;	      
  case FT:	      
    write_ft_info(fp, class);
    break;	      
  case EP:	      
    write_ep_info(fp, class);
    break;	      
  case CG:	      
    write_cg_info(fp, class);
    break;
  case UA:	      
    write_ua_info(fp, class);
    break;
  default:
    printf("setparams: (Internal error): Unknown benchmark type %d\n", type);
    exit(1);
  }
  write_convertdouble_info(type, fp);
  write_compiler_info(type, fp);
  fclose(fp);
  return;
}


/* 
 * write_sp_info(): Write SP specific info to config file
 */

void write_sp_info(FILE *fp, char class) 
{
  int problem_size, niter;
  char *dt;
  if      (class == 'S') { problem_size = 12;  dt = "0.015d0";   niter = 100; }
  else if (class == 'W') { problem_size = 36;  dt = "0.0015d0";  niter = 400; }
  else if (class == 'A') { problem_size = 64;  dt = "0.0015d0";  niter = 400; }
  else if (class == 'B') { problem_size = 102; dt = "0.001d0";   niter = 400; }
  else if (class == 'C') { problem_size = 162; dt = "0.00067d0"; niter = 400; }
  else if (class == 'D') { problem_size = 408; dt = "0.00030d0"; niter = 500; }
  else if (class == 'E') { problem_size = 1020; dt = "0.0001d0"; niter = 500; }
  else {
    printf("setparams: Internal error: invalid class %c\n", class);
    exit(1);
  }
  fprintf(fp, "%sinteger problem_size, niter_default\n", FINDENT);
  fprintf(fp, "%sparameter (problem_size=%d, niter_default=%d)\n", 
	       FINDENT, problem_size, niter);
  fprintf(fp, "%sdouble precision dt_default\n", FINDENT);
  fprintf(fp, "%sparameter (dt_default = %s)\n", FINDENT, dt);
}
  
/* 
 * write_bt_info(): Write BT specific info to config file
 */

void write_bt_info(FILE *fp, char class) 
{
  int problem_size, niter;
  char *dt;
  if      (class == 'S') { problem_size = 12;  dt = "0.010d0";   niter = 60; }
  else if (class == 'W') { problem_size = 24;  dt = "0.0008d0";  niter = 200; }
  else if (class == 'A') { problem_size = 64;  dt = "0.0008d0";  niter = 200; }
  else if (class == 'B') { problem_size = 102; dt = "0.0003d0";  niter = 200; }
  else if (class == 'C') { problem_size = 162; dt = "0.0001d0";  niter = 200; }
  else if (class == 'D') { problem_size = 408; dt = "0.00002d0";  niter = 250; }
  else if (class == 'E') { problem_size = 1020; dt = "0.4d-5";    niter = 250; }
  else {
    printf("setparams: Internal error: invalid class %c\n", class);
    exit(1);
  }
  fprintf(fp, "%sinteger problem_size, niter_default\n", FINDENT);
  fprintf(fp, "%sparameter (problem_size=%d, niter_default=%d)\n", 
	       FINDENT, problem_size, niter);
  fprintf(fp, "%sdouble precision dt_default\n", FINDENT);
  fprintf(fp, "%sparameter (dt_default = %s)\n", FINDENT, dt);
}
  
/* 
 * write_dc_info(): Write DC specific info to config file
 */


void write_dc_info(FILE *fp, char class)
{
  long int input_tuples, attrnum;
  if      (class == 'S') { input_tuples = 1000;     attrnum = 5; }
  else if (class == 'W') { input_tuples = 100000;   attrnum = 10; }
  else if (class == 'A') { input_tuples = 1000000;  attrnum = 15; }
  else if (class == 'B') { input_tuples = 10000000; attrnum = 20; }
  else {
    printf("setparams: Internal error: invalid class %c\n", class);
    exit(1);
  }
  fprintf(fp, "long long int input_tuples=%ld, attrnum=%ld;\n",
              input_tuples, attrnum);
}


/* 
 * write_lu_info(): Write LU specific info to config file
 */

void write_lu_info(FILE *fp, char class) 
{
  int isiz1, isiz2, itmax, inorm, problem_size;
  char *dt_default;

  if      (class == 'S') { problem_size = 12;  dt_default = "0.5d0"; itmax = 50; }
  else if (class == 'W') { problem_size = 33;  dt_default = "1.5d-3"; itmax = 300; }
  else if (class == 'A') { problem_size = 64;  dt_default = "2.0d0"; itmax = 250; }
  else if (class == 'B') { problem_size = 102; dt_default = "2.0d0"; itmax = 250; }
  else if (class == 'C') { problem_size = 162; dt_default = "2.0d0"; itmax = 250; }
  else if (class == 'D') { problem_size = 408; dt_default = "1.0d0"; itmax = 300; }
  else if (class == 'E') { problem_size = 1020; dt_default = "0.5d0"; itmax = 300; }
  else {
    printf("setparams: Internal error: invalid class %c\n", class);
    exit(1);
  }
  inorm = itmax;
  isiz1 = problem_size;
  isiz2 = problem_size;
  

  fprintf(fp, "\nc full problem size\n");
  fprintf(fp, "%sinteger isiz1, isiz2, isiz3\n", FINDENT);
  fprintf(fp, "%sparameter (isiz1=%d, isiz2=%d, isiz3=%d)\n", 
	       FINDENT, isiz1, isiz2, problem_size );

  fprintf(fp, "\nc number of iterations and how often to print the norm\n");
  fprintf(fp, "%sinteger itmax_default, inorm_default\n", FINDENT);
  fprintf(fp, "%sparameter (itmax_default=%d, inorm_default=%d)\n", 
	  FINDENT, itmax, inorm);

  fprintf(fp, "%sdouble precision dt_default\n", FINDENT);
  fprintf(fp, "%sparameter (dt_default = %s)\n", FINDENT, dt_default);
  
}

/* 
 * write_mg_info(): Write MG specific info to config file
 */

void write_mg_info(FILE *fp, char class) 
{
  int problem_size, nit, log2_size, lt_default, lm;
  int ndim1, ndim2, ndim3;
  if      (class == 'S') { problem_size = 32; nit = 4; }
/*  else if (class == 'W') { problem_size = 64; nit = 40; }*/
  else if (class == 'W') { problem_size = 128; nit = 4; }
  else if (class == 'A') { problem_size = 256; nit = 4; }
  else if (class == 'B') { problem_size = 256; nit = 20; }
  else if (class == 'C') { problem_size = 512; nit = 20; }
  else if (class == 'D') { problem_size = 1024; nit = 50; }
  else if (class == 'E') { problem_size = 2048; nit = 50; }
  else {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }
  log2_size = ilog2(problem_size);
  /* lt is log of largest total dimension */
  lt_default = log2_size;
  /* log of log of maximum dimension on a node */
  lm = log2_size;
  ndim1 = lm;
  ndim3 = log2_size;
  ndim2 = log2_size;

  fprintf(fp, "%sinteger nx_default, ny_default, nz_default\n", FINDENT);
  fprintf(fp, "%sparameter (nx_default=%d, ny_default=%d, nz_default=%d)\n", 
	  FINDENT, problem_size, problem_size, problem_size);
  fprintf(fp, "%sinteger nit_default, lm, lt_default\n", FINDENT);
  fprintf(fp, "%sparameter (nit_default=%d, lm = %d, lt_default=%d)\n", 
	  FINDENT, nit, lm, lt_default);
  fprintf(fp, "%sinteger debug_default\n", FINDENT);
  fprintf(fp, "%sparameter (debug_default=%d)\n", FINDENT, 0);
  fprintf(fp, "%sinteger ndim1, ndim2, ndim3\n", FINDENT);
  fprintf(fp, "%sparameter (ndim1 = %d, ndim2 = %d, ndim3 = %d)\n", 
	  FINDENT, ndim1, ndim2, ndim3);
  fprintf(fp, "%sinteger%s one, nr, nv, ir\n", 
          FINDENT, (problem_size > 1024)? "*8" : "");
  fprintf(fp, "%sparameter (one=1)\n", FINDENT);
}


/* 
 * write_is_info(): Write IS specific info to config file
 */

void write_is_info(FILE *fp, char class) 
{
  if( class != 'S' &&
      class != 'W' &&
      class != 'A' &&
      class != 'B' &&
      class != 'C' &&
      class != 'D')
  {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }
}


/* 
 * write_cg_info(): Write CG specific info to config file
 */

void write_cg_info(FILE *fp, char class) 
{
  int na,nonzer,niter;
  char *shift,*rcond="1.0d-1";
  char *shiftS="10.",
       *shiftW="12.",
       *shiftA="20.",
       *shiftB="60.",
       *shiftC="110.",
       *shiftD="500.",
       *shiftE="1.5d3";


  if( class == 'S' )
  { na=1400; nonzer=7; niter=15; shift=shiftS; }
  else if( class == 'W' )
  { na=7000; nonzer=8; niter=15; shift=shiftW; }
  else if( class == 'A' )
  { na=14000; nonzer=11; niter=15; shift=shiftA; }
  else if( class == 'B' )
  { na=75000; nonzer=13; niter=75; shift=shiftB; }
  else if( class == 'C' )
  { na=150000; nonzer=15; niter=75; shift=shiftC; }
  else if( class == 'D' )
  { na=1500000; nonzer=21; niter=100; shift=shiftD; }
  else if( class == 'E' )
  { na=9000000; nonzer=26; niter=100; shift=shiftE; }
  else
  {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }
  fprintf( fp, "%sinteger            na, nonzer, niter\n", FINDENT );
  fprintf( fp, "%sdouble precision   shift, rcond\n", FINDENT );
  fprintf( fp, "%sparameter(  na=%d,\n", FINDENT, na );
  fprintf( fp, "%s             nonzer=%d,\n", CONTINUE, nonzer );
  fprintf( fp, "%s             niter=%d,\n", CONTINUE, niter );
  fprintf( fp, "%s             shift=%s,\n", CONTINUE, shift );
  fprintf( fp, "%s             rcond=%s )\n", CONTINUE, rcond );
  
}



/* 
 * write_ua_info(): Write UA specific info to config file
 */

void write_ua_info(FILE *fp, char class) 
{
  int lelt, lmor,refine_max, niter, nmxh, fre;
  char *alpha;

  fre = 5;
  if( class == 'S' )
  { lelt=250;lmor=11600;       refine_max=4;  niter=50;  nmxh=10; alpha="0.040d0"; }
  else if( class == 'W' )
  { lelt=700;lmor=26700;       refine_max=5;  niter=100; nmxh=10; alpha="0.060d0"; }
  else if( class == 'A' )
  { lelt=2400;lmor=92700;      refine_max=6;  niter=200; nmxh=10; alpha="0.076d0"; }
  else if( class == 'B' )
  { lelt=8800;  lmor=334600;   refine_max=7;  niter=200; nmxh=10; alpha="0.076d0"; }
  else if( class == 'C' )
  { lelt=33500; lmor=1262100;  refine_max=8;  niter=200; nmxh=10; alpha="0.067d0"; }
  else if( class == 'D' )
  { lelt=515000;lmor=19500000; refine_max=10; niter=250; nmxh=10; alpha="0.046d0"; }
  else
  {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }
  
  fprintf( fp, "%sinteger          lelt, lmor, refine_max, fre_default\n", FINDENT );
  fprintf( fp, "%sinteger          niter_default, nmxh_default\n", FINDENT );
  fprintf( fp, "%scharacter        class_default\n", FINDENT );
  fprintf( fp, "%sdouble precision alpha_default\n", FINDENT );
  fprintf( fp, "%sparameter(  lelt=%d,\n", FINDENT, lelt );
  fprintf( fp, "%s            lmor=%d,\n", CONTINUE, lmor );
  fprintf( fp, "%s             refine_max=%d,\n", CONTINUE, refine_max );
  fprintf( fp, "%s             fre_default=%d,\n", CONTINUE, fre );
  fprintf( fp, "%s             niter_default=%d,\n", CONTINUE, niter );
  fprintf( fp, "%s             nmxh_default=%d,\n", CONTINUE, nmxh );
  fprintf( fp, "%s             class_default=\"%c\",\n", CONTINUE, class );
  fprintf( fp, "%s             alpha_default=%s )\n", CONTINUE, alpha );
  
}


/* 
 * write_ft_info(): Write FT specific info to config file
 */

void write_ft_info(FILE *fp, char class) 
{
  /* easiest way (given the way the benchmark is written)
   * is to specify log of number of grid points in each
   * direction m1, m2, m3. nt is the number of iterations
   */
  int nx, ny, nz, maxdim, niter;
  if      (class == 'S') { nx = 64; ny = 64; nz = 64; niter = 6;}
  else if (class == 'W') { nx = 128; ny = 128; nz = 32; niter = 6;}
  else if (class == 'A') { nx = 256; ny = 256; nz = 128; niter = 6;}
  else if (class == 'B') { nx = 512; ny = 256; nz = 256; niter =20;}
  else if (class == 'C') { nx = 512; ny = 512; nz = 512; niter =20;}
  else if (class == 'D') { nx = 2048; ny = 1024; nz = 1024; niter =25;}
  else if (class == 'E') { nx = 4096; ny = 2048; nz = 2048; niter =25;}
  else {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }
  maxdim = nx;
  if (ny > maxdim) maxdim = ny;
  if (nz > maxdim) maxdim = nz;
  fprintf(fp, "%sinteger nx, ny, nz, maxdim, niter_default\n", FINDENT);
  fprintf(fp, "%sinteger%s ntotal, nxp, nyp, ntotalp\n", FINDENT,
          (nx > 1024)? "*8" : "");
  fprintf(fp, "%sparameter (nx=%d, ny=%d, nz=%d, maxdim=%d)\n", 
          FINDENT, nx, ny, nz, maxdim);
  fprintf(fp, "%sparameter (niter_default=%d)\n", FINDENT, niter);
  fprintf(fp, "%sparameter (nxp=nx+1, nyp=ny)\n", FINDENT);
  fprintf(fp, "%sparameter (ntotal=nx*nyp*nz)\n", FINDENT);
  fprintf(fp, "%sparameter (ntotalp=nxp*nyp*nz)\n", FINDENT);

}

/*
 * write_ep_info(): Write EP specific info to config file
 */

void write_ep_info(FILE *fp, char class)
{
  /* easiest way (given the way the benchmark is written)
   * is to specify log of number of grid points in each
   * direction m1, m2, m3. nt is the number of iterations
   */
  int m;
  if      (class == 'S') { m = 24; }
  else if (class == 'W') { m = 25; }
  else if (class == 'A') { m = 28; }
  else if (class == 'B') { m = 30; }
  else if (class == 'C') { m = 32; }
  else if (class == 'D') { m = 36; }
  else if (class == 'E') { m = 40; }
  else {
    printf("setparams: Internal error: invalid class type %c\n", class);
    exit(1);
  }

  fprintf(fp, "%scharacter class\n",FINDENT);
  fprintf(fp, "%sparameter (class =\'%c\')\n",
                  FINDENT, class);
  fprintf(fp, "%sinteger m\n", FINDENT);
  fprintf(fp, "%sparameter (m=%d)\n", FINDENT, m);
}


/* 
 * This is a gross hack to allow the benchmarks to 
 * print out how they were compiled. Various other ways
 * of doing this have been tried and they all fail on
 * some machine - due to a broken "make" program, or
 * F77 limitations, of whatever. Hopefully this will
 * always work because it uses very portable C. Unfortunately
 * it relies on parsing the make.def file - YUK. 
 * If your machine doesn't have <string.h> or <ctype.h>, happy hacking!
 * 
 */

#define VERBOSE
#define LL 400
#define DEFFILE "../config/make.def"
#define DEFAULT_MESSAGE "(none)"
FILE *deffile;
void write_compiler_info(int type, FILE *fp)
{
  char line[LL];
  char f77[LL], flink[LL], f_lib[LL], f_inc[LL], fflags[LL], flinkflags[LL];
  char compiletime[LL], randfile[LL];
  char cc[LL], cflags[LL], clink[LL], clinkflags[LL],
       c_lib[LL], c_inc[LL];
  struct tm *tmp;
  time_t t;
  deffile = fopen(DEFFILE, "r");
  if (deffile == NULL) {
    printf("\n\
setparams: File %s doesn't exist. To build the NAS benchmarks\n\
           you need to create is according to the instructions\n\
           in the README in the main directory and comments in \n\
           the file config/make.def.template\n", DEFFILE);
    exit(1);
  }
  strcpy(f77, DEFAULT_MESSAGE);
  strcpy(flink, DEFAULT_MESSAGE);
  strcpy(f_lib, DEFAULT_MESSAGE);
  strcpy(f_inc, DEFAULT_MESSAGE);
  strcpy(fflags, DEFAULT_MESSAGE);
  strcpy(flinkflags, DEFAULT_MESSAGE);
  strcpy(randfile, DEFAULT_MESSAGE);
  strcpy(cc, DEFAULT_MESSAGE);
  strcpy(cflags, DEFAULT_MESSAGE);
  strcpy(clink, DEFAULT_MESSAGE);
  strcpy(clinkflags, DEFAULT_MESSAGE);
  strcpy(c_lib, DEFAULT_MESSAGE);
  strcpy(c_inc, DEFAULT_MESSAGE);

  while (fgets(line, LL, deffile) != NULL) {
    if (*line == '#') continue;
    /* yes, this is inefficient. but it's simple! */
    check_line(line, "F77", f77);
    check_line(line, "FLINK", flink);
    check_line(line, "F_LIB", f_lib);
    check_line(line, "F_INC", f_inc);
    check_line(line, "FFLAGS", fflags);
    check_line(line, "FLINKFLAGS", flinkflags);
    check_line(line, "RAND", randfile);
    check_line(line, "CC", cc);
    check_line(line, "CFLAGS", cflags);
    check_line(line, "CLINK", clink);
    check_line(line, "CLINKFLAGS", clinkflags);
    check_line(line, "C_LIB", c_lib);
    check_line(line, "C_INC", c_inc);
  }

  
  (void) time(&t);
  tmp = localtime(&t);
  (void) strftime(compiletime, (size_t)LL, "%d %b %Y", tmp);


  switch(type) {
      case FT:
      case SP:
      case BT:
      case MG:
      case LU:
      case EP:
      case CG:
      case UA:
          put_string(fp, "compiletime", compiletime);
          put_string(fp, "npbversion", VERSION);
          put_string(fp, "cs1", f77);
          put_string(fp, "cs2", flink);
          put_string(fp, "cs3", f_lib);
          put_string(fp, "cs4", f_inc);
          put_string(fp, "cs5", fflags);
          put_string(fp, "cs6", flinkflags);
	  put_string(fp, "cs7", randfile);
          break;
      case IS:
      case DC:
          put_def_string(fp, "COMPILETIME", compiletime);
          put_def_string(fp, "NPBVERSION", VERSION);
          put_def_string(fp, "CC", cc);
          put_def_string(fp, "CFLAGS", cflags);
          put_def_string(fp, "CLINK", clink);
          put_def_string(fp, "CLINKFLAGS", clinkflags);
          put_def_string(fp, "C_LIB", c_lib);
          put_def_string(fp, "C_INC", c_inc);
          break;
      default:
          printf("setparams: (Internal error): Unknown benchmark type %d\n", 
                                                                         type);
          exit(1);
  }

}

void check_line(char *line, char *label, char *val)
{
  char *original_line;
  int n;
  original_line = line;
  /* compare beginning of line and label */
  while (*label != '\0' && *line == *label) {
    line++; label++; 
  }
  /* if *label is not EOS, we must have had a mismatch */
  if (*label != '\0') return;
  /* if *line is not a space, actual label is longer than test label */
  if (!isspace(*line) && *line != '=') return ; 
  /* skip over white space */
  while (isspace(*line)) line++;
  /* next char should be '=' */
  if (*line != '=') return;
  /* skip over white space */
  while (isspace(*++line));
  /* if EOS, nothing was specified */
  if (*line == '\0') return;
  /* finally we've come to the value */
  strcpy(val, line);
  /* chop off the newline at the end */
  n = strlen(val)-1;
  if (n >= 0 && val[n] == '\n')
    val[n--] = '\0';
  if (n >= 0 && val[n] == '\r')
    val[n--] = '\0';
  /* treat continuation */
  while (val[n] == '\\' && fgets(original_line, LL, deffile)) {
     line = original_line;
     while (isspace(*line)) line++;
     if (isspace(*original_line)) val[n++] = ' ';
     while (*line && *line != '\n' && *line != '\r' && n < LL-1)
       val[n++] = *line++;
     val[n] = '\0';
     n--;
  }
/*  if (val[n] == '\\') {
    printf("\n\
setparams: Error in file make.def. Because of the way in which\n\
           command line arguments are incorporated into the\n\
           executable benchmark, you can't have any continued\n\
           lines in the file make.def, that is, lines ending\n\
           with the character \"\\\". Although it may be ugly, \n\
           you should be able to reformat without continuation\n\
           lines. The offending line is\n\
  %s\n", original_line);
    exit(1);
  } */
}

int check_include_line(char *line, char *filename)
{
  char *include_string = "include";
  /* compare beginning of line and "include" */
  while (*include_string != '\0' && *line == *include_string) {
    line++; include_string++; 
  }
  /* if *include_string is not EOS, we must have had a mismatch */
  if (*include_string != '\0') return(0);
  /* if *line is not a space, first word is not "include" */
  if (!isspace(*line)) return(0); 
  /* skip over white space */
  while (isspace(*++line));
  /* if EOS, nothing was specified */
  if (*line == '\0') return(0);
  /* next keyword should be name of include file in *filename */
  while (*filename != '\0' && *line == *filename) {
    line++; filename++; 
  }  
  if (*filename != '\0' || 
      (*line != ' ' && *line != '\0' && *line !='\n')) return(0);
  else return(1);
}


#define MAXL 46
void put_string(FILE *fp, char *name, char *val)
{
  int len;
  len = strlen(val);
  if (len > MAXL) {
    val[MAXL] = '\0';
    val[MAXL-1] = '.';
    val[MAXL-2] = '.';
    val[MAXL-3] = '.';
    len = MAXL;
  }
  fprintf(fp, "%scharacter %s*%d\n", FINDENT, name, len);
  fprintf(fp, "%sparameter (%s=\'%s\')\n", FINDENT, name, val);
}

/* need to escape quote (") in val */
int fix_string_quote(char *val, char *newval, int maxl)
{
  int len;
  int i, j;
  len = strlen(val);
  i = j = 0;
  while (i < len && j < maxl) {
    if (val[i] == '"')
      newval[j++] = '\\';
    if (j < maxl)
      newval[j++] = val[i++];
  }
  newval[j] = '\0';
  return j;
}

/* NOTE: is the ... stuff necessary in C? */
void put_def_string(FILE *fp, char *name, char *val0)
{
  int len;
  char val[MAXL+3];
  len = fix_string_quote(val0, val, MAXL+2);
  if (len > MAXL) {
    val[MAXL] = '\0';
    val[MAXL-1] = '.';
    val[MAXL-2] = '.';
    val[MAXL-3] = '.';
    len = MAXL;
  }
  fprintf(fp, "#define %s \"%s\"\n", name, val);
}

void put_def_variable(FILE *fp, char *name, char *val)
{
  int len;
  len = strlen(val);
  if (len > MAXL) {
    val[MAXL] = '\0';
    val[MAXL-1] = '.';
    val[MAXL-2] = '.';
    val[MAXL-3] = '.';
    len = MAXL;
  }
  fprintf(fp, "#define %s %s\n", name, val);
}



#if 0

/* this version allows arbitrarily long lines but 
 * some compilers don't like that and they're rarely
 * useful 
 */

#define LINELEN 65
void put_string(FILE *fp, char *name, char *val)
{
  int len, nlines, pos, i;
  char line[100];
  len = strlen(val);
  nlines = len/LINELEN;
  if (nlines*LINELEN < len) nlines++;
  fprintf(fp, "%scharacter*%d %s\n", FINDENT, nlines*LINELEN, name);
  fprintf(fp, "%sparameter (%s = \n", FINDENT, name);
  for (i = 0; i < nlines; i++) {
    pos = i*LINELEN;
    if (i == 0) fprintf(fp, "%s\'", CONTINUE);
    else        fprintf(fp, "%s", CONTINUE);
    /* number should be same as LINELEN */
    fprintf(fp, "%.65s", val+pos);
    if (i == nlines-1) fprintf(fp, "\')\n");
    else             fprintf(fp, "\n");
  }
}

#endif


/* integer log base two. Return error is argument isn't
 * a power of two or is less than or equal to zero 
 */

int ilog2(int i)
{
  int log2;
  int exp2 = 1;
  if (i <= 0) return(-1);

  for (log2 = 0; log2 < 30; log2++) {
    if (exp2 == i) return(log2);
    if (exp2 > i) break;
    exp2 *= 2;
  }
  return(-1);
}


/* Power function. We could use pow from the math library, but then
 * we would have to insist on always linking with the math library, just
 * for this function. Since we only need pow with integer exponents,
 * we'll code it ourselves here.
 */

double power(double base, int i)
{
  double x;

  if (i==0) return (1.0);
  else if (i<0) {
    base = 1.0/base;
    i = -i;
  }
  x = 1.0;
  while (i>0) {
    x *=base;
    i--;
  }
  return (x);
}
    

void write_convertdouble_info(int type, FILE *fp)
{
  switch(type) {
  case SP:
  case BT:
  case LU:
  case FT:
  case MG:
  case EP:
  case CG:
  case UA:
    fprintf(fp, "%slogical  convertdouble\n", FINDENT);
#ifdef CONVERTDOUBLE
    fprintf(fp, "%sparameter (convertdouble = .true.)\n", FINDENT);
#else
    fprintf(fp, "%sparameter (convertdouble = .false.)\n", FINDENT);
#endif
    break;
  }
}
