// ---------------------------------------------------------------------------
// - Integer.cpp                                                             -
// - standard object library - integer class implementation                  -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Real.hpp"
#include "Stdsid.hxx"
#include "Vector.hpp"
#include "Utility.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Character.hpp"
#include "Exception.hpp"
#include "InputStream.hpp"
#include "OutputStream.hpp"
#include "ccnv.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - public section                                                        -
  // -------------------------------------------------------------------------

  // compute the opposite of the current integer
  
  Integer operator - (const Integer& x) {
    x.rdlock ();
    try {
      Integer result = -x.d_value;
      x.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }
  
  // add two integers together from two integers

  Integer operator + (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value + y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // substract two integers together from two integers
  
  Integer operator - (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value - y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }
  
  // multiply two integers together from two integers
  
  Integer operator * (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value * y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }
    
  // divide two integers together from two integers
  
  Integer operator / (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      if (y.d_value == 0) {
	throw Exception ("integer-error","division by zero");
      }
      Integer result = x.d_value / y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // return the remainder of this number with its argument

  Integer operator % (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      if (y.d_value == 0) {
	throw Exception ("integer-error","division by zero");
      }
      Integer result = x.d_value % y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // bitwise negate an integer
  Integer operator ~ (const Integer& x) {
    x.rdlock ();
    try {
      Integer result = ~x.d_value;
      x.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

  // bitwise and an integer with another one

  Integer operator & (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value & y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // bitwise or an integer with another one

  Integer operator | (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value | y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // bitwise xor an integer with another one

  Integer operator ^ (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Integer result = x.d_value ^ y.d_value;
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a new default integer
  
  Integer::Integer (void) {
    d_value = 0;
  }

  // create a new integer from a long integer

  Integer::Integer (const t_long value) {
    d_value = value;
  }

  // create a new integer from a string

  Integer::Integer (const String& value) {
    d_value = Utility::tolong (value);
  }
  
  // copy constructor for this integer

  Integer::Integer (const Integer& that) {
    that.rdlock ();
    try {
      d_value = that.d_value;
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // return the class name

  String Integer::repr (void) const {
    return "Integer";
  }

  // return a clone of this object

  Object* Integer::clone (void) const {
    return new Integer (*this);
  }

  // return a literal representation of this integer

  String Integer::toliteral (void) const {
    rdlock ();
    try {
      String result = tostring ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a string representation on this integer

  String Integer::tostring (void) const {
    rdlock ();
    try {
      String result = Utility::tostring (d_value);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the integer serial code

  t_byte Integer::serialid (void) const {
    return SERIAL_INTG_ID;
  }

  // serialize this integer

  void Integer::wrstream (OutputStream& os) const {
    rdlock ();
    try {
      t_byte data[8];
      c_ohton (d_value, data);
      os.write ((char*) data, 8);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // deserialize this integer

  void Integer::rdstream (InputStream& is) {
    wrlock ();
    try {
      t_byte data[8];
      for (long i = 0; i < 8; i++) data[i] = (t_byte) is.read ();
      d_value = c_ontoh (data);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // set an integer with a value

  Integer& Integer::operator = (const t_long value) {
    wrlock ();
    try {
      d_value = value;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set an integer with a value

  Integer& Integer::operator = (const Integer& that) {
   // check for self assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      d_value = that.d_value;
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // add an integer to this one

  Integer& Integer::operator += (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    try {
      d_value += x.d_value;
      unlock   ();
      x.unlock ();
      return *this;
    } catch (...) {
      unlock   ();
      x.unlock ();
      throw;
    }
  }

  // substract an integer to this one
  
  Integer& Integer::operator -= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    try {
      d_value -= x.d_value;
      unlock ();
      x.unlock   ();
      return *this;
    } catch (...) {
      unlock   ();
      x.unlock ();
      throw;
    }
  }

  // multiply an integer with this one
  
  Integer& Integer::operator *= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    try {
      d_value *= x.d_value;
      unlock ();
      x.unlock   ();
      return *this;
    } catch (...) {
      unlock   ();
      x.unlock ();
      throw;
    }
  }

  // divide an integer with this one
  
  Integer& Integer::operator /= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    try {
      if (x.d_value == 0) {
	throw Exception ("integer-error","division by zero");
      }
      d_value /= x.d_value;
      unlock ();
      x.unlock   ();
      return *this;
    } catch (...) {
      unlock   ();
      x.unlock ();
      throw;
    }
  }

  // prefix add one to the integer

  Integer& Integer::operator ++ (void) {
    wrlock ();
    try {
      ++d_value;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // postfix add one to the integer

  Integer Integer::operator ++ (int) {
    wrlock ();
    try {
      Integer result = *this;
      d_value++;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // prefix sub one to the integer

  Integer& Integer::operator -- (void) {
    wrlock ();
    try {
      --d_value;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // postfix sub one to the integer

  Integer Integer::operator -- (int) {
    wrlock ();
    try {
      Integer result = *this;
      d_value--;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // shift left this integer

  Integer Integer::operator << (const long asl) const {
    rdlock ();
    try {
      Integer result = d_value << (asl % 64);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // shift right this integer

  Integer Integer::operator >> (const long asr) const {
    rdlock ();
    try {
      Integer result = d_value >> (asr % 64);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compare an integer with a native value

  bool Integer::operator == (const t_long value) const {
    rdlock ();
    try {
      bool result = (d_value == value);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  bool Integer::operator != (const t_long value) const {
    rdlock ();
    try {
      bool result = (d_value != value);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compare two integers

  bool Integer::operator == (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value == value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }

  // compare two integers

  bool Integer::operator != (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value != value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }

  // compare two integers

  bool Integer::operator < (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value <  value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }

  // compare two integers

  bool Integer::operator <= (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value <= value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }
  
  // compare two integers

  bool Integer::operator > (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value > value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }

  // compare two integers

  bool Integer::operator >= (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    try {
      bool result = (d_value >= value.d_value);
      unlock ();
      value.unlock ();
      return result;
    } catch (...) {
      unlock ();
      value.unlock ();
      throw;
    }
  }

  // return true if the integer is zero

  bool Integer::iszero (void) const {
    rdlock ();
    try {
      bool result = (d_value == 0);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the integer is even

  bool Integer::iseven (void) const {
    rdlock ();
    try {
      bool result = ((d_value & 1) == 0);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the integer is odd

  bool Integer::isodd (void) const {
    rdlock ();
    try {
      bool result = ((d_value & 1) == 1);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the absolute value of this number

  Integer Integer::abs (void) const {
    rdlock ();
    try {
      Integer result = (d_value < 0) ? -d_value : d_value;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // convert this integer into a hexadecimal representation

  String Integer::tohexa (void) const {
    rdlock ();
    try {
      // prepare result string
      String result = (d_value < 0) ? "-0x" : "0x";
      // bind the integer value
      t_octa data = d_value < 0 ? -d_value : d_value;
      result += Utility::tohexa (data);
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // convert this integer into a hexadecimal string

  String Integer::tohstr (void) const {
    rdlock ();
    try {
      // bind the integer value
      t_octa data = d_value < 0 ? -d_value : d_value;
      String result = Utility::tohexa (data);
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a native integer value

  t_long Integer::tolong (void) const {
    rdlock ();
    try {
      t_long result = d_value;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 27;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_OR    = zone.intern ("or");
  static const long QUARK_OPP   = zone.intern ("++");
  static const long QUARK_OMM   = zone.intern ("--");
  static const long QUARK_ADD   = zone.intern ("+");
  static const long QUARK_SUB   = zone.intern ("-");
  static const long QUARK_MUL   = zone.intern ("*");
  static const long QUARK_DIV   = zone.intern ("/");
  static const long QUARK_EQL   = zone.intern ("==");
  static const long QUARK_NEQ   = zone.intern ("!=");
  static const long QUARK_LTH   = zone.intern ("<");
  static const long QUARK_LEQ   = zone.intern ("<=");
  static const long QUARK_GTH   = zone.intern (">");
  static const long QUARK_GEQ   = zone.intern (">=");
  static const long QUARK_AEQ   = zone.intern ("+=");
  static const long QUARK_SEQ   = zone.intern ("-=");
  static const long QUARK_MEQ   = zone.intern ("*=");
  static const long QUARK_DEQ   = zone.intern ("/=");
  static const long QUARK_ABS   = zone.intern ("abs");
  static const long QUARK_AND   = zone.intern ("and");
  static const long QUARK_SHL   = zone.intern ("shl");
  static const long QUARK_SHR   = zone.intern ("shr");
  static const long QUARK_XOR   = zone.intern ("xor");
  static const long QUARK_MOD   = zone.intern ("mod");
  static const long QUARK_NOT   = zone.intern ("not");
  static const long QUARK_ODDP  = zone.intern ("odd-p");
  static const long QUARK_EVENP = zone.intern ("even-p");
  static const long QUARK_ZEROP = zone.intern ("zero-p");

  // evaluate an object to a native value

  t_long Integer::evalto (Runnable* robj, Nameset* nset, Object* object) {
    Object* obj = (object == nilp) ? nilp : object->eval (robj, nset);
    Integer* val = dynamic_cast <Integer*> (obj);
    if (val == nilp) throw Exception ("type-error", "nil object to evaluate");
    return val->tolong ();
  }

  // create a new object in a generic way

  Object* Integer::mknew (Vector* argv) {
    if ((argv == nilp) || (argv->length () == 0)) return new Integer;
    if (argv->length () != 1) 
      throw Exception ("argument-error", 
		       "too many argument with integer constructor");
    // try to map the integer argument
    Object* obj = argv->get (0);
    if (obj == nilp) return new Integer;

    // try an integer object
    Integer* ival = dynamic_cast <Integer*> (obj);
    if (ival != nilp) return new Integer (*ival);

    // try a real object
    Real* rval = dynamic_cast <Real*> (obj);
    if (rval != nilp) return new Integer (rval->tolong ());

    // try a character object
    Character* cval = dynamic_cast <Character*> (obj);
    if (cval != nilp) return new Integer (cval->toquad ());

    // try a string object
    String* sval = dynamic_cast <String*> (obj);
    if (sval != nilp) return new Integer (*sval);

    // illegal object
    throw Exception ("type-error", "illegal object with integer constructor",
		     obj->repr ());
  }

  // return true if the given quark is defined

  bool Integer::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? Number::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }

  // operate this object with another object

  Object* Integer::oper (t_oper type, Object* object) {
    Integer* iobj = dynamic_cast <Integer*> (object);
    Real*    dobj = dynamic_cast <Real*>    (object);
    switch (type) {
    case Object::ADD:
      if (iobj != nilp) return new Integer (*this + *iobj);
      if (dobj != nilp) return new Integer (*this + dobj->tolong ());
      break;
    case Object::SUB:
      if (iobj != nilp) return new Integer (*this - *iobj);
      if (dobj != nilp) return new Integer (*this - dobj->tolong ());
      break;
    case Object::MUL:
      if (iobj != nilp) return new Integer (*this * *iobj);
      if (dobj != nilp) return new Integer (*this * dobj->tolong ());
      break;
    case Object::DIV:
      if (iobj != nilp) return new Integer (*this / *iobj);
      if (dobj != nilp) return new Integer (*this / dobj->tolong ());
      break;
    case Object::UMN:
      return new Integer (-(*this));
      break;
    case Object::EQL:
      if (iobj != nilp) return new Boolean (*this == *iobj);
      if (dobj != nilp) return new Boolean (*this == dobj->tolong ());
      break;
    case Object::NEQ:
      if (iobj != nilp) return new Boolean (*this != *iobj);
      if (dobj != nilp) return new Boolean (*this != dobj->tolong ());
      break;
    case Object::GEQ:
      if (iobj != nilp) return new Boolean (*this >= *iobj);
      if (dobj != nilp) return new Boolean (*this >= dobj->tolong ());
      break;
    case Object::GTH:
      if (iobj != nilp) return new Boolean (*this > *iobj);
      if (dobj != nilp) return new Boolean (*this > dobj->tolong ());
      break;
    case Object::LEQ:
      if (iobj != nilp) return new Boolean (*this <= *iobj);
      if (dobj != nilp) return new Boolean (*this <= dobj->tolong ());
      break;
    case Object::LTH:
      if (iobj != nilp) return new Boolean (*this < *iobj);
      if (dobj != nilp) return new Boolean (*this < dobj->tolong ());
      break;
    }
    throw Exception ("type-error", "invalid operand with integer",
		     Object::repr (object));
  }

  // set an object to this integer

  Object* Integer::vdef (Runnable* robj, Nameset* nset, Object* object) {
    wrlock ();
    try {
      Integer* iobj = dynamic_cast <Integer*> (object);
      if (iobj != nilp) {
	*this = *iobj;
	robj->post (this);
	unlock ();
	return this;
      }
      Real* dobj = dynamic_cast <Real*> (object);
      if (dobj != nilp) {
	*this = dobj->tolong ();
	robj->post (this);
	unlock ();
	return this;
      }
      Character* cobj = dynamic_cast <Character*> (object);
      if (cobj != nilp) {
	*this = (t_long) cobj->toquad ();
	robj->post (this);
	unlock ();
	return this;
      }
      throw Exception ("type-error", "invalid object with integer vdef",
		       Object::repr (object));
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // apply this object with a set of arguments and a quark

  Object* Integer::apply (Runnable* robj, Nameset* nset, const long quark,
			  Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_ABS)   return new Integer (abs    ());
      if (quark == QUARK_EVENP) return new Boolean (iseven ());
      if (quark == QUARK_ODDP)  return new Boolean (isodd  ());
      if (quark == QUARK_ZEROP) return new Boolean (iszero ());

      if (quark == QUARK_NOT)   return new Integer (~(*this));
      if (quark == QUARK_OPP) {
	wrlock ();
	try {
	  ++(*this);
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_OMM) {
	wrlock ();
	try {
	  --(*this);
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      } 
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_ADD) return oper (Object::ADD, argv->get (0));
      if (quark == QUARK_SUB) return oper (Object::SUB, argv->get (0));
      if (quark == QUARK_MUL) return oper (Object::MUL, argv->get (0));
      if (quark == QUARK_DIV) return oper (Object::DIV, argv->get (0));
      if (quark == QUARK_EQL) return oper (Object::EQL, argv->get (0));
      if (quark == QUARK_NEQ) return oper (Object::NEQ, argv->get (0));
      if (quark == QUARK_LTH) return oper (Object::LTH, argv->get (0));
      if (quark == QUARK_LEQ) return oper (Object::LEQ, argv->get (0));
      if (quark == QUARK_GTH) return oper (Object::GTH, argv->get (0));
      if (quark == QUARK_GEQ) return oper (Object::GEQ, argv->get (0));

      if (quark == QUARK_AEQ) {
	wrlock ();
	try {
	  t_long val = argv->getlong (0);
	  *this += val;
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_SEQ) {
	wrlock ();
	try {
	  t_long val = argv->getlong (0);
	  *this -= val;
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MEQ) {
	wrlock ();
	try {
	  t_long val = argv->getlong (0);
	  *this *= val;
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_DEQ) {
	wrlock ();
	try {
	  t_long val = argv->getlong (0);
	  *this /= val;
	  robj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MOD) {
	t_long val = argv->getlong (0);
	return new Integer (*this % val);
      }
      if (quark == QUARK_SHL) {
	t_long asl = argv->getlong (0);
	Object* result = new Integer (*this << asl);
	return result;
      }
      if (quark == QUARK_SHR) {
	t_long asr = argv->getlong (0);
	Object* result = new Integer (*this >> asr);
	return result;
      }
      if (quark == QUARK_XOR) {
	t_long val = argv->getlong (0);
	Object* result = new Integer (*this ^ val);
	return result;
      }
      if (quark == QUARK_AND) {
	t_long val = argv->getlong (0);
	Object* result = new Integer (*this & val);
	return result;
      }
      if (quark == QUARK_OR) {
	t_long val = argv->getlong (0);
	Object* result = new Integer (*this | val);
	return result;
      }
    } 
    // call the number method
    return Number::apply (robj, nset, quark, argv);
  }
}
