/*
 *	File: binomint.c
 *
 *      (C) IWTS
 *          KU Nijmegen
 *          The Netherlands
 *
 *      Author: R. Harald Baayen
 *
 *      History:
 *
 *      - feb 1997, version 1.0
 *      - march 1999, version 1.1: full spectrum at interpolated rank for
 *                                 -s option number of rank; result in
 *                                 .fis file
 *
 *      Description:
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "lex_cons.h"


/* EXTERN FUNCTIONS */

/* functions for numerical procedures */

extern double	gammln ();
extern double	nchoosek ();
extern int	find ();
extern double	expV ();
extern double	expVm ();
extern double   factln();


/* argument reading, file manipulation, and help function */

extern int	leesgetal ();
extern void	change_extension ();
extern void	help ();


/* GLOBAL VARIABLES */

double    N,                         /* tokens */
          V,                         /* types */
          m[MAXM3+2],                /* ranks m */
          Vm[MAXM3+2],               /* V(m,N) */
          CHUNKS[MAXCHUNKS3],        /* the chunk sizes */
          chunksize, remainDer;      /* chunk variables */

int       token, type,               /* dummy variables for scanf */
          lines,                     /* index for number of lines in spectrum file */
          verbose,                   /* Boolean for extra print statements */
          mmax,                      /* number of ranks m = 1, 2, ..., mmax required */
          indx,                      /* index for the arrays m[] and Vm[] */
          i, j, k,                   /* index variable */
          intmax,                    /* number of intervals to calculate */
          nchunks,                   /* number of chunks */
          header,                    /* Boolean for presence of header */
          r,                         /* index for ranks m */
          fis, fischunk,           
          aantal;

char      new_name[MAXWORDLENGTH],   /* variables for extension handling    */
          base_name[MAXWORDLENGTH],
          woord[MAXWORDLENGTH],
          *s;


FILE      *fpspectrum,               /* fpspectrum: the input file */
          *fpfis,                    /* full interpolated spectrum */
          *fpbinom;


/* MAIN () */

int main (argc, argv)
int	argc;
char	*argv[];

{
    /* DEFAULTS */

    verbose = 0;               /* default: no extra print statements */
    mmax = 5;                  /* show m=1..3 */
    nchunks = DEF_CHUNKS;      /* use DEF_CHUNKS chunks */
    header = 1;                /* spectrum file has header */
    fis = 0; fischunk = 0;

    while ((--argc > 0) && ((*++argv)[0] == '-')) {
       for ( s = argv[0]+1; *s != '\0'; s++) {
        switch (*s) {
           case 'h':
            help();
            break;
           case 'H':      /* input files without headers! */
            header = 0;
            break;
           case 'x':
            fischunk = leesgetal (s, &aantal);
            for (; aantal > 0; aantal--){
                  s++;
            }
            break;
           case 's':
            fis = leesgetal (s, &aantal);
            for (; aantal > 0; aantal--){
                  s++;
            }
            break;
           case 'k':
            nchunks = leesgetal (s, &aantal);
            for (; aantal > 0; aantal--){
                  s++;
            }
            break;
           case 'm':
            mmax = leesgetal (s, &aantal);
            for (; aantal > 0; aantal--){
                  s++;
            }
            break;
           case 'v':
            verbose = 1;
            break;
           default:
            fprintf(stderr, "binomint: illegal option %c\n", *s);
            argc = 0;
            exit(1);
            break;
        }
       }
    }

    /* FILE HANDLING */
 
    if (argc == 0) {
      help ();
    }

    if (((fis==0) && (fischunk !=0))||((fis!=0)&&(fischunk==0))) {
      fprintf(stderr, "please specify both the s and the x option\n");
      exit(1);
    }
   

    /* open input spectrum, should have .spect extension */

    if ((fpspectrum = fopen(*argv, "r")) == NULL) {
        fprintf(stderr, "binomint: can't open spectrum file %s\n", *argv);
        exit(1);
    }
  
    /* file name handling output files */

    strncpy(base_name, *argv, strlen(*argv) - 4);

    change_extension (base_name, new_name, ".bin");
    if ((fpbinom = fopen(new_name, "w")) == NULL){
       fprintf(stderr, "binomint: can't open output file %s\n", new_name);
       exit(1);
    }

    if (fis > 0) {
       change_extension (base_name, new_name, ".fis");
       if ((fpfis = fopen(new_name, "w")) == NULL){
          fprintf(stderr, "binomint: can't open output file %s\n", new_name);
          exit(1);
       }
    }

    /* load spectrum file */

    if (header){
      fscanf(fpspectrum, "%s ", woord);  /* m */
      fscanf(fpspectrum, "%s ", woord);  /* Vm */
    }

    lines = 0;
    while (fscanf(fpspectrum, "%d %d", &token, &type) != EOF)  {
        lines++;
        m[lines] = (double) token;           /* m */
        Vm[lines] = (double) type;           /* V(m,N) */
        N = N + (double) (type * token);     /* N */
        V = V + (double) type;               /* V */
    }
    if (fis > m[lines]) fis = m[lines];

    fprintf(stderr, "binomint: loaded spectrum, N = %d, V = %d\n", 
            (int) N, (int) V);
    if (lines > MAXM3) {
        fprintf (stderr, 
          "binomint: number of ranks in input exceeds array bounds\n");
        exit(1);
    }
    if (mmax > m[lines]) {
        mmax = m[lines];
        fprintf(stderr, "m has been reset to maximum rank (%d)\n", (int) m[lines]);
    }

    chunksize = floor(N/(nchunks*1.0));
    remainDer = N - ((nchunks*1.0) * chunksize);
    for (k = 1; k <= nchunks; k++)   CHUNKS[k] = chunksize;
    for (k = 1; k <= remainDer; k++) CHUNKS[k]++;
    for (k = 2; k <= nchunks; k++)   CHUNKS[k] += CHUNKS[k-1];


    if (fis > 0) {
        fprintf(fpfis, "         m         EVm         k\n");
        for (r = 1; r <= fis; r++) {
            indx = find (r, 0);
            fprintf(fpfis, "%10d %14.6f %10d\n", \
                    r, expVm(CHUNKS[fischunk], indx, (double) r), fischunk); 
        }
        fclose(fpfis);
    }

    fprintf(fpbinom, "         N         EV  ");
    for (k = 1; k <= mmax; k++) {
        fprintf(fpbinom, "      EV%d ", k);
    }
    fprintf(fpbinom, "\n");

    for (k = 1; k <= nchunks; k++) {
        fprintf(fpbinom, "%10.2f %10.2f  ", CHUNKS[k], expV(CHUNKS[k])  );
        for (r = 1; r <= mmax; r++) {
            indx = find (r,0);
            fprintf(fpbinom, " %8.2f ", expVm(CHUNKS[k], indx, (double) r)); 
        }
        fprintf(fpbinom, "\n");
    }

    return (0);
}


