#include "eparser.h"
#include "logger.h"

#include "eiostream.h"

#include "efile.h"

//#include "evarcommon.h"

#include "eparserinterpreter.h"

#include "etable.h"
#include "ematrix.h"
#include "vector2.h"
#include "vector3.h"
#include "vector4.h"
#include "eoption.h"
#include "evar.h"

#include "edistcomp.h"
#include "ernd.h"

#include <math.h>
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif

#include "evarcommon.h"

evararray map(const efunc& func,const evararray& arr);

typedef unsigned int uint;

uint& uintssum(uint& a,uint b){ a+=b; return(a); }
uint& uintssub(uint& a,uint b){ a-=b; return(a); }
uint uintdec(uint& a) { return(a--); }
uint& uintdecpre(uint& a) { return(--a); }
uint uintinc(uint& a) { return(a++); }
uint& uintincpre(uint& a) { return(++a); }
uint uintand(uint a,uint b){ return(a&b); }
uint uintor(uint a,uint b){ return(a|b); }
uint uintxor(uint a,uint b){ return(a^b); }
uint uintsum(uint a,uint b){ return(a+b); }
uint uintsub(uint a,uint b){ return(a-b); }
uint uintmul(uint a,uint b){ return(a*b); }
uint uintdiv(uint a,uint b){ return(b!=0?a/b:0); }
uint uintmod(uint a,uint b){ return(a%b); }
uint uintneg(uint a) { return(-a); }
uint uintmin(uint a,uint b){ return(MIN(a,b));}
uint uintmax(uint a,uint b){ return(MAX(a,b));}
uint uintshl(uint a,uint b) { return(a<<b); }
uint uintshr(uint a,uint b) { return(a>>b); }


bool uintnot(uint a){ return(!a); }
bool uintlt(uint a,uint b){ return(a<b); }
bool uintgt(uint a,uint b){ return(a>b); }
bool uintlteq(uint a,uint b){ return(a<=b); }
bool uintgteq(uint a,uint b){ return(a>=b); }
bool uinteq(uint a,uint b)  { return(a==b); }
bool uintneq(uint a,uint b) { return(a!=b); }

int& intssum(int& a,int b){ a+=b; return(a); }
int& intssub(int& a,int b){ a-=b; return(a); }
int intdec(int& a) { return(a--); }
int& intdecpre(int& a) { return(--a); }
int intinc(int& a) { return(a++); }
int& intincpre(int& a) { return(++a); }
int intand(int a,int b){ return(a&b); }
int intor(int a,int b){ return(a|b); }
int intxor(int a,int b){ return(a^b); }
int intsum(int a,int b){ return(a+b); }
int intsub(int a,int b){ return(a-b); }
int intmul(int a,int b){ return(a*b); }
int intdiv(int a,int b){ return(b!=0?a/b:0); }
int intmod(int a,int b){ return(a%b); }
int intneg(int a) { return(-a); }
int intmin(int a,int b){ return(MIN(a,b));}
int intmax(int a,int b){ return(MAX(a,b));}
int intshl(int a,int b) { return(a<<b); }
int intshr(int a,int b) { return(a>>b); }



bool intnot(int a){ return(!a); }
bool intlt(int a,int b){ return(a<b); }
bool intgt(int a,int b){ return(a>b); }
bool intlteq(int a,int b){ return(a<=b); }
bool intgteq(int a,int b){ return(a>=b); }
bool inteq(int a,int b)  { return(a==b); }
bool intneq(int a,int b) { return(a!=b); }

long int& longintssum(long int& a,long int b){ a+=b; return(a); }
long int& longintssub(long int& a,long int b){ a-=b; return(a); }
long int longintdec(long int& a) { return(a--); }
long int& longintdecpre(long int& a) { return(--a); }
long int longintinc(long int& a) { return(a++); }
long int& longintincpre(long int& a) { return(++a); }
long int longintand(long int a,long int b){ return(a&b); }
long int longintor(long int a,long int b){ return(a|b); }
long int longintxor(long int a,long int b){ return(a^b); }
long int longintsum(long int a,long int b){ return(a+b); }
long int longintsub(long int a,long int b){ return(a-b); }
long int longintmul(long int a,long int b){ return(a*b); }
long int longintdiv(long int a,long int b){ return(b!=0?a/b:0); }
long int longintmod(long int a,long int b){ return(a%b); }
long int longintneg(long int a) { return(-a); }
long int longintmin(long int a,long int b){ return(MIN(a,b));}
long int longintmax(long int a,long int b){ return(MAX(a,b));}
long int longintshl(long int a,long int b) { return(a<<b); }
long int longintshr(long int a,long int b) { return(a>>b); }


bool longintnot(long int a){ return(!a); }
bool longintlt(long int a,long int b){ return(a<b); }
bool longintgt(long int a,long int b){ return(a>b); }
bool longintlteq(long int a,long int b){ return(a<=b); }
bool longintgteq(long int a,long int b){ return(a>=b); }
bool longinteq(long int a,long int b)  { return(a==b); }
bool longintneq(long int a,long int b) { return(a!=b); }

unsigned long& ulongssum(unsigned long& a,unsigned long b){ a+=b; return(a); }
unsigned long& ulongssub(unsigned long& a,unsigned long b){ a-=b; return(a); }
unsigned long ulongdec(unsigned long& a) { return(a--); }
unsigned long& ulongdecpre(unsigned long& a) { return(--a); }
unsigned long ulonginc(unsigned long& a) { return(a++); }
unsigned long& ulongincpre(unsigned long& a) { return(++a); }
unsigned long ulongand(unsigned long a,unsigned long b){ return(a&b); }
unsigned long ulongor(unsigned long a,unsigned long b){ return(a|b); }
unsigned long ulongxor(unsigned long a,unsigned long b){ return(a^b); }
unsigned long ulongsum(unsigned long a,unsigned long b){ return(a+b); }
unsigned long ulongsub(unsigned long a,unsigned long b){ return(a-b); }
unsigned long ulongmul(unsigned long a,unsigned long b){ return(a*b); }
unsigned long ulongdiv(unsigned long a,unsigned long b){ return(b!=0?a/b:0); }
unsigned long ulongmod(unsigned long a,unsigned long b){ return(a%b); }
unsigned long ulongneg(unsigned long a) { return(-a); }
unsigned long ulongmin(unsigned long a,unsigned long b){ return(MIN(a,b));}
unsigned long ulongmax(unsigned long a,unsigned long b){ return(MAX(a,b));}
unsigned long ulongshl(int a,int b) { return(a<<b); }
unsigned long ulongshr(int a,int b) { return(a>>b); }

bool ulongnot(unsigned long a){ return(!a); }
bool ulonglt(unsigned long a,unsigned long b){ return(a<b); }
bool ulonggt(unsigned long a,unsigned long b){ return(a>b); }
bool ulonglteq(unsigned long a,unsigned long b){ return(a<=b); }
bool ulonggteq(unsigned long a,unsigned long b){ return(a>=b); }
bool ulongeq(unsigned long a,unsigned long b)  { return(a==b); }
bool ulongneq(unsigned long a,unsigned long b) { return(a!=b); }


bool chareqstr(char a,const estr& b)  { return(b.len()==1 && a==b[0]); }


estr uintstrsum(uint a,const estr& b){ return(a+b); }
estr intstrsum(int a,const estr& b){ return(a+b); }
estr longintstrsum(long int a,const estr& b){ return(a+b); }
estr ulongstrsum(unsigned long a,const estr& b){ return(a+b); }
estr floatstrsum(float a,const estr& b){ return(a+b); }
estr doublestrsum(double a,const estr& b){ return(a+b); }

float& floatssum(float& a,float b){ a+=b; return(a); }
float& floatssub(float& a,float b){ a-=b; return(a); }
float floatdec(float& a) { return(a--); }
float& floatdecpre(float& a) { return(--a); }
float floatinc(float& a) { return(a++); }
float& floatincpre(float& a) { return(++a); }
float floatsum(float a,float b){ return(a+b); }
float floatsub(float a,float b){ return(a-b); }
float floatmul(float a,float b){ return(a*b); }
float floatdiv(float a,float b){ return(a/b); }
float floatneg(float a){ return(-a); }
float floatmin(float a,float b){ return(MIN(a,b));}
float floatmax(float a,float b){ return(MAX(a,b));}


