#ifndef _VAR_DATA_H 
#define _VAR_DATA_H

/*

SCALAR VAR_DATAIABLE C++ CLASS...

A scaler variable is a number or a string variable put 
together in one object.  The "var" class is a C++ strong
data type.  

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

*/

#include <string>
#include <map>
#include <vector>
#include <list>
#include <sstream>
#include <typeinfo>
#include <sys/types.h>
#include <limits.h>
#include <float.h>
#include <complex>
#include "st.h"
#include "str.h"
#include "varop.h"
#include "hashmap.h"
#include "bfstream.h"
#include "bsstream.h"

namespace string_tools {

using namespace std;
using namespace disk_tools;
using namespace text_tools;

class var;

class var_exception { 
   const char * file; 
   int the_line;
   const char * message;
public:
   var_exception(const char * file,
                 const int the_line,
                 const char * message) {
      this->file = file;
      this->the_line = the_line;
      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;
};


template< class K, class D >
class Node {
public:
   K key;
   D data;
   Node * next;
};

template< class K, class D, class Compare >
class Map {
   Node< K, D > * root;
   size_t the_size;

public:
   Map() { 
      the_size = 0;
      root = 0;
   }

   ~Map() {
      Node< K, D > * n;
      while(the_size) {
         n = root; 
         root = root->next;
         delete n;
         the_size--;
      }
   }

   D & operator[](const K & index) {
      Compare comp;
      Node< K, D > * n;
      for(n = root; n; n = n->next) {
         if(!comp(n->key,index) && !comp(index,n->key)) {
            return n->data;
         }
      }
      n = new Node< K, D >();
      n->next = root;
      n->key = index;
      root = n;
      the_size++;
      return n->data;
   }
  
   size_t size() const { return the_size; }
};

inline
int hashpjw(const char* t)
{
   unsigned h=0, g;
   for(;*t;++t) {
   h = (h << 4)+ *t;
   if(g=h&0xf0000000) {
      h ^= g>>24;
      h ^= g;
   }
   }
   return h;
}


template< class T >
class hash_function {
public:
   size_t operator()(const T & arg1) {
      size_t ret = (size_t)hashpjw(arg1.c_str());
      /*
      string str = arg1.to_string();
      for(size_t i = 0; i < str.size(); i++) {
         ret += str[i] * i;
      }
      return ret;
      */
      return ret;
   }
};

class VAR_DATA {
public:
   friend class var;
   //typedef hashmap< var, var, hash_function< var > > var_map_type;
   typedef map< var, var > var_map_type;
   typedef vector< var > var_array_type;
   typedef void (*decon_type)(void *);
   typedef list< var > var_list_type;
   //class value_type {
   //public:
   union value_type {
      complex< double > * dvalue;
      long lvalue;
      var_map_type * hvalue;
      var_array_type * avalue;
      var_list_type * list_value;
      void * pvalue;
      var * vvalue;
      string * svalue;
/*
      value_type() {
         dvalue = new complex< double >(0.0);
         lvalue = 0L;
         hvalue = 0;
         avalue = 0;
         list_value = 0;
         pvalue = 0;
         vvalue = 0;
      }

      value_type(const value_type & cp) {
         hvalue = 0;
         avalue = 0;
         list_value = 0;
         pvalue = 0;
         vvalue = 0;
         *this = cp;
      }

      value_type & operator=(const value_type & right) {
         if(dvalue) delete dvalue;
         dvalue = new complex< double >(*right.dvalue);
         lvalue = right.lvalue;
         if(pvalue) delete [] (char*)pvalue;
         pvalue = right.pvalue;
         vvalue = right.vvalue;
         if(hvalue) delete hvalue;
         if(avalue) delete avalue;
         if(list_value) delete list_value;
         hvalue = 0;
         avalue = 0;
         list_value = 0;
         if(right.hvalue) {
            hvalue = new var_map_type();
            *hvalue = *right.hvalue;
         }
         if(right.avalue) {
            avalue = new var_array_type();
            *avalue = *right.avalue;
         }
         if(right.list_value) {
            list_value = new var_list_type();         
            *list_value = *right.list_value;
         }
         return *this;
      }
*/
   };
   enum var_type {
      LONG_TYPE,
      DOUBLE_TYPE,
      STRING_TYPE,
      ARRAY_TYPE,
      LIST_TYPE,
      HASH_TYPE,
      FUNCTION_TYPE,
      POINTER_TYPE,
      REFERENCE_TYPE,
   }; 

   int count;
   value_type value;
   var_type type;
   var_type ret_type;
   decon_type decon;
   //string svalue;
   long each_index;
   size_t buffer_size;

