// ---------------------------------------------------------------------------
// - Complex.cpp                                                             -
// - standard object library - complex 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-2023 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Math.hpp"
#include "Stdsid.hxx"
#include "Vector.hpp"
#include "Utility.hpp"
#include "Boolean.hpp"
#include "Numeral.hpp"
#include "Complex.hpp"
#include "Evaluable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "InputStream.hpp"
#include "OutputStream.hpp"
#include "ccnv.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // the number format
  const String Complex::CV_NUM_FRMT = "COMPLEX";

  // this procedure multiply two complex numbers
  static void cmul (t_real z[2], const t_real x[2], const t_real y[2]) {
    z[0] = (x[0] * y[0]) - (x[1] * y[1]);
    z[1] = (x[0] * y[1]) + (x[1] * y[0]); 
  }

  // this procedure divides two complex numbers
  static void cdiv (t_real z[2], const t_real x[2], const t_real y[2]) {
    // compute y module
    t_real m = y[0]*y[0] + y[1]*y[1];
    // compute division
    z[0] = ((x[0] * y[0]) + (x[1] * y[1])) / m;
    z[1] = ((x[1] * y[0]) - (x[0] * y[1])) / m;
  }

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

  // compute the opposite of a complex

  Complex operator - (const Complex& x) {
    x.rdlock ();
    try {
      Complex result (-x.d_zval[0], -x.d_zval[1]);
      x.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

  // add two complex together

  Complex operator + (const Complex& x, const Complex& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Complex result (x.d_zval[0] + y.d_zval[0], x.d_zval[1] + y.d_zval[1]);
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // subtract two complex

  Complex operator - (const Complex& x, const Complex& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      Complex result (x.d_zval[0] - y.d_zval[0], x.d_zval[1] - y.d_zval[1]);
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // multiply two complex

  Complex operator * (const Complex& x, const Complex& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // compute multiplication
      t_real zval[2]; cmul (zval, x.d_zval, y.d_zval);
      // map the complex number
      Complex result (zval[0], zval[1]);
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // divide two complex

  Complex operator / (const Complex& x, const Complex& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // check for null
      if (y.iszero () == true) {
	throw Exception ("complex-error", "null argument with divide");
      }
      // compute division
      t_real zval[2]; cdiv (zval, x.d_zval, y.d_zval);
      // map the complex number
      Complex result (zval[0], zval[1]);
      x.unlock ();
      y.unlock ();
      return result;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // create a complex by module and argument

  Complex Complex::toz (const t_real zmod, const t_real zarg) {
    // create complex by argument
    Complex result (Math::cos (zarg), Math::sin(zarg));
    // include module
    result.d_zval[0]*= zmod;
    result.d_zval[1]*= zmod;
    // here it is
    return result;
  }
  
  // absolute compare two complex nmumbers

  bool Complex::acmp (const Complex& z, const Complex& w) {
    return
      Math::acmp (z.d_zval[0], w.d_zval[0]) &&
      Math::acmp (z.d_zval[1], w.d_zval[1]);
  }
  
  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a new default complex

  Complex::Complex (void) {
    d_zval[0] = 0.0;
    d_zval[1] = 0.0;
  }

  // create a new complex from a native complex

  Complex::Complex (const t_real rval) {
    d_zval[0] = rval;
    d_zval[1] = 0.0;
  }

  // create a new complex from an Integer

  Complex::Complex (const Integer& lval) {
    d_zval[0] = (t_real) lval.tolong ();
    d_zval[1] = 0.0;
  }

  // create a new complex from a string

  Complex::Complex (const String& sval) {
    Utility::tocplx (d_zval, sval);
  }

  // create a complex by values

  Complex::Complex (const t_real rval, const t_real ival) {
    d_zval[0] = rval;
    d_zval[1] = ival;
  }
  
  // copy constructor for this complex

  Complex::Complex (const Complex& that) {
    that.rdlock ();
    try {
      d_zval[0] = that.d_zval[0];
      d_zval[1] = that.d_zval[1];
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // set an complex with a value

  Complex& Complex::operator = (const t_real rval) {
    wrlock ();
    try {
      d_zval[0] = rval;
      d_zval[1] = 0.0;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // assign a complex to this one

  Complex& Complex::operator = (const Complex& that) {
    // check for self-assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      d_zval[0] = that.d_zval[0];
      d_zval[1] = that.d_zval[1];
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // add a complex to this one

  Complex& Complex::operator += (const Complex& x) {
    wrlock   ();
    x.rdlock ();
    try {
      d_zval[0] += x.d_zval[0];
      d_zval[1] += x.d_zval[1];
      unlock ();
      x.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      x.unlock ();
      throw;
    }
  }

  // subtract a complex to this one

  Complex& Complex::operator -= (const Complex& x) {
    wrlock   ();
    x.rdlock ();
    try {
      d_zval[0] -= x.d_zval[0];
      d_zval[1] -= x.d_zval[1];
      unlock ();
      x.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      x.unlock ();
      throw;
    }
  }

  // multiply a real with this one

  Complex& Complex::operator *= (const t_real x) {
    wrlock ();
    try {
      d_zval[0] *= x; d_zval[1] *= x;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // multiply a complex with this one

  Complex& Complex::operator *= (const Complex& x) {
    wrlock   ();
    x.rdlock ();
    try {
      t_real zval[2]; cmul (zval, d_zval, x.d_zval);
      d_zval[0] = zval[0]; d_zval[1] = zval[1];
      unlock ();
      x.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      x.unlock ();
      throw;
    }
  }

  // divide a real with this one

  Complex& Complex::operator /= (const t_real x) {
    wrlock ();
    try {
      d_zval[0] /= x; d_zval[1] /= x;
      unlock ();
      return *this;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // divide a complex with this one

  Complex& Complex::operator /= (const Complex& x) {
    wrlock   ();
    x.rdlock ();
    try {
      t_real zval[2]; cdiv (zval, d_zval, x.d_zval);
      d_zval[0] = zval[0]; d_zval[1] = zval[1];
      unlock ();
      x.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      x.unlock ();
      throw;
    }
  }

  // compare an complex with a native value
  
  bool Complex::operator == (const t_long lval) const {
    rdlock ();
    try {
      bool result = ((d_zval[0] == (t_real) lval) && (d_zval[1] == 0.0));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // compare an complex with a native value
  
  bool Complex::operator != (const t_long lval) const {
    rdlock ();
    try {
      bool result = ((d_zval[0] != (t_real) lval) || (d_zval[1] != 0.0));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compare a complex with a complex value
  
  bool Complex::operator == (const t_real rval) const {
    rdlock ();
    try {
      bool result = ((d_zval[0] == rval) && (d_zval[1] == 0.0));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // compare an complex with a native value
  
  bool Complex::operator != (const t_real rval) const {
    rdlock ();
    try {
      bool result = ((d_zval[0] != rval) && (d_zval[1] != 0.0));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compare two complex

  bool Complex::operator == (const Complex& zval) const {
    rdlock ();
    zval.rdlock ();
    try {
      bool result = ((d_zval[0] == zval.d_zval[0]) &&
		     (d_zval[1] == zval.d_zval[1]));
      unlock ();
      zval.unlock ();
      return result;
    } catch (...) {
      unlock ();
      zval.unlock ();
      throw;
    }
  }

  // compare two complex

  bool Complex::operator != (const Complex& zval) const {
    rdlock ();
    zval.rdlock ();
    try {
      bool result = ((d_zval[0] != zval.d_zval[0]) ||
		     (d_zval[1] != zval.d_zval[1]));
      unlock ();
      zval.unlock ();
      return result;
    } catch (...) {
      unlock ();
      zval.unlock ();
      throw;
    }
  }

  // return the class name

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

  // return a clone of this object

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

  // clear this complex

  void Complex::clear (void) {
    wrlock ();
    try {
      d_zval[0] = 0.0;
      d_zval[1] = 0.0;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a literal representation of this complex

  String Complex::toliteral (void) const {
    rdlock ();
    try {
      String result = Utility::tostring (d_zval[0]);
      if (d_zval[1] >= 0.0) result += '+';
      result += Utility::tostring (d_zval[1]);
      result += 'i';
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a string representation of this complex

  String Complex::tostring (void) const {
    rdlock ();
    try {
      String result = Utility::tostring (d_zval[0]);
      if (d_zval[1] != 0.0) {
	if (d_zval[1] > 0.0) result += '+';
	result += Utility::tostring (d_zval[1]);
	result += 'i';
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // format a styled string representation

  String Complex::format (const Style& lstl) const {
    rdlock ();
    try {
      String result = lstl.format (d_zval[0]);
      if (d_zval[1] != 0.0) {
	if (d_zval[1] > 0.0) result += '+';
	result += lstl.format (d_zval[1]);
	result += 'i';
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
	
  // return the complex did

  t_word Complex::getdid (void) const {
    return SRL_DEOD_STD;
  }

  // return the complex sid

  t_word Complex::getsid (void) const {
    return SRL_CPLX_SID;
  }

  // serialize this complex

  void Complex::wrstream (OutputStream& os) const {
    rdlock ();
    try {
      t_byte data[8];
      c_rhton (d_zval[0], data);
      os.write ((char*) data, 8);
      c_rhton (d_zval[1], data);
      os.write ((char*) data, 8);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // deserialize this complex

  void Complex::rdstream (InputStream& is) {
    wrlock ();
    try {
      t_byte data[8];
      for (long i = 0; i < 8; i++) data[i] = (t_byte) is.read ();
      d_zval[0] = c_ontor (data);
      for (long i = 0; i < 8; i++) data[i] = (t_byte) is.read ();
      d_zval[1] = c_ontor (data);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the number format

  String Complex::tofrmt (void) const {
    rdlock ();
    try {
      String result = CV_NUM_FRMT;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // return true if the number is 0

  bool Complex::iszero (void) const {
    rdlock ();
    try {
      bool result = ((d_zval[0] == 0.0) && (d_zval[1] == 0.0));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the viewable size
  
  long Complex::tosize (void) const {
    rdlock ();
    try {
      long result = sizeof (d_zval);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the viewable data
  
  t_byte* Complex::tobyte (void) {
    wrlock ();
    try {
      auto result = reinterpret_cast<t_byte*>(d_zval);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the viewable data
  
  const t_byte* Complex::tobyte (void) const {
    rdlock ();
    try {
      auto result = reinterpret_cast<const t_byte*>(d_zval);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compare with a complex by absolute precision

  bool Complex::cmp (const Complex& z) const {
    rdlock ();
    try {
      bool result =
	Math::acmp (d_zval[0], z.d_zval[0]) &&
	Math::acmp (d_zval[1], z.d_zval[1]);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // check if a complex is real

  bool Complex::isreal (void) const {
    rdlock ();
    try {
      bool status = (d_zval[1] == 0.0);
      unlock ();
      return status;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // get the complex real part

  t_real Complex::getrval (void) const {
    rdlock ();
    try {
      t_real result = d_zval[0];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the complex imaginary part

  t_real Complex::getival (void) const {
    rdlock ();
    try {
      t_real result = d_zval[1];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compute the complex module square

  t_real Complex::tosqm (void) const {
    rdlock ();
    try {
      t_real result = d_zval[0]*d_zval[0] + d_zval[1]*d_zval[1];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compute the complex module

  t_real Complex::tomod (void) const {
    rdlock ();
    try {
      t_real result = Math::sqrt (d_zval[0]*d_zval[0] + d_zval[1]*d_zval[1]);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
	
  // compute the complex argument

  t_real Complex::toarg (void) const {
    rdlock ();
    try {
      t_real result = Math::atan (d_zval[1], d_zval[0]);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compute the complex cojugate

  Complex Complex::tocjv (void) const {
    rdlock ();
    try {
      Complex result (d_zval[0], -d_zval[1]);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // compute a complex power

  Complex Complex::pow (const t_real x) const {
    rdlock ();
    try {
      // compute module and argument
      t_real zmod = tomod ();
      t_real zarg = toarg ();
      // get result
      Complex result = Complex::toz (Math::pow (zmod, x), zarg * x);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

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

  // the object supported quarks
  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_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_POW     = zone.intern ("pow");
  static const long QUARK_ZEROP   = zone.intern ("zero-p");
  static const long QUARK_REALP   = zone.intern ("real-p");
  static const long QUARK_TOSQM   = zone.intern ("square-module");
  static const long QUARK_TOMOD   = zone.intern ("module");
  static const long QUARK_TOARG   = zone.intern ("argument");
  static const long QUARK_GETRVAL = zone.intern ("get-real-part");
  static const long QUARK_GETIVAL = zone.intern ("get-imaginary-part");

  // create a new complex in a generic way

  Object* Complex::mknew (Vector* argv) {
    long argc = (argv == nullptr) ? 0L : argv->length ();
    // check for 0 argument
    if (argc == 0L) return new Complex;
    // check for 1 argument
    if (argc == 1L) {
      // try to map the complex argument
      Object* obj = argv->get (0);
      if (obj == nullptr) return new Complex;
      // try an integer object
      auto ival = dynamic_cast <Integer*> (obj);
      if (ival != nullptr) return new Complex (ival->tolong ());
      // try a numeral object
      auto nval = dynamic_cast <Numeral*> (obj);
      if (nval != nullptr) return new Complex (nval->toreal());
      // try a real object
      auto rval = dynamic_cast <Real*> (obj);
      if (rval != nullptr) return new Complex (rval->toreal());
      // try a complex object
      auto zval = dynamic_cast <Complex*> (obj);
      if (zval != nullptr) return new Complex (*zval);
      // try a string object
      String* sval = dynamic_cast <String*> (obj);
      if (sval != nullptr) return new Complex (*sval);
      // illegal object
      throw Exception ("type-error", "illegal object with complex constructor",
		       obj->repr ());
    }
    // check for 2 arguments
    if (argc == 2) {
      t_real rval = argv->getrint (0);
      t_real ival = argv->getrint (1);
      return new Complex (rval, ival);
    }
    throw Exception ("argument-error", 
		     "too many argument with complex constructor");
  }

  // return true if the given quark is defined

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

  // operate this complex with another object

  Object* Complex::oper (t_oper type, Object* object) {
    auto iobj = dynamic_cast <Integer*> (object);
    auto nobj = dynamic_cast <Numeral*> (object);
    auto dobj = dynamic_cast <Real*>    (object);
    auto zobj = dynamic_cast <Complex*> (object);
    switch (type) {
    case Object::OPER_ADD:
      if (iobj != nullptr) return new Complex (*this + *iobj);
      if (nobj != nullptr) return new Complex (*this + nobj->toreal());
      if (dobj != nullptr) return new Complex (*this + dobj->toreal());
      if (zobj != nullptr) return new Complex (*this + *zobj);
      break;
    case Object::OPER_SUB:
      if (iobj != nullptr) return new Complex (*this - *iobj);
      if (nobj != nullptr) return new Complex (*this - nobj->toreal());
      if (dobj != nullptr) return new Complex (*this - dobj->toreal());
      if (zobj != nullptr) return new Complex (*this - *zobj);
      break;
    case Object::OPER_MUL:
      if (iobj != nullptr) return new Complex (*this * *iobj);
      if (nobj != nullptr) return new Complex (*this * nobj->toreal());
      if (dobj != nullptr) return new Complex (*this * dobj->toreal());
      if (zobj != nullptr) return new Complex (*this * *zobj);
      break;
    case Object::OPER_DIV:
      if (iobj != nullptr) return new Complex (*this / *iobj);
      if (nobj != nullptr) return new Complex (*this / nobj->toreal());
      if (dobj != nullptr) return new Complex (*this / dobj->toreal());
      if (zobj != nullptr) return new Complex (*this / *zobj);
      break;
    case Object::OPER_UMN:
      return new Complex (-(*this));
      break;
    case Object::OPER_EQL:
      if (iobj != nullptr) return new Boolean (*this == *iobj);
      if (nobj != nullptr) return new Boolean (*this == nobj->toreal());
      if (dobj != nullptr) return new Boolean (*this == dobj->toreal());
      if (zobj != nullptr) return new Boolean (*this == *zobj);
      break;
    case Object::OPER_QEQ:
      if (iobj != nullptr) return new Boolean (cmp(iobj->tolong()));
      if (nobj != nullptr) return new Boolean (cmp(nobj->toreal()));
      if (dobj != nullptr) return new Boolean (cmp(dobj->toreal()));
      if (zobj != nullptr) return new Boolean (cmp(*zobj));
    case Object::OPER_NEQ:
      if (iobj != nullptr) return new Boolean (*this != *iobj);
      if (nobj != nullptr) return new Boolean (*this != nobj->toreal());
      if (dobj != nullptr) return new Boolean (*this != dobj->toreal());
      if (zobj != nullptr) return new Boolean (*this != *zobj);
      break;
    default:
      break;
    }
    throw Exception ("type-error", "invalid operand with complex",
		     Object::repr (object));
  }

  // set an object to this complex

  Object* Complex::vdef (Evaluable* zobj, Nameset* nset, Object* object) {
    wrlock ();
    try {
      auto iobj = dynamic_cast <Integer*> (object);
      if (iobj != nullptr) {
	*this = *iobj;
	zobj->post (this);
	unlock ();
	return this;
      }
      auto nobj = dynamic_cast <Numeral*> (object);
      if (nobj != nullptr) {
	*this = nobj->toreal();
	zobj->post (this);
	unlock ();
	return this;
      }
      auto cobj = dynamic_cast <Complex*> (object);
      if (cobj != nullptr) {
	*this = *cobj;
	zobj->post (this);
	unlock ();
	return this;
      }
      throw Exception ("type-error", "invalid object with complex vdef",
		       Object::repr (object));
    } catch (...) {
      unlock ();
      throw;
    }
  }

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

  Object* Complex::apply (Evaluable* zobj, Nameset* nset, const long quark,
		       Vector* argv) {
    // get the number of arguments
    long argc = (argv == nullptr) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_TOSQM)   return new Real    (tosqm ());
      if (quark == QUARK_TOMOD)   return new Real    (tomod ());
      if (quark == QUARK_TOARG)   return new Real    (toarg ());
      if (quark == QUARK_GETRVAL) return new Real    (getrval ());
      if (quark == QUARK_GETIVAL) return new Real    (getival ());
      if (quark == QUARK_ZEROP)   return new Boolean (iszero  ());
      if (quark == QUARK_REALP)   return new Boolean (isreal  ());
    }

    if (argc == 1) {
      if (quark == QUARK_ADD) return oper (Object::OPER_ADD, argv->get (0));
      if (quark == QUARK_SUB) return oper (Object::OPER_SUB, argv->get (0)); 
      if (quark == QUARK_MUL) return oper (Object::OPER_MUL, argv->get (0)); 
      if (quark == QUARK_DIV) return oper (Object::OPER_DIV, argv->get (0)); 
      if (quark == QUARK_EQL) return oper (Object::OPER_EQL, argv->get (0)); 
      if (quark == QUARK_NEQ) return oper (Object::OPER_NEQ, argv->get (0)); 
      if (quark == QUARK_AEQ) {
	wrlock ();
	try {
	  t_real rval = argv->getrint (0);
	  *this += rval;
	  zobj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_SEQ) {
	wrlock ();
	try {
	  t_real rval = argv->getrint (0);
	  *this -= rval;
	  zobj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MEQ) {
	wrlock ();
	try {
	  t_real rval = argv->getrint (0);
	  *this *= rval;
	  zobj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_DEQ) {
	wrlock ();
	try {
	  t_real rval = argv->getrint (0);
	  *this /= rval;
	  zobj->post (this);
	  unlock ();
	  return this;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_POW) {
	t_real x = argv->getreal (0);
	return new Complex (pow (x));
      }
    }
    // call the number method
    return Number::apply (zobj, nset, quark, argv);
  }

  // -------------------------------------------------------------------------
  // - global section                                                        -
  // -------------------------------------------------------------------------

  Complex operator "" _i (long double iarg) {
    return Complex (0.0, iarg);
  }
}
