/* File : operator_overload.i */
/*
This is a test of all the possible operator overloads

see bottom for a set of possible tests
*/
%module operator_overload

#if defined(SWIGPYTHON)
%warnfilter(SWIGWARN_IGNORE_OPERATOR_EQ,
	    SWIGWARN_IGNORE_OPERATOR_INDEX,
	    SWIGWARN_IGNORE_OPERATOR_PLUSPLUS,
	    SWIGWARN_IGNORE_OPERATOR_MINUSMINUS,
	    SWIGWARN_IGNORE_OPERATOR_LAND,
	    SWIGWARN_IGNORE_OPERATOR_LOR);
#endif

#if defined(SWIGC)
%warnfilter(SWIGWARN_IGNORE_OPERATOR_EQ, 
      SWIGWARN_IGNORE_OPERATOR_PLUSPLUS);
#endif

#if !defined(SWIGLUA) && !defined(SWIGR)
%rename(Equal) operator =;
%rename(PlusEqual) operator +=;
%rename(MinusEqual) operator -=;
%rename(MultiplyEqual) operator *=;
%rename(DivideEqual) operator /=;
%rename(PercentEqual) operator %=;
%rename(Plus) operator +;
%rename(Minus) operator -;
%rename(Multiply) operator *;
%rename(Divide) operator /;
%rename(Percent) operator %;
%rename(Not) operator !;
%rename(IndexIntoConst) operator[](unsigned idx) const;
%rename(IndexInto) operator[](unsigned idx);
%rename(Functor) operator ();
%rename(EqualEqual) operator ==;
%rename(NotEqual) operator !=;
%rename(LessThan) operator <;
%rename(LessThanEqual) operator <=;
%rename(GreaterThan) operator >;
%rename(GreaterThanEqual) operator >=;
%rename(And) operator &&;
%rename(Or) operator ||;
%rename(PlusPlusPrefix) operator++();
%rename(PlusPlusPostfix) operator++(int);
%rename(MinusMinusPrefix) operator--();
%rename(MinusMinusPostfix) operator--(int);
#endif

%rename(IndexInto) *::operator[](unsigned idx); // some languages have a %rename *::operator[] already in place, which seems to takes precedence over the above %rename operator[].

#ifdef SWIGCSHARP
%csmethodmodifiers operator++() "protected";
%csmethodmodifiers operator++(int) "private";
%csmethodmodifiers operator--() "private";
%csmethodmodifiers operator--(int) "protected";
%typemap(csinterfaces) Op "global::System.IEquatable<Op>"
%typemap(cscode) Op %{
  public static Op operator++(Op op) {
    // Unlike C++, operator++ must not modify the parameter and both prefix and postfix operations call this method
    Op newOp = new Op(op.i);
    newOp.PlusPlusPostfix(0);
    return newOp;
  }
  public static Op operator--(Op op) {
    // Unlike C++, operator-- must not modify the parameter and both prefix and postfix operations call this method
    Op newOp = new Op(op.i);
    newOp.MinusMinusPrefix();
    return newOp;
  }

  // Start of operator== handling
  // See https://sourceforge.net/p/swig/bugs/884/
  // Implementation guided by https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type
  public override bool Equals(object obj) {
     return this.Equals((Op)obj);
  }
  public override int GetHashCode() {
     return i.GetHashCode();
  }
  public bool Equals(Op obj) {
    if ((object)obj == null) {
      return false;
    }
    // Optimization for a common success case.
    if (global::System.Object.ReferenceEquals(this, obj)) {
      return true;
    }
    // If run-time types are not exactly the same, return false.
    if (this.GetType() != obj.GetType()) {
      return false;
    }
    return this.EqualEqual(obj);
  }
  public static bool operator==(Op lhs, Op rhs) {
    // Casting to object required to avoid infinite loop and stack overflow
    if ((object)lhs == null) {
      if ((object)rhs == null) {
        return true;
      }
      return false;
    }
    return lhs.Equals(rhs);
  }
  public static bool operator!=(Op lhs, Op rhs) {
    return !(lhs == rhs);
  }
  // End of operator== handling
%}
#endif

#ifdef SWIGPHP
// "And" and "Or" can't be used as function names in PHP.
%rename(AndOperator) operator &&;
%rename(OrOperator) operator ||;
#endif

#if defined(SWIGPYTHON)
%feature("python:slot", "tp_str", functype="reprfunc") Op::__str__;
#endif