   VAR_DATA() {
      count = 0;
      type = LONG_TYPE;
      //value.lvalue = 0L;
      ret_type = LONG_TYPE;
      //decon = 0;
      each_index = 0;
      buffer_size = 0;
      memset((char*)&value, 0, sizeof(value_type));
   }
   VAR_DATA(const var_type t) {
/*
      value.pvalue = 0;
      value.hvalue = 0;
      value.list_value = 0;
      value.vvalue = 0;
      value.avalue = 0;
*/
      memset((char*)&value, 0, sizeof(value_type));

      buffer_size = 0;
      ret_type = LONG_TYPE;
      type = t;
      //decon = 0;
      each_index = 0;
      count = 0;
      if(type == LONG_TYPE) {
         value.lvalue = 0L;
      }
      else if(type == DOUBLE_TYPE) {
         value.dvalue = new complex< double >(0.0);
      }
      else if(type == STRING_TYPE) {
         value.svalue = new string();
      }
      else if(type == ARRAY_TYPE) {
         value.avalue = new var_array_type();
      }
      else if(type == HASH_TYPE) {
         value.hvalue = new var_map_type();
      }
      else if(type == FUNCTION_TYPE) {
         value.pvalue = 0;
      }
      else if(type == REFERENCE_TYPE) {
         value.vvalue = 0;
      }
      else if(type == LIST_TYPE) {
         value.list_value = new var_list_type();
      }
	   else {
		  value.lvalue = 0;
	  }
   }
   template< class T >
   VAR_DATA(T * t) {
      value.pvalue = (void*)t;
      type = VAR_DATA::POINTER_TYPE;
      count = 0;   
      decon = 0;
      buffer_size = 0;
   }
   VAR_DATA(const VAR_DATA & cp);
   ~VAR_DATA();
   VAR_DATA & operator=(const VAR_DATA & right) {
      if(this != &right) {
         each_index = right.each_index;
         count = 0;
         if(value.pvalue && buffer_size > 0) {
            value.pvalue = new char [right.buffer_size];
            memcpy(value.pvalue,right.value.pvalue,right.buffer_size);
         }
         if(type == LIST_TYPE)
            delete value.list_value;
         else if(type == ARRAY_TYPE)
            delete value.avalue;
         else if(type == HASH_TYPE)
            delete value.hvalue;
         else if(type == DOUBLE_TYPE)
            delete value.dvalue;
         else if(type == STRING_TYPE)
            delete value.svalue;
         value = right.value;
         if(right.type == LIST_TYPE)
            value.list_value = new var_list_type(*right.value.list_value);
         else if(right.type == ARRAY_TYPE)
            value.avalue = new var_array_type(*right.value.avalue);
         else if(right.type == HASH_TYPE)
            value.hvalue = new var_map_type(*right.value.hvalue);
         else if(right.type == DOUBLE_TYPE)
            value.dvalue = new complex< double >(*right.value.dvalue);
         else if(right.type == STRING_TYPE)
            value.svalue = new string(*right.value.svalue);
         type = right.type;
         //svalue = right.svalue;
         decon = right.decon;
         buffer_size = right.buffer_size;
      }
      return *this;
   }
   complex< double > & get_dvalue() { return (*(value.dvalue)); }
   const string & get_svalue() { return (*(value.svalue)); }
   void inc();
   bool dec();
   friend class var; 
};

template< class T >
inline
void decon(void * ptr)
{
   if(ptr) {
      T * data = (T*)ptr;
      delete data; 
   }
}

template< class T >
inline
void cast(void * ptr, T * & data)
{
   if(ptr) {
      data = (T*)ptr;
   }
}

template< class T >
T * get_ptr(void * ptr)
{
   return (T*)ptr;
}

class var {
friend class VAR_DATA;
public:
   typedef VAR_DATA::var_array_type::iterator iterator;
   //typedef VAR_DATA::var_map_type::reverse_iterator reverse_iterator;

private:
   VAR_DATA * data;

   bool dec() {
      bool ret = false;
      if(data) { 
         if(data->dec()) {
            delete data;
            data = 0;
            ret = true;
         }
      }
      return ret;
   }

   void inc() {
      if(data) {
         data->inc();
      }
   }

public:
   var() {
      data = new VAR_DATA();
      data->type = VAR_DATA::STRING_TYPE;
      data->value.svalue = new string("");
      inc();
   }
   var(const size_t sz) {
      data = new VAR_DATA();
      data->value.lvalue = (unsigned long)sz;
      data->type = VAR_DATA::LONG_TYPE;
      inc();
   }
   var(const var & cp)  {
      data = new VAR_DATA(*cp.data);
      data->count = 0;
      inc();
   }
   var(void * ptr, size_t sz) {  
      data = new VAR_DATA();
      data->value.pvalue = ptr;
      data->buffer_size = sz;
      data->type = VAR_DATA::POINTER_TYPE;
      data->count = 0;
      inc();
   }
   var(void * ptr, VAR_DATA::decon_type decon) {
      data = new VAR_DATA(VAR_DATA::POINTER_TYPE);
      data->value.pvalue = ptr;
      data->decon = decon;
      inc();
   }
   template< class T >
   var(T * f, VAR_DATA::var_type rt) {
      data = new VAR_DATA();
      data->value.pvalue = (void*)f;
      data->type = VAR_DATA::FUNCTION_TYPE;
      data->ret_type = rt;
      inc();
   }
   var(void * p) {
      data = new VAR_DATA();
      data->value.pvalue = p;
      data->buffer_size = 0;
      data->decon = 0;
      data->type = VAR_DATA::POINTER_TYPE;
      inc();
   }
   var(const bool b) {
      data = new VAR_DATA();
      data->value.lvalue = b;
      data->type = VAR_DATA::LONG_TYPE;
      inc();
   }
   var(const double & d) {
      data = new VAR_DATA();
      data->value.dvalue = new complex< double >(d);
      data->type = VAR_DATA::DOUBLE_TYPE;
      inc();
   }
   var(const complex< double > & d) {
      data = new VAR_DATA();
      data->value.dvalue = new complex< double >(d);
      data->type = VAR_DATA::DOUBLE_TYPE;
      inc();
   }
   var(const long & l) {
      data = new VAR_DATA();
      data->value.lvalue = l;
      data->type = VAR_DATA::LONG_TYPE;
      inc();
   }
   var(const int l) {
      data = new VAR_DATA();
      data->value.lvalue = l;
      data->type = VAR_DATA::LONG_TYPE;
      inc();
   }
   var(const VAR_DATA::var_type t) {
      data = new VAR_DATA(t);
      inc();
   }
   var(var * p) {
      data = new VAR_DATA();
      data->type = VAR_DATA::REFERENCE_TYPE;
      data->value.vvalue = p->get_ptr();
      inc();
   }
   var(const char * str) {
      data = new VAR_DATA();
      data->value.svalue = new string(str);
      data->type = VAR_DATA::STRING_TYPE;
      inc();
   }
   var(const string & str) {
      data = new VAR_DATA();
      data->value.svalue = new string(str);
      data->type = VAR_DATA::STRING_TYPE;
      inc();
   }
   ~var() {
      dec();
   }

