// ---------------------------------------------------------------------------
// - Relatif.cpp                                                             -
// - standard object library - relatif big number 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-2006 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Real.hpp"
#include "Input.hpp"
#include "Stdsid.hxx"
#include "Vector.hpp"
#include "Buffer.hpp"
#include "Relatif.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "Character.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "ccnv.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - memory section                                                        -
  // -------------------------------------------------------------------------

  // reset an array of bytes
  static inline void rst_bytes (const long size, t_byte* bytes) {
    for (long i = 0; i < size; i++) bytes[i] = 0;
  }

  // this procedure compare two array of bytes and return true if the
  // first is greater or equal to the other one
  static bool geq_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte) {
    if (xsize < ysize) return false;
    if (xsize > ysize) return true;
    for (long i = xsize - 1; i >= 0; i--) {
      if (xbyte[i] > ybyte[i]) return true;
      if (xbyte[i] < ybyte[i]) return false;
    }
    // here the numbers are equal
    return true;
  }

  // this procedure compare two array of bytes and return true if the
  // first is greater than the other one
  static bool gth_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte) {
    if (xsize < ysize) return false;
    if (xsize > ysize) return true;
    for (long i = xsize - 1; i >= 0; i--) {
      if (xbyte[i] > ybyte[i]) return true;
      if (xbyte[i] < ybyte[i]) return false;
    }
    // here the numbers are equal
    return false;
  }

  // this procedure adds two array of bytes and return a new array 
  static long add_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte, t_byte** rbyte) {
    // get an approximate result size
    long    size = (xsize > ysize) ? xsize : ysize;
    t_byte* data = new t_byte[size+1];
    t_byte  cary = 0;
    // loop and add
    for (long i = 0; i < size; i++) {
      t_word xw = (i < xsize) ? xbyte[i] : 0;
      t_word yw = (i < ysize) ? ybyte[i] : 0;
      t_word rw = xw + yw + cary;
      data[i]   = (t_byte) rw;
      cary      = (t_byte) (rw >> 8);
    }
    // now readjust the result
    data[size] = cary;
    *rbyte = data;
    return (cary == 0) ? size : (size+1);
  }

  // this procedure substract two arrays of bytes, assuming the first one is 
  // bigger than the second one and return a new array
  static long sub_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte, t_byte** rbyte) {
    // get the result size
    t_byte* data = new t_byte[xsize];
    t_byte  cary = 0;
    // loop and substract
    for (long i = 0; i < xsize; i++) {
      t_word xw = xbyte[i];
      t_word yw = (i < ysize) ? ybyte[i] : 0;
      t_word rw = xw - yw - cary;
      data[i]   = (t_byte) rw;
      cary      = (t_byte) (rw >> 15);
    }
    // now readjust the result
    *rbyte = data;
    return xsize;
  }

  // this procedure multiply one array of bytes with one byte and shift the
  // result by the argument index
  static void mul_idata (const long xsize, t_byte* xbyte, const t_byte y,
			 const long isize, t_byte* idata, const long index) {
    // initialize cary and index
    t_byte cary = 0;
    for (long i = 0; i < index; i++) idata[i] = 0;
    // loop in the array
    for (long i = 0; i < xsize; i++) {
      t_word xw      = xbyte[i];
      t_word yw      = y;
      t_word rw      = (xw * yw) + cary;
      idata[i+index] = (t_byte) rw;
      cary           = (t_byte) (rw >> 8);
    }
    idata[xsize+index] = cary;
    for (long i = xsize + index + 1; i < isize; i++) idata[i] = 0;
  }

  // this procedure adds the intermediate multiplication result into
  // the result array - both array have the same size
  static void add_idata (const long rsize, t_byte* rdata, t_byte* idata) {
    t_byte cary = 0;
    for (long i = 0; i < rsize; i++) {
      t_word xw = rdata[i];
      t_word yw = idata[i];
      t_word rw = xw + yw + cary;
      rdata[i]  = (t_byte) rw;
      cary      = (t_byte) (rw >> 8);
    }
  }

  // this procedure multiply two unsigned array of bytes, the result is an
  // array of bytes those size is maximum the sum of the sizes
  static long mul_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte, t_byte** rbyte) {
    // allocate the result array
    long    rsize = xsize + ysize;
    t_byte* rdata = new t_byte[rsize];
    rst_bytes (rsize, rdata);
    // allocate the intermediate result
    t_byte* idata = new t_byte[rsize];
    // loop in the second array
    for (long i = 0; i < ysize; i++) {
      mul_idata (xsize, xbyte, ybyte[i], rsize, idata, i);
      add_idata (rsize, rdata, idata);
    }
    delete [] idata;
    *rbyte = rdata;
    return rsize;
  }

  // this procedure sets the intermediate array with a byte value
  static void set_ibyte (const long isize, t_byte* ibyte, const t_byte val) {
    // shift the intermediate array by one
    for (long i = isize - 1; i > 0; i--) ibyte[i] = ibyte[i-1];
    ibyte[0] = val;
  }

  // this procedure check that a quotient multiplied by the divisor is not
  // bigger than the intermediate array
  static bool chk_ibyte (const long isize, t_byte* ibyte, t_byte* mbyte,
			 const long ysize, t_byte* ybyte, const t_byte qb) {
    // the multiplication result
    rst_bytes (isize, mbyte);
    // multiply the divisor by the byte value
    t_byte cary = 0;
    for (long i = 0; i < ysize; i++) {
      t_word xw = ybyte[i];
      t_word yw = qb;
      t_word rw = (xw * yw) + cary;
      mbyte[i]  = (t_byte) rw;
      cary      = (t_byte) (rw >> 8);
    }
    mbyte[ysize] = cary;
    // compare and return
    return geq_bytes (isize, ibyte, isize, mbyte);
  }

  // this procedure divides the intermediate array by the divisor and return
  // a single digit - the intermediate array is left with the remainder
  static t_byte div_ibyte (const long isize, t_byte* ibyte, t_byte* mbyte,
			   const long ysize, t_byte* ybyte) {
    // check that we are greater
    if (geq_bytes (isize, ibyte, ysize, ybyte) == false) return 0;
    // extract tentative digit
    t_word xb = ibyte[isize-1];
    t_word yb = ybyte[ysize-1];
    t_byte rb = xb / yb;
    if ((rb == 0) && (isize > 1)) {
      xb = (xb << 8) + ibyte[isize-2];
      rb = xb / yb;
    }
    // now adjust if we are too big
    while (chk_ibyte (isize, ibyte, mbyte, ysize, ybyte, rb) == false) rb--;
    // here we have the good quotient value - the mbyte array has the
    // multiplication result - we just have to substract the intermediate
    // array with the mbyte array and we are set
    t_byte cary = 0;
    for (long i = 0; i < isize; i++) {
      t_word xw = ibyte[i];
      t_word yw = mbyte[i];
      t_word rw = xw - yw - cary;
      ibyte[i]  = (t_byte) rw;
      cary      = (t_byte) (rw >> 15);
    }    
    return rb;
  }

  // this procedure divides two unsigned array of bytes, the result is two
  // array of bytes with the quotient or the remainder
  static long div_bytes (const long xsize, t_byte* xbyte, 
			 const long ysize, t_byte* ybyte, 
			 t_byte** rbyte,   bool dflag) {
    // initialize the quotient and intermediate array
    long    qsize = xsize;
    long    isize = ysize + 1;
    t_byte* qbyte = new t_byte[qsize];
    t_byte* ibyte = new t_byte[isize];
    t_byte* mbyte = new t_byte[isize];
    // reset the arrays
    rst_bytes (isize, ibyte);
    rst_bytes (qsize, qbyte);
    // initialize the quotient length and loop in the digit
    long qlen = 0;
    for (long i = xsize - 1; i >= 0; i--) {
      // fill in the intermediate array 
      set_ibyte (isize, ibyte, xbyte[i]);
      // get the single digit which divide the intermediate array with
      // with the divisor - the intermediate array is at this stage fill
      // with the difference
      qbyte[qlen++] = div_ibyte (isize, ibyte, mbyte, ysize, ybyte);
    }
    // now fill in the result - if the division flag is set - we have to
    // reverse the quotient arrray - if not we return the remainder which
    // in the intermediate array
    long result = 0;
    if (dflag == true) {
      *rbyte = new t_byte[qlen];
      for (long i = 0; i < qlen; i++) (*rbyte)[i] = qbyte[qlen-i-1];
      result = qlen;
    } else {
      *rbyte = new t_byte[isize];
      for (long i = 0; i < isize; i++) (*rbyte)[i] = ibyte[i];
      result = isize;
    }
    // clean the arrays and return length
    delete [] ibyte;
    delete [] mbyte;
    delete [] qbyte;
    return result;
  }

  // this function convert a character according to a base
  static inline long ctol (const char c, const long base) {
    switch (base) {
    case 2:
      if (c == '0') return 0;
      if (c == '1') return 1;
      break;
    case 10:
      if ((c >= '0') && (c <= '9')) return (long) (c - '0');
      break;
    case 16:
      if ((c >= '0') && (c <= '9')) return  (long) (c - '0');
      if ((c >= 'a') && (c <= 'f')) return ((long) (c - 'a')) + 10;
      if ((c >= 'A') && (c <= 'F')) return ((long) (c - 'A')) + 10;
      break;
    }
    throw Exception ("format-error", "cannot convert character in base");
  }
  
  // this function convert a string to a relatif
  static Relatif ator (const String& s) {
    // initialize result
    long    base   = 10;
    Relatif basval = 1;
    Relatif result = 0;  
    
    // check for size first
    long len = s.length ();
    if (len == 0) return result;
    // process one character
    if (len == 1) {
      result = ctol (s[0], 10);
      return result;
    }    
    // here we have at least two characters - it can be the sign, the format
    // or a normal number
    bool sign  = false;
    long index = 0;
    // check for the sign
    if (s[0] == '-') {
      index++;
      sign = true;
      goto format;
    }
    if (s[0] == '+') {
      index++;
      sign = false;
      goto format;
    }
    
    // check for the format
  format:
    if (s[index] != '0') goto number;
    index++;
    if (index >= len) return result;
    if ((s[index] == 'x') || (s[index] == 'X')) {
      index++;
      if (index >= len)
	throw Exception ("format-error", "cannot convert to realtif", s);
      base = 16;
      goto number;
    }
    if ((s[index] == 'b') || (s[index] == 'B')) {
      index++;
      if (index >= len)
	throw Exception ("format-error", "cannot convert to realtif", s);
      base = 2;
      goto number;
    }
    
    // compute the number value
  number:
    // check for the last index
    long max = len - 1;
    if ((s[max] == 'r') || (s[max] == 'R')) max--;
    // loop in the digits
    for (long i = max; i >= index ; i--) {
      result = result + (basval * ctol (s[i], base));
      basval = basval * base;
    };
    return (sign) ? -result : result;
  }

  // shift left an array of bytes

  static long shl_bytes (const long size, t_byte* bytes, const long val,
			 t_byte** rbyte) {
    // compute the amount of byte shift
    long lbs     = val / 8;
    long len     = size + lbs;
    t_byte* data = new t_byte[len+1];
    rst_bytes (len+1, data);
    for (long i = 0;   i < lbs; i++) data[i] = 0;
    for (long i = lbs; i < len; i++) {
      long j = i - lbs;
      data[i] = (j < size) ? bytes[j] : 0;
    }
    data[len] = 0;
    // compute the amount of bit shift
    long   bsh  = val % 8;
    t_byte cary = 0;
    for (long i = lbs; i < len; i++) {
      t_word xb = data[i];
      t_word rb = (xb << bsh) + cary;
      data[i]   = (t_byte) rb;
      cary      = (t_byte) (rb >> 8);
    }
    data[len] = cary;
    // here is the result
    *rbyte = data;
    return len+1;
  }

  static long shr_bytes (const long size, t_byte* bytes, const long val,
			 t_byte** rbyte) {
    // compute the amount of byte shift
    long rbs = val / 8; 
    long len = size - rbs;
    // check for 0 length
    if (len <= 0) {
      *rbyte = new t_byte (0);
      return 1;
    }
    // build result and shift amount
    t_byte* data = new t_byte[len];
    for (long i = 0; i < len; i++) data[i] = bytes[i+rbs];
    // now shift right the remainder
    long   bsh    = val % 8;
    t_byte borrow = 0;
    for (long i = len - 1; i >= 0; i--) {
      t_word xb = ((t_word) data[i]) << 8;
      t_word bb = ((t_word) borrow)  << 8;
      t_word rb = (xb >> bsh) + bb;
      borrow    = (t_byte) rb;
      data[i]   = (t_byte) (rb >> 8);
    }
    // here it is
    *rbyte = data;
    return len;
  }

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

  // create a default relatif

  Relatif::Relatif (void) {
    d_size = 1;
    p_byte = new t_byte (0);
    d_sign = false;
  }

  // create a relatif with a native integer

  Relatif::Relatif (const t_long value) {
    d_size = 8;
    p_byte = new t_byte[8];
    d_sign = (value < 0);
    // copy the value
    t_byte bval[8];
    c_ohton ((value < 0) ? -value : value, bval);
    for (long i = 0; i < 8; i++) p_byte[i] = bval[7-i];
    normalize ();
  }

  // create a relatif from a string

  Relatif::Relatif (const String& value) {
    d_size = 0;
    p_byte = nilp;
    d_sign = false;
    *this  = ator (value);
  }

  // create a relatif from an array of bytes

  Relatif::Relatif (const long size, t_byte* byte, const bool sign) {
    d_size = size;
    p_byte = byte;
    d_sign = sign;
  }

  // copy contruct this relatif

  Relatif::Relatif (const Relatif& that) {
    that.rdlock ();
    d_size = that.d_size;
    d_sign = that.d_sign;
    if (d_size != 0) {
      p_byte = new t_byte[d_size];
      for (long i = 0; i < d_size; i++) p_byte[i] = that.p_byte[i];
    }
    that.unlock ();
  }

  // destroy this relatif

  Relatif::~Relatif (void) {
    delete [] p_byte;
  }

  // return the class name

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

  // return a literal representation of this relatif

  String Relatif::toliteral (void) const {
    rdlock ();
    String result = tostring () + 'R';
    unlock ();
    return result;
  }

  // get a string representation on this relatif

  String Relatif::tostring (void) const {
    rdlock ();
    try {
      Relatif baseval = 10;
      Relatif dataval = d_sign ? -(*this) : (*this);
      Buffer  buffer;
      // loop until we reach 0
      while (dataval != 0) {
	Relatif val = dataval % 10;
	buffer.pushback ((char) ('0' + (char) val.p_byte[0]));
	dataval     = dataval / baseval;
      }
      // readjust for sign and null value
      if (buffer.length () == 0) {
	unlock ();
	return '0';
      }
      String result;
      if (d_sign == true) result = result + '-';
      result = result + buffer.tostring ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a clone of this relatif

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

  // return the relatif serial code

  t_byte Relatif::serialid (void) const {
    return SERIAL_RELT_ID;
  }

  // serialize this relatif

  void Relatif::wrstream (Output& os) const {
    rdlock ();
    // serialize the size
    Integer size (d_size);
    size.wrstream (os);
    // serialize the sign
    Boolean sign (d_sign);
    sign.wrstream (os);
    // serialize the array
    os.write ((char*) p_byte, d_size);
    unlock ();
  }

  // deserialize this relatif

  void Relatif::rdstream (Input& is) {
    wrlock ();
    // clean the old value
    delete [] p_byte;
    // deserialize the size
    Integer size;
    size.rdstream (is);
    d_size = size.tointeger ();
    // deserialzie the sign
    Boolean sign;
    sign.rdstream (is);
    d_sign = sign.toboolean ();
    // deserialize the data
    p_byte = new t_byte[d_size];
    for (long i = 0; i < d_size; i++) p_byte[i] = is.read ();
    unlock ();
  }

  // assign a relatif to this one

  Relatif& Relatif::operator = (const Relatif& that) {
    if (this == &that) return *this;
    wrlock ();
    that.rdlock ();
    delete [] p_byte;
    d_size = that.d_size;
    d_sign = that.d_sign;
    p_byte = new t_byte[d_size];
    for (long i = 0; i < d_size; i++) p_byte[i] = that.p_byte[i];
    that.unlock ();
    unlock ();
    return *this;
  }

  // change the sign of a relatif

  Relatif operator - (const Relatif& x) {
    x.rdlock ();
    Relatif result = x;
    result.d_sign  = !x.d_sign;
    x.unlock ();
    return result;
  }

  // add two relatifs

  Relatif operator + (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // compute the type of operation to do
      bool subf = x.d_sign ^ y.d_sign;
      // prepare the result
      long    rsize = 0;
      bool    rsign = false;
      t_byte* rbyte = nilp;
      if (subf == false) {
	rsize = add_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte,&rbyte);
	rsign = x.d_sign;
      } else {
	bool gflag = x.gth (y);
	if (gflag == true) {
	  rsize = sub_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
	  rsign = false;
	} else {
	  rsize = sub_bytes (y.d_size, y.p_byte, x.d_size, x.p_byte, &rbyte);
	  rsign = true;
	}
      }
      // normalize and return
      Relatif result (rsize, rbyte, rsign);
      result.normalize ();
      y.unlock ();
      x.unlock ();
      return result;
    } catch (...) {
      y.unlock ();
      x.unlock ();
      throw;
    }
  }

  // substract two relatifs

  Relatif operator - (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      long    rsize = 0;
      bool    rsign = false;
      t_byte* rbyte = nilp;
      // do x - y
      if ((x.d_sign == false) && (y.d_sign == false)) {
	bool gflag = x.gth (y);
	if (gflag == true) {
	  rsize = sub_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
	  rsign = false;
	} else {
	  rsize = sub_bytes (y.d_size, y.p_byte, x.d_size, x.p_byte, &rbyte);
	  rsign = true;
	}
      }
      // do (-x) - y = - (x + y)
      if ((x.d_sign == true) && (y.d_sign == false)) {
	rsize = add_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
	rsign = true;
      }
      // do x - (-y) = (x + y)
      if ((x.d_sign == false) && (y.d_sign == true)) {
	rsize = add_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
	rsign = false;
      }
      // do (-x) - (-y) = (y - x)
      if ((x.d_sign == true) && (y.d_sign == true)) {
	bool gflag = y.gth (x);
	if (gflag == true) {
	  rsize = sub_bytes (y.d_size, y.p_byte, x.d_size, x.p_byte, &rbyte);
	  rsign = false;
	} else {
	  rsize = sub_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
	  rsign = true;
	}
      }    
      // normalize and return
      Relatif result (rsize, rbyte, rsign);
      result.normalize ();
      y.unlock ();
      x.unlock ();
      return result;
    } catch (...) {
      y.unlock ();
      x.unlock ();
      throw;
    }
  }

  // multiply two relatifs

  Relatif operator * (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      long    rsize = 0;
      bool    rsign = false;
      t_byte* rbyte = nilp;
      rsize = mul_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte);
      rsign = x.d_sign ^ y.d_sign;
      Relatif result (rsize, rbyte, rsign);
      result.normalize ();
      y.unlock ();
      x.unlock ();
      return result;
    } catch (...) {
      y.unlock ();
      x.unlock ();
      throw;
    }
  }

  // divide two relatifs

  Relatif operator / (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // check if we divide by 0
      if (y == 0) {
	y.unlock ();
	x.unlock ();
	throw Exception ("division-error", "cannot divide by 0");
      }
      // first check if the result is 0
      long    rsize = 0;
      bool    rsign = false;
      t_byte* rbyte = nilp;
      if (x.geq (y) == false) {
	Relatif result;
	y.unlock ();
	x.unlock ();
	return result;
      }
      // proceed with normal division
      rsize = div_bytes (x.d_size, x.p_byte, y.d_size, y.p_byte, &rbyte, true);
      rsign = x.d_sign ^ y.d_sign;
      Relatif result (rsize, rbyte, rsign);
      result.normalize ();
      y.unlock ();
      x.unlock ();
      return result;
    } catch (...) {
      y.unlock ();
      x.unlock ();
      throw;
    }
  }

  // get the modulo of two relatifs

  Relatif operator % (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // check if we divide by 0
      if (y == 0) {
	y.unlock ();
	x.unlock ();
	throw Exception ("division-error", "cannot divide by 0");
      }
      // first check if the result is 0
      long    rsize = 0;
      bool    rsign = false;
      t_byte* rbyte = nilp;
      if (x.geq (y) == false) {
	Relatif result = x;
	y.unlock ();
	x.unlock ();
	return result;
      }
      // proceed with normal division
      rsize = div_bytes (x.d_size,x.p_byte,y.d_size, y.p_byte, &rbyte, false);
      rsign = x.d_sign ^ y.d_sign;
      Relatif result (rsize, rbyte, rsign);
      result.normalize ();
      y.unlock ();
      x.unlock ();
      return result;
    } catch (...) {
      y.unlock ();
      x.unlock ();
      throw;
    }
  }

  // bitwise negate a relatif

  Relatif operator ~ (const Relatif& x) {
    x.rdlock ();
    t_byte* xbyte = new t_byte[x.d_size];
    for (long i = 0; i < x.d_size; i++) xbyte[i] = !x.p_byte[i];
    Relatif result (x.d_size, xbyte, x.d_sign);
    x.unlock ();
    return result;
  }

  // bitwise and two relatifs

  Relatif operator & (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    long size = (x.d_size > y.d_size) ? x.d_size : y.d_size;
    t_byte* rbyte = new t_byte[size];
    for (long i = 0; i < size; i++) {
      t_byte xb = (i < x.d_size) ? x.p_byte[i] : 0;
      t_byte yb = (i < y.d_size) ? y.p_byte[i] : 0;
      rbyte[i]  = xb & yb;
    }
    Relatif result (size, rbyte, x.d_sign & y.d_sign);
    y.unlock ();
    x.unlock ();
    return result;
  }

  // bitwise or two relatifs

  Relatif operator | (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    long size = (x.d_size > y.d_size) ? x.d_size : y.d_size;
    t_byte* rbyte = new t_byte[size];
    for (long i = 0; i < size; i++) {
      t_byte xb = (i < x.d_size) ? x.p_byte[i] : 0;
      t_byte yb = (i < y.d_size) ? y.p_byte[i] : 0;
      rbyte[i]  = xb | yb;
    }
    Relatif result (size, rbyte, x.d_sign | y.d_sign);
    y.unlock ();
    x.unlock ();
    return result;
  }

  // bitwise xor two relatifs

  Relatif operator ^ (const Relatif& x, const Relatif& y) {
    x.rdlock ();
    y.rdlock ();
    long size = (x.d_size > y.d_size) ? x.d_size : y.d_size;
    t_byte* rbyte = new t_byte[size];
    for (long i = 0; i < size; i++) {
      t_byte xb = (i < x.d_size) ? x.p_byte[i] : 0;
      t_byte yb = (i < y.d_size) ? y.p_byte[i] : 0;
      rbyte[i]  = xb ^ yb;
    }
    Relatif result (size, rbyte, x.d_sign ^ y.d_sign);
    x.unlock ();
    y.unlock ();
    return result;
  }

  // add a relatif to this one

  Relatif& Relatif::operator += (const Relatif& x) {
    wrlock   ();
    x.rdlock ();
    *this = *this + x;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // subtract an integer to this one
  
  Relatif& Relatif::operator -= (const Relatif& x) {
    wrlock   ();
    x.rdlock ();
    *this = *this - x;
    x.unlock ();
    unlock   ();
    return *this;
  }

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

  // divide an integer with this one
  
  Relatif& Relatif::operator /= (const Relatif& x) {
    wrlock   ();
    x.rdlock ();
    *this = *this / x;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // prefix add one to the relatif

  Relatif& Relatif::operator ++ (void) {
    wrlock ();
    *this += 1;
    unlock ();
    return *this;
  }

  // postfix add one to the relatif

  Relatif Relatif::operator ++ (int) {
    wrlock ();
    Relatif result = *this;
    *this += 1;
    unlock ();
    return result;
  }

  // prefix sub one to the relatif

  Relatif& Relatif::operator -- (void) {
    wrlock ();
    *this -= 1;
    unlock ();
    return *this;
  }

  // postfix sub one to the relatif

  Relatif Relatif::operator -- (int) {
    wrlock ();
    Relatif result = *this;
    *this -= 1;
    unlock ();
    return result;
  }

  // compare two relatifs

  bool Relatif::operator == (const Relatif& value) const {
    rdlock ();
    value.rdlock ();
    // check size and sign first
    if ((d_size != value.d_size) || (d_sign != value.d_sign)) {
      value.unlock ();
      unlock ();
      return false;
    }
    // loop and compare
    for (long i = 0; i < d_size; i++) {
      if (p_byte[i] != value.p_byte[i]) {
	value.unlock ();
	unlock ();
	return false;
      }
    }
    value.unlock ();
    unlock ();
    return true;
  }

  // compare two relatifs

  bool Relatif::operator != (const Relatif& value) const {
    return !(*this == value);
  }

  // compare two relatifs

  bool Relatif::operator < (const Relatif& value) const {
    rdlock ();
    value.rdlock ();
    // check against sign first
    if ((d_sign == true)  && (value.d_sign == false)) {
      value.unlock ();
      unlock ();
      return true;
    }
    if ((d_sign == false) && (value.d_sign == true)){
      value.unlock ();
      unlock ();
      return false;
    }
    // the numbers have the same sign
    bool result = d_sign ? (!value.geq (*this)) : (!this->geq (value));
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two relatifs

  bool Relatif::operator <= (const Relatif& value) const {
    rdlock ();
    value.rdlock ();
    // check against sign first
    if ((d_sign == true)  && (value.d_sign == false)) {
      value.unlock ();
      unlock ();
      return true;
    }
    if ((d_sign == false) && (value.d_sign == true)){
      value.unlock ();
      unlock ();
      return false;
    }
    // the numbers have the same sign
    bool result = d_sign ? (!value.gth (*this)) : (!this->gth (value));
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two relatifs

  bool Relatif::operator > (const Relatif& value) const {
    rdlock ();
    value.rdlock ();
    // check against sign first
    if ((d_sign == true)  && (value.d_sign == false)) {
      value.unlock ();
      unlock ();
      return false;
    }
    if ((d_sign == false) && (value.d_sign == true)){
      value.unlock ();
      unlock ();
      return true;
    }
    // the numbers have the same sign
    bool result = d_sign ? (value.gth (*this)) : (this->gth (value));
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two relatifs

  bool Relatif::operator >= (const Relatif& value) const {
    rdlock ();
    value.rdlock ();
    // check against sign first
    if ((d_sign == true)  && (value.d_sign == false)) {
      value.unlock ();
      unlock ();
      return false;
    }
    if ((d_sign == false) && (value.d_sign == true)){
      value.unlock ();
      unlock ();
      return true;
    }
    // the numbers have the same sign
    bool result = d_sign ? (value.geq (*this)) : (this->geq (value));
    value.unlock ();
    unlock ();
    return result;
  }

  // return true if the relatif is zero

  bool Relatif::iszero (void) const {
    rdlock ();
    for (long i = 0; i < d_size; i++) {
      if (p_byte[i] != 0x00) {
	unlock ();
	return false;
      }
    }
    unlock ();
    return true;  
  }

  // return true if the relatif is odd

  bool Relatif::iseven (void) const {
    rdlock ();
    bool result = ((p_byte[0] & 1) == 0);
    unlock ();
    return result;  
  }

  // return true if the relatif is odd

  bool Relatif::isodd (void) const {
    rdlock ();
    bool result = ((p_byte[0] & 1) == 1);
    unlock ();
    return result;  
  }

  // shift left this relatif by a certain amount

  Relatif Relatif::shl (const long pos) const {
    rdlock ();
    t_byte* rbyte = nilp;
    long    rsize = shl_bytes (d_size, p_byte, pos, &rbyte);
    Relatif result (rsize,  rbyte, d_sign);
    result.normalize ();
    unlock ();
    return result;
  }

  // shift right this relatif by a certain amount

  Relatif Relatif::shr (const long pos) const {
    rdlock ();
    t_byte* rbyte = nilp;
    long    rsize = shr_bytes (d_size, p_byte, pos, &rbyte);
    Relatif result (rsize,  rbyte, d_sign);
    result.normalize ();
    unlock ();
    return result;
  }

  // return the absolute value of this number

  Relatif Relatif::abs (void) const {
    rdlock ();
    Relatif result = *this;
    result.d_sign  = false;
    unlock ();
    return result;
  }

  // return a native integer value - note that we use a lsb first
  // representation, but we have only msb first conversion function
  // that is why we reverse the byte array before conversion

  t_long Relatif::tointeger (void) const {
    rdlock ();
    t_byte bval[8];
    rst_bytes (8, bval);
    long len = (d_size < 9) ? d_size : 8;
    for (long i = 0; i < len; i++) bval[7-i] = p_byte[i];
    t_long result = c_ontoh (bval);
    unlock ();
    return d_sign ? -result : result;
  }

  // -------------------------------------------------------------------------
  // - 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");

  // create a new object in a generic way

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

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

    // try a relatif object
    Relatif* xval = dynamic_cast <Relatif*> (obj);
    if (xval != nilp) return new Relatif (*xval);

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

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

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

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

  // return true if the given quark is defined

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

  // operate this object with another object

  Object* Relatif::oper (t_oper type, Object* object) {
    Integer* iobj = dynamic_cast <Integer*> (object);
    Relatif* dobj = dynamic_cast <Relatif*> (object);
    switch (type) {
    case Object::ADD:
      if (iobj != nilp) return new Relatif (*this + iobj->tointeger ());
      if (dobj != nilp) return new Relatif (*this + *dobj);
      break;
    case Object::SUB:
      if (iobj != nilp) return new Relatif (*this - iobj->tointeger ());
      if (dobj != nilp) return new Relatif (*this - *dobj);
      break;
    case Object::MUL:
      if (iobj != nilp) return new Relatif (*this * iobj->tointeger ());
      if (dobj != nilp) return new Relatif (*this * *dobj);
      break;
    case Object::DIV:
      if (iobj != nilp) return new Relatif (*this / iobj->tointeger ());
      if (dobj != nilp) return new Relatif (*this / *dobj);
      break;
    case Object::UMN:
      return new Relatif (-(*this));
      break;
    case Object::EQL:
      if (iobj != nilp) return new Boolean (*this == iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this == *dobj);
      break;
    case Object::NEQ:
      if (iobj != nilp) return new Boolean (*this != iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this != *dobj);
      break;
    case Object::GEQ:
      if (iobj != nilp) return new Boolean (*this >= iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this >= *dobj);
      break;
    case Object::GTH:
      if (iobj != nilp) return new Boolean (*this >  iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this >  *dobj);
      break;
    case Object::LEQ:
      if (iobj != nilp) return new Boolean (*this <= iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this <= *dobj);
      break;
    case Object::LTH:
      if (iobj != nilp) return new Boolean (*this <  iobj->tointeger ());
      if (dobj != nilp) return new Boolean (*this <  *dobj);
      break;
    }
    throw Exception ("type-error", "invalid operand with relatif",
		     Object::repr (object));
  }
  
  // set an object to this relatif
  
  Object* Relatif::vdef (Runnable* robj, Nameset* nset, Object* object) {
    Integer* iobj = dynamic_cast <Integer*> (object);
    if (iobj != nilp) {
      *this = iobj->tointeger ();
      return this;
    }
    Real* fobj = dynamic_cast <Real*> (object);
    if (fobj != nilp) {
      *this = fobj->tointeger ();
      return this;
    }
    Relatif* dobj = dynamic_cast <Relatif*> (object);
    if (dobj != nilp) {
      *this = *dobj;
      return this;
    }
    throw Exception ("type-error", "invalid object with relatif vdef",
		     Object::repr (object));
  }

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

  Object* Relatif::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 Relatif (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_OPP) {
	++(*this);
	robj->post (this);
	return this;
      }
      if (quark == QUARK_OMM) {
	--(*this);
	robj->post (this);
	return this;
      }
      if (quark == QUARK_NOT) {
	return new Relatif (~(*this));
      }
    }

    // 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) {
	t_long val = argv->getint (0);
	*this += val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_SEQ) {
	t_long val = argv->getint (0);
	*this -= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_MEQ) {
	t_long val = argv->getint (0);
	*this *= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_DEQ) {
	t_long val = argv->getint (0);
	*this /= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_MOD) {
	Object*  obj = argv->get (0);
	Relatif* dobj = dynamic_cast <Relatif*> (obj);
	if (dobj != nilp) {
	  Object* result = new Relatif (*this % *dobj);
	  return result;
	}
	Integer* iobj = dynamic_cast <Integer*> (obj);
	if (iobj != nilp) {
	  Object* result = new Relatif (*this % iobj->tointeger ());
	  return result;
	}
	throw Exception ("type-error", "invalid object with mod operator",
			 Object::repr (obj));
      }
      if (quark == QUARK_SHL) {
	t_long val = argv->getint (0);
	Object* result = new Relatif (shl (val));
	return result;
      }
      if (quark == QUARK_SHR) {
	t_long val = argv->getint (0);
	Object* result = new Relatif (shr (val));
	return result;
      }
      if (quark == QUARK_XOR) {
	Object*  obj = argv->get (0);
	Relatif* dobj = dynamic_cast <Relatif*> (obj);
	if (dobj != nilp) {
	  Object* result = new Relatif (*this ^ *dobj);
	  return result;
	}
	Integer* iobj = dynamic_cast <Integer*> (obj);
	if (iobj != nilp) {
	  Object* result = new Relatif (*this ^ iobj->tointeger ());
	  return result;
	}
	throw Exception ("type-error", "invalid object with xor operator",
			 Object::repr (obj));
      }
      if (quark == QUARK_AND) {
	Object*   obj = argv->get (0);
	Relatif* dobj = dynamic_cast <Relatif*> (obj);
	if (dobj != nilp) {
	  Object* result = new Relatif (*this & *dobj);
	  return result;
	}
	Integer* iobj = dynamic_cast <Integer*> (obj);
	if (iobj != nilp) {
	  Object* result = new Relatif (*this & iobj->tointeger ());
	  return result;
	}
	throw Exception ("type-error", "invalid object with and operator",
			 Object::repr (obj));
      }
      if (quark == QUARK_OR) {
	Object*   obj = argv->get (0);
	Relatif* dobj = dynamic_cast <Relatif*> (obj);
	if (dobj != nilp) {
	  Object* result = new Relatif (*this | *dobj);
	  return result;
	}
	Integer* iobj = dynamic_cast <Integer*> (obj);
	if (iobj != nilp) {
	  Object* result = new Relatif (*this | iobj->tointeger ());
	  return result;
	}
	throw Exception ("type-error", "invalid object with or operator",
			 Object::repr (obj));
      }
    }
    // call the literal method
    return Literal::apply (robj, nset, quark, argv);
  }
  
  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // normalize a relatif

  void Relatif::normalize (void) {
    // find the lowest non null index
    long index = d_size - 1;
    while (index > 0) {
      if (p_byte[index] != 0) break;
      index--;
    }
    d_size = index + 1;
    // reset sign for null value
    if ((d_size == 1) && (p_byte[0] == 0)) d_sign = false;
  }

  // compare two abolute relatifs

  bool Relatif::gth (const Relatif& value) const {
    return gth_bytes (d_size, p_byte, value.d_size, value.p_byte);
  }  

  // compare two abolute relatifs

  bool Relatif::geq (const Relatif& value) const {
    return geq_bytes (d_size, p_byte, value.d_size, value.p_byte);
  }
}