bool floatlt(float a,float b){ return(a<b); }
bool floatgt(float a,float b){ return(a>b); }
bool floatlteq(float a,float b){ return(a<=b); }
bool floatgteq(float a,float b){ return(a>=b); }
bool floateq(float a,float b)  { return(a==b); }
bool floatneq(float a,float b) { return(a!=b); }

double& doublessum(double& a,double b){ a+=b; return(a); }
double& doublessub(double& a,double b){ a-=b; return(a); }
double doubledec(double& a) { return(a--); }
double& doubledecpre(double& a) { return(--a); }
double doubleinc(double& a) { return(a++); }
double& doubleincpre(double& a) { return(++a); }
double doublesum(double a,double b){ return(a+b); }
double doublesub(double a,double b){ return(a-b); }
double doublemul(double a,double b){ return(a*b); }
double doublediv(double a,double b){ return(a/b); }
double doubleneg(double a){ return(-a); }
double doublemin(double a,double b){ return(MIN(a,b));}
double doublemax(double a,double b){ return(MAX(a,b));}

bool doublelt(double a,double b){ return(a<b); }
bool doublegt(double a,double b){ return(a>b); }
bool doublelteq(double a,double b){ return(a<=b); }
bool doublegteq(double a,double b){ return(a>=b); }
bool doubleeq(double a,double b)  { return(a==b); }
bool doubleneq(double a,double b) { return(a!=b); }

bool boolnot(bool a){ return(!a); }
bool booland(bool a,bool b){ return(a&&b); }
bool boolor(bool a,bool b) { return(a||b); }
bool boolxor(bool a,bool b){ return(a^b);  }

#ifndef MIN
  #define MIN(a,b) ((a)<(b)?(a):(b))
#endif

#ifndef MAX
  #define MAX(a,b) ((a)>(b)?(a):(b))
#endif

#include "eheap.h"


estrarrayof<int> countarr(const evararray& sarr)
{
  estrhashof<int> tmphash;
  estrarrayof<int> tmparr;
  for (int i=0; i<sarr.size(); ++i){
    if (sarr[i].getTypeid()==typeid(estr)){
      estr& tmpstr(sarr[i].get<estr>());
      if (!tmphash.exists(tmpstr)){
        tmphash.add(tmpstr,1);
      }else
        tmphash[tmpstr]++;
    }
  }
  for (int i=0; i<tmphash.size(); ++i)
    tmparr.add(tmphash.keys(i),tmphash.values(i));
  heapsort(tmparr);
  return(tmparr);
}

/*
estrhashof<int> countarr(const evararray& sarr)
{
  estrhashof<int> tmphash;
  for (int i=0; i<sarr.size(); ++i){
    if (sarr[i].getTypeid()==typeid(estr)){
      estr& tmpstr(sarr[i].get<estr>());
      if (!tmphash.exists(tmpstr)){
        tmphash.add(tmpstr,1);
      }else
        tmphash[tmpstr]++;
    }
  }
  return(tmphash);
}
*/

evararray uniquearr(const evararray& sarr)
{
  evararray tmparr;
  estrhash tmphash;
  for (int i=0; i<sarr.size(); ++i){
    if (sarr[i].getTypeid()==typeid(estr)){
      estr& tmpstr(sarr[i].get<estr>());
      if (!tmphash.exists(tmpstr)){
        tmparr.add(sarr[i]);
        tmphash.add(tmpstr,estr());
      }
    }else if (tmparr.find(sarr[i])==-1)
      tmparr.add(sarr);
  }
  return(tmparr);
}

estrarrayof<elongarray> uniqueset(const evararray& sarr)
{
  estrhashof<elongarray> tmphash;
  estrarrayof<elongarray> tmparr;
  for (long i=0; i<sarr.size(); ++i){
    if (sarr[i].getTypeid()==typeid(estr)){
      estr& tmpstr(sarr[i].get<estr>());
      if (!tmphash.exists(tmpstr)){
        tmphash.add(tmpstr,elongarray(i));
      }else
        tmphash[tmpstr].add(i);
    }
  }
  for (long i=0; i<tmphash.size(); ++i){
    tmparr.add(tmphash.keys(i),tmphash.values(i));
//    heapsort(tmparr);
  }
  return(tmparr);
}


evar maxvar(const evar& v1,const evar& v2)
{
  if (v1>=v2) return(v1);
  return(v2);
}

evar minvar(const evar& v1,const evar& v2)
{
  if (v1<=v2) return(v1);
  return(v2);
}

evararray divarr(evararray& farr,float mval)
{
  evararray tmparr;
  evar fv(mval);
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i]/fv);
  return(tmparr);
}
evararray subarr(evararray& farr,float mval)
{
  evararray tmparr;
  evar fv(mval);
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i]-fv);
  return(tmparr);
}
evararray sumarr(const evararray& farr,float mval)
{
  evararray tmparr;
  evar fv(mval);
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i]+fv);
  return(tmparr);
}

evararray invarr(const evararray& farr)
{
  evararray tmparr;
  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(double(1.0/farr[i].get<double>()));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(1.0/farr[i].get<float>()));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(1.0/farr[i].get<long>());
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(1.0/farr[i].get<int>());
  }
  return(tmparr);
}

evararray mularr(const evararray& farr,float mval)
{
  evararray tmparr;
  evar fv(mval);
  tmparr.reserve(farr.size());
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i]*fv);
  return(tmparr);
}

evararray normarr(const evararray& farr)
{
  evararray tmparr;
  if (farr.size()==0) return(tmparr);
  double min,max,tmpv;
  if (farr[0].getTypeid()==typeid(double))
    tmpv=farr[0].get<double>();
  else if (farr[0].getTypeid()==typeid(float))
    tmpv=farr[0].get<float>();
  else if (farr[0].getTypeid()==typeid(long))
    tmpv=farr[0].get<long>();
  else if (farr[0].getTypeid()==typeid(int))
    tmpv=farr[0].get<int>();
  min=tmpv;
  max=tmpv;

  for (int i=0; i<farr.size(); ++i){
    double tmpv;
    if (farr[i].getTypeid()==typeid(double))
      tmpv=farr[i].get<double>();
    else if (farr[i].getTypeid()==typeid(float))
      tmpv=farr[i].get<float>();
    else if (farr[i].getTypeid()==typeid(long))
      tmpv=farr[i].get<long>();
    else if (farr[i].getTypeid()==typeid(int))
      tmpv=farr[i].get<int>();
    if (tmpv>max) max=tmpv;
    if (tmpv<min) min=tmpv;
  }

  // return 0.0 for all values if they are the same
  if (max==min){ tmparr.init(farr.size(),0.0); return(tmparr); }

  for (int i=0; i<farr.size(); ++i){
    double tmpv;
    if (farr[i].getTypeid()==typeid(double))
      tmpv=farr[i].get<double>();
    else if (farr[i].getTypeid()==typeid(float))
      tmpv=farr[i].get<float>();
    else if (farr[i].getTypeid()==typeid(long))
      tmpv=farr[i].get<long>();
    else if (farr[i].getTypeid()==typeid(int))
      tmpv=farr[i].get<int>();
    tmparr.add((tmpv-min)/(max-min));
  }
  return(tmparr);
}



evararray minarr(const evararray& farr,double mval)
{
  evararray tmparr;
  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(double(MIN(farr[i].get<double>(),mval)));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(MIN(farr[i].get<float>(),mval)));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(MIN(farr[i].get<long>(),mval)));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(MIN(farr[i].get<int>(),mval)));
  }
  return(tmparr);
}
evararray maxarr(const evararray& farr,double mval)
{
  evararray tmparr;
  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(double(MAX(farr[i].get<double>(),mval)));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(MAX(farr[i].get<float>(),mval)));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(MAX(farr[i].get<long>(),mval)));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(MAX(farr[i].get<int>(),mval)));
  }
  return(tmparr);
}
evararray minarr(const evararray& farr,float mval)
{
  evararray tmparr;
  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(double(MIN(farr[i].get<double>(),mval)));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(MIN(farr[i].get<float>(),mval)));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(MIN(farr[i].get<long>(),mval)));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(MIN(farr[i].get<int>(),mval)));
  }
  return(tmparr);
}
evararray maxarr(const evararray& farr,float mval)
{
  evararray tmparr;
  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(double(MAX(farr[i].get<double>(),mval)));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(MAX(farr[i].get<float>(),mval)));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(MAX(farr[i].get<long>(),mval)));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(MAX(farr[i].get<int>(),mval)));
  }
  return(tmparr);
}
eintarray& logarr(eintarray& farr)
{
  for (int i=0; i<farr.size(); ++i) farr[i]=log(farr[i]);
  return(farr);
}
efloatarray& logarr(efloatarray& farr)
{
  for (int i=0; i<farr.size(); ++i) farr[i]=log(farr[i]);
  return(farr);
}
edoublearray& logarr(edoublearray& farr)
{
  for (int i=0; i<farr.size(); ++i) farr[i]=log(farr[i]);
  return(farr);
}