   void set_value(const var & val) {
      if(is_ref()) {
         var * ptr = get_ptr();
         if(ptr == 0)
            throw "null pointer...";
         *ptr = val;
      }
      else {
         *this = val;
      }
   }

   void set_pointer(void * ptr) {
      if(is_ref())
         data->value.vvalue->set_pointer(ptr);
      else  {
         data->value.pvalue = ptr;
         data->decon = 0;
      }
   }

   size_t get_buffer_size() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->get_buffer_size();
      }
      return data->buffer_size;
   }
   
   VAR_DATA::var_map_type::iterator each() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->each();
      }
      VAR_DATA::var_map_type::iterator ptr;
      long index = 0L;
      for(ptr = data->value.hvalue->begin(); ptr != data->value.hvalue->end(); ptr++, index++) {
         if(index == data->each_index) {
            data->each_index++;
            break;
         }
      }
      if(ptr == data->value.hvalue->end()) {
         data->each_index = 0L;
      }
      return ptr;
   }

   void push(const var & v) {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         if(data->value.vvalue == 0)
            throw "null pointer...";
         data->value.vvalue->push(v);
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         data->value.avalue->insert(data->value.avalue->begin(), v);
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         data->value.list_value->push_front(v);
      }
      else {
         data->type = VAR_DATA::LIST_TYPE;
         data->value.list_value = new VAR_DATA::var_list_type();
         data->value.list_value->push_front(v);
         data->count = 1;
      }
   }

   void clear() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         if(data->value.vvalue == 0)
            throw "null pointer...";
         data->value.vvalue->clear();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         data->value.avalue->clear();
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         data->value.list_value->clear();
      }
      else if(data->type == VAR_DATA::STRING_TYPE) {
         data->value.svalue->clear();
      }
      else {
         throw "nothing to clear in var...";
      }
   }

   void append(const var & v) {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         if(data->value.vvalue == 0)
            throw "null pointer...";
         data->value.vvalue->append(v);
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         data->value.avalue->insert(data->value.avalue->end(), v);
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         data->value.list_value->push_back(v);
      }
      else {
         data->type = VAR_DATA::LIST_TYPE;
         data->value.list_value = new VAR_DATA::var_list_type();
         data->value.list_value->push_back(v);
         data->count = 1;
      }
   }

  void insert(const size_t & index, const var & v) {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         if(data->value.vvalue == 0)
            throw "null pointer...";
         data->value.vvalue->insert(index,v);
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         var count = 0;
         VAR_DATA::var_array_type::iterator ptr;
         for(ptr = data->value.avalue->begin(); ptr != data->value.avalue->end(); ptr++, count++) {
            if(count == index) {
               data->value.avalue->insert(ptr, v);
               break;
            }
         }
         if(count != index) {
            while(var(data->value.avalue->size()) < index)
               data->value.avalue->push_back(0);
            data->value.avalue->push_back(v);
         }
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         if(data->value.list_value == 0)
            throw "null pointer...";
         var count = 0;
         VAR_DATA::var_list_type::iterator ptr;
         for(ptr = data->value.list_value->begin(); ptr != data->value.list_value->end(); ptr++, count++) {
            if(count == index) {
               data->value.list_value->insert(ptr, v);
               break;
            }
         }
         if(count != index) {
            while(var(data->value.list_value->size()) < index)
               data->value.list_value->push_back(0);
            data->value.list_value->push_back(v);
         }
      }
      else {
         data->type = VAR_DATA::LIST_TYPE;
         data->value.list_value = new VAR_DATA::var_list_type();
         while(var(data->value.list_value->size()) < index)
            data->value.list_value->push_back(0);
         data->value.list_value->push_back(v);
         data->count = 1;
      }
   }

   var & top() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->top();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         return data->value.avalue->front();
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         return data->value.list_value->front();
      }
      else {
         throw "not a stack...";
      }
   }

   var pop() {
      var temp;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         temp = data->value.vvalue->pop();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         if(data->value.avalue->size() == 0) {
            throw "empty stack...";
         }
         temp = data->value.avalue->front();
         data->value.avalue->erase(data->value.avalue->begin());
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         if(data->value.list_value->size() == 0) {
            throw "empty stack...";
         }
         temp = data->value.list_value->front();
         data->value.list_value->pop_front();
      }
      else {
         throw "not a stack...";
      }
      return temp;
   }

   long hash() {
      string str = to_string();
      long sum = 0;
      for(int i = 0; i < str.size(); i++) {
         sum += str[i] * i;
      }
      return sum;
   }

   VAR_DATA::var_map_type::iterator hbegin() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->hbegin();
      }
      VAR_DATA::var_map_type::iterator ptr;
      if(data->type != VAR_DATA::HASH_TYPE) {
         throw "not a hash map (1)...";
      }
      ptr = data->value.hvalue->begin();
      return ptr;
   }

   VAR_DATA::var_map_type::iterator hend() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->hend();
      }
      if(data->type != VAR_DATA::HASH_TYPE) {
         throw "not a hash map (2)...";
      }
      VAR_DATA::var_map_type::iterator ptr = data->value.hvalue->end();
      return ptr;
   }

   iterator begin() {
      iterator ret;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         ret = data->value.vvalue->begin();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         ret = data->value.avalue->begin();
      }
      else {
         throw "not an array...";
      }
      return ret;
   }
   iterator end() {
      iterator ret;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         ret = data->value.vvalue->end();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         ret = data->value.avalue->end();
      }
      else {
         throw "not an array...";
      }
      return ret;
   }