#ifdef SWIGD
// Due to the way operator overloading is implemented in D2, the postfix
// increment/decrement operators are ignored.
%warnfilter(SWIGWARN_IGNORE_OPERATOR_PLUSPLUS, SWIGWARN_IGNORE_OPERATOR_MINUSMINUS);
#endif

%rename(IntCast) operator int();
%rename(DoubleCast) operator double();

%inline %{
#include <stdio.h>

#if defined(_MSC_VER)
  #include <iso646.h> /* for named logical operator, eg 'operator or' */
#endif

class Op {
public:
  int i;
  Op(int a=0) : i(a)
  {}
  Op(const Op& o) : i(o.i)
  {}
  virtual ~Op()
  {}

  friend Op operator &&(const Op& a,const Op& b){return Op(a.i&&b.i);}
  friend Op operator or(const Op& a,const Op& b){return Op(a.i||b.i);}

  Op &operator=(const Op& o) {
    i=o.i;
    return *this;
  }
  // +=,-=... are member fns
  Op &operator+=(const Op& o){ i+=o.i; return *this; }
  Op &operator-=(const Op& o){ i-=o.i; return *this; }
  Op &operator*=(const Op& o){ i*=o.i; return *this; }
  Op &operator/=(const Op& o){ i/=o.i; return *this; }
  Op &operator%=(const Op& o){ i%=o.i; return *this; }
  // the +,-,*,... are friends
  // (just to make life harder)
  friend Op operator+(const Op& a,const Op& b){return Op(a.i+b.i);}
  friend Op operator-(const Op& a,const Op& b);
  friend Op operator*(const Op& a,const Op& b){return Op(a.i*b.i);}
  friend Op operator/(const Op& a,const Op& b){return Op(a.i/b.i);}
  friend Op operator%(const Op& a,const Op& b){return Op(a.i%b.i);}

  // unary operators
  Op operator-() const {return Op(-i);}
  bool operator !() const {return !(i);}

  // overloading the [] operator
  // need 2 versions: get & set
  // note: C++ can be a little mixed up upon which version it calls
  // most of the time it calls the second version
  int operator[](unsigned idx)const
  {	  if (idx==0) return i; return 0;}
  int& operator[](unsigned idx)
  {	  if (idx==0) return i; static int j;j=0; return j;}

  // overloading the () operator
  // this can have many parameters so we will test this
  int operator()(int a=0){return i+a;}
  int operator()(int a,int b){return i+a+b;}

  // increment/decrement operators
  Op& operator++() {++i; return *this;} // prefix ++
  Op operator++(int) {Op o = *this; ++(*this); return o;} // postfix ++
  Op& operator--() {--i; return *this;} // prefix --
  Op operator--(int) {Op o = *this; --(*this); return o;} // postfix --

  // TODO: <<,<<=

  // cast operators
  operator double() { return i; }
  virtual operator int() { return i; }

  // This method just checks that the operators are implemented correctly
  static void sanity_check();
};

// just to complicate matters
// we have a couple of non class operators
inline bool operator==(const Op& a,const Op& b){return a.i==b.i;}
inline bool operator!=(const Op& a,const Op& b){return a.i!=b.i;}
inline bool operator< (const Op& a,const Op& b){return a.i<b.i;}
inline bool operator<=(const Op& a,const Op& b){return a.i<=b.i;}
inline bool operator> (const Op& a,const Op& b){return a.i>b.i;}
inline bool operator>=(const Op& a,const Op& b){return a.i>=b.i;}

%}

%{
  // This one is not declared inline as VC++7.1 gets mixed up with the unary operator-
  Op operator-(const Op& a,const Op& b){return Op(a.i-b.i);}
%}

// in order to wrap this correctly we need to extend the class
// to make the friends & non members part of the class
%extend Op {
        Op operator &&(const Op& b){return *$self && b;}
        Op operator or(const Op& b){return  *self || b;}

	Op operator+(const Op& b){return *$self + b;}
	Op operator-(const Op& b){return *$self - b;}
	Op operator*(const Op& b){return *$self * b;}
	Op operator/(const Op& b){return *$self / b;}
	Op operator%(const Op& b){return *$self % b;}

	bool operator==(const Op& b){return *$self == b;}
	bool operator!=(const Op& b){return *$self != b;}
	bool operator< (const Op& b){return *$self <  b;}
	bool operator<=(const Op& b){return *$self <= b;}
	bool operator> (const Op& b){return *$self >  b;}
	bool operator>=(const Op& b){return *$self >= b;}

	// subtraction with reversed arguments
	Op __rsub__(const int b){return Op(b - $self->i);}

	// we also add the __str__() fn to the class
	// this allows it to be converted to a string (so it can be printed)
	const char* __str__()
	{
		static char buffer[255];
		sprintf(buffer,"Op(%d)",$self->i);
		return buffer;
	}
	// to get the [] operator working correctly we need to extend with two function
	// __getitem__ & __setitem__
	int __getitem__(unsigned i)
	{	return (*$self)[i];	}
	void __setitem__(unsigned i,int v)
	{	(*$self)[i]=v;	}
}