edoublearray absarr(const edoublearray& farr)
{
  edoublearray resarr;
  resarr.reserve(farr.size());
  for (long i=0; i<farr.size(); ++i) resarr.add(fabs(farr[i]));
  return(resarr);
}

edoublearray movavgarr(const edoublearray& farr,int wlen)
{
  if (farr.size()<wlen) return(edoublearray());

  edoublearray warr; 
  warr.reserve(farr.size());
  double wsum=0.0;
  long i=0;

  for (; i<wlen/2 && i<farr.size(); ++i)
    wsum+=farr[i];

  for (; i<wlen && i<farr.size(); ++i){
    wsum+=farr[i];
    warr.add(wsum/(i+1));
  }

  for (; i<farr.size(); ++i){
    wsum+=farr[i]-farr[i-wlen];
    warr.add(wsum/wlen);
  }

  for (; i-wlen/2<farr.size(); ++i){
    wsum-=farr[i-wlen];
    warr.add(wsum/(farr.size()-i+wlen));
  }
  return(warr);
}


evararray tanarr(const evararray& farr)
{
  evararray tmparr;

  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(tan(farr[i].get<double>()));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(tan(farr[i].get<float>())));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(tan(farr[i].get<long>())));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(tan(farr[i].get<int>())));
  }
  return(tmparr);
}
evararray logarr(const evararray& farr)
{
  evararray tmparr;

  for (int i=0; i<farr.size(); ++i){
    if (farr[i].getTypeid()==typeid(double))
      tmparr.add(log(farr[i].get<double>()));
    else if (farr[i].getTypeid()==typeid(float))
      tmparr.add(float(log(farr[i].get<float>())));
    else if (farr[i].getTypeid()==typeid(long))
      tmparr.add(long(log(farr[i].get<long>())));
    else if (farr[i].getTypeid()==typeid(int))
      tmparr.add(int(log(farr[i].get<int>())));
  }
  return(tmparr);
}
evararray mularr(const evararray& farr,const evararray& farr2)
{
  evararray tmparr;
  if (farr.size()!=farr2.size()) return(tmparr);
  
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i] * farr2[i]);
  return(tmparr);
}
evararray sumarr(evararray& farr,evararray& farr2)
{
  evararray tmparr;
  if (farr.size()!=farr2.size()) return(tmparr);
  
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i] + farr2[i]);
  return(tmparr);
}
evararray subarr(evararray& farr,evararray& farr2)
{
  evararray tmparr;
  if (farr.size()!=farr2.size()) return(tmparr);
  
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i] - farr2[i]);
  return(tmparr);
}
evararray divarr(evararray& farr,evararray& farr2)
{
  evararray tmparr;
  if (farr.size()!=farr2.size()) return(tmparr);
  
  for (int i=0; i<farr.size(); ++i)
    tmparr.add(farr[i] / farr2[i]);
  return(tmparr);
}

earray<eintarray> matcharr(const evararray& arr1,const evararray& arr2){
  eintarray tmparr1,tmparr2;
  for (int i=0; i<arr1.size(); ++i){
    for (int j=0; j<arr2.size(); ++j){
      if (arr1[i]==arr2[j]){
        tmparr1.add(i);
        tmparr2.add(j);
        break;
      }
    }
  }
  earray<eintarray> tmpres;
  tmpres.add(tmparr1);
  tmpres.add(tmparr2);
  return(tmpres);
}

eintarray findarr(const evararray& sarr,const estr& needle)
{
  eintarray tmparr;
  for (int i=0; i<sarr.size(); ++i){
    if (sarr[i].getTypeid()==typeid(estr) && sarr[i].get<estr>().find(needle)!=-1)
      tmparr.add(i);
  }
  return(tmparr);
}

eintarray inarr(const evararray& sarr,const evararray& arrneedle)
{
  eintarray tmparr;
  if (arrneedle.size()==0) return(tmparr);

  if (arrneedle[0].getTypeid()==typeid(estr)){
    estrhash tmphash;
    for (int i=0; i<arrneedle.size(); ++i)
      tmphash.add(arrneedle[i],estr());
    for (int i=0; i<sarr.size(); ++i)
      if (tmphash.exists(sarr[i]))
        tmparr.add(i);
    return(tmparr);
  }
  for (int i=0; i<sarr.size(); ++i){
    int j=arrneedle.find(sarr[i]);
    if (j!=-1)
      tmparr.add(j);
/*
    for (int j=0; j<arrneedle.size(); ++j){
      if (sarr[i]==arrneedle[j]){
        tmparr.add(i);
        break;
      }
    }
*/
  }
  return(tmparr);
}

evararray joinarr(const evararray& sarr,const evararray& arrneedle)
{
  eintarray tmparr1,tmparr2;
  if (arrneedle.size()==0) return(evararray(tmparr1,tmparr2));

  if (arrneedle[0].getTypeid()==typeid(estr)){
    estrhashof<int> tmphash;
    for (int i=0; i<arrneedle.size(); ++i)
      tmphash.add(arrneedle[i],i);
    for (int i=0; i<sarr.size(); ++i)
      if (tmphash.exists(sarr[i])){
        tmparr1.add(i);
        tmparr2.add(tmphash[sarr[i].get<estr>()]);
      }
    return(evararray(tmparr1,tmparr2));
  }
  for (int i=0; i<sarr.size(); ++i){
    int j=arrneedle.find(sarr[i]);
    if (j!=-1){
      tmparr1.add(j);
      tmparr2.add(i);
    }
/*
    for (int j=0; j<arrneedle.size(); ++j){
      if (sarr[i]==arrneedle[j]){
        tmparr.add(i);
        break;
      }
    }
*/
  }
  return(evararray(tmparr1,tmparr2));
}



eintarray gtarr(evararray& farr,float v)
{
  eintarray tmparr;
  evar fv(v);
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]>fv)
      tmparr.add(i);
  return(tmparr);
}
eintarray ltarr(evararray& farr,float v)
{
  evar fv(v);
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]<fv)
      tmparr.add(i);
  return(tmparr);
}
eintarray gtearr(evararray& farr,float v)
{
  evar fv(v);
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]>=fv)
      tmparr.add(i);
  return(tmparr);
}
eintarray ltearr(evararray& farr,float v)
{
  evar fv(v);
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]<=fv)
      tmparr.add(i);
  return(tmparr);
}
eintarray eqarr(evararray& farr,float v)
{
  evar fv(v);
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]==fv)
      tmparr.add(i);
  return(tmparr);
}
eintarray neqarr(evararray& farr,float v)
{
  evar fv(v);
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]!=fv)
      tmparr.add(i);
  return(tmparr);
}

eintarray xorarr(eintarray& arr1,eintarray& arr2)
{
  eintarray tarr;
  int i=0,j=0;
  for (; i<arr1.size() && j<arr2.size(); ){
    if (arr1[i]<arr2[j]) { tarr.add(arr1[i]); ++i; }
    else if (arr2[j]<arr1[i]) { tarr.add(arr2[j]); ++j; }
    else { ++i; ++j; }
  }
  for (;i<arr1.size(); ++i) tarr.add(arr1[i]);
  for (;j<arr2.size(); ++j) tarr.add(arr2[j]);
  return(tarr);
}
eintarray orarr(eintarray& arr1,eintarray& arr2)
{
  eintarray tarr;
  int i=0,j=0;
  for (; i<arr1.size() && j<arr2.size(); ){
    if (arr1[i]<arr2[j]) { tarr.add(arr1[i]); ++i; }
    else if (arr2[j]<arr1[i]) { tarr.add(arr2[j]); ++j; }
    else { tarr.add(arr1[i]); ++i; ++j; }
  }
  for (;i<arr1.size(); ++i) tarr.add(arr1[i]);
  for (;j<arr2.size(); ++j) tarr.add(arr2[j]);
  return(tarr);
}
eintarray andarr(eintarray& arr1,eintarray& arr2)
{
  eintarray tarr;
  int i=0,j=0;
  for (; i<arr1.size() && j<arr2.size(); ){
    if (arr1[i]<arr2[j]) ++i;
    else if (arr2[j]<arr1[i]) ++j;
    else { tarr.add(arr1[i]); ++i; ++j; }
  }
  return(tarr);
}
eintarray nullarr(const evararray& arr)
{
  eintarray tmparr;
  for (int i=0; i<arr.size(); ++i){
    if (arr[i].isNull()) 
      tmparr.add(i);
  }
  return(tmparr);
}