/*
   reverse_iterator rbegin() {
      reverse_iterator ret;
      if(data->type == VAR_DATA::ARRAY_TYPE) {
         ret = data->value.avalue->rbegin();
      }
      return ret;
   }
   reverse_iterator rend() {
      reverse_iterator ret;
      if(data->type == VAR_DATA::ARRAY_TYPE) {
         ret = data->value.avalue->rend();
      }
      return ret;
   }
*/
   const string get_type_string() const {
      string type = "<null>";
      switch(data->type) {
      case VAR_DATA::REFERENCE_TYPE:
      type = data->value.vvalue->get_type_string();
      break;
      case VAR_DATA::LONG_TYPE:
      type = "long";
      break;
      case VAR_DATA::DOUBLE_TYPE:
      type = "double";
      break;
      case VAR_DATA::STRING_TYPE:
      type = "string";
      break;
      case VAR_DATA::ARRAY_TYPE:
      type = "array";
      break;
      case VAR_DATA::HASH_TYPE:
      type = "hash";
      break;
      case VAR_DATA::FUNCTION_TYPE:
      type = "function";
      break;
      case VAR_DATA::POINTER_TYPE:
      type = "pointer";
      break;
      }
      return type;
   }
   var & operator=(const var & right) {
      if(this != &right) {
         dec();
         if(right.data->type == VAR_DATA::LONG_TYPE) {
            data = new VAR_DATA(VAR_DATA::LONG_TYPE);
            data->value.lvalue = right.data->value.lvalue;
         }
         else if(right.data->type == VAR_DATA::DOUBLE_TYPE) {
            data = new VAR_DATA(VAR_DATA::DOUBLE_TYPE);
            *data->value.dvalue = *right.data->value.dvalue;
         }
         else if(right.data->type == VAR_DATA::STRING_TYPE) {
            data = new VAR_DATA(VAR_DATA::STRING_TYPE);
            *data->value.svalue = *right.data->value.svalue;
         }
         else {
            data = right.data;
         }
         inc();
      }
      return *this;
   }
   var operator+(const var & right) {
      var ret;
      ___BIN_OP_IF((*this),right,+)
      return ret;
   }
   var operator-(const var & right) {
      var ret;
      ___ABIN_OP_IF((*this),right,-)
      return ret;
   }
   var operator*(const var & right) {
      var ret;
      ___ABIN_OP_IF((*this),right,*)
      return ret;
   }
   var operator/(const var & right) {
      var ret;
      ___ABIN_OP_IF((*this),right,/)
      return ret;
   }
   var operator%(const var & right) {
      var ret;
      ___INT_ABIN_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;
      ___INT_ABIN_OP_IF((*this),right,<<);
      return ret;
   }
   var operator>>(const var & right) const {
      var ret;
      ___INT_ABIN_OP_IF((*this),right,>>);
      return ret;
   }
   var operator^(const var & right) const {
      var ret;
      ___INT_ABIN_OP_IF((*this),right,^);
      return ret;
   }
   var operator|(const var & right) const {
      var ret;
      ___INT_ABIN_OP_IF((*this),right,|);
      return ret;
   }
   var operator&(const var & right) const {
      var ret;
      ___INT_ABIN_OP_IF((*this),right,&);
      return ret;
   }
   var operator&&(const var & right) const {
      var ret;
      ___BBIN_OP_IF((*this),right,&&);
      return ret;
   }
   var operator||(const var & right) const {
      var ret;
      ___BBIN_OP_IF((*this),right,||);
      return ret;
   }
   var operator==(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,==);
      return ret;
   }
   var operator!=(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,!=);
      return ret;
   }
   var operator-() const {
      var ret;
      if(data->type == VAR_DATA::LONG_TYPE)
         ret = var(-data->value.lvalue);
      else if(data->type == VAR_DATA::DOUBLE_TYPE)
         ret = var(-(*data->value.dvalue));
      else
         ret = -to_int();
      return ret;
   }
   var operator<(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,<)
      return ret;
   }
   var operator>(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,>)
      return ret;
   }
   var operator<=(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,<=)
      return ret;
   }
   var operator>=(const var & right) const {
      var ret;
      ___BIN_OP_IF((*this),right,>=)
      return ret;
   }
   const var & get_value() const {
      if(is_ref()) {
         return data->value.vvalue->get_value();
      }
      return *this;
   }
   var & get_value() {
      if(is_ref()) {
         return data->value.vvalue->get_value();
      }
      return *this;
   }
   var operator!() const {
      var ret;
      if(is_ref())
         ret = !get_value();
      else if(data->type == VAR_DATA::LONG_TYPE)
         ret = var(!data->value.lvalue);
      else
         ret = !to_long();
      return ret;
   }
   var operator~() const {
      var ret;
      if(is_ref())
         ret = var((long)~((unsigned long)get_value().to_long()));
      else if(data->type == VAR_DATA::LONG_TYPE)
         ret = var((long)~((unsigned long)data->value.lvalue));
      else
         ret = var((long)~((unsigned long)to_long()));
      return ret;
   }
   var & operator++() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         (*data->value.vvalue)++;
      }
      else if(data->type == VAR_DATA::LONG_TYPE) {
         data->value.lvalue = data->value.lvalue + 1;
      }
      else if(data->type == VAR_DATA::DOUBLE_TYPE) {
         *data->value.dvalue = *data->value.dvalue + complex< double >(1);
      }
      else if(data->type == VAR_DATA::STRING_TYPE) {
         if(data->value.svalue->find(".") != string::npos) {
            double temp = atof(data->value.svalue->c_str()) + 1;
            operator=(var(temp));
         }
         else {
            long temp = atol(data->value.svalue->c_str()) + 1;
            operator=(var(temp));
         }
      }
      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::LONG_TYPE) {
         data->value.lvalue = data->value.lvalue + 1;
      }
      else if(data->type == VAR_DATA::DOUBLE_TYPE) {
         *data->value.dvalue = *data->value.dvalue + complex< double >(1);
      }
      else if(data->type == VAR_DATA::STRING_TYPE) {
         if(data->value.svalue->find(".") != string::npos) {
            double temp = atof(data->value.svalue->c_str()) + 1;
            operator=(var(temp));
         }
         else {
            long temp = atol(data->value.svalue->c_str()) + 1;
            operator=(var(temp));
         }
      }
      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::LONG_TYPE) {
         data->value.lvalue = data->value.lvalue - 1;
      }
      else if(data->type == VAR_DATA::DOUBLE_TYPE) {
         *data->value.dvalue = *data->value.dvalue - complex< double >(1);
      }
      else if(data->type == VAR_DATA::STRING_TYPE) {
         if(data->value.svalue->find(".") != string::npos) {
            double temp = atof(data->value.svalue->c_str()) - 1;
            operator=(var(temp));
         }
         else {
            long temp = atol(data->value.svalue->c_str()) - 1;
            operator=(var(temp));
         }
      }
      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::LONG_TYPE) {
         data->value.lvalue = data->value.lvalue - 1;
      }
      else if(data->type == VAR_DATA::DOUBLE_TYPE) {
         *data->value.dvalue = *data->value.dvalue - complex< double >(1);
      }
      else if(data->type == VAR_DATA::STRING_TYPE) {
         if(data->value.svalue->find(".") != string::npos) {
            double temp = atof(data->value.svalue->c_str()) - 1;
            operator=(var(temp));
         }
         else {
            long temp = atol(data->value.svalue->c_str()) - 1;
            operator=(var(temp));
         }
      }
      else {
         throw var_exception("decrement...", __LINE__, __FILE__);
      }
      return *this;
   }
   bool find(const var & dat) {
      bool ret = false;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         ret = data->value.vvalue->find(dat);
      }
      else if(data->type == VAR_DATA::HASH_TYPE) {
         ret = data->value.hvalue->find(dat) != data->value.hvalue->end();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         for(var i = 0; i < size(); i++) {
            if((*this)[i] == dat) {
               ret = true;
               break;
            }
         }
      }
      return ret;
   }
   void remove(const var & val) {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         data->value.vvalue->remove(val);
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         VAR_DATA::var_array_type::iterator ptr = data->value.avalue->begin();
         int count = 0;
         while(count < val.to_int()) {
            ptr++;
            count++;
         }
         data->value.avalue->erase(ptr);
      }
      else if(data->type == VAR_DATA::HASH_TYPE) {
         VAR_DATA::var_map_type::iterator ptr = data->value.hvalue->find(val);
         if(ptr != data->value.hvalue->end()) {
            data->value.hvalue->erase(ptr);
         }
      }
   }
   var & operator[](const var & index) {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return (*data->value.vvalue)[index];
      }
      if(index.is_ref()) {
         return (*this)[*index.data->value.vvalue];
      }
      if(data->type == VAR_DATA::ARRAY_TYPE 
         && (index.data->type == VAR_DATA::LONG_TYPE)) {
         VAR_DATA::var_array_type & p = *(data->value.avalue);
         int n = index.to_int();
         while(p.size() <= n) {
            p.push_back(var(0));
         }
         return p[n];
      }
      else if(data->type == VAR_DATA::HASH_TYPE) {
         VAR_DATA::var_map_type & p = *(data->value.hvalue);
         return p[index];
      }
      else if(data->type == VAR_DATA::LIST_TYPE
              && (index.get_type() == VAR_DATA::LONG_TYPE)) {
         VAR_DATA::var_list_type::iterator ptr;
         var count = 0;
         while(var(data->value.list_value->size()) <= index) {
            data->value.list_value->push_back(0);
         }
         for(ptr = data->value.list_value->begin(); ptr != data->value.list_value->end(); ptr++) {
            if(count == index) {
               break;
            }
            count++;
         }
         return *ptr;
      }
      else if(index.get_type() == VAR_DATA::LONG_TYPE) {
         dec();
         if(data == 0) data = new VAR_DATA();
         data->value.avalue = new VAR_DATA::var_array_type();
         data->type = VAR_DATA::ARRAY_TYPE;
         data->count = 1;
         VAR_DATA::var_array_type & p = *(data->value.avalue);
         int n = index.to_int();
         while(p.size() <= n) {
            p.push_back(var(0));
         }
         return p[n];
      }
      else {
         if(data->type == VAR_DATA::ARRAY_TYPE) {
            VAR_DATA::var_array_type * temp = data->value.avalue;
            VAR_DATA::var_map_type * the_map = new VAR_DATA::var_map_type();
            for(int i = 0; i < temp->size(); i++) {
               (*the_map)[var(i)] = (*temp)[i];
            }
            dec();
            if(data == 0) data = new VAR_DATA();
            data->value.hvalue = the_map;
            data->value.avalue = 0;
         }
         else {
            dec();
            if(data == 0) data = new VAR_DATA();
            data->value.hvalue = new VAR_DATA::var_map_type();
         }
         data->type = VAR_DATA::HASH_TYPE;
         data->count = 1;
         VAR_DATA::var_map_type & p = *(data->value.hvalue);
         return p[index];
      }
   }
   int get_ref_count() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->get_ref_count();
      }
      return data->count;
   }
   var * get_pointer() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue;
      }
      else if(data->type == VAR_DATA::LONG_TYPE) {
         if(data->value.lvalue == 0) {
             return 0;
         }
      }
      return 0;
   }
   void * get_raw_pointer() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->get_raw_pointer();
      }
      if(data->type == VAR_DATA::POINTER_TYPE) {
         return data->value.pvalue;
      }
      else {
         throw "not a pointer (get_raw_pointer)...";
      }
      return 0;
   }
   var operator()() {
      var ret;
      if(data->type == VAR_DATA::FUNCTION_TYPE) {
         switch(data->ret_type) {
         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::LONG_TYPE: {
            typedef long (*pf_type)(T);
            pf_type pf = (pf_type)data->value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::DOUBLE_TYPE: {
            typedef double (*pf_type)(T);
            pf_type pf = (pf_type)data->value.pvalue;
            ret = pf(arg1);
         }
         break;
         case VAR_DATA::POINTER_TYPE: {
            typedef void* (*pf_type)(T);
            pf_type pf = (pf_type)data->value.pvalue;
            ret = pf(arg1);
         }
         break;
         default: {
            typedef void (*pf_type)(T);
            pf_type pf = (pf_type)data->value.pvalue;
            pf(arg1);
         }
         }
      }
      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::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::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->value.svalue->size();
      }
      else if(data->type == VAR_DATA::ARRAY_TYPE) {
         ret = data->value.avalue->size();
      }
      else if(data->type == VAR_DATA::HASH_TYPE) {
         ret = data->value.hvalue->size();
      }
      else if(data->type == VAR_DATA::LIST_TYPE) {
         ret = data->value.list_value->size();
      }
      else if(data->type == VAR_DATA::POINTER_TYPE) {
         ret = data->buffer_size;
      }
      return ret;
   }
   string to_str() const {
      if(is_ref()) return data->value.vvalue->to_str();
      return to_string();
   }
   string to_string() const {
      string ret;
      if(is_ref()) return data->value.vvalue->to_string();
      if(data->type == VAR_DATA::STRING_TYPE) {
         ret = *data->value.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->real();
         else if(data->type == VAR_DATA::ARRAY_TYPE) {
            str << "(";
            for(VAR_DATA::var_array_type::iterator ptr = data->value.avalue->begin(); ptr != data->value.avalue->end(); ptr++) {
               str << ptr->to_string() << ","; 
            }
            str << ")";
            ret = str.str();
            return ret;
         }
         else 
            str << 0;
         str >> ret;
      }
      return ret;
   }
   operator string() {
      return to_string();
   }
   const char * c_str() const {
      if(is_ref()) return data->value.vvalue->c_str();
      return data->value.svalue->c_str();
   }
   var to_number() {
      var ret;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->to_number();
      }
      if(data->type == VAR_DATA::STRING_TYPE) {
         if(data->value.svalue->size()) {
            if(isdigit((*data->value.svalue)[0]) && data->value.svalue->find(".") == 0) {
               ret = atoi(data->value.svalue->c_str());
            }
            else if(isdigit((*data->value.svalue)[0]) && data->value.svalue->find(".") != 0) {
               ret = atof(data->value.svalue->c_str());
            }
            else {
               ret = 0;
            }
         }
      }
      else if(data->type == VAR_DATA::LONG_TYPE) {
         ret = *this;
      }
      else if(data->type == VAR_DATA::DOUBLE_TYPE) {
         ret = *this;
      }
      else {
         ret = 0;
      }
      return ret;
   }
   long to_long() const {
      long ret = 0L;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->to_long();
      }
      if(data->type == VAR_DATA::STRING_TYPE) {
         ret = atol(data->value.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->real();
         else
            str << 0;
         str >> ret;
      }
      return ret;
   }
   float to_float() const {
      float ret = 0.0F;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->to_float();
      }
      if(data->type == VAR_DATA::STRING_TYPE) {
		  stringstream ss;
		  ss << *data->value.svalue;
		  ss >> ret;
      }
      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->real();
         else
            str << 0;
         str >> ret;
      }
      return ret;
   }
   bool is_list() {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_list();
      return data->type == VAR_DATA::LIST_TYPE;
   }
   bool is_array() {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_list();
      return data->type == VAR_DATA::ARRAY_TYPE;
   }
   bool is_pointer() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->is_pointer();
      }
      return data->type == VAR_DATA::POINTER_TYPE;
   }
   bool is_reference() { 
      return data->type == VAR_DATA::REFERENCE_TYPE;
   }
   bool is_ref() const {
      return data->type == VAR_DATA::REFERENCE_TYPE;
   }
   bool is_ptr_ref() const {
      return data->type == VAR_DATA::REFERENCE_TYPE || data->type == VAR_DATA::POINTER_TYPE;
   }
   bool is_ptr() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_ptr();
      return data->type == VAR_DATA::POINTER_TYPE;
   }
   void * to_pointer() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE) 
         return (void*)data->value.vvalue->to_pointer();
      if(data->type == VAR_DATA::POINTER_TYPE)
         return data->value.pvalue;
      else 
         return 0;
   }
   const var & to_var() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->to_var();
      return *this;
   }
   bool is_double() const {
      bool ret = false;
      if(data->type == VAR_DATA::REFERENCE_TYPE) return data->value.vvalue->is_double();
      if(data->type == VAR_DATA::DOUBLE_TYPE) {
         ret = true;
      }
      return ret;
   }
   complex< double > to_complex() {
      if(data->type == VAR_DATA::REFERENCE_TYPE) return data->value.vvalue->to_complex();
      return data->get_dvalue();
   }
   int to_int() const {
      int ret = 0;
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->to_int();
      }
      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->real());
      else if(data->type == VAR_DATA::STRING_TYPE)
         ret = atoi(data->value.svalue->c_str());
      else
         ret = 0;
      return ret;
   }

   var * get_ptr() { 
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->get_ptr();
      return this;
   }

   double to_double() const {
      double ret = 0.0;
      if(data->type == VAR_DATA::REFERENCE_TYPE) return data->value.vvalue->is_double();
      if(data->type == VAR_DATA::DOUBLE_TYPE) {
         ret = data->value.dvalue->real();
      }
      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->value.svalue->c_str());
         else 
            ret = 0.0;
      }
      return ret;
   }
   bool to_bool() const {
      bool ret = false;
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->to_bool();
      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.dvalue->real());
      else if(data->type == VAR_DATA::STRING_TYPE)
         ret = static_cast< bool >(atoi(data->value.svalue->c_str()));
      else 
         ret = false;
      return ret;
   }
   short to_short() const {
      short ret = 0;
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->to_short();
      if(data->type == VAR_DATA::STRING_TYPE) {
         ret = atoi(data->value.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->real());
         else
            ret = 0;
      }
      return ret;
   }
   char to_char() const {
      char ret = '\0';
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->to_char();
      if(data->type == VAR_DATA::STRING_TYPE) {
         ret = data->value.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->real());
         else
            ret = '\0';
      }
      return ret;
   }

   bool is_long() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_long();
      return data->type == VAR_DATA::LONG_TYPE;
   }
   bool is_int() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_int();
      return data->type == VAR_DATA::LONG_TYPE;
   }
   bool is_string() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE)
         return data->value.vvalue->is_string();
      return data->type == VAR_DATA::STRING_TYPE;
   }
   VAR_DATA::var_type get_type() const {
      if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return data->value.vvalue->get_type();
      }
      return data->type;
   }
