/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   http://lammps.sandia.gov, Sandia National Laboratories
   Steve Plimpton, sjplimp@sandia.gov

   Copyright (2003) Sandia Corporation.  Under the terms of Contract
   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
   certain rights in this software.  This software is distributed under 
   the GNU General Public License.

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

#include "math.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "unistd.h"
#include "variable.h"
#include "universe.h"
#include "atom.h"
#include "update.h"
#include "group.h"
#include "domain.h"
#include "modify.h"
#include "compute.h"
#include "fix.h"
#include "output.h"
#include "thermo.h"
#include "memory.h"
#include "error.h"

using namespace LAMMPS_NS;

#define VARDELTA 4

enum{INDEX,LOOP,EQUAL,WORLD,UNIVERSE,ULOOP,ATOM};
enum{VALUE,ATOMARRAY,TYPEARRAY,ADD,SUB,MULT,DIV,NEG,POW,EXP,LN,SQRT};

/* ---------------------------------------------------------------------- */

Variable::Variable(LAMMPS *lmp) : Pointers(lmp)
{
  MPI_Comm_rank(world,&me);

  nvar = maxvar = 0;
  names = NULL;
  style = NULL;
  num = NULL;
  index = NULL;
  data = NULL;
}

/* ---------------------------------------------------------------------- */

Variable::~Variable()
{
  for (int i = 0; i < nvar; i++) {
    delete [] names[i];
    for (int j = 0; j < num[i]; j++) delete [] data[i][j];
    delete [] data[i];
  }
  memory->sfree(names);
  memory->sfree(style);
  memory->sfree(num);
  memory->sfree(index);
  memory->sfree(data);
}

/* ----------------------------------------------------------------------
   called by variable command in input script
------------------------------------------------------------------------- */

