/*
* Copyright (C) 2001-2008, CompHEP Collaboration
* Copyright (C) 1997, Slava Ilyin
* changes by V.Ilyin for #-mdl, 6 Sept 2000.
* Add Comphep-ROOT interface and 
* parameter dependence for cross sec., Slava Bunichev Mar.4 2007.
* ------------------------------------------------------
* $Id: runVegas.c,v 1.12 2004/05/10 12:48:15 kryukov Exp $
*
* $Log: runVegas.c,v $
* Revision 1.12  2004/05/10 12:48:15  kryukov
* This version is equal to 4.4.1
*
* Revision 1.11.2.1  2004/04/30 15:09:37  sherstnv
* Block q_kin.c has been replaced. I took it from old CompHEP_41.10.
*
* Revision 1.10  2003/07/20 14:34:37  kryukov
* Change width menu. Remove rwidth global vars.
* gwidth=0(fix), 1(overall), 2(running).
*
* Revision 1.9  2003/06/25 18:46:41  kryukov
* *** empty log message ***
*
* Revision 1.8  2003/04/22 09:01:19  kryukov
* Apply indent to improve readability of files
*
* Revision 1.7  2003/04/21 16:11:47  kryukov
* Remove misprint: Gev -> GeV
*
* Revision 1.6  2003/04/20 08:54:39  kryukov
* Remove some misprints
*
* Revision 1.5  2003/04/08 11:18:17  kryukov
* Change algorithm for counting negPoints: if(fabs(ret_val)<computer_eps)...
*/
#include <unistd.h>
#include <stdarg.h>
#include <math.h>

#include "service2/include/chep_limits.h"
#include "service2/include/files.h"
#include "service2/include/tptcmac.h"
#include "service2/include/unix_utils.h"
#include "service2/include/4_vector.h"
#include "chep_crt/include/crt_util.h"

#include "out_ext.h"

#include "cut.h"
#include "q_kin.h"
#include "regul.h"
#include "rw_sess.h"
#include "subproc.h"
#include "vegas.h"
#include "strfun.h"
#include "alphas_menu.h"
#include "histogram.h"
#include "evnt_menu.h"
#include "runVegas.h"
#include "num_serv.h"
#include "roothisto.h"

extern double computer_eps;

int nSess = 1;
double rapidity = 0;

typedef struct
  {
    long ncall0;
    int itmx0;
    int wrtEvnt;
  }
mcintr_;
static mcintr_ mcintr_1 = { 10000, 5, 0 };

long get_ncalls (void) {
  return mcintr_1.ncall0;
}

int get_iters (void) {
  return mcintr_1.itmx0;
}

void set_ncalls (long ncalls) {
  mcintr_1.ncall0 = ncalls;
}

void set_iters (int iteras) {
  mcintr_1.itmx0 = iteras;
}

vegas_integral integral = { 0, 0, 0, 0, 0, 0 };
static vegasGrid *veg_Ptr = NULL;

int
WriteVegasGrid (FILE * f)
{
  if (veg_Ptr)
    {
      int i, j;
      double *x = veg_Ptr->x_grid;
      fprintf (f, " Vegas_grid: dim=%d  size=%d\n", veg_Ptr->ndim,
               veg_Ptr->ndmx);
      for (i = 0; i < veg_Ptr->ndim; i++)
        {
          for (j = 0; j <= veg_Ptr->ndmx; j++)
            fprintf (f, " %.15E", *(x++));
          fprintf (f, "\n");
        }
    }
  else
    {
      fprintf (f, " Vegas_grid: dim=%d  size=%d\n", 0, 0);
    }
  return 0;
}

int
ReadVegasGrid (FILE * f)
{
  int i, j;
  int ndim, ndmx;
  double *x;

  if (veg_Ptr)
    {
      vegas_finish (veg_Ptr);
      veg_Ptr = NULL;
    }
  fscanf (f, " Vegas_grid: dim=%d  size=%d\n", &ndim, &ndmx);
  if (ndim && ndmx)
    {
      veg_Ptr = vegas_init (ndim, ndmx);
      x = veg_Ptr->x_grid;
      for (i = 0; i < ndim; i++) {
        for (j = 0; j <= ndmx; j++) {
          fscanf (f, " %lf", (x++));
        }
      }
    }
  return 0;
}

static int nCall;
static double badPoints;
static double negPoints;
static int hFill = 0;