/*
   operator int() const {
      return (int)data->value.lvalue;
   }
*/
   operator void*() const {
      return (void*)data->value.pvalue;
   }
/*
   operator bool() const {
      return (bool)data->value.lvalue;
   }
   operator long() const {
      return data->value.lvalue;
   }
   operator double() const {
      return data->value.dvalue;
   }
   template< class T >
   operator T*() {
      if(data->type == VAR_DATA::POINTER_TYPE) {
         return (T*)data->value.pvalue;
      }
      else if(data->type == VAR_DATA::REFERENCE_TYPE) {
         return (T*)data->value.vvalue;
      }
      return 0;
   }
*/
   template< class T >
   T * operator->() {
      if(data->type == VAR_DATA::POINTER_TYPE) {
         return get_ptr< T >(data->value.pvalue);
      }
      return 0;
   }
   friend T_ostream & operator<<(T_ostream & out, const var & v);
   friend T_istream & operator>>(T_istream & in, var & v);
   friend bfstream & operator<<(bfstream & out, const var & v);
   friend bfstream & operator>>(bfstream & in, var & v);

   void set_type(VAR_DATA::var_type t) {
      data->type = t;
   }

   var keys() {
      if(is_ref()) return data->value.vvalue->keys();
      var arr(VAR_DATA::ARRAY_TYPE);
      if(data->type == VAR_DATA::HASH_TYPE) {
         for(VAR_DATA::var_map_type::iterator ptr = data->value.hvalue->begin(); ptr != data->value.hvalue->end(); ptr++) {
            arr[(int)arr.size()] = var(ptr->first);
         }
      }
      else {
         throw "keys function called on a var that is not a hash...";
      }
      return arr;
   }

   var values() {
      if(is_ref()) return data->value.vvalue->values();
      var arr(VAR_DATA::ARRAY_TYPE);
      if(data->type == VAR_DATA::HASH_TYPE) {
         for(VAR_DATA::var_map_type::iterator ptr = data->value.hvalue->begin(); ptr != data->value.hvalue->end(); ptr++) {
            arr[(int)arr.size()] = var(ptr->second);
         }
      }
      else {
         throw "keys function called on a var that is not a hash...";
      }
      return arr;
   }
};

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

