/* 
  * Copyright (C) 1999, Alexander Pukhov 
  * Copyright (C) 2001-2003, CompHEP Collaboration
  *----------------------------------------------------
  * $Id: pdf.c,v 1.5 2003/07/01 16:30:33 kryukov Exp $
  *
  * $Log: pdf.c,v $
  * Revision 1.5  2003/07/01 16:30:33  kryukov
  * Now, first line is an active in pdf menu
  *
*/
#include "pdf.h"

static double lambda = 0.200;

void 
delPdfList (pdfList * list)
{
  while (list)
    {
      pdfList *next = list->next;;
      free (list->pathfile);
      free (list->file);
      free (list->name);
      free (list);
      list = next;
    }
}


void 
makePdfList (char *pathfile, char *file, char *parton, pdfList ** list)
{
  char s[100];
  char dName[100];
  int pos;
  FILE *f = fopen (pathfile, "r");
  if (!f)
    return;

  while (fscanf (f, "%s", s) == 1)
    {
      if (s[0] == '#')
	{
	  if (strcmp (s + 1, "distribution"))
	    {
	      fclose (f);
	      return;
	    }
	  fscanf (f, " \"%[^\"]%*c", dName);
	  pos = 1;
	}
      else
	{
	  char *p;
	  if (s[0] == '(')
	    while (s[strlen (s) - 1] != ')')
	      fscanf (f, "%s", s + strlen (s));

	  p = strstr (s, parton);
	  if (p && (p == s || p[-1] == '(' || p[-1] == ','))
	    {
	      p += strlen (parton);
	      if (p[0] == 0 || p[0] == ')' || p[0] == ',')
		{
		  pdfList *new = malloc (sizeof (pdfList));
		  new->name = malloc (strlen (dName) + 1);
		  strcpy (new->name, dName);
		  new->pathfile = malloc (strlen (pathfile) + 1);
		  strcpy (new->pathfile, pathfile);
		  new->file = malloc (strlen (file) + 1);
		  strcpy (new->file, file);
		  new->position = pos;
		  new->next = *list;
		  *list = new;
		}
	    }
	  pos++;
	}
    }
  fclose (f);
  return;
}

void 
freePdfData (pdfStr * data)
{
  if (data->x_grid);
  data->x_grid = NULL;
  if (data->q_grid);
  data->q_grid = NULL;
  if (data->alpha);
  data->alpha = NULL;
  if (data->strfun);
  data->strfun = NULL;
  if (data->filename);
  data->filename = NULL;
}

int 
getPdfData (char *pathfile, char *file, int n_parton, pdfStr * data)
{
  char pattern[20];
  char buff[20];
  char c;
  int nx = 0, nq = 0, i;
  FILE *f = fopen (pathfile, "r");
  int xMinOn = 0, xMaxOn = 0, qMinOn = 0, qMaxOn = 0;

  data->nq = 0;
  data->nx = 0;
  data->number = n_parton;
  data->x_grid = NULL;
  data->q_grid = NULL;
  data->alpha = NULL;
  data->strfun = NULL;
  data->mass = 1;
  data->lin = 0;
  data->filename = NULL;

  if (!f)
    return -1;

  i = strlen (file);
  data->filename = malloc (i * sizeof (char));
  strcpy (data->filename, file);

  sprintf (pattern, "%d-parton", n_parton);

  while (1 == fscanf (f, "%c", &c))
    {
      if (c == '#')
	{
	  double qq;
	  int i;

	  fscanf (f, "%s", buff);
	  if (!strcmp (buff, "Mass"))
	    {
	      if (1 != fscanf (f, "%lf", &data->mass))
		goto errexit;
	    }
	  else if (!strcmp (buff, "Lambda"))
	     while (fscanf (f, "%lf", &lambda));
	  else if (!strcmp (buff, "Q_grid"))

	    {
	      long fpos = ftell (f);

	      if (data->q_grid || data->strfun)
		goto errexit;
	      while (fscanf (f, "%lf", &qq))
		nq++;
	      if (nq < 3)
		goto errexit;
	      data->nq = nq;
	      data->q_grid = malloc (nq * sizeof (double));
	      fseek (f, fpos, SEEK_SET);
	      for (i = 0; i < nq; i++)
		fscanf (f, "%lf", data->q_grid + i);
	      if (data->q_grid[0] <= 0)
		goto errexit;
	      data->q_grid[0] = log (data->q_grid[0]);
	      for (i = 1; i < nq; i++)
		if (data->q_grid[i - 1] >= data->q_grid[i])
		  goto errexit;
		else
		  data->q_grid[i] = log (data->q_grid[i]);
	    }
	  else if (!strcmp (buff, "lExtrapolation"))
	    data->lin = 1;
	  else if (!strcmp (buff, "X_grid"))
	    {
	      long fpos = ftell (f);

	      if (data->x_grid)
		goto errexit;
	      while (fscanf (f, "%lf", &qq))
		nx++;
	      data->nx = nx;
	      if (nx < 3)
		goto errexit;
	      data->x_grid = malloc (nx * sizeof (double));
	      fseek (f, fpos, SEEK_SET);
	      for (i = 0; i < nx; i++)
		fscanf (f, "%lf", data->x_grid + i);
	      for (i = 1; i < nx; i++)
		if (data->x_grid[i - 1] >= data->x_grid[i])
		  goto errexit;
	    }
	  else if (!strcmp (buff, "Alpha"))
	    {
	      if (!data->q_grid)
		goto errexit;
	      data->alpha = malloc (nq * sizeof (double));
	      for (i = 0; i < nq; i++)
		if (fscanf (f, "%lf", data->alpha + i) != 1)
		  goto errexit;
	      if (fscanf (f, "%lf", &qq) == 1)
		goto errexit;
	    }
	  else if (!strcmp (buff, pattern))
	    {
	      int nn = nq ? nq * nx : nx;

	      data->strfun = malloc (nn * sizeof (double));
	      for (i = 0; i < nn; i++)
		if (fscanf (f, "%lf", data->strfun + i) != 1)
		  goto errexit;
	      if (fscanf (f, "%lf", &qq) == 1)
		goto errexit;
	      switch (fscanf (f, "powers %lf %lf", &(data->pow0), &(data->pow1)))
		{
		case 0:
		  data->pow0 = 0;
		case 1:
		  data->pow1 = 0;
		}
	      break;
	    }
	  else if (!strcmp (buff, "x_min"))
	    {
	      if (fscanf (f, "%lf", &(data->x_min)) != 1)
		goto errexit;
	      xMinOn = 1;
	    }
	  else if (!strcmp (buff, "x_max"))
	    {
	      if (fscanf (f, "%lf", &(data->x_max)) != 1)
		goto errexit;
	      xMaxOn = 1;
	    }
	  else if (!strcmp (buff, "q_min"))
	    {
	      if (fscanf (f, "%lf", &(data->q_min)) != 1)
		goto errexit;
	      qMinOn = 1;
	    }
	  else if (!strcmp (buff, "q_max"))
	    {
	      if (fscanf (f, "%lf", &(data->q_max)) != 1)
		goto errexit;
	      qMaxOn = 1;
	    }
	}
    }
  fclose (f);
  if (!xMinOn)
    data->x_min = data->x_grid[0];
  if (!xMaxOn)
    data->x_max = data->x_grid[data->nx - 1];

  if (data->q_grid)
    if (!qMinOn)
      data->q_min = data->q_grid[0];
  if (!qMaxOn)
    data->q_max = data->q_grid[data->nq - 1];

  return 0;
errexit:
  {
    int errpos = ftell (f);
    fclose (f);
    freePdfData (data);
    return errpos;
  }
}