static double
me2_func (double *x, double wgt)
{
  int ntot = nin_ + nout_;
  double ret_val = 0.;
  double ytilda;
  /* preparation of kinematics for scalar products */
  double factor_0 = mkmom (x, &ytilda);

  nCall++;
  if (factor_0) {
    lorrot (rapidity + ytilda, ntot);
    factor_0 *= calcCutFactor ();
    if (factor_0) {
      int err = 0;
  /* call for 'running strong coupling constant' */
      alf_ (qcd_Scale_chep ());

      ret_val = factor_0 * sqme_ (proces_1.nsub, pvect, &err);
      if (err) {
        badPoints += (ret_val > 0 ? ret_val * wgt : -ret_val * wgt);
      }
      if (fabs (ret_val) < computer_eps) {
        negPoints += ret_val * wgt;
      }
    }
  }

  if (hFill) {
    fillHists (ret_val * wgt);
  }

  return ret_val;
}


static int gui_vegas (FILE * prtfile, int nLine) {
  int i;
  int nln = nLine;
  double sd;
  double avgi;

  if (integral.n_it == 0) {
    integral.s0 = 0;
    integral.s1 = 0;
    integral.s2 = 0;
    integral.nCallTot = 0;
  }

  for (i = 0; i < mcintr_1.itmx0; ++i) {
    char errtxt[100] = "";

    nCall = 0;
    negPoints = 0;
    badPoints = 0;
    hFill = 1;
    if (vegas_int (veg_Ptr, mcintr_1.ncall0, me2_func, &avgi, &sd)) {
      break;
    }
    integral.old = 1;
    negPoints /= nCall;
    badPoints /= nCall;
    integral.nCallTot += nCall;
    scrcolor (FGmain, BGmain);
    printLn (prtfile, &nln, "%3d    %12.4E     %10.2E %8d ", ++integral.n_it, avgi, 100 * sd / ABS (avgi), nCall);
    if (negPoints < 0) {
      sprintf (errtxt, " Negative points %.1G%%;", -100 * negPoints / (avgi - 2 * negPoints));
    }
    if (badPoints) {
      sprintf (errtxt + strlen (errtxt), "Bad Precision %.1G%%;", 100 * badPoints / (avgi - 2 * negPoints));
    }
    if (errtxt[0]) {
      scrcolor (Red, BGmain);
      printLn (prtfile, &nln, "%s", errtxt);
    }
    sd = 1 / (sd * sd);
    integral.s0 += sd;
    integral.s1 += avgi * sd;
    integral.s2 += avgi * avgi * sd;
  }

  if (integral.n_it > 1) {
  double ksi = (integral.s2 - integral.s1 * integral.s1 / integral.s0) / (integral.n_it - 1);
    scrcolor (FGmain, BGmain);
    printLn (prtfile, &nln, " < >   %12.4E     %10.2E %8d %8.1G",
             integral.s1 / integral.s0,
             100 * sqrt (integral.s0) / ABS (integral.s1),
             integral.nCallTot, ksi);
  }
  return nln;
}

static int gui_vegas_new (FILE * prtfile, int nLine) {
  int i;
  int stop;
  int nln = nLine;
  double sd;
  double avgi;

  if (integral.n_it == 0) {
    integral.s0 = 0;
    integral.s1 = 0;
    integral.s2 = 0;
    integral.nCallTot = 0;
  }

  for (i = 0; i < mcintr_1.itmx0; ++i) {
    int nCubs;
    char errtxt[128] = "";

    nCall = 0;
    negPoints = 0;
    badPoints = 0;
    hFill = 1;
    nCubs = generateVegasCubs (veg_Ptr, mcintr_1.ncall0 / 2);
    setStopSymb (0);
/*    clone (vegas_int (veg_Ptr, mcintr_1.ncall0, 1.5, me2_func, &avgi, &sd));*/
    for (;;) {
      int cCub = (int)(nCubs * getCurCub ());
      stop = informline (cCub, nCubs);
      setStopSymb (stop);
      if (1. == getCurCub () || stop) break;
    }
    if (stop) break;
    integral.old = 1;
    negPoints /= nCall;
    badPoints /= nCall;
    integral.nCallTot += nCall;
    scrcolor (FGmain, BGmain);
    printLn (prtfile, &nln, "%3d    %12.4E     %10.2E %8d ", ++integral.n_it, avgi, 100 * sd / ABS (avgi), nCall);
    if (negPoints < 0) {
      sprintf (errtxt, " Negative points %.1G%%;", -100 * negPoints / (avgi - 2 * negPoints));
    }
    if (badPoints) {
      sprintf (errtxt + strlen (errtxt), "Bad Precision %.1G%%;", 100 * badPoints / (avgi - 2 * negPoints));
    }

    if (errtxt[0]) {
      scrcolor (Red, BGmain);
      printLn (prtfile, &nln, "%s", errtxt);
    }
    sd = 1 / (sd * sd);
    integral.s0 += sd;
    integral.s1 += avgi * sd;
    integral.s2 += avgi * avgi * sd;
  }

  if (integral.n_it > 1) {
    double ksi = (integral.s2 - integral.s1 * integral.s1 / integral.s0) / (integral.n_it - 1);
    scrcolor (FGmain, BGmain);
    printLn (prtfile, &nln, " < >   %12.4E     %10.2E %8d %8.1G",
             integral.s1 / integral.s0,
             100 * sqrt (integral.s0) / ABS (integral.s1),
             integral.nCallTot, ksi);
  }

  return nln;
}