inline
T_ostream & operator<<(T_ostream & out, const var & v) 
{
   switch(v.data->type) {
   case VAR_DATA::REFERENCE_TYPE:
   out << (*v.data->value.vvalue);
   break;

   case VAR_DATA::STRING_TYPE:
   out << (*(v.data->value.svalue));
   break;

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

   case VAR_DATA::DOUBLE_TYPE:
   if(v.data->value.dvalue->imag() != 0.0)
      out << v.data->value.dvalue->imag() << "i";
   else
      out << v.data->value.dvalue->real();
   break;

   case VAR_DATA::HASH_TYPE:
   out << "(";
   for(VAR_DATA::var_map_type::iterator ptr = v.data->value.hvalue->begin(); ptr != v.data->value.hvalue->end(); ptr++) {
      if(ptr != v.data->value.hvalue->begin()) {
         out << ",";
      }
      out << ptr->first << " => " << ptr->second;
   }
   out << ")";
   break;

   case VAR_DATA::FUNCTION_TYPE:
   out << "<FUNCTION>";
   break;

   case VAR_DATA::POINTER_TYPE:
   if(v.data->value.pvalue == 0)
      out << "null";
   else
      out << "<POINTER>";
   break;

   case VAR_DATA::ARRAY_TYPE:
   out << "(";
   for(int i = 0; i < v.data->value.avalue->size(); i++) {
      if(i != 0) {
         out << ",";
      }
      out << (*v.data->value.avalue)[i];
   }
   out << ")";
   break;

   case VAR_DATA::LIST_TYPE:
   out << "(";
   for(VAR_DATA::var_list_type::iterator ptr = v.data->value.list_value->begin(); ptr != v.data->value.list_value->end(); ptr++) {
      if(ptr != v.data->value.list_value->begin()) {
         out << ",";
      }
      out << (*ptr);
   }
   out << ")";
   break;
   }
   return out;
}