eintarray eqarr(evararray& farr,evar& v)
{
  eintarray tmparr;
  for (int i=0; i<farr.size(); ++i)
    if (farr[i]==v)
      tmparr.add(i);
  return(tmparr);
}

eintarray seqarr(int e,int s,int i)
{
  if (e<i || s<0) return(eintarray());

  eintarray tmparr;
  tmparr.reserve((e-i)/s);
  for (int ti=i; ti<e; ti+=s)
    tmparr.add(ti);
  return(tmparr);
}

eintarray rndarr(int m,int n)
{
  if (n<0 || n>m) n=m;

  eintarray tmparr(seqarr(m,1,0)),tmparr2;
  tmparr2.reserve(n);
  for (int ti=0; ti<n; ++ti){
    int ri=rnd.uniformint(m-ti)+ti;
    if (ri>ti)
      tmparr.swap(ti,ri);
    tmparr2.add(tmparr[ti]);
  }
  return(tmparr2);
}








evar evararrayInit(evararray& arr)
{
  evararray *varr=new evararray(arr);
  return(varr);
}

evar evarhashInit(evararray& arr)
{
  evarhash *vhash=new evarhash;
  for (int i=0; i+1<arr.size(); i+=2)
    vhash->add(arr[i].get<estr>(),arr[i+1]);
  return(vhash);
}

void epregisterAction(const estr& name,const efunc& f)
{
  getParser().actions.add(name,f);
}

void epregisterModuleFunc3(emodule& m,const efunc& f,const estr& fname)
{
  m.registerFunc(f,fname);
  if (!getParser().funcs.exists(fname))
    getParser().funcs.add(fname,earray<efunc>());

  getParser().funcs[fname].add(f);
}