void Variable::set(int narg, char **arg)
{
  if (narg < 3) error->all("Illegal variable command");

  // if var already exists, just skip, except EQUAL vars

  if (find(arg[0]) >= 0 && strcmp(arg[1],"equal") != 0) return;
      
  // make space for new variable

  if (nvar == maxvar) {
    maxvar += VARDELTA;
    names = (char **)
      memory->srealloc(names,maxvar*sizeof(char *),"var:names");
    style = (int *) memory->srealloc(style,maxvar*sizeof(int),"var:style");
    num = (int *) memory->srealloc(num,maxvar*sizeof(int),"var:num");
    index = (int *) memory->srealloc(index,maxvar*sizeof(int),"var:index");
    data = (char ***) 
      memory->srealloc(data,maxvar*sizeof(char **),"var:data");
  }

  // INDEX
  // num = listed args, index = 1st value, data = copied args

  if (strcmp(arg[1],"index") == 0) {
    style[nvar] = INDEX;
    num[nvar] = narg - 2;
    index[nvar] = 0;
    data[nvar] = new char*[num[nvar]];
    copy(num[nvar],&arg[2],data[nvar]);

  // LOOP
  // num = N, index = 1st value, data = list of NULLS since never used

  } else if (strcmp(arg[1],"loop") == 0) {
    if (narg != 3) error->all("Illegal variable command");
    style[nvar] = LOOP;
    num[nvar] = atoi(arg[2]);
    index[nvar] = 0;
    data[nvar] = new char*[num[nvar]];
    for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
    
  // EQUAL
  // remove pre-existing var if also style EQUAL (allows it to be reset)
  // num = 2, index = 1st value
  // data = 2 values, 1st is string to eval, 2nd is filled on retrieval

  } else if (strcmp(arg[1],"equal") == 0) {
    if (narg != 3) error->all("Illegal variable command");
    if (find(arg[0]) >= 0) {
      if (style[find(arg[0])] != EQUAL)
	error->all("Cannot redefine variable as a different style");
      remove(find(arg[0]));
    }
    style[nvar] = EQUAL;
    num[nvar] = 2;
    index[nvar] = 0;
    data[nvar] = new char*[num[nvar]];
    copy(1,&arg[2],data[nvar]);
    data[nvar][1] = NULL;
    
  // WORLD
  // num = listed args, index = partition this proc is in, data = copied args
  // error check that num = # of worlds in universe

  } else if (strcmp(arg[1],"world") == 0) {
    style[nvar] = WORLD;
    num[nvar] = narg - 2;
    if (num[nvar] != universe->nworlds)
      error->all("World variable count doesn't match # of partitions");
    index[nvar] = universe->iworld;
    data[nvar] = new char*[num[nvar]];
    copy(num[nvar],&arg[2],data[nvar]);

  // UNIVERSE and ULOOP
  // for UNIVERSE: num = listed args, data = copied args
  // for ULOOP: num = N, data = list of NULLS since never used
  // index = partition this proc is in
  // universe proc 0 creates lock file
  // error check that all other universe/uloop variables are same length

  } else if (strcmp(arg[1],"universe") == 0 || strcmp(arg[1],"uloop") == 0) {
    if (strcmp(arg[1],"universe") == 0) {
      style[nvar] = UNIVERSE;
      num[nvar] = narg - 2;
      data[nvar] = new char*[num[nvar]];
      copy(num[nvar],&arg[2],data[nvar]);
    } else {
      if (narg != 3) error->all("Illegal variable command");
      style[nvar] = ULOOP;
      num[nvar] = atoi(arg[2]);
      data[nvar] = new char*[num[nvar]];
      for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
    }

    if (num[nvar] < universe->nworlds)
      error->all("Universe/uloop variable count < # of partitions");
    index[nvar] = universe->iworld;

    if (universe->me == 0) {
      FILE *fp = fopen("tmp.lammps.variable","w");
      fprintf(fp,"%d\n",universe->nworlds);
      fclose(fp);
    }

    for (int jvar = 0; jvar < nvar; jvar++)
      if (num[jvar] && (style[jvar] == UNIVERSE || style[jvar] == ULOOP) && 
	  num[nvar] != num[jvar])
	error->all("All universe/uloop variables must have same # of values");

    if (me == 0) {
      if (universe->uscreen)
	fprintf(universe->uscreen,
		"Initial ${%s} setting: value %d on partition %d\n",
		arg[0],index[nvar]+1,universe->iworld);
      if (universe->ulogfile)
	fprintf(universe->ulogfile,
		"Initial ${%s} setting: value %d on partition %d\n",
		arg[0],index[nvar]+1,universe->iworld);
    }

  // ATOM
  // num = 1, index = 1st value
  // data = 1 value, string to eval

  } else if (strcmp(arg[1],"atom") == 0) {
    if (narg != 3) error->all("Illegal variable command");
    style[nvar] = ATOM;
    num[nvar] = 1;
    index[nvar] = 0;
    data[nvar] = new char*[num[nvar]];
    copy(1,&arg[2],data[nvar]);
    
  } else error->all("Illegal variable command");

  // set name of variable
  // must come at end, since EQUAL reset may have removed name

  names[nvar] = new char[strlen(arg[0]) + 1];
  strcpy(names[nvar],arg[0]);
  nvar++;
}

/* ----------------------------------------------------------------------
   single-value INDEX variable created by command-line argument
------------------------------------------------------------------------- */

void Variable::set(char *name, char *value)
{
  char **newarg = new char*[3];
  newarg[0] = name;
  newarg[1] = (char *) "index";
  newarg[2] = value;
  set(3,newarg);
  delete [] newarg;
}

/* ----------------------------------------------------------------------
   increment variable(s)
   return 0 if OK if successfully incremented
   return 1 if any variable is exhausted, free the variable to allow re-use
------------------------------------------------------------------------- */