inline
T_istream & operator>>(T_istream & in, var & v) 
{
   string s;
   in >> s;
   if(is_int(s)) {
      long l;
      stringstream ss;
      ss << s;
      ss >> l;
      v = var(l);
   }
   else if(is_float(s)) {
      double d;
      stringstream ss;
      ss << s;
      ss >> d;
      v = var(d);
   }
   else {
      v = var(s);
   }
   return in;
}

inline
bfstream & operator<<(bfstream & out, const var & v) 
{
   if(v.get_type() == VAR_DATA::REFERENCE_TYPE) {
      out << v.get_value();
   }
   else if(v.get_type() == VAR_DATA::STRING_TYPE) {
      out << (int)v.get_type() <<  v.to_string();
   }
   else if(v.get_type() == VAR_DATA::LONG_TYPE) {
      out << (int)v.get_type() << v.to_long();
   }
   else if(v.get_type() == VAR_DATA::DOUBLE_TYPE) {
      out << (int)v.get_type() << v.to_double();
   }
   else {
      out << (int)VAR_DATA::STRING_TYPE << v.to_string();
   }
   return out;
}

inline
bfstream & operator>>(bfstream & in, var & v) 
{
   int vtype;
   in >> vtype;
   if(vtype == VAR_DATA::REFERENCE_TYPE) {
      in >> v.get_value();
   }
   else if(vtype == VAR_DATA::STRING_TYPE) {
      string s;
      in >> s;
      v = var(s);
   }
   else if(vtype == VAR_DATA::LONG_TYPE) {
      long lval;
      in >> lval;
      v = var(lval);
   }
   else if(vtype == VAR_DATA::DOUBLE_TYPE) {
      double dval;
      in >> dval;
      v = var(dval);
   }
   else {
      string s;
      in >> s;
      v = var(s);
   }
   return in;
}

