#ifndef _VAR_DATA_H 
#define _VAR_DATA_H

/*

This is the intellectual proprerty of Matthew W. Coan.

SCALAR VARIABLE C++ CLASS...

A scaler variable is a number and a string variable put 
together in one object.  The "var" class is a C++ strong
data type.  The t_var< T > template class can be used
like garbage collection by using a reference count 
that deletes the object whtne the refernece count drops
to zero.

Author: Matthew W. Coan
Date: Fri May 25 16:20:16 EDT 2018

*/

#include <assert.h>
#include <sys/types.h>
#include <limits.h>
#include <float.h>
#include <cctype>
#include <string> 
#include <map>
#include <sstream>
//#include "str.h"
#include "varop.h"

//namespace string_tools {

using namespace std;

/*
inline
double
my_atof(const char * arg)
{
   double a = 0.0;
   //a = atof(arg);
   double a = 0.0;
   char buffer[1024];
   char* p;
   strcpy(buffer, arg);
   int i = strlen(buffer);
   if(i) {
      i--;
      while(buffer[i] == '0' && i > 0) {
         buffer[i] = '\0';
         i--;
      }
   }
   p = strstr(buffer, ".");
   if(p) {
      *p = '\0';
      int temp = atoi(arg);
      a = (double)temp;
      p++;
      int temp2 = atoi(p);
      double temp3 = (double)temp2;
      while(temp3 >= 1.0) {
         temp3 = temp3 * 0.1;
      }
      a = a + temp3;
   }
   else {
      a = (double)atoi(arg);
   }
   return a;
}
*/


class var;

class var_exception { 
   const char * file; 
   int the_line;
   const char * message;
public:
   var_exception(const char * message,
                 int line,
                 const char * file) {
      this->file = file;
      this->the_line = the_line;
      this->message = message;
   }
   var_exception(const char * message) {
      this->file = "";
      this->the_line = 0;
      this->message = message;
   }

   const char * get_message() const { return message; }
   const char * get_file() const { return file; }
   int get_line() const { return the_line; }
};

class var_compare {
public:
   bool operator()(const var & v1, const var & v2) const;
};
typedef map< var, var, var_compare > var_map_type;
typedef void * (*decon_type)(void *);
union value_type {
   char cvalue;
   short svalue;
   int ivalue;
   float gvalue;
   double dvalue;
   long lvalue;
   bool bvalue;
   var * vvalue;
   var_map_type * avalue;
   void * pvalue;
};
class VAR_DATA {
public:
   enum var_type {
      CHAR_TYPE,
      SHORT_TYPE,
      INT_TYPE,
      FLOAT_TYPE,
      LONG_TYPE,
      DOUBLE_TYPE,
      BOOL_TYPE,
      STRING_TYPE,
      ARRAY_TYPE,
      FUNCTION_TYPE,
      POINTER_TYPE,
      VOID_TYPE,
      REFERENCE_TYPE,
   };

   size_t * count;
   value_type value;
   var_type type;
   var_type ret_type;
   decon_type decon;
   string svalue;

   VAR_DATA() {
      count = 0;
      type = INT_TYPE;
      value.ivalue = 0;
      ret_type = INT_TYPE;
      decon = 0;
   }

   VAR_DATA(const VAR_DATA & cp) {
      count = cp.count;
      type = cp.type;
      value = cp.value;
      ret_type = cp.ret_type;
      decon = cp.decon;
      svalue = cp.svalue;
   }

   VAR_DATA(const VAR_DATA::var_type t) {
      ret_type = INT_TYPE;
      type = t;
      decon = 0;
      if(type == CHAR_TYPE) {
         value.cvalue = '\0';
         count = 0;
      }
      else if(type == SHORT_TYPE) {
         value.svalue = 0;
         count = 0;
      }
      else if(type == INT_TYPE) {
         value.ivalue = 0;
         count = 0;
      }
      else if(type == FLOAT_TYPE) {
         value.ivalue = 0;
         count = 0;
      }
      else if(type == LONG_TYPE) {
         value.lvalue = 0;
         count = 0;
      }
      else if(type == DOUBLE_TYPE) {
         value.dvalue = 0.0;
         count = 0;
      }
      else if(type == BOOL_TYPE) {
         value.bvalue = false;
         count = 0;
      }
      else if(type == STRING_TYPE) {
         count = 0;
      }
      else if(type == ARRAY_TYPE) {
         value.avalue = new var_map_type();
         count = new size_t;
         *count = 1;
      }
      else if(type == FUNCTION_TYPE) {
         value.pvalue = 0;
         count = 0;
      }
   }