int Variable::next(int narg, char **arg)
{
  int ivar;

  if (narg == 0) error->all("Illegal next command");

  // check that variables exist and are all the same style
  // exception: UNIVERSE and ULOOP variables can be mixed in same next command

  for (int iarg = 0; iarg < narg; iarg++) {
    ivar = find(arg[iarg]);
    if (ivar == -1) error->all("Invalid variable in next command");
    if (style[ivar] == ULOOP && style[find(arg[0])] == UNIVERSE) continue;
    else if (style[ivar] == UNIVERSE && style[find(arg[0])] == ULOOP) continue;
    else if (style[ivar] != style[find(arg[0])])
      error->all("All variables in next command must be same style");
  }

  // invalid styles EQUAL or WORLD or ATOM

  int istyle = style[find(arg[0])];
  if (istyle == EQUAL || istyle == WORLD || istyle == ATOM)
    error->all("Invalid variable style with next command");

  // increment all variables in list
  // if any variable is exhausted, set flag = 1 and remove var to allow re-use

  int flag = 0;

  if (istyle == INDEX || istyle == LOOP) {
    for (int iarg = 0; iarg < narg; iarg++) {
      ivar = find(arg[iarg]);
      index[ivar]++;
      if (index[ivar] >= num[ivar]) {
	flag = 1;
	remove(ivar);
      }
    }

  } else if (istyle == UNIVERSE || istyle == ULOOP) {

    // wait until lock file can be created and owned by proc 0 of this world
    // read next available index and Bcast it within my world
    // set all variables in list to nextindex

    int nextindex;
    if (me == 0) {
      while (1) {
	if (!rename("tmp.lammps.variable","tmp.lammps.variable.lock")) break;
	usleep(100000);
      }
      FILE *fp = fopen("tmp.lammps.variable.lock","r");
      fscanf(fp,"%d",&nextindex);
      fclose(fp);
      fp = fopen("tmp.lammps.variable.lock","w");
      fprintf(fp,"%d\n",nextindex+1);
      fclose(fp);
      rename("tmp.lammps.variable.lock","tmp.lammps.variable");
      if (universe->uscreen)
	fprintf(universe->uscreen,
		"Increment via next: value %d on partition %d\n",
		nextindex+1,universe->iworld);
      if (universe->ulogfile)
	fprintf(universe->ulogfile,
		"Increment via next: value %d on partition %d\n",
		nextindex+1,universe->iworld);
    }
    MPI_Bcast(&nextindex,1,MPI_INT,0,world);

    for (int iarg = 0; iarg < narg; iarg++) {
      ivar = find(arg[iarg]);
      index[ivar] = nextindex;
      if (index[ivar] >= num[ivar]) {
	flag = 1;
	remove(ivar);
      }
    }
  }

  return flag;
}

/* ----------------------------------------------------------------------
   return ptr to the data text associated with a variable
   if EQUAL var, evaluates variable and puts result in str
   return NULL if no variable or index is bad, caller must respond
------------------------------------------------------------------------- */

char *Variable::retrieve(char *name)
{
  int ivar = find(name);
  if (ivar == -1) return NULL;
  if (index[ivar] >= num[ivar]) return NULL;

  char *str;
  if (style[ivar] == INDEX || style[ivar] == WORLD || 
      style[ivar] == UNIVERSE) {
    str = data[ivar][index[ivar]];
  } else if (style[ivar] == LOOP || style[ivar] == ULOOP) {
    char *value = new char[16];
    sprintf(value,"%d",index[ivar]+1);
    int n = strlen(value) + 1;
    if (data[ivar][0]) delete [] data[ivar][0];
    data[ivar][0] = new char[n];
    strcpy(data[ivar][0],value);
    delete [] value;
    str = data[ivar][0];
  } else if (style[ivar] == EQUAL) {
    char result[32];
    double answer = evaluate(data[ivar][0],NULL);
    sprintf(result,"%.10g",answer);
    int n = strlen(result) + 1;
    if (data[ivar][1]) delete [] data[ivar][1];
    data[ivar][1] = new char[n];
    strcpy(data[ivar][1],result);
    str = data[ivar][1];
  } else if (style[ivar] == ATOM) return NULL;

  return str;
}