inline
bstringstream & operator<<(bstringstream & out, const var & v) 
{
   if(v.get_type() == VAR_DATA::REFERENCE_TYPE) {
      out << v.get_value();
   }
   else if(v.get_type() == VAR_DATA::STRING_TYPE) {
      out << (int)v.get_type() <<  v.to_string();
   }
   else if(v.get_type() == VAR_DATA::LONG_TYPE) {
      out << (int)v.get_type() << v.to_long();
   }
   else if(v.get_type() == VAR_DATA::DOUBLE_TYPE) {
      out << (int)v.get_type() << v.to_double();
   }
   else {
      out << (int)VAR_DATA::STRING_TYPE << v.to_string();
   }
   return out;
}

inline
bstringstream & operator>>(bstringstream & in, var & v) 
{
   int vtype;
   in >> vtype;
   if(vtype == VAR_DATA::REFERENCE_TYPE) {
      in >> v.get_value();
   }
   else if(vtype == VAR_DATA::STRING_TYPE) {
      string s;
      in >> s;
      v = var(s);
   }
   else if(vtype == VAR_DATA::LONG_TYPE) {
      long lval;
      in >> lval;
      v = var(lval);
   }
   else if(vtype == VAR_DATA::DOUBLE_TYPE) {
      double dval;
      in >> dval;
      v = var(dval);
   }
   else {
      string s;
      in >> s;
      v = var(s);
   }
   return in;
}

inline
T_ostream & operator<<(T_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
VAR_DATA::VAR_DATA(const VAR_DATA & cp) 
{
   each_index = cp.each_index;
   count = 0;
   value = cp.value;
   if(cp.type == POINTER_TYPE && cp.buffer_size > 0) {
      value.pvalue = new char[cp.buffer_size];
      memcpy(value.pvalue, cp.value.pvalue, cp.buffer_size);
   }
   else if(cp.type == LIST_TYPE)
      value.list_value = new var_list_type(*cp.value.list_value);
   else if(cp.type == ARRAY_TYPE)
      value.avalue = new var_array_type(*cp.value.avalue);
   else if(cp.type == HASH_TYPE)
      value.hvalue = new var_map_type(*cp.value.hvalue);
   else if(cp.type == DOUBLE_TYPE)
      value.dvalue = new complex< double >(*cp.value.dvalue);
   else if(cp.type == STRING_TYPE)
      value.svalue = new string(*cp.value.svalue);
   type = cp.type;
   buffer_size = cp.buffer_size;
   //svalue = cp.svalue;
   decon = cp.decon;
}

inline
bool VAR_DATA::dec() {
   bool ret = false;
   count--;
   
   if(count == 0) {
      ret = true;
   }
   else if(count < 0) {
      throw "negative reference count...";
   }
   return ret;
}

inline
void 
VAR_DATA::inc()
{
   count++;
}

inline
VAR_DATA::~VAR_DATA() {
   if(type == ARRAY_TYPE)
      delete value.avalue;
   else if(type == HASH_TYPE)
      delete value.hvalue;
   else if(type == LIST_TYPE)
      delete value.list_value;
   else if(type == POINTER_TYPE && buffer_size > 0) {
      if(value.pvalue != 0) {
         delete [] (char*)value.pvalue;
         value.pvalue = 0;
      }
   }
   else if(type == POINTER_TYPE && decon != 0) {
      if(value.pvalue != 0) {
         decon(value.pvalue);
         value.pvalue = 0;
      }
   }
   else if(type == DOUBLE_TYPE)
      delete value.dvalue;
   else if(type == STRING_TYPE)
      delete value.svalue;
}

 template< class T >
 class t_var : public var {
 public:
    t_var(T * p) : var(p) {
    }
    T * operator->() {
       return (T*)to_pointer();
    }
 };
 


}

#endif /* END _VAR_DATA_H */