void epregisterCommon()
{
#ifdef EUTILS_REGISTER_COMMON
  epregisterClassConverterCast2(bool,char);
  epregisterClassConverterCast2(bool,int);
  epregisterClassConverterCast2(bool,long int);
  epregisterClassConverterCast2(bool,unsigned long);
  epregisterClassConverterCast2(char,int);
  epregisterClassConverterCast2(char,long int);
  epregisterClassConverterCast2(char,unsigned long int);
  epregisterClassConverterCast2(int,uint);
  epregisterClassConverterCast2(int,long);
  epregisterClassConverterCast2(int,long int);
  epregisterClassConverterCast2(int,unsigned long);
  epregisterClassConverterCast2(int,float);
  epregisterClassConverterCast2(uint,float);
  epregisterClassConverterCast2(long int,float);
  epregisterClassConverterCast2(unsigned long,float);
  epregisterClassConverterCast2(int,double);
  epregisterClassConverterCast2(long int,double);
  epregisterClassConverterCast2(unsigned long,double);
  epregisterClassConverterCast2(float,double);
  epregisterClassConverterCast2(uint,double);
  epregisterClassConverterCast2(uint,long int);
  epregisterClassConverterCast2(unsigned long,long int);
  epregisterClassConverterCast2(unsigned long,uint);


  epregisterClassConverterCast(estr,bool);
  epregisterClassConverterCast(estr,char);
  epregisterClassConverterCast(estr,int);
  epregisterClassConverterCast(estr,uint);
  epregisterClassConverterCast(estr,unsigned long);
  epregisterClassConverterCast(estr,long int);
  epregisterClassConverterCast(estr,float);
  epregisterClassConverterCast(estr,double);
  
  epregisterClassConverterCast(efile,estr);

  epregisterClassConverterMethod(int,estr,i);
  epregisterClassConverterMethod(float,estr,f);
  epregisterClassConverterMethod(double,estr,d);


  epregisterClass(uint);
  epregisterClassCout(uint);
  epregisterClassSerializeType(uint);
  epregisterClassJSONSerializeType(uint);
  epregisterFuncMethod(uint,uintinc,"++");
  epregisterFuncMethod(uint,uintdec,"--");
  epregisterFuncMethod(uint,uintincpre,"++prefix");
  epregisterFuncMethod(uint,uintdecpre,"--prefix");
  epregisterFuncMethod(uint,uintssum,"+=");
  epregisterFuncMethod(uint,uintssub,"-=");
  epregisterFuncMethod(uint,uintsum,"+");  // the order between uintsum and uintstrsum is important, when a sum is occurring between int + float, if uintstrsum is selected first, then the float will be converted to a string and then concatenated, which is an undesired case. having uintsum first, makes the float be converted to an int and then summed
  epregisterFuncMethod(uint,uintstrsum,"+"); 
  epregisterFuncMethod(uint,uintand,"&");
  epregisterFuncMethod(uint,uintor,"|");
  epregisterFuncMethod(uint,uintxor,"^");
  epregisterFuncMethod(uint,uintsub,"-");
  epregisterFuncMethod(uint,uintneg,"-unary");
  epregisterFuncMethod(uint,uintmul,"*");
  epregisterFuncMethod(uint,uintdiv,"/");
  epregisterFuncMethod(uint,uintmod,"%");
  epregisterFuncMethod(uint,uintnot,"!");
  epregisterFuncMethod(uint,uintgt,">");
  epregisterFuncMethod(uint,uintlt,"<");
  epregisterFuncMethod(uint,uintgteq,">=");
  epregisterFuncMethod(uint,uintlteq,"<=");
  epregisterFuncMethod(uint,uinteq,"==");
  epregisterFuncMethod(uint,uintneq,"!=");
  epregisterFuncMethod(uint,uintshl,"<<");
  epregisterFuncMethod(uint,uintshr,">>");
  epregisterFunc2(uintmin,uint,(uint,uint));
  epregisterFunc2(uintmax,uint,(uint,uint));


  epregisterClass(int);
  epregisterClassCout(int);
  epregisterClassSerializeType(int);
  epregisterClassJSONSerializeType(int);
  epregisterFuncMethod(int,intinc,"++");
  epregisterFuncMethod(int,intdec,"--");
  epregisterFuncMethod(int,intincpre,"++prefix");
  epregisterFuncMethod(int,intdecpre,"--prefix");
  epregisterFuncMethod(int,intssum,"+=");
  epregisterFuncMethod(int,intssub,"-=");
  epregisterFuncMethod(int,intsum,"+");
  epregisterFuncMethod(int,intstrsum,"+");
  epregisterFuncMethod(int,intand,"&");
  epregisterFuncMethod(int,intor,"|");
  epregisterFuncMethod(int,intxor,"^");
  epregisterFuncMethod(int,intsub,"-");
  epregisterFuncMethod(int,intneg,"-unary");
  epregisterFuncMethod(int,intmul,"*");
  epregisterFuncMethod(int,intdiv,"/");
  epregisterFuncMethod(int,intmod,"%");
  epregisterFuncMethod(int,intnot,"!");
  epregisterFuncMethod(int,intgt,">");
  epregisterFuncMethod(int,intlt,"<");
  epregisterFuncMethod(int,intgteq,">=");
  epregisterFuncMethod(int,intlteq,"<=");
  epregisterFuncMethod(int,inteq,"==");
  epregisterFuncMethod(int,intneq,"!=");
  epregisterFuncMethod(int,intshl,"<<");
  epregisterFuncMethod(int,intshr,">>");
  epregisterFunc2(intmin,int,(int,int));
  epregisterFunc2(intmax,int,(int,int));

  epregisterClass(long int);
  epregisterClassCout(long int);
  epregisterClassSerializeType(long int);
  epregisterClassJSONSerializeType(long int);
  epregisterFuncMethod(long int,longintinc,"++");
  epregisterFuncMethod(long int,longintdec,"--");
  epregisterFuncMethod(long int,longintincpre,"++prefix");
  epregisterFuncMethod(long int,longintdecpre,"--prefix");
  epregisterFuncMethod(long int,longintssum,"+=");
  epregisterFuncMethod(long int,longintssub,"-=");
  epregisterFuncMethod(long int,longintsum,"+");
  epregisterFuncMethod(long int,longintstrsum,"+");
  epregisterFuncMethod(long int,longintand,"&");
  epregisterFuncMethod(long int,longintor,"|");
  epregisterFuncMethod(long int,longintxor,"^");
  epregisterFuncMethod(long int,longintsub,"-");
  epregisterFuncMethod(long int,longintneg,"-unary");
  epregisterFuncMethod(long int,longintmul,"*");
  epregisterFuncMethod(long int,longintdiv,"/");
  epregisterFuncMethod(long int,longintmod,"%");
  epregisterFuncMethod(long int,longintnot,"!");
  epregisterFuncMethod(long int,longintgt,">");
  epregisterFuncMethod(long int,longintlt,"<");
  epregisterFuncMethod(long int,longintgteq,">=");
  epregisterFuncMethod(long int,longintlteq,"<=");
  epregisterFuncMethod(long int,longinteq,"==");
  epregisterFuncMethod(long int,longintneq,"!=");
  epregisterFuncMethod(long int,longintshl,"<<");
  epregisterFuncMethod(long int,longintshr,"<<");
  epregisterFunc2(longintmin,long int,(long int,long int));
  epregisterFunc2(longintmax,long int,(long int,long int));

  epregisterClass(unsigned long);
  epregisterClassCout(unsigned long);
  epregisterClassSerializeType(unsigned long);
  epregisterClassJSONSerializeType(unsigned long);
  epregisterFuncMethod(unsigned long,ulonginc,"++");
  epregisterFuncMethod(unsigned long,ulongdec,"--");
  epregisterFuncMethod(unsigned long,ulongincpre,"++prefix");
  epregisterFuncMethod(unsigned long,ulongdecpre,"--prefix");
  epregisterFuncMethod(unsigned long,ulongssum,"+=");
  epregisterFuncMethod(unsigned long,ulongssub,"-=");
  epregisterFuncMethod(unsigned long,ulongsum,"+");
  epregisterFuncMethod(unsigned long,ulongstrsum,"+");
  epregisterFuncMethod(unsigned long,ulongand,"&");
  epregisterFuncMethod(unsigned long,ulongor,"|");
  epregisterFuncMethod(unsigned long,ulongxor,"^");
  epregisterFuncMethod(unsigned long,ulongsub,"-");
  epregisterFuncMethod(unsigned long,ulongneg,"-");
  epregisterFuncMethod(unsigned long,ulongmul,"*");
  epregisterFuncMethod(unsigned long,ulongdiv,"/");
  epregisterFuncMethod(unsigned long,ulongmod,"%");
  epregisterFuncMethod(unsigned long,ulongnot,"!");
  epregisterFuncMethod(unsigned long,ulonggt,">");
  epregisterFuncMethod(unsigned long,ulonglt,"<");
  epregisterFuncMethod(unsigned long,ulonggteq,">=");
  epregisterFuncMethod(unsigned long,ulonglteq,"<=");
  epregisterFuncMethod(unsigned long,ulongeq,"==");
  epregisterFuncMethod(unsigned long,ulongneq,"!=");
  epregisterFuncMethod(unsigned long,ulongshl,"<<");
  epregisterFuncMethod(unsigned long,ulongshr,"<<");
  epregisterFunc2(ulongmin,unsigned long,(unsigned long,unsigned long));
  epregisterFunc2(ulongmax,unsigned long,(unsigned long,unsigned long));


  epregisterClass(bool);
  epregisterClassCout(bool);
  epregisterClassSerializeType(bool);
  epregisterClassJSONSerializeType(bool);
  epregisterClassConstructor(bool,(int));
  epregisterFuncMethod(bool,boolnot,"!");
  epregisterFuncMethod(bool,booland,"&&");
  epregisterFuncMethod(bool,boolor,"||");
  epregisterFuncMethod(bool,boolxor,"^");

  epregisterClass(char);
  epregisterClassCout(char);
  epregisterClassSerializeType(char);
  epregisterFuncMethod(char,chareqstr,"==");

  epregisterClass(float);
  epregisterClassCout(float);
  epregisterClassSerializeType(float);
  epregisterClassJSONSerializeType(float);
  epregisterFuncMethod(float,floatinc,"++");
  epregisterFuncMethod(float,floatdec,"--");
  epregisterFuncMethod(float,floatincpre,"++prefix");
  epregisterFuncMethod(float,floatdecpre,"--prefix");
  epregisterFuncMethod(float,floatssum,"+=");
  epregisterFuncMethod(float,floatssub,"-=");
  epregisterFuncMethod(float,floatsum,"+");
  epregisterFuncMethod(float,floatstrsum,"+");
  epregisterFuncMethod(float,floatsub,"-");
  epregisterFuncMethod(float,floatneg,"-unary");
  epregisterFuncMethod(float,floatmul,"*");
  epregisterFuncMethod(float,floatdiv,"/");
  epregisterFuncMethod(float,floatgt,">");
  epregisterFuncMethod(float,floatlt,"<");
  epregisterFuncMethod(float,floatgteq,">=");
  epregisterFuncMethod(float,floatlteq,"<=");
  epregisterFuncMethod(float,floateq,"==");
  epregisterFuncMethod(float,floatneq,"!=");
  epregisterFunc2(floatmin,float,(float,float));
  epregisterFunc2(floatmax,float,(float,float));


  epregisterClass(double);
  epregisterClassCout(double);
  epregisterClassSerializeType(double);
  epregisterClassJSONSerializeType(double);
  epregisterFuncMethod(double,doubleinc,"++");
  epregisterFuncMethod(double,doubledec,"--");
  epregisterFuncMethod(double,doubleincpre,"++prefix");
  epregisterFuncMethod(double,doubledecpre,"--prefix");
  epregisterFuncMethod(double,doublessum,"+=");
  epregisterFuncMethod(double,doublessub,"-=");
  epregisterFuncMethod(double,doublesum,"+");
  epregisterFuncMethod(double,doublestrsum,"+");
  epregisterFuncMethod(double,doublesub,"-");
  epregisterFuncMethod(double,doubleneg,"-unary");
  epregisterFuncMethod(double,doublemul,"*");
  epregisterFuncMethod(double,doublediv,"/");
  epregisterFuncMethod(double,doublegt,">");
  epregisterFuncMethod(double,doublelt,"<");
  epregisterFuncMethod(double,doublegteq,">=");
  epregisterFuncMethod(double,doublelteq,"<=");
  epregisterFuncMethod(double,doubleeq,"==");
  epregisterFuncMethod(double,doubleneq,"!=");
  epregisterFunc2(doublemin,double,(double,double));
  epregisterFunc2(doublemax,double,(double,double));


  epregisterClass(efunc);
  epregisterClassMethod4(efunc,operator=,efunc&,(const efunc&),"=");
  epregisterClassMethod4(efunc,operator(),evar,() const,"()");
  epregisterClassMethod4(efunc,operator(),evar,(const evararray& args) const,"()");
  epregisterClassMethod2(efunc,call,evar,(const estr&) const);
  epregisterClassMethod2(efunc,call,evar,(const evararray&) const);
  epregisterClassSerializeMethod(efunc);
  epregisterClassJSONSerializeMethod(efunc);

//  epregisterClass(ethread);
//  epregisterClassConstructor(ethread,());
//  epregisterClassMethod(ethread,run);
//  epregisterClassMethod(ethread,isBusy);
//  epregisterClassMethod(ethread,wait);

  epregisterClass(estr);
  epregisterClassCout(estr);
  epregisterClassSerializeMethod(estr);
  epregisterClassJSONSerializeMethod(estr);
  epregisterClassConstructor(estr,(int));
  epregisterClassConstructor(estr,(long));
  epregisterClassConstructor(estr,(float));
  epregisterClassConstructor(estr,(double));
  epregisterClassMethod(estr,h);
  epregisterClassMethod(estr,i);
  epregisterClassMethod(estr,f);
  epregisterClassMethod(estr,d);
  epregisterClassMethod(estr,hex);
  epregisterClassMethod(estr,hex2bin);
  epregisterClassMethod(estr,is_hex);
  epregisterClassMethod(estr,is_int);
  epregisterClassMethod(estr,is_float);
  epregisterClassMethod(estr,is_num);
#ifdef EUTILS_HAVE_ZLIB
//  epregisterFunc2((estr(*)(const estr&))compress,"compress");
//  epregisterFunc2((estr(*)(const estr&))decompress,"decompress");
  epregisterFunc2(decompress,estr,(const estr&));
  epregisterFunc2(compress,estr,(const estr&));
  epregisterFunc2(gzdecompress,estr,(const estr&));
  epregisterFunc2(gzcompress,estr,(const estr&));
#endif
  
  epregisterClassMethodD(estr,substr,evararray(-1));
  epregisterClassMethodD(estr,erase,evararray(-1));
  epregisterClassMethodD(estr,find,evararray(0));
  epregisterClassMethod2(estr,getline,estr,());
  epregisterClassMethod(estr,replace);
  epregisterClassMethod(estr,grep);
  epregisterClassMethod(estr,egrep);
  epregisterClassMethod(estr,egrepo);
  epregisterClassMethod(estr,explode);
  epregisterClassMethod(estr,len);
  epregisterClassMethod4(estr,operator<,bool,(const estr&) const,"<");
  epregisterClassMethod4(estr,operator<=,bool,(const estr&) const,"<=");
  epregisterClassMethod4(estr,operator>,bool,(const estr&) const,">");
  epregisterClassMethod4(estr,operator>=,bool,(const estr&) const,">=");
  epregisterClassMethod4(estr,operator==,bool,(const estr&) const,"==");
  epregisterClassMethod4(estr,operator!=,bool,(const estr&) const,"!=");
  epregisterClassMethod4(estr,operator+,estr,(const estr&) const,"+");
  epregisterClassMethod4(estr,operator+=,estr&,(const estr&),"+=");
  epregisterClassMethod4(estr,operator[],char&,(long int),"[]");

  epregisterClassConverterCast(estrarray,estr);

  epregisterClassCout(ebasearrayof);
  epregisterClassSerializeMethod(ebasearrayof);
  epregisterClassJSONSerializeMethod(ebasearrayof);

  epregisterClass(ebaseoption);
  epregisterClassMethod4(ebaseoption,operator=,int,(const estr&),"=");

  epregisterClass(estrhashof<evar>);
  epregisterClassCout(estrhashof<evar>);
  epregisterClassInheritance(estrhashof<evar>,ebasearrayof);
  epregisterClassSerializeMethod(estrhashof<evar>);
  epregisterClassMethod4(estrhashof<evar>,operator[],evar&,(const estr&),"[]");
  epregisterClassMethod(estrhashof<evar>,exists);

  epregisterClassInheritance(estrhashof<eclassBase>,ebasearrayof);
  epregisterClassInheritance(estrhashof<eclassPropertyBase>,ebasearrayof);
  epregisterClassInheritance(estrhashof<ebasicarray<eclassMethodBase*> >,ebasearrayof);
  epregisterClassInheritance(estrhashof<eclassBase*>,ebasearrayof);

  epregisterClass(estrarrayof<evararray>);
  epregisterClassInheritance(estrarrayof<evararray>,ebasearrayof);
  epregisterClassSerializeMethod(estrarrayof<evararray>);
  epregisterClassJSONSerializeMethod(estrarrayof<evararray>);

  epregisterClass(estrarrayof<int>);
  epregisterClassInheritance(estrarrayof<int>,ebasearrayof);
  epregisterClassSerializeMethod(estrarrayof<int>);
  epregisterClassJSONSerializeMethod(estrarrayof<int>);

  epregisterClass(estrarray);
  epregisterClassInheritance(estrarray,ebasearrayof);
  epregisterClassSerializeMethod(estrarray);
  epregisterClassJSONSerializeMethod(estrarray);

  epregisterClass(estrhash);
  epregisterClassInheritance(estrhash,ebasearrayof);
  epregisterClassSerializeMethod(estrhash);
  epregisterClassJSONSerializeMethod(estrhash);



  epregisterClassCout(ebasearray);
  epregisterClassSerializeMethod(ebasearray);
  epregisterClassJSONSerializeMethod(ebasearray);


  epregisterClass(earray<estr>);
  epregisterClassPolymorphic(earray<estr>,ebasearray);
  epregisterClassInheritance(earray<estr>,ebasearray);
  epregisterClassConverterCast(earray<estr>,earray<evar>);
  epregisterClassSerializeMethod(earray<estr>);
  epregisterClassJSONSerializeMethod(earray<estr>);

  epregisterClass(edoublearray);
  epregisterClassPolymorphic(edoublearray,ebasearray);
  epregisterClassInheritance(edoublearray,ebasearray);
  epregisterClassConverterCast(edoublearray,earray<evar>);
  epregisterClassSerializeMethod(edoublearray);
  epregisterClassJSONSerializeMethod(edoublearray);

  epregisterClass(efloatarray);
  epregisterClassPolymorphic(efloatarray,ebasearray);
  epregisterClassInheritance(efloatarray,ebasearray);
  epregisterClassConverterCast(efloatarray,earray<evar>);
  epregisterClassSerializeMethod(efloatarray);
  epregisterClassJSONSerializeMethod(efloatarray);

  epregisterFunc3(maxvar,"max");
  epregisterFunc3(minvar,"min");

  epregisterFunc(apply);
  epregisterFunc(map);

  epregisterFunc(invarr);
  epregisterFunc2(logarr,eintarray&,(eintarray&));
  epregisterFunc2(logarr,efloatarray&,(efloatarray&));
  epregisterFunc2(logarr,edoublearray&,(edoublearray&));
  epregisterFunc3(absarr,"abs");

  epregisterFunc2(movavgarr,edoublearray,(const edoublearray&,int));

  epregisterClass(estrhashof<int>);
  epregisterClassInheritance(estrhashof<int>,ebasearrayof);

  epregisterClass(estrarrayof<elongarray>);
  epregisterClassInheritance(estrarrayof<elongarray>,ebasearrayof);

  epregisterFunc(uniqueset);
  epregisterFunc3(uniquearr,"unique");
  epregisterFunc3(countarr,"count");
  epregisterFunc3((int(*)(const eintarray&))maxarr<int>,"max");
  epregisterFunc3((float(*)(const efloatarray&))maxarr<float>,"max");
  epregisterFunc3((double(*)(const edoublearray&))maxarr<double>,"max");
  epregisterFunc3((evar(*)(const evararray&))maxarr<evar>,"max");

  epregisterFunc3((int(*)(const eintarray&))minarr<int>,"min");
  epregisterFunc3((float(*)(const efloatarray&))minarr<float>,"min");
  epregisterFunc3((double(*)(const edoublearray&))minarr<double>,"min");
  epregisterFunc3((evar(*)(const evararray&))minarr<evar>,"min");

  epregisterFunc3((evararray(*)(const evararray&,double))minarr,"min");
  epregisterFunc3((evararray(*)(const evararray&,double))maxarr,"max");
  epregisterFunc3((evararray(*)(const evararray&,float))minarr,"min");
  epregisterFunc3((evararray(*)(const evararray&,float))maxarr,"max");
  epregisterFunc3((evararray(*)(const evararray&))tanarr,"tan");
  epregisterFunc3((evararray(*)(const evararray&))logarr,"log");


  epregisterClass(eintarray);
  epregisterClassPolymorphic(eintarray,ebasearray);
  epregisterClassInheritance(eintarray,ebasearray);
  epregisterClassConverterCast(eintarray,earray<evar>);
  epregisterClassConverterCast(eintarray,estrhashof<evar>);
  epregisterClassSerializeMethod(eintarray);
  epregisterClassJSONSerializeMethod(eintarray);
  
  epregisterClassMethod4(eintarray,operator[],int&,(size_t),"[]");
  epregisterClassMethod4(eintarray,operator[],eintarray,(const eintarray&) const,"[]");

  epregisterClass(elongarray);
  epregisterClassPolymorphic(elongarray,ebasearray);
  epregisterClassInheritance(elongarray,ebasearray);
  epregisterClassConverterCast(elongarray,earray<evar>);
  epregisterClassConverterCast(elongarray,estrhashof<evar>);
  epregisterClassSerializeMethod(elongarray);
  epregisterClassJSONSerializeMethod(elongarray);

  epregisterClass(evarhash);
  epregisterClassConstructorFunc(evarhash,evarhashInit);
  epregisterClassSerializeMethod(evarhash);
  epregisterClassJSONSerializeMethod(evarhash);
  epregisterClassInheritance(evarhash,ebasearray);

  epregisterClass(evararray);
  epregisterClassConstructorFunc(evararray,evararrayInit);
  epregisterClassSerializeMethod(evararray);
  epregisterClassJSONSerializeMethod(evararray);
  epregisterClassInheritance(evararray,ebasearray);
  epregisterFuncMethod2<evararray>((evararray (*)(const evararray&,const evararray&))mularr,"*");
  epregisterFuncMethod2<evararray>((evararray (*)(const evararray&,float))mularr,"*");
//  epregisterFunc(mularr);
  epregisterFuncMethod2<evararray>((evararray (*)(evararray&,evararray&))sumarr,"+");
  epregisterFuncMethod2<evararray>((evararray (*)(const evararray&,float))sumarr,"+");
//  epregisterFuncMethod(evararray,sumarr,"+");
  epregisterFuncMethod2<evararray>((evararray (*)(evararray&,evararray&))subarr,"-");
  epregisterFuncMethod2<evararray>((evararray (*)(evararray&,float))subarr,"-");
//  epregisterFuncMethod(evararray,subarr,"-");
  epregisterFuncMethod2<evararray>((evararray (*)(evararray&,evararray&))divarr,"/");
  epregisterFuncMethod2<evararray>((evararray (*)(evararray&,float))divarr,"/");
//  epregisterFuncMethod(evararray,divarr,"/");
  epregisterFuncMethod(evararray,gtarr,">");
  epregisterFuncMethod(evararray,ltarr,"<");
  epregisterFuncMethod(evararray,gtearr,">=");
  epregisterFuncMethod(evararray,ltearr,"<=");
  epregisterFuncMethod2<evararray>((eintarray (*)(evararray&,evar&))eqarr,"==");
//  epregisterFuncMethod2<evararray>((eintarray (*)(evararray&,float))eqarr,"==");
  epregisterFuncMethod(evararray,neqarr,"!=");


  epregisterFunc3(normarr,"normarr");
  epregisterFunc3(matcharr,"matcharr");
  epregisterFunc3(findarr,"find");
  epregisterFunc3(inarr,"inarray");
  epregisterFunc3(joinarr,"joinarray");
  epregisterFuncD(seqarr,evararray(1,0));
  epregisterFuncD(rndarr,evararray(-1));

  epregisterFuncMethod(eintarray,orarr,"|");
  epregisterFuncMethod(eintarray,andarr,"&");
  epregisterFuncMethod(eintarray,xorarr,"^");
  epregisterFunc3(nullarr,"isNull");


  epregisterClass(etable);
  epregisterClassCout(etable);
  epregisterClassJSONSerializeMethod(etable);
  epregisterClassSerializeMethod(etable);

  epregisterClass(evector2);
  epregisterClassCout(evector2);
  epregisterClassConstructor(evector2,(double,double));

  epregisterClass(evector3);
  epregisterClassConstructor(evector3,(double,double,double));
  epregisterClassCout(evector3);

  epregisterClass(evector4);
  epregisterClassConstructor(evector4,(double,double,double,double));
  epregisterClassCout(evector4);

  epregisterClass(ematrix);
  epregisterClassCout(ematrix);

  epregisterClass(evector);
  epregisterClassCout(evector);

  epregisterFunc(include);

  epregisterClass(evarRemote);
  epregisterClassSerializeMethod(evarRemote);

  // register all functions, needed here if one wants to use them in argument parsing
  epregisterFunctions();
#endif
}