double expVm (M, ind, rank)
double M, rank;
int ind;
{
  int j;
  double sum, p, x;
  extern double factln();
   
  /* if a rank does not exist, ind will be greater than lines;
     
  */
  if (M != N){
   sum = NULL_F;
   p = M/N;
   for (j = ind; (j <= lines); j++) {
    x = log(Vm[j])+factln((int)m[j])-factln((int)(m[j]-rank))-factln((int)rank)+\
         rank*log(p) + (m[j]-rank)*log(1-p);
    sum += exp(x);
    /*
    sum += Vm[j]*nchoosek(m[j],rank)*exp(rank*log(p))*exp((m[j]-rank)*log(1-p));
    */
   }
   return(sum);
  }
  else{
      return(Vm[find ((int) rank, 1)]);
  }
}


double expV (M)
double M;
{
  int r;
  double sum, p;

  if (M != N){
    p = M/N;
    sum = NULL_F;
    for (r = 1; r <= lines; r++) 
       sum += Vm[r] * exp(m[r] * log(1.0 - p));
    return(V - sum);
  }
  else return(V);
}


int find (k, final)
int	k, final;

{
  int		r;

  for (r = 1; m[r] <= k; r++) ;
  if (m[r-1] == k) {
    /* start summation with the required rank */
    return (r - 1);
  }
  else {
    if (final==0) {
        /* start summation with the next available rank */
        return(r);
    } else {
      return (MAXM3 + 1);  /* for which Vm = 0 */
    }
  }
}


void help ()
{
  fprintf (stderr, "binomint text.spc\n");
  fprintf (stderr, "OPTIONS:\n");
  fprintf (stderr, "     -h: display help\n");
  fprintf (stderr, "     -m: number of interpolated ranks (default: 5)\n");
  fprintf (stderr, "     -k: number of chunks for interpolation (default: 20)\n");
  fprintf (stderr, "     -H: input files lack header (default: with header)\n");
  fprintf (stderr, "     -s: number of ranks to be calculated at chunk -x\n");
  fprintf (stderr, "     -x: chunk for which to calculate -s ranks\n");
  fprintf (stderr, "INPUT:\n");
  fprintf (stderr, "     text.spc: (m Vm)\n");
  fprintf (stderr, "OUTPUT:\n");
  fprintf (stderr, "     text.bin: (N EV EV1 EV2 ... EVm)\n");
  exit (1);
}