/* ----------------------------------------------------------------------
   search for name in list of variables names
   return index or -1 if not found
------------------------------------------------------------------------- */
  
int Variable::find(char *name)
{
  for (int i = 0; i < nvar; i++)
    if (strcmp(name,names[i]) == 0) return i;
  return -1;
}

/* ----------------------------------------------------------------------
   remove Nth variable from list and compact list
------------------------------------------------------------------------- */
  
void Variable::remove(int n)
{
  delete [] names[n];
  for (int i = 0; i < num[n]; i++) delete [] data[n][i];
  delete [] data[n];

  for (int i = n+1; i < nvar; i++) {
    names[i-1] = names[i];
    style[i-1] = style[i];
    num[i-1] = num[i];
    index[i-1] = index[i];
    data[i-1] = data[i];
  }
  nvar--;
}

/* ----------------------------------------------------------------------
   copy narg strings from **from to **to 
------------------------------------------------------------------------- */
  
void Variable::copy(int narg, char **from, char **to)
{
  int n;

  for (int i = 0; i < narg; i++) {
    n = strlen(from[i]) + 1;
    to[i] = new char[n];
    strcpy(to[i],from[i]);
  }
}

/* ----------------------------------------------------------------------
   recursive evaluation of a "string"
   string is defined as one of several items:
     number = 0.0, -5.45, 2.8e-4, ...
     thermo keyword = ke, vol, atoms, ...
     variable keyword = v_a, v_myvar, ...
     math function = div(x,y), mult(x,y), add(x,y), ...
     group function = mass(group), xcm(group,x), ...
     atom vector = x[123], y[3], vx[34], ...
     compute vector = c_mytemp[0], c_thermo_pressure[3], ...
     fix vector = f_indent[0], f_setforce[3], ...
   numbers start with a digit or "." or "-" (no parens or brackets)
   keywords start with a lowercase letter (no parens or brackets)
   functions contain ()
     can have 1 or 2 args, each of which can be a "string"
   vectors contain []
     single arg must be integer
     for atom vectors, arg is global ID of atom
     for compute/fix vectors, 0 is the scalar value, 1-N are vector values
   see lists of valid functions & vectors below
   return answer = value of string
------------------------------------------------------------------------- */