eparser* parser=0x00;

void setParser(eparser& _parser)
{
  if (parser==&_parser) return;
  if (parser) delete parser;
  parser=&_parser;
}

eparser& getParser()
{
  if (!parser){
    getLogger();
    parser = new eparser();
    parser->registerParser();
  }
  return(*parser);
}

void epregisterAlias(const estr& alias,const estr& var)
{
  getParser().argAlias.add(alias,var);
}
void epregisterEnd()
{
  getParser().registerEnd();
}

void eparseArgs()
{
  getParser().parseArgs();
}

void eparseArgs(int _argvc,char* _argv)
{
  getParser().parseArgs(_argvc,_argv);
}

bool epfuncExists(const estr& funcname)
{
  if (getParser().funcs.exists(funcname))
    return(true);
  
  return(false);
}

bool epvarExists(const estr& varname)
{
  if (getParser().objects.exists(varname))
    return(true);
  return(false);
}

eparser::eparser(): showHelp(false),userRegister(false),parsing(false),showRegister(false)
{
  // WARNING: dont put any class or method registering here
  //  classes use eparser to register
}

eparser::~eparser()
{
  if (showHelp){
//    if (userRegister) cout << "you should call parser->registerEnd() after you registered all your functions" << endl;  // we dont use ldwarn here because the logger object might no longer exist, as we have no control over the order of destruction of objects at the end of a program
    registerEnd();
  }
}