/*
Suggested list of operator overloads (mainly from python)

Operators overloaded with their C++ equivalent
__add__,__sub__,__mul__,__div__,__mod__	+,-,*,/,%
__iadd__,__isub__,__imul__,__idiv__,__imod__	+=,-=,*=,/=,%=

__eq__,__ne__,__lt__,__le__,__gt__,__ge__ ==,!=,<,<=,>,>=
__not__,__neg__	unary !, unary -
__and__,__or__,__xor__	logical and,logical or,logical xor
__rshift__,__lshift__ >>,<<

__getitem__,__setitem__ for operator[]

Operators overloaded without C++ equivalents
__pow__ for power operator
__str__ converts object to a string (should return a const char*)
__concat__ for concatenation (if language supports)

*/

%inline %{
class OpDerived : public Op {
public:
  OpDerived(int a=0) : Op(a)
  {}

  // overloaded
  virtual operator int() { return i*2; }
};
%}


%{
#include <stdexcept>
#define ASSERT(X) { if (!(X)) { throw std::runtime_error(#X); } }

void Op::sanity_check()
{
	// test routine:
	Op a;
	Op b=5;
	Op c=b;	// copy construct
	Op d=2;
        Op dd=d; // assignment operator

	// test equality
	ASSERT(a!=b);
	ASSERT(b==c);
	ASSERT(a!=d);
        ASSERT(d==dd);

	// test <
	ASSERT(a<b);
	ASSERT(a<=b);
	ASSERT(b<=c);
	ASSERT(b>=c);
	ASSERT(b>d);
	ASSERT(b>=d);

	// test +=
	Op e=3;
	e+=d;
	ASSERT(e==b);
	e-=c;
	ASSERT(e==a);
	e=Op(1);
	e*=b;
	ASSERT(e==c);
	e/=d;
	ASSERT(e==d);
	e%=c;
	ASSERT(e==d);

	// test +
	Op f(1),g(1);
	ASSERT(f+g==Op(2));
	ASSERT(f-g==Op(0));
	ASSERT(f*g==Op(1));
	ASSERT(f/g==Op(1));
	ASSERT(f%g==Op(0));

	// test unary operators
	ASSERT(!a==true);
	ASSERT(!b==false);
	ASSERT(-a==a);
	ASSERT(-b==Op(-5));

	// test []
	Op h=3;
	ASSERT(h[0]==3);
	ASSERT(h[1]==0);
	h[0]=2;	// set
	ASSERT(h[0]==2);
	h[1]=2;	// ignored
	ASSERT(h[0]==2);
	ASSERT(h[1]==0);

	// test ()
	Op i=3;
	ASSERT(i()==3);
	ASSERT(i(1)==4);
	ASSERT(i(1,2)==6);

	// plus add some code to check the __str__ fn
	//ASSERT(str(Op(1))=="Op(1)");
	//ASSERT(str(Op(-3))=="Op(-3)");

        // test ++ and --
        Op j(100);
        int original = j.i;
        {
          Op newOp = j++;
          int newInt = original++;
          ASSERT(j.i == original);
          ASSERT(newOp.i == newInt);
        }
        {
          Op newOp = j--;
          int newInt = original--;
          ASSERT(j.i == original);
          ASSERT(newOp.i == newInt);
        }
        {
          Op newOp = ++j;
          int newInt = ++original;
          ASSERT(j.i == original);
          ASSERT(newOp.i == newInt);
        }
        {
          Op newOp = --j;
          int newInt = --original;
          ASSERT(j.i == original);
          ASSERT(newOp.i == newInt);
        }

        // cast operators
        Op k=3;
        int check_k = k;
        ASSERT (check_k == 3);

        Op l=4;
        double check_l = l;
        ASSERT (check_l == 4);
}

%}