double Variable::evaluate(char *str, Tree *tree)
{
  double answer = 0.0;
  if (tree) {
    tree->type = VALUE;
    tree->left = tree->right = NULL;
  }

  // string is a "function" since contains ()
  // grab one or two args, separated by ","
  // evaulate args recursively
  // if tree = NULL, evaluate math or group function
  // else store as leaf in tree

  if (strchr(str,'(')) {
    if (str[strlen(str)-1] != ')') error->all("Cannot evaluate variable");

    char *ptr = strchr(str,'(');
    int n = ptr - str;
    char *func = new char[n+1];
    strncpy(func,str,n);
    func[n] = '\0';

    char *comma = ++ptr;
    int level = 0;
    while (1) {
      if (*comma == '\0') error->all("Cannot evaluate variable");
      else if (*comma == ',' && level == 0) break;
      else if (*comma == ')' && level == 0) break;
      else if (*comma == '(') level++;
      else if (*comma == ')') level--;
      comma++;
    }

    char *arg1,*arg2;
    n = comma - ptr;
    arg1 = new char[n+1];
    strncpy(arg1,ptr,n);
    arg1[n] = '\0';

    if (*comma == ',') {
      ptr = comma + 1;
      comma = &str[strlen(str)-1];
      n = comma - ptr;
      arg2 = new char[n+1];
      strncpy(arg2,ptr,n);
      arg2[n] = '\0';
    } else arg2 = NULL;
    
    double value1,value2;

    // customize by adding math function to this list and to if statement
    //   add(x,y),sub(x,y),mult(x,y),div(x,y),neg(x),
    //   pow(x,y),exp(x),ln(x),sqrt(x)

    if (strcmp(func,"add") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = ADD;
	tree->left = new Tree();
	tree->right = new Tree();
	value1 = evaluate(arg1,tree->left);
	value2 = evaluate(arg2,tree->right);
      } else answer = evaluate(arg1,NULL) + evaluate(arg2,NULL);

    } else if (strcmp(func,"sub") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = SUB;
	tree->left = new Tree();
	tree->right = new Tree();
	value1 = evaluate(arg1,tree->left);
	value2 = evaluate(arg2,tree->right);
      } else answer = evaluate(arg1,NULL) - evaluate(arg2,NULL);

    } else if (strcmp(func,"mult") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = MULT;
	tree->left = new Tree();
	tree->right = new Tree();
	value1 = evaluate(arg1,tree->left);
	value2 = evaluate(arg2,tree->right);
      } else answer = evaluate(arg1,NULL) * evaluate(arg2,NULL);

    } else if (strcmp(func,"div") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = DIV;
	tree->left = new Tree();
	tree->right = new Tree();
	value1 = evaluate(arg1,tree->left);
	value2 = evaluate(arg2,tree->right);
      } else {
	value1 = evaluate(arg1,NULL);
	value2 = evaluate(arg2,NULL);
	if (value2 == 0.0) error->all("Cannot evaluate variable");
	answer = value1 / value2;
      }

    } else if (strcmp(func,"neg") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = NEG;
	tree->left = new Tree();
	value1 = evaluate(arg1,tree->left);
      } else answer = -evaluate(arg1,NULL);

    } else if (strcmp(func,"pow") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = POW;
	tree->left = new Tree();
	tree->right = new Tree();
	value1 = evaluate(arg1,tree->left);
	value2 = evaluate(arg2,tree->right);
      } else {
	value1 = evaluate(arg1,NULL);
	value2 = evaluate(arg2,NULL);
	if (value2 == 0.0) error->all("Cannot evaluate variable");
	answer = pow(value1,value2);
      }

    } else if (strcmp(func,"exp") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = EXP;
	tree->left = new Tree();
	value1 = evaluate(arg1,tree->left);
      } else answer = exp(evaluate(arg1,NULL));

    } else if (strcmp(func,"ln") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = LN;
	tree->left = new Tree();
	value1 = evaluate(arg1,tree->left);
      } else {
	value1 = evaluate(arg1,NULL);
	if (value1 == 0.0) error->all("Cannot evaluate variable");
	answer = log(value1);
      }

    } else if (strcmp(func,"sqrt") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      if (tree) {
	tree->type = SQRT;
	tree->left = new Tree();
	value1 = evaluate(arg1,tree->left);
      } else {
	value1 = evaluate(arg1,NULL);
	if (value1 == 0.0) error->all("Cannot evaluate variable");
	answer = sqrt(value1);
      }

    // customize by adding group function to this list and to if statement
    //   mass(group),charge(group),xcm(group,dim),vcm(group,dim),
    //   fcm(group,dim),bound(group,xmin),gyration(group)

    } else if (strcmp(func,"mass") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      atom->check_mass();
      answer = group->mass(igroup);

    } else if (strcmp(func,"charge") == 0) {
      if (arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      answer = group->charge(igroup);

    } else if (strcmp(func,"xcm") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      atom->check_mass();
      double masstotal = group->mass(igroup);
      double xcm[3];
      group->xcm(igroup,masstotal,xcm);
      if (strcmp(arg2,"x") == 0) answer = xcm[0];
      else if (strcmp(arg2,"y") == 0) answer = xcm[1];
      else if (strcmp(arg2,"z") == 0) answer = xcm[2];
      else error->all("Cannot evaluate variable");

    } else if (strcmp(func,"vcm") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      atom->check_mass();
      double masstotal = group->mass(igroup);
      double vcm[3];
      group->vcm(igroup,masstotal,vcm);
      if (strcmp(arg2,"x") == 0) answer = vcm[0];
      else if (strcmp(arg2,"y") == 0) answer = vcm[1];
      else if (strcmp(arg2,"z") == 0) answer = vcm[2];
      else error->all("Cannot evaluate variable");

    } else if (strcmp(func,"fcm") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      double fcm[3];
      group->fcm(igroup,fcm);
      if (strcmp(arg2,"x") == 0) answer = fcm[0];
      else if (strcmp(arg2,"y") == 0) answer = fcm[1];
      else if (strcmp(arg2,"z") == 0) answer = fcm[2];
      else error->all("Cannot evaluate variable");

    } else if (strcmp(func,"bound") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      double minmax[6];
      group->bounds(igroup,minmax);
      if (strcmp(arg2,"xmin") == 0) answer = minmax[0];
      else if (strcmp(arg2,"xmax") == 0) answer = minmax[1];
      else if (strcmp(arg2,"ymin") == 0) answer = minmax[2];
      else if (strcmp(arg2,"ymax") == 0) answer = minmax[3];
      else if (strcmp(arg2,"zmin") == 0) answer = minmax[4];
      else if (strcmp(arg2,"zmax") == 0) answer = minmax[5];
      else error->all("Cannot evaluate variable");

    } else if (strcmp(func,"gyration") == 0) {
      if (!arg2) error->all("Cannot evaluate variable");
      int igroup = group->find(arg1);
      if (igroup == -1) error->all("Variable group ID does not exist");
      atom->check_mass();
      double masstotal = group->mass(igroup);
      double xcm[3];
      group->xcm(igroup,masstotal,xcm);
      answer = group->gyration(igroup,masstotal,xcm);

    } else error->all("Invalid math/group function in variable");

    delete [] func;
    delete [] arg1;
    delete [] arg2;

  // string is a "vector" since contains []
  // index = everything between [] evaluated as integer
  // if vector name starts with "c_", trailing chars are compute ID
  //   check if compute ID exists, invoke it with index as arg
  // if vector name starts with "cf_", trailing chars are fix ID
  //   check if fix ID exists, call its scalar or vector fn with index as arg
  // else is atom vector
  //   find which proc owns atom via atom->map()
  //   grab atom-based value with index as global atom ID

  } else if (strchr(str,'[')) {
    if (str[strlen(str)-1] != ']') error->all("Cannot evaluate variable");

    char *ptr = strchr(str,'[');
    int n = ptr - str;
    char *vector = new char[n+1];
    strncpy(vector,str,n);
    vector[n] = '\0';

    char *arg;
    ptr++;
    char *ptr2 = &str[strlen(str)-1];
    n = ptr2 - ptr;
    arg = new char[n+1];
    strncpy(arg,ptr,n);
    arg[n] = '\0';

    if (strncmp(vector,"c_",2) == 0) {
      n = strlen(vector) - 2 + 1;
      char *id = new char[n];
      strcpy(id,&vector[2]);

      int icompute;
      for (icompute = 0; icompute < modify->ncompute; icompute++)
	if (strcmp(id,modify->compute[icompute]->id) == 0) break;
      if (icompute == modify->ncompute)
	error->all("Invalid compute ID in variable");
      delete [] id;
      modify->compute[icompute]->init();

      // call compute_scalar() if index = 0, else compute_vector()
      // if defined, first call Compute object's pre-compute(s)

      int index = atoi(arg);
      if (index == 0) {
	if (modify->compute[icompute]->scalar_flag == 0)
	  error->all("Variable compute ID does not compute scalar info");
	if (modify->compute[icompute]->npre)
	  for (int ic = 0; ic < modify->compute[icompute]->npre; ic++) {
	    int ipre = 
	      modify->find_compute(modify->compute[icompute]->id_pre[ic]);
	    if (ipre < 0) error->all("Could not pre-compute in variable");
	    double tmp = modify->compute[ipre]->compute_scalar();
	  }
	answer = modify->compute[icompute]->compute_scalar();
      } else if (index > 0) {
	if (modify->compute[icompute]->vector_flag == 0)
	  error->all("Variable compute ID does not compute vector info");
	if (index > modify->compute[icompute]->size_vector)
	  error->all("Variable compute ID vector is not large enough");
	if (modify->compute[icompute]->npre)
	  for (int ic = 0; ic < modify->compute[icompute]->npre; ic++) {
	    int ipre = 
	      modify->find_compute(modify->compute[icompute]->id_pre[ic]);
	    if (ipre < 0) error->all("Could not pre-compute in variable");
	    modify->compute[ipre]->compute_vector();
	  }
	modify->compute[icompute]->compute_vector();
	answer = modify->compute[icompute]->vector[index-1];
      } else error->all("Invalid compute ID index in variable");

    } else if (strncmp(vector,"f_",2) == 0) {
      n = strlen(vector) - 2 + 1;
      char *id = new char[n];
      strcpy(id,&vector[2]);

      int ifix;
      for (ifix = 0; ifix < modify->nfix; ifix++)
	if (strcmp(id,modify->fix[ifix]->id) == 0) break;
      if (ifix == modify->nfix)
	error->all("Invalid fix ID in variable");
      delete [] id;

      // call compute_scalar() if index = 0, else compute_vector()

      int index = atoi(arg);
      if (index == 0) {
	if (modify->fix[ifix]->scalar_flag == 0)
	  error->all("Variable fix ID does not compute scalar info");
	answer = modify->fix[ifix]->compute_scalar();
      } else if (index > 0) {
	if (modify->fix[ifix]->vector_flag == 0)
	  error->all("Variable fix ID does not compute vector info");
	if (index > modify->fix[ifix]->size_vector)
	  error->all("Variable fix ID vector is not large enough");
	answer = modify->fix[ifix]->compute_vector(index-1);
      } else error->all("Invalid fix ID index in variable");

    } else if (tree) {

      if (strlen(arg)) error->all("Invalid atom vector in variable");

      // customize by adding atom vector to this list and to if statement
      // mass,x,y,z,vx,vy,vz,fx,fy,fz

      tree->type = ATOMARRAY;
      tree->nstride = 3;

      if (strcmp(vector,"mass") == 0) {
	if (atom->mass) {
	  tree->type = TYPEARRAY;
	  tree->array = atom->mass;
	} else {
	  tree->nstride = 1;
	  tree->array = atom->rmass;
	}
      }
      else if (strcmp(vector,"x") == 0) tree->array = &atom->x[0][0];
      else if (strcmp(vector,"y") == 0) tree->array = &atom->x[0][1];
      else if (strcmp(vector,"z") == 0) tree->array = &atom->x[0][2];
      else if (strcmp(vector,"vx") == 0) tree->array = &atom->v[0][0];
      else if (strcmp(vector,"vy") == 0) tree->array = &atom->v[0][1];
      else if (strcmp(vector,"vz") == 0) tree->array = &atom->v[0][2];
      else if (strcmp(vector,"fx") == 0) tree->array = &atom->f[0][0];
      else if (strcmp(vector,"fy") == 0) tree->array = &atom->f[0][1];
      else if (strcmp(vector,"fz") == 0) tree->array = &atom->f[0][2];
      
      else error->all("Invalid atom vector in variable");

    } else {

      if (atom->map_style == 0)
	error->all("Cannot use atom vector in variable unless atom map exists");

      int index = atom->map(atoi(arg));

      // customize by adding atom vector to this list and to if statement
      // mass,x,y,z,vx,vy,vz,fx,fy,fz

      double mine;
      if (index >= 0 && index < atom->nlocal) {

	if (strcmp(vector,"mass") == 0) {
	  if (atom->mass) mine = atom->mass[atom->type[index]];
	  else mine = atom->rmass[index];
	}
        else if (strcmp(vector,"x") == 0) mine = atom->x[index][0];
	else if (strcmp(vector,"y") == 0) mine = atom->x[index][1];
	else if (strcmp(vector,"z") == 0) mine = atom->x[index][2];
	else if (strcmp(vector,"vx") == 0) mine = atom->v[index][0];
	else if (strcmp(vector,"vy") == 0) mine = atom->v[index][1];
	else if (strcmp(vector,"vz") == 0) mine = atom->v[index][2];
	else if (strcmp(vector,"fx") == 0) mine = atom->f[index][0];
	else if (strcmp(vector,"fy") == 0) mine = atom->f[index][1];
	else if (strcmp(vector,"fz") == 0) mine = atom->f[index][2];
	
	else error->one("Invalid atom vector in variable");
	
      } else mine = 0.0;

      MPI_Allreduce(&mine,&answer,1,MPI_DOUBLE,MPI_SUM,world);
    }

    delete [] vector;
    delete [] arg;

  // string is "keyword" since starts with lowercase letter
  // if keyword starts with "v_", trailing chars are variable name
  //   evaluate it via retrieve(), convert it to double
  // else is thermo keyword
  //   evaluate it via evaluate_keyword()

  } else if (islower(str[0])) {

    if (strncmp(str,"v_",2) == 0) {
      int n = strlen(str) - 2 + 1;
      char *id = new char[n];
      strcpy(id,&str[2]);

      char *v = retrieve(id);
      if (v == NULL) error->all("Invalid variable name in variable");
      delete [] id;

      answer = atof(v);

    } else {
      int flag = output->thermo->evaluate_keyword(str,&answer);
      if (flag) error->all("Invalid thermo keyword in variable");
    }

  // string is a number since starts with digit or "." or "-"
  // just copy to result

  } else if (isdigit(str[0]) || str[0] == '.' || str[0] == '-') {

    answer = atof(str);

  // string is an error

  } else error->all("Cannot evaluate variable");

  // store answer in tree and return it

  if (tree) tree->value = answer;
  return answer;
}