void eparser::registerEnd()
{
/*
  if (showHelp){
    help();
    exit(0);
  }

  int i;
  for (i=0; i<initvalues.size(); ++i){
//    cout << initvalues.keys(i)+"="+initvalues.values(i) << endl;
    if (initvalues.values(i).len())
      epinterpret(initvalues.keys(i)+"="+initvalues.values(i)+";");
    else
      epinterpret(initvalues.keys(i)+"()"+";");
  }
*/

/*
  int i,j;
  estrarray infostr;
  estr section;
  printf("\n");
  for (i=0; i<cmds.size(); ++i){
      if (section!=cmds[i].section){
        section=cmds[i].section;
        printf("\n");
        printf(" %s\n",section._str);
      }
      infostr = cmds[i].info.explode("\n");
      
      printf("  -%s --%-15s %s\n",cmds[i].argname._str,cmds[i].name._str,infostr[0]._str);
      for (j=1; j<infostr.size(); ++j)
        printf("                       %s\n",infostr[j]._str);
  }
  printf("\n");
*/
}

void eparser::help(const estr& line)
{
  if (onHelp.isSet()){
    onHelp.call();
    return;
  }
  estr tmp;
  tmp=(programName.len()?programName:estr(efile(args[0]).basename()));
  if (programVersion.len())
    tmp+=" v"+programVersion;
  cout << tmp << endl;
  if (programShort.len())
    cout << programShort << endl;
  cout << endl;

  cout << "Usage:" << endl;
  cout << "    " << estr(efile(args[0]).basename()) << " " << programUsage << endl;
  cout << endl;

  if (programExamples.len()){
    cout << programExamples << endl;
    cout << endl;
  }

  if (programMisc.len()){
    cout << programMisc << endl;
    cout << endl;
  }

  cout << "Actions:" << endl;
  int i;
  for (i=0; i<actions.size(); ++i){
    estr infostr;
    if (actionInfo.exists(actions.keys(i)))
      infostr=actionInfo.values(actions.keys(i))+"   ";

    printf("  --%-15s %s\n",actions.keys(i)._str,infostr._str);
  }
  



  cout << "Optional arguments:" << endl;
  for (i=0; i<objects.size(); ++i){
    if (objects.values(i).getTypeid()==typeid(unsigned char) || objects.values(i).getTypeid()==typeid(unsigned long) || objects.values(i).getTypeid()==typeid(unsigned int) || objects.values(i).getTypeid()==typeid(char) || objects.values(i).getTypeid()==typeid(bool) || objects.values(i).getTypeid()==typeid(long) || objects.values(i).getTypeid()==typeid(float) || objects.values(i).getTypeid()==typeid(double) || objects.values(i).getTypeid()==typeid(int) || objects.values(i).getTypeid()==typeid(estr)){
      estr classname(getClassName(&objects.values(i).getTypeid()));
      estr defvalue(objects.values(i));
      
      estr infostr;
      if (objectInfo.exists(objects.keys(i)))
        infostr=objectInfo.values(objects.keys(i))+"   ";

      printf("  --%-15s %s[type: %s default: %s]\n",objects.keys(i)._str,infostr._str,classname._str,defvalue._str);
    }
  }
/*
  cout << " - registered functions:"<<endl;
  for (i=0; i<funcs.size(); ++i)
    cout << funcs.keys(i) << endl;
*/
  if (programAuthors.len()){
    cout << endl;
    cout << programAuthors << endl;
  }

  if (parsing)
    showRegister=true;
  exit(0);
}

void eparser::save(const estr& filename)
{
  int i;
  estr data;

  for (i=0; i<objects.size(); ++i){
    objects.keys(i).serial(data);
    objects[i].serial(data);
  }

  efile file(filename,"w");
  file.write(data);
  file.close();
}

void eparser::load(const estr& filename)
{
  estr data;
  efile file(filename,"r");
  file.read(data);
  file.close();

  int i=0;
  while (i < data.len()){
    estr varname;
    i=varname.unserial(data,i);
    if (objects.exists(varname)){
      evar var;
      i=var.unserial(data,i);
      objects[varname]=var;
    }else{
      evar *var=new evar;
      i=var->unserial(data,i);
      objects.add(varname,var);
//      lwarn("object: "+varname+" does not exist while loading environment: "+filename);
    }
    cout << "loaded varname: "<<varname<<endl;
  }
}