   //template< class T >
   VAR_DATA(void* t) {
      value.pvalue = t;
      type = POINTER_TYPE;
      count = new size_t;
      *count = 1;   
   }

   ~VAR_DATA() {
      dec();
   }
   VAR_DATA & operator=(const VAR_DATA & right) {
      if(this != &right) {
         dec();
         count = right.count;
         value = right.value;
         type = right.type;
         svalue = right.svalue;
         ret_type = right.ret_type;
         decon = right.decon;
         inc();
      }
      return *this;
   }
   void inc();
   bool dec();
   friend class var; 
};

class var {
public:
   typedef map< var, var, var_compare >::iterator iterator;
   //typedef var_map_type::reverse_iterator reverse_iterator;

private:
   VAR_DATA data;

   bool dec() {
      bool ret = false;
      if(data.dec()) {
         ret = true;
      }
      return ret;
   }

   void def_init() {
      data.value.ivalue = 0;
      data.type = VAR_DATA::INT_TYPE;
      data.count = 0;
   }

   var * get_ref() {
      var * ptr = this;
      while(ptr->data.type == VAR_DATA::REFERENCE_TYPE) {
         ptr = ptr->data.value.vvalue;
      }
      return ptr;
   }
public:
   var() {
      data.type = VAR_DATA::INT_TYPE;
      data.value.ivalue = 0;
   }
   var(const size_t sz) {
      data.value.lvalue = sz;
      data.type = VAR_DATA::LONG_TYPE;
   }
   var(const var & cp)  {
   //: data(cp.data) {
      //data.inc();
      data = cp.data;
   }
   var(void * ptr) {  
      data.value.pvalue = ptr;
      data.type = VAR_DATA::POINTER_TYPE;
   }
/*
   template< class T >
   var(T ptr) :data(ptr) {
      data.decon = (decon_type)T::~T;
   }
*/
   template< class T >
   var(T f, const VAR_DATA::var_type rt) {
      data.value.pvalue = (void*)f;
      data.type = VAR_DATA::FUNCTION_TYPE;
      data.ret_type = rt;
   }
   var(const bool b) {
      data.value.bvalue = b;
      data.type = VAR_DATA::BOOL_TYPE;
   }
   var(const double d) {
      data.value.dvalue = d;
      data.type = VAR_DATA::DOUBLE_TYPE;
   }
   var(const long l) {
      data.value.lvalue = l;
      data.type = VAR_DATA::LONG_TYPE;
   }
   var(const int l) {
      data.value.ivalue = l;
      data.type = VAR_DATA::INT_TYPE;
   }
   var(const float f) {
      data.value.dvalue = f;
      data.type = VAR_DATA::DOUBLE_TYPE;
   }
   var(const VAR_DATA::var_type t) 
   : data(t) {
   }
   var(var * p) {
      data.value.vvalue = p->get_ref();
      data.type = VAR_DATA::REFERENCE_TYPE;
      //data.inc();
   }
   var(const char * str) {
      data.svalue = str;
      data.type = VAR_DATA::STRING_TYPE;
   }
   var(const string & str) {
      data.svalue = str;
      data.type = VAR_DATA::STRING_TYPE;
   }
   ~var() {
   }
   string to_str() const {
      return var::to_string();
   }
   string to_string() const {
      string ret;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_string();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = data.svalue;
      }
      else {
         stringstream str;
         if(data.type == VAR_DATA::LONG_TYPE)
            str << data.value.lvalue;
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            str << data.value.dvalue;
         else if(data.type == VAR_DATA::BOOL_TYPE)
            str << data.value.ivalue;
         else if(data.type == VAR_DATA::INT_TYPE)
            str << data.value.ivalue;
         else if(data.type == VAR_DATA::FLOAT_TYPE)
            str << data.value.gvalue;
         else 
            str << 0;
         str >> ret;
      }
      return ret;
   }
   const char * c_str() const {
      return data.svalue.c_str();
   }
   long to_long() const {
      long ret = 0L;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_long();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = atol(data.svalue.c_str());
      }
      else {
         stringstream str;
         if(data.type == VAR_DATA::LONG_TYPE)
            str << data.value.lvalue;
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            str << data.value.dvalue;
         else if(data.type == VAR_DATA::BOOL_TYPE)
            str << data.value.bvalue;
         else if(data.type == VAR_DATA::INT_TYPE)
            str << data.value.ivalue;
         else
            str << 0;
         str >> ret;
      }
      return ret;
   }
   float to_float() const {
      float ret = 0.0F;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_float();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = atof(data.svalue.c_str());
      }
      else {
         stringstream str;
         if(data.type == VAR_DATA::LONG_TYPE)
            str << data.value.lvalue;
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            str << data.value.dvalue;
         else if(data.type == VAR_DATA::BOOL_TYPE)
            str << data.value.bvalue;
         else
            str << 0;
         str >> ret;
      }
      return ret;
   }
   void* to_pointer() const {
      void* ret = 0;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_pointer();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = (void*)data.svalue.c_str();
      }
      else if(data.type == VAR_DATA::POINTER_TYPE) {
         ret = data.value.pvalue;
      }
      else {
         ret = data.value.pvalue;
      }
      return ret;
   }
   const var & to_var() const {
      return *this;
   }
   int to_int() const {
      int ret = 0;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_int();
      }
      else if(data.type == VAR_DATA::INT_TYPE) {
         ret = data.value.ivalue;
      }
      else {
         if(data.type == VAR_DATA::LONG_TYPE)
            ret = static_cast< int >(data.value.lvalue);
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            ret = static_cast< int >(data.value.dvalue);
         else if(data.type == VAR_DATA::BOOL_TYPE)
            ret = data.value.ivalue;
         else if(data.type == VAR_DATA::STRING_TYPE)
            ret = atoi(data.svalue.c_str());
         else
            ret = 0;
      }
      return ret;
   }
   
   double to_double() const {
      double ret = 0.0;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_double();
      }
      else if(data.type == VAR_DATA::DOUBLE_TYPE) {
         ret = data.value.dvalue;
      }
      else if(data.type == VAR_DATA::FLOAT_TYPE) {
         ret = data.value.dvalue;
      }
      else {
         if(data.type == VAR_DATA::LONG_TYPE)
            ret = static_cast< double >(data.value.lvalue);
         else if(data.type == VAR_DATA::STRING_TYPE)
            ret = atof(data.svalue.c_str());
         else if(data.type == VAR_DATA::BOOL_TYPE)
            ret = static_cast< double >(data.value.ivalue);
         else if(data.type == VAR_DATA::INT_TYPE)
            ret = static_cast< double >(data.value.ivalue);
         else 
            ret = 0.0;
      }
      return ret;
   }
   bool to_bool() const {
      bool ret = false;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_bool();
      }
      else if(data.type == VAR_DATA::BOOL_TYPE) {
         ret = data.value.bvalue;
      }
      else {
         if(data.type == VAR_DATA::LONG_TYPE)
            ret = static_cast< bool >(data.value.lvalue);
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            ret = static_cast< bool >(data.value.ivalue);
         else if(data.type == VAR_DATA::STRING_TYPE)
            ret = static_cast< bool >(atoi(data.svalue.c_str()));
         else if(data.type == VAR_DATA::INT_TYPE)
            ret = static_cast< bool >(data.value.ivalue);
         else 
            ret = false;
      }
      return ret;
   }
   short to_short() const {
      short ret = 0;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_short();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = atoi(data.svalue.c_str());
      }
      else {
         if(data.type == VAR_DATA::LONG_TYPE)
            ret = static_cast< short >(data.value.lvalue);
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            ret = static_cast< short >(data.value.dvalue);
         else if(data.type == VAR_DATA::BOOL_TYPE)
            ret = static_cast< short >(data.value.bvalue);
         else if(data.type == VAR_DATA::INT_TYPE)
            ret = static_cast< short >(data.value.ivalue);
         else
            ret = 0;
      }
      return ret;
   }
   char to_char() const {
      char ret = '\0';
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->to_char();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = data.svalue.c_str()[0];
      }
      else {
         if(data.type == VAR_DATA::LONG_TYPE)
            ret = static_cast< char >(data.value.lvalue);
         else if(data.type == VAR_DATA::DOUBLE_TYPE)
            ret = static_cast< char >(data.value.dvalue);
         else if(data.type == VAR_DATA::BOOL_TYPE)
            ret = static_cast< char > (data.value.ivalue);
         else if(data.type == VAR_DATA::INT_TYPE)
            ret = static_cast< char >(data.value.ivalue);
         else
            ret = '\0';
      }
      return ret;
   }

   bool is_bool() const {
      return data.type == VAR_DATA::BOOL_TYPE;
   }
   bool is_long() const {
      return data.type == VAR_DATA::LONG_TYPE;
   }
   bool is_int() const {
      return data.type == VAR_DATA::INT_TYPE;
   }
   bool is_float() const {
      return data.type == VAR_DATA::FLOAT_TYPE;
   }
   bool is_double() const {
      return data.type == VAR_DATA::DOUBLE_TYPE || data.type == VAR_DATA::FLOAT_TYPE;
   }
   bool is_string() const {
      return data.type == VAR_DATA::STRING_TYPE;
   }
   bool is_pointer() const {
      return data.type == VAR_DATA::POINTER_TYPE;
   }
   bool is_ref() const {
      return data.type == VAR_DATA::REFERENCE_TYPE;
   }
   iterator begin() {
      iterator ret;
      if(data.type == VAR_DATA::ARRAY_TYPE) {
         ret = data.value.avalue->begin();
      }
      return ret;
   }
   iterator end() {
      iterator ret;
      if(data.type == VAR_DATA::ARRAY_TYPE) {
         ret = data.value.avalue->end();
      }
      return ret;
   }
   /*
   reverse_iterator rbegin() {
      reverse_iterator ret;
      if(data.type == ARRAY_TYPE) {
         ret = data.value.avalue->rbegin();
      }
      return ret;
   }
   reverse_iterator rend() {
      reverse_iterator ret;
      if(data.type == ARRAY_TYPE) {
         ret = data.value.avalue->rend();
      }
      return ret;
   }
   */
   var & get_value() {
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         return *(data.value.vvalue);
      }
      return *this;
   }
   var & operator=(const var & right) {
      //cout << "var::operator=()" << endl;
      if(this != &right) {
         if(is_ref() && right.is_ref()) {
            data.value.vvalue->data = right.data.value.vvalue->data;
         }
         else if(is_ref() && !right.is_ref()) {
            data.value.vvalue->data = right.data;
         }
         else if(!is_ref() && right.is_ref()) {
            //dec();
            data = right.data.value.vvalue->data;
            //data.inc();
         }
         else {
            //dec();
            data = right.data;
            //data.inc();
         }
      }
      return *this;
   }

   var operator==(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue == *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue == right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this == *right.data.value.vvalue;
      }
      else {
      ___BIN_OP_IF((*this),right,==)
      }
      return ret;
   }
   var operator!=(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue != *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue != right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this != *right.data.value.vvalue;
      }
      else {
         ___BIN_OP_IF((*this),right,!=)
      }
      return ret;
   }
   var operator+(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue + *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue + right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this + *right.data.value.vvalue;
      }
      else {
         ___BIN_OP_IF((*this),right,+)
      }
      return ret;
   }
   var operator-(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue - *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue - right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this - *right.data.value.vvalue;
      }
      else {
         ___ABIN_OP_IF((*this),right,-)
      }
      return ret;
   }
   var operator*(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue * *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue * right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this * *right.data.value.vvalue;
      }
      else {
         ___ABIN_OP_IF((*this),right,*)
      }
      return ret;
   }
   var operator/(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue / *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue / right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this / *right.data.value.vvalue;
      }
      else {
         ___ABIN_OP_IF((*this),right,/)
      }
      return ret;
   }
   var operator%(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue % *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue % right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this % *right.data.value.vvalue;
      }
      else {
         ___INT_BIN_OP_IF((*this),right,%)
      }
      return ret;
   }
   var & operator+=(const var & right) {
      *this = *this + right;
      return *this;
   }
   var & operator-=(const var & right) {
      *this = *this - right;
      return *this;
   }
   var & operator/=(const var & right) {
      *this = *this / right;
      return *this;
   }
   var & operator*=(const var & right) {
      *this = *this * right;
      return *this;
   }
   var & operator%=(const var & right) {
      *this = *this % right;
      return *this;
   }
   var & operator<<=(const var & right) {
      *this = *this << right;
      return *this;
   }
   var & operator>>=(const var & right) {
      *this = *this >> right;
      return *this;
   }
   var & operator&=(const var & right) {
      *this = *this & right;
      return *this;
   }
   var & operator|=(const var & right) {
      *this = *this | right;
      return *this;
   }
   var & operator^=(const var & right) {
      *this = *this ^ right;
      return *this;
   }
   var operator<<(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue << *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue << right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this << *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,<<);
      }
      return ret;
   }
   var operator>>(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue >> *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue >> right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this >> *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,>>);
      }
      return ret;
   }
   var operator^(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue ^ *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue ^ right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this ^ *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,^);
      }
      return ret;
   }
   var operator|(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue | *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue | right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this | *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,|);
      }
      return ret;
   }
   var operator&(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue & *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue & right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this & *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,&);
      }
      return ret;
   }
   var operator&&(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue && *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue && right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this && *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,&&);
      }
      return ret;
   }
   var operator||(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue || *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue || right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this || *right.data.value.vvalue;
      }
      else {
      ___INT_BIN_OP_IF((*this),right,||);
      }
      return ret;
   }
   var operator<(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue < *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue < right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this < *right.data.value.vvalue;
      }
      else {
      ___BIN_OP_IF((*this),right,<)
      }
      return ret;
   }
   var operator>(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue > *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue > right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this > *right.data.value.vvalue;
      }
      else {
      ___BIN_OP_IF((*this),right,>)
      }
      return ret;
   }
   var operator<=(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = *data.value.vvalue <= *right.data.value.vvalue;
      }
      else if(is_ref() && !right.is_ref()) {
         ret = *data.value.vvalue <= right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this <= *right.data.value.vvalue;
      }
      else {
      ___BIN_OP_IF((*this),right,<=)
      }
      return ret;
   }
   var operator>=(const var & right) const {
      var ret;
      if(is_ref() && right.is_ref()) {
         ret = (*data.value.vvalue) >= (*right.data.value.vvalue);
      }
      else if(is_ref() && !right.is_ref()) {
         ret = (*data.value.vvalue) >= right;
      }
      else if(!is_ref() && right.is_ref()) {
         ret = *this >= (*right.data.value.vvalue);
      }
      else {
      ___BIN_OP_IF((*this),right,>=)
      }
      return ret;
   }
   var operator!() const {
      var ret;
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         ret = !(*data.value.vvalue);
      else if(data.type == VAR_DATA::INT_TYPE) 
         ret = var(!data.value.ivalue);
      else if(data.type == VAR_DATA::LONG_TYPE)
         ret = var(!data.value.lvalue);
      else
         ret = var(!data.value.lvalue);
      return ret;
   }
   var operator~() const {
      var ret;
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         ret = ~(*data.value.vvalue);
      else if(data.type == VAR_DATA::INT_TYPE) 
         ret = var(~data.value.ivalue);
      else if(data.type == VAR_DATA::LONG_TYPE)
         ret = var(~data.value.lvalue);
      else 
         ret = var(~to_long());
      return ret;
   }
   
   var & operator++() {
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         (*data.value.vvalue)++;
      else if(data.type == VAR_DATA::INT_TYPE) {
         if(data.value.ivalue == INT_MAX) {
            long tmp;
            tmp = data.value.ivalue;
            tmp++;
            data.value.lvalue = tmp;
            data.type = VAR_DATA::LONG_TYPE;
         }
         else {
            data.value.ivalue++;
         }
      }
      else if(data.type == VAR_DATA::FLOAT_TYPE) {
         if(data.value.gvalue > (FLT_MAX-1.0F) || data.value.gvalue == FLT_MAX) {
            double tem;
            tem = data.value.gvalue;
            tem++;
            data.value.dvalue = tem;
            data.type = VAR_DATA::DOUBLE_TYPE;
         }
         else {
            data.value.gvalue++; 
         }
      }
      else if(data.type == VAR_DATA::LONG_TYPE) {
         data.value.lvalue++;
      }
      else if(data.type == VAR_DATA::DOUBLE_TYPE) {
         data.value.dvalue++;
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         if(data.svalue.find(".") != string::npos) {
            double tem;
            tem = atof(data.svalue.c_str()) + 1;
            operator=(var(tem));
         }
         else {
            long tem;
            tem = atol(data.svalue.c_str()) + 1;
            operator=(var(tem));
         }
      }
      else {
         throw var_exception("increment..."/*, __LINE__, __FILE__*/);
      }
      return *this;
   }
   var & operator++(int) {
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         (*data.value.vvalue)++;
      else if(data.type == VAR_DATA::INT_TYPE) {
         if(data.value.ivalue == INT_MAX) {
            long tmp;
            tmp = data.value.ivalue;
            tmp++;
            data.value.lvalue = tmp;
            data.type = VAR_DATA::LONG_TYPE;
         }
         else {
            data.value.ivalue++;
         }
      }
      else if(data.type == VAR_DATA::FLOAT_TYPE) {
         if(data.value.gvalue > (FLT_MAX-1.0F) || data.value.gvalue == FLT_MAX) {
            double tmp;
            tmp = data.value.gvalue;
            tmp++;
            data.value.dvalue = tmp;
            data.type = VAR_DATA::DOUBLE_TYPE;
         }
         else {
            data.value.gvalue++;
         } 
      }
      else if(data.type == VAR_DATA::LONG_TYPE) {
         data.value.lvalue++;
      }
      else if(data.type == VAR_DATA::DOUBLE_TYPE) {
         data.value.dvalue++;
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         if(data.svalue.find(".") != string::npos) {
            double tmp;
            tmp = atof(data.svalue.c_str()) + 1;
            operator=(var(tmp));
         }
         else {
            long tmp;
            tmp = atol(data.svalue.c_str()) + 1;
            operator=(var(tmp));
         }
      }
      else {
         throw var_exception("increment..."/*, __LINE__, __FILE__*/);
      }
      return *this;
   }
   var & operator--() {
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         (*data.value.vvalue)--;
      else if(data.type == VAR_DATA::INT_TYPE) {
         if(data.value.ivalue == INT_MIN) {
            long tmp;
            tmp = data.value.ivalue;
            tmp--;
            data.type = VAR_DATA::LONG_TYPE;
            data.value.lvalue = tmp;
         }
         else {
            data.value.ivalue--;
         }
      }
      else if(data.type == VAR_DATA::FLOAT_TYPE) {
         if(data.value.gvalue > (FLT_MIN+1.0F) || data.value.gvalue == FLT_MIN) {
            double tmp;
            tmp = data.value.gvalue;
            tmp--;
            data.value.dvalue = tmp;
            data.type = VAR_DATA::DOUBLE_TYPE;
         }
         else {
            data.value.gvalue--;
         } 
      }
      else if(data.type == VAR_DATA::LONG_TYPE) {
         data.value.lvalue--;
      }
      else if(data.type == VAR_DATA::DOUBLE_TYPE) {
         data.value.dvalue--;
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         if(data.svalue.find(".") != string::npos) {
            double tmp;
            tmp = atof(data.svalue.c_str()) - 1;
            operator=(var(tmp));
         }
         else {
            long tmp;
            tmp = atol(data.svalue.c_str()) - 1;
            operator=(var(tmp));
         }
      }
      else {
         throw var_exception("decrement..."/*, __LINE__, __FILE__*/);
      }
      return *this;
   }
   
   var & operator--(int) {
      if(data.type == VAR_DATA::REFERENCE_TYPE)
         (*data.value.vvalue)--;
      else if(data.type == VAR_DATA::INT_TYPE) {
         if(data.value.ivalue == INT_MIN) {
            long tmp;
            tmp = data.value.ivalue;
            tmp--;
            data.type = VAR_DATA::LONG_TYPE;
            data.value.lvalue = tmp;
         }
         else {
            data.value.ivalue--;
         } 
      }
      else if(data.type == VAR_DATA::FLOAT_TYPE) {
         if(data.value.gvalue <= (FLT_MIN+1.0F) || data.value.gvalue == FLT_MIN) {
            double tmp;
            tmp = data.value.gvalue;
            tmp--;
            data.value.dvalue = tmp;
            data.type = VAR_DATA::DOUBLE_TYPE;
         }
         else {
            data.value.gvalue--;
         }
      }
      else if(data.type == VAR_DATA::LONG_TYPE) {
         data.value.lvalue--;
      }
      else if(data.type == VAR_DATA::DOUBLE_TYPE) {
         data.value.dvalue--;
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         if(data.svalue.find(".") != string::npos) {
            double tmp;
            tmp = atof(data.svalue.c_str()) - 1;
            operator=(var(tmp));
         }
         else {
            long tmp;
            tmp = atol(data.svalue.c_str()) - 1;
            operator=(var(tmp));
         }
      }
      else {
         throw var_exception("decrement...");
      }
      return *this;
   }
   
   var & operator[](const var & index) {
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         return data.value.vvalue->operator[](index);
      }
      else if(data.type == VAR_DATA::ARRAY_TYPE) {
         return data.value.avalue->operator[](index);
      }
      /*
      else if(data.type == STRING_TYPE) {
         return data.svalue[index.to_int()];
      }
      */
      else {
         dec();
         data.value.avalue = new var_map_type();
         data.type = VAR_DATA::ARRAY_TYPE;
         data.count = new size_t;
         *data.count = 1;
         return (*(data.value.avalue))[index];
      }
   }
   var operator()() {
      var ret;
      //cout << "var::operator()()" << endl;
      if(data.type == VAR_DATA::FUNCTION_TYPE) {
         switch(data.ret_type) {
         case VAR_DATA::INT_TYPE: {
            typedef int (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf();
         }
         break;
         case VAR_DATA::FLOAT_TYPE: {
            typedef float (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf();
         }
         break;
         case VAR_DATA::LONG_TYPE: {
            typedef long (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf();
         }
         break;
         case VAR_DATA::DOUBLE_TYPE: {
            typedef double (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf();
         }
         break;
         case VAR_DATA::POINTER_TYPE: {
            typedef void* (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf();
         }
         break;
         default: {
            typedef void (*pf_type)();
            pf_type pf = (pf_type)data.value.pvalue;
            pf();
         }
         }
      }
      return ret;
   }
   template< class T >
   var operator()(T arg1) {
      var ret;
      if(data.type == VAR_DATA::FUNCTION_TYPE) {
         switch(data.ret_type) {
         case VAR_DATA::INT_TYPE: {
            typedef int (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::FLOAT_TYPE: {
            typedef float (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::LONG_TYPE: {
            typedef long (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::DOUBLE_TYPE: {
            typedef double (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::POINTER_TYPE: {
            typedef void* (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::VOID_TYPE: {
            typedef void (*pf_type)(T arg);
            pf_type pf = (pf_type)data.value.pvalue;
            pf(arg1);
         }
         break;
         }
      }
      return ret;
   }
   template< class T, class TT >
   var operator()(T arg1, TT arg2) {
      var ret;
      if(data.type == VAR_DATA::FUNCTION_TYPE) {
         switch(data.ret_type) {
         case VAR_DATA::INT_TYPE: {
            typedef int (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2);
         }
         break;
         case VAR_DATA::FLOAT_TYPE: {
            typedef float (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2);
         }
         break;
         case VAR_DATA::LONG_TYPE: {
            typedef long (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2);
         }
         break;
         case VAR_DATA::DOUBLE_TYPE: {
            typedef double (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2);
         }
         break;
         case VAR_DATA::POINTER_TYPE: {
            typedef void* (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2);
         }
         break;
         default: {
            typedef void (*pf_type)(T,TT);
            pf_type pf = (pf_type)data.value.pvalue;
            pf(arg1,arg2);
         }
         }
      }
      return ret;
   }
   template< class T, class TT, class TTT >
   var operator()(T arg1, TT arg2, TTT arg3) {
      var ret;
      if(data.type == VAR_DATA::FUNCTION_TYPE) {
         switch(data.ret_type) {
         case VAR_DATA::INT_TYPE: {
            typedef int (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2,arg3);
         }
         break;
         case VAR_DATA::FLOAT_TYPE: {
            typedef float (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2,arg3);
         }
         break;
         case VAR_DATA::LONG_TYPE: {
            typedef long (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2,arg3);
         }
         break;
         case VAR_DATA::DOUBLE_TYPE: {
            typedef double (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2,arg3);
         }
         break;
         case VAR_DATA::POINTER_TYPE: {
            typedef void* (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            ret = pf(arg1,arg2,arg3);
         }
         break;
         default: {
            typedef void (*pf_type)(T,TT,TTT);
            pf_type pf = (pf_type)data.value.pvalue;
            pf(arg1,arg2,arg3);
         }
         }
      }
      return ret;
   }
   size_t size() {
      size_t ret = 0;
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         ret = data.value.vvalue->size();
      }
      else if(data.type == VAR_DATA::STRING_TYPE) {
         ret = data.svalue.size();
      }
      else if(data.type == VAR_DATA::ARRAY_TYPE) {
         ret = data.value.avalue->size();
      }
      return ret;
   }
   VAR_DATA::var_type get_type() const {
      return data.type;
   }
   VAR_DATA::var_type get_ref_type() const {
      if(data.type == VAR_DATA::REFERENCE_TYPE) {
         return data.value.vvalue->data.type;
      }
      return data.type;
   }
   operator string() const {
      return var::to_string();
   }
   operator bool() {
      return to_bool();
   }
   operator long() {
      return to_long();
   }
   operator int() {
      return to_int();
   }
   operator double() {
      return to_double();
   }
   operator void*() {
      size_t ival = to_int();
      return *(void**)&ival;
   }
   /*
   template< class T >
   operator T*() {
      if(data.type == POINTER_TYPE) {
         return (T*)data.value.pvalue;
      }
      return 0;
   }
   template< class T >
   T * operator->() {
      if(data.type == POINTER_TYPE) {
         return get_ptr< T >(data.value.pvalue);
      }
      return 0;
   }
   */
   friend ostream & operator<<(ostream & out, const var & v);
   friend istream & operator>>(istream & in, var & v);
   friend bool VAR_DATA::dec();
   friend void VAR_DATA::inc();
};

inline
bool 
var_compare::operator()(const var & v1, const var & v2) const
{
   return (v1 < v2).to_bool();
}


inline
ostream & operator<<(ostream & out, const var & v) 
{
   switch(v.data.type) {
   case VAR_DATA::CHAR_TYPE:
   out << v.data.value.cvalue;
   break;

   case VAR_DATA::SHORT_TYPE:
   out << v.data.value.svalue;
   break;

   case VAR_DATA::INT_TYPE:
   out << v.data.value.ivalue;
   break;

   case VAR_DATA::FLOAT_TYPE:
   out << v.data.value.gvalue;
   break;

   case VAR_DATA::STRING_TYPE:
   out << v.data.svalue;
   break;

   case VAR_DATA::LONG_TYPE:
   out << v.data.value.lvalue;
   break;

   case VAR_DATA::DOUBLE_TYPE:
   out << v.data.value.dvalue;
   break;

   case VAR_DATA::BOOL_TYPE:
   out << v.data.value.bvalue;
   break;

   case VAR_DATA::ARRAY_TYPE:
   break;

   case VAR_DATA::FUNCTION_TYPE:
   break;

   case VAR_DATA::POINTER_TYPE:
   break;

   case VAR_DATA::REFERENCE_TYPE:
   out << *v.data.value.vvalue;
   break;
   }
   return out;
}

inline
istream & operator>>(istream & in, var & v) 
{
   string s;
   in >> s;
   if(s.size()) {
      if(isdigit(s[0])) {
         long l;
         stringstream ss;
         ss << s;
         ss >> l;
         v = var(l);
      }
      else if(isdigit(s[0]) && s.find(".") != string::npos) {
         double d;
         stringstream ss;
         ss << s;
         ss >> d;
         v = var(d);
      }
      else {
         v = var(s);
      }
   }
   return in;
}

inline
ostream & operator<<(ostream & out, const var_exception & ex) 
{
   out << "error: " << ex.get_message() << endl;
   out << "file: " << ex.get_file() << endl;
   out << "line: " << ex.get_line() << endl;
   return out;
}

inline
bool VAR_DATA::dec() {
   bool ret = false;
   if(type == REFERENCE_TYPE) {
      ret = value.vvalue->data.dec();
   }
   else if(count) {
      (*count)--;
      if(*count == 0) {
         ret = true;
         delete count;
         count = 0;
         if(type == STRING_TYPE) {
            svalue = "";
         }
         else if(type == ARRAY_TYPE) {
            delete value.avalue;
            value.avalue = 0;
         }
/*
         else if(type == POINTER_TYPE) {
            delete decon(value.pvalue);
            value.pvalue = 0;
         }
*/
         else {
         }
      }
   }
   return ret;
}

inline
void VAR_DATA::inc() 
{
   if(count) {
      (*count)++;
   }
   else if(type == REFERENCE_TYPE) {
      if(value.vvalue->data.count) {
         (*(value.vvalue->data.count))++;
      }
   }
}

/*
template< class T >
class t_var : public var {
public:
   t_var(T * ptr) : var(ptr) {
      
   }
   T * operator->() {
      if(is_pointer()) {
         return (T*)data.value.pvalue;
      }
      else {
         throw "not a pointer...";
      }
   }
};
*/

//}

#endif /* END _VAR_DATA_H */