/* ---------------------------------------------------------------------- */

void Variable::build_parse_tree(int ivar)
{
  if (style[ivar] != ATOM)
    error->all("Cannot build parse tree for variable that is not atom style");
  ptree = new Tree();
  double tmp = evaluate(data[ivar][0],ptree);
}

/* ---------------------------------------------------------------------- */

void Variable::evaluate_parse_tree(int igroup, double *result)
{
  int groupbit = group->bitmask[igroup];
  int *mask = atom->mask;
  int nlocal = atom->nlocal;

  for (int i = 0; i < nlocal; i++) {
    if (mask[i] && groupbit) result[i] = eval_tree(ptree,i);
    else result[i] = 0.0;
  }
}

/* ---------------------------------------------------------------------- */

void Variable::free_parse_tree()
{
  free_tree(ptree);
}

/* ---------------------------------------------------------------------- */

double Variable::eval_tree(Tree *tree, int i)
{
  if (tree->type == VALUE) return tree->value;
  if (tree->type == ATOMARRAY) return tree->array[i*tree->nstride];
  if (tree->type == TYPEARRAY) return tree->array[atom->type[i]];

  if (tree->type == ADD)
    return eval_tree(tree->left,i) + eval_tree(tree->right,i);
  if (tree->type == SUB)
    return eval_tree(tree->left,i) - eval_tree(tree->right,i);
  if (tree->type == MULT)
    return eval_tree(tree->left,i) * eval_tree(tree->right,i);
  if (tree->type == DIV)
    return eval_tree(tree->left,i) / eval_tree(tree->right,i);
  if (tree->type == NEG)
    return -eval_tree(tree->left,i);
  if (tree->type == POW)
    return pow(eval_tree(tree->left,i),eval_tree(tree->right,i));
  if (tree->type == EXP)
    return exp(eval_tree(tree->left,i));
  if (tree->type == LN)
    return log(eval_tree(tree->left,i));
  if (tree->type == SQRT)
    return sqrt(eval_tree(tree->left,i));

  return 0.0;
}

/* ---------------------------------------------------------------------- */

void Variable::free_tree(Tree *tree)
{
  if (tree->left) free_tree(tree->left);
  if (tree->right) free_tree(tree->right);
  delete tree;
}