static double 
inte2 (double *xa, double *ya, double x)
{
  double x0 = x - xa[0];
  double x1 = x - xa[1];
  return (ya[0] * x1 - ya[1] * x0) / (xa[0] - xa[1]);
}


static double 
inte3 (double *xa, double *ya, double x)
{
  double x0 = x - xa[0];
  double x1 = x - xa[1];
  double x2 = x - xa[2];
  double x01 = 1. / (xa[0] - xa[1]);
  double x02 = 1. / (xa[0] - xa[2]);
  double x12 = 1. / (xa[1] - xa[2]);

  return ya[0] * x1 * x2 * x01 * x02 - ya[1] * x0 * x2 * x01 * x12 + ya[2] * x0 * x1 * x02 * x12;
}

static int 
leftX (int dim, double *xa, double x)
{
  int k1, k2, k3;

  if (x < xa[0])
    return 0;
  if (x > xa[dim - 1])
    return dim - 3;

  k1 = 0;
  k2 = dim - 1;

  while (k2 - k1 > 1)
    {
      k3 = (k1 + k2) / 2;
      if (xa[k3] > x)
	k2 = k3;
      else
	k1 = k3;
    }

  k3 = k1;
  if (k3 < 0)
    k3 = 0;
  if (k3 > dim - 2)
    k3 = dim - 2;
  return k3;
}


double 
interFunc (double x, double q, pdfStr * W)
{
  int px = leftX (W->nx, W->x_grid, x);
  double tmp[3];

  if (W->q_grid && (q > W->q_max || q < W->q_min))
    fprintf (stderr, " Q=%E out of range\n", q);
  if (x > W->x_max || x < W->x_min)
    fprintf (stderr, " X=%E out of range\n", x);
  if (W->lin)
    {
      if (W->nq)
	{
	  int i;
	  double logQ = log (q);
	  int pq = leftX (W->nq, W->q_grid, logQ);

	  for (i = 0; i < 2; i++)
	    tmp[i] = inte2 (W->x_grid + px, W->strfun + W->nx * (pq + i) + px, x);
	  return inte2 (W->q_grid + pq, tmp, logQ);
	}
      return inte2 (W->x_grid + px, W->strfun + px, x);
    }
  else
    {
      if (px > W->nx - 3)
	px = W->nx - 3;
      if (W->nq)
	{
	  int i;
	  double logQ = log (q);
	  int pq = leftX (W->nq, W->q_grid, logQ);

	  if (pq > W->nq - 3)
	    pq = W->nq - 3;

	  for (i = 0; i < 3; i++)
	    tmp[i] = inte3 (W->x_grid + px, W->strfun + W->nx * (pq + i) + px, x);
	  return inte3 (W->q_grid + pq, tmp, logQ);
	}
      return inte3 (W->x_grid + px, W->strfun + px, x);
    }
  return 0.0;
}

double 
interAlpha (double q, pdfStr * W)
{
  double logQ = log (q);
  int pq = leftX (W->nq, W->q_grid, logQ);

  if (W->lin)
    {
      return inte2 (W->q_grid + pq, W->alpha, logQ);
    }
  else
    {
      if (pq > W->nq - 3)
	pq = W->nq - 3;
      return inte3 (W->q_grid + pq, W->alpha + pq, logQ);
    }
  return 0.0;
}

double
QCDLambda (void)
{
  return lambda;
}