int
runVegas (int init)
{
  char mess[64];
  char fname[64];
  FILE *iprt = NULL;
  int mode;
  void *pscr = NULL;
  static int n_Line = 7;
  int ndim = imkmom ();

  if (2 == nin_) {
    strcpy (mess, " Cross section [pb] ");
  } else {
    strcpy (mess, "  Width     [GeV]   ");
  }

/* save current session parameters */
  w_sess__ (NULL);

/* open protocol and resulting files */
  sprintf (fname, "%sprt_%d", outputDir, nSess);
  iprt = fopen (fname, "a");
  if (!ftell (iprt)) {
    fprintf (iprt, "    CompHEP kinematics module \n The session parameters:\n");
    w_sess__ (iprt);
    fprintf (iprt, "===================================\n");
  }

/* init kinematics */
  if (init && veg_Ptr)
    {
      vegas_finish (veg_Ptr);
      veg_Ptr = NULL;
    }
  if (!veg_Ptr)
    veg_Ptr = vegas_init (ndim, MAX_NDMX);

  correctHistList ();

  mcintr_1.ncall0 = 2 * generateVegasCubs (veg_Ptr, mcintr_1.ncall0 / 2.);

/* Main loop */
  if (!integral.old || 7 == n_Line) {
    n_Line = 7;
    scrcolor (Blue, BGmain);
    printLn (iprt, &n_Line, " #IT  %20s Error %%    nCall   chi**2", mess);
  }

  for (;;)
    {
      char strmen[] = "\030"
        " Itmx   =    N2         "
        " nCall  =    N1         "
        " Set  Distributions     "
        " Start integration      "
        " Display Distributions  "
        " Combine ROOT-hist      "
        " Clear statistic        "
        " Clear  grid            "
        " Generate events        ";

      improveStr (strmen, "N1", "%d", mcintr_1.ncall0);
      improveStr (strmen, "N2", "%d", mcintr_1.itmx0);

      menu1 (54, 7, "", strmen, "n_veg_*", &pscr, &mode);
      switch (mode)
        {
        case 0:
          if (iprt) fclose (iprt);
          return 0;

        case 1:
          correctInt (50, 12, "Enter new value ", &mcintr_1.itmx0, 1);
          break;

        case 2:
          correctLong (50, 12, "Enter new value ", &mcintr_1.ncall0, 1);
          mcintr_1.ncall0 = 2 * generateVegasCubs (veg_Ptr, mcintr_1.ncall0 / 2.);
          break;

        case 3:
          editHist ();
          break;

        case 4:
          n_Line = gui_vegas (iprt, n_Line);
          init = 1;
          messanykey (54, 11, "Integration is over");
          break;

        case 5:
          showHist ();
          break;

        case 6:
          combine_root ();
          break;

        case 7:
          integral.s0 = 0;
          integral.s1 = 0;
          integral.s2 = 0;
          integral.n_it = 0;
          integral.nCallTot = 0;
          clearHists ();
          messanykey (54, 13, "Old results for integral\n"
                      "and distributions\nare deleted.\n"
                      "But grid is saved");
          break;

        case 8:
          vegas_finish (veg_Ptr);
          veg_Ptr = vegas_init (ndim, MAX_NDMX);
          init = 1;
          messanykey (57, 11, "OK");
          break;

        case 9:
          {
            sprintf (fname, "%sevents_%d.txt", outputDir, nSess);
            hFill = 0;
            menu_EventGenerator (veg_Ptr, me2_func, fname, iprt, init);
            init = 0;
          }
          break;
        }
    }
}