void eparser::registerParser()
{
  epregisterClassPropertyI(elogger,level,"verbose level");
  epregisterClassPropertyI(elogger,modules,"verbose modules");
  epregister2(getLogger(),"logger");
  epregisterClassMethodI(eparser,help,"show command help");
  epregisterClassMethodI(eparser,load,"load registered variables from file");
  epregisterClassMethodI(eparser,save,"save registered variables to file");
  epregisterClassPropertyI(eparser,objects,"global variables");
  epregisterCommon();
  epregister(parser);
  epregisterAlias("h","parser.help");
  epregisterAlias("help","parser.help");
  epregisterAlias("save","parser.save");
  epregisterAlias("load","parser.load");
//  getLogger()->level=1;
}

estr escape_quotes(const estr& str)
{
  estr tmpstr(str);
  tmpstr.replace("\"","\\\"");
  return(tmpstr);
}

void eparser::execArg(estr cmd,const estr& args)
{
//  int i;
  if (cmd.len()==0) return;

  if (cmd[0]=='-') cmd.del(0,1);
  if (cmd[0]=='-') cmd.del(0,1);

//  cout << argAlias << endl;
  if (argAlias.exists(cmd))
    cmd=argAlias[cmd];

  evar var(epinterpret(cmd+";"));
  ldieif(estr(var.getClass()) == "empty","unknown argument: "+cmd);

  if (args.len() && args[0]=='{' && args[args.len()-1]=='}'){
    epinterpret(cmd+"="+args.substr(1,-2)+";");
    return;
  }

//  cout << cmd << " --- " << var.getClass() << endl;
  if (var.getTypeid()==typeid(efunc))
    var.get<efunc>().call(evararray(args));
  else if (var.getTypeid()==typeid(estr) || var.getTypeid()==typeid(estrarray))
    epinterpret(cmd+"=\""+escape_quotes(args)+"\";");
  else if (var.getTypeid()==typeid(int))
    epinterpret(cmd+"="+args.i()+";");
  else if (var.getTypeid()==typeid(long int))
    epinterpret(cmd+"="+args.l()+";");
  else if (var.getTypeid()==typeid(float))
    epinterpret(cmd+"="+args.f()+";");
  else if (var.getTypeid()==typeid(double))
    epinterpret(cmd+"="+args.d()+";");
  else if (var.getTypeid()==typeid(bool)){
    if (args=="false" || args=="0")
      epinterpret(cmd+"=0;");
    else if (args=="true" || args=="1")
      epinterpret(cmd+"=1;");
    else
      ldie("invalid value for boolean argument: "+cmd);
  } else
    epinterpret(cmd+"=\""+args+"\";");

/*
  int i;
  for (i=0; i<initvalues.size(); ++i){
//    cout << initvalues.keys(i)+"="+initvalues.values(i) << endl;
    if (initvalues.values(i).len())
      epinterpret(initvalues.keys(i)+"="+initvalues.values(i)+";");
    else
      epinterpret(initvalues.keys(i)+"()"+";");
  }
*/

//  initvalues.add(cmd,args);

/*
  ldinfo("executing cmd: "+cmd+", args: "+args);
  int i;
  i=cmd.find(".");
  if (i>0){
    estr prop=cmd.substr(i+1);
    cmd.del(i);
    if (epvarExists(cmd)){
      ldinfo("found var: "+cmd);
      evar obj(epget(cmd).var);
      estr objclass = obj.getClass();
      if (classes.exists(objclass)){
        if (classes.values(objclass).method_exists(prop)){
          ldinfo("calling method: "+prop);
          obj.call(prop,args);
        }else if (classes.values(objclass).property_exists(prop)){
          ldinfo("setting property: "+prop);
          obj.set(prop,args);
        }else
          lwarn("Method or property: "+prop+" not found for obj: "+cmd);
      }else
        lwarn("Object class not registered");
    } else {
      initpropvalues.add(cmd,args);
      ldinfo("stored init prop value "+cmd);
    }
    return;
  }

  if (epvarExists(cmd)){
    ldinfo("found var: "+cmd);
    epget(cmd).set(args);
    return;
  }
  if (epfuncExists(cmd)){
    ldinfo("found function: "+cmd);
    epcall(cmd,args);
    return;
  }
  initvalues.add(cmd,args);
  ldinfo("stored init value "+cmd);
*/
}

void eparser::parseArgs(int _argvc,char* _argv)
{
  args.clear();
  for (int i=0; i<_argvc; ++i)
    args.add(_argv[i]);
  parseArgs();
}

void eparser::parseArgs()
{
//  argv=&_argv;
//  argvc=&_argvc;
  if (args.size()==0) return;

  earray<estr> newargs;
  estr fargs;
  estr fcmd;

  parsing=true;
  estr setaction;

  newargs.add(args[0]);
 
  int i;
  bool process=true;
  for (i=1; i<args.size(); ++i){
    if (!process || args[i].len()==0 || args[i]=="-" || (args[i][0]!='-' && args[i][0]!='+'))
      { newargs.add(args[i]); continue; }

    if (args[i]=="--") { process=false; continue; } // do not parse arguments after a "--" argument

    fcmd=args[i];
    if (fcmd.len() && fcmd[0]=='-') fcmd.del(0,1);
    if (fcmd.len() && fcmd[0]=='-') fcmd.del(0,1);

    if (actions.exists(fcmd)){
      setaction=fcmd;
      process=false;
      continue;
    }else{
      evar tmpvar;
      if (argAlias.exists(fcmd))
        tmpvar.set(argAlias[fcmd]);
      else if (objects.exists(fcmd)) 
        tmpvar.set(objects[fcmd]);

      // single flag for boolean values
      if (!tmpvar.isNull() && tmpvar.getTypeid()==typeid(bool)){
        tmpvar.get<bool>()=true;
        continue;
      }
      fargs.clear();
      if (i+1<args.size() && (args[i+1]=="-" || args[i+1].is_num() || (args[i+1].len()>0 && args[i+1][0]!='-' && args[i+1][0]!='+'))){
        fargs=args[i+1];
        ++i;
      }
      execArg(fcmd,fargs);
    }
  }

  args=newargs;
  parsing=false;
  if (setaction.len()>0)
    actions[setaction].call();
}

evararray map(const efunc& func,const evararray& arr)
{
  evararray argarr;
  argarr.add(evar());

  evararray res;
  for (long i=0; i<arr.size(); ++i){
    argarr[0].set(arr[i]);
    res.add(func.call(argarr));
  }
  return(res);
}

evar apply(const efunc& func,const evararray& args)
{
  eintarray argarr;
  ebasicarray<ebasearray*> argvar;
  evararray tmparr;
  int i;
  for (i=0; i<func.fArgs.size() && i<args.size(); ++i){
    if (*func.fArgs[i]!=args[i].getTypeid() && !args[i].isConvertible(*func.fArgs[i])){
      if (args[i].getTypeid()==typeid(evararray) || args[i].isConvertible(typeid(ebasearray))){
        argarr.add(i);
        argvar.add(&args[i].convert(typeid(ebasearray)).get<ebasearray>());
        tmparr.add(evar());
      }else{
        lwarn("Apply failed, argument "+estr(i)+" of type: "+args[i].getClass()+" does not match function argument: "+func.fArgs[i]->name());
        return(evar());
      }
    }else{
      tmparr.add(args[i]);
    }
  }
  if (argarr.size()==0)
    return(func.call(args));

  eintarray ind;
  ind.init(argarr.size(),0);
  evvararray results;

  while (ind[0] < argvar[0]->size()){
    for (i=0; i<ind.size(); ++i){
      tmparr[argarr[i]].set(argvar[0]->getvar(ind[0]));
      if (*func.fArgs[argarr[i]]!=args[argarr[i]].getTypeid() && !args[argarr[i]].isConvertible(*func.fArgs[argarr[i]])){
        lwarn("Apply failed, argument "+estr(argarr[i])+" of type: "+args[argarr[i]].getClass()+" does not match function argument: "+func.fArgs[argarr[i]]->name());
        return(evar());
      }
    }
    results.add(tmparr,func.call(tmparr));
    for (i=ind.size()-1; i>=0; --i){
      ++ind[i];
      if (ind[i]==argvar[i]->size() && i>0) ind[i]=0;
      else break;
    }
  }
  return(results); 
}



