/*
 * Demonstration of operator overloading.
 *
 * Execute in the XL-Console:
 *     Complex.main(null)
 *
 *
 *
 * Introduction to operator overloading:
 *
 * Most operators can be overloaded by providing specially named functions.
 * The name of an operator function is the string "operator" followed by
 * the symbol of the operator. For instance the name "operator==" represents
 * the equality operator.
 *
 * When overloading an operator, the name and number of parameters must match. 
 * For example the name "operator-" could refer to the binary or the unary 
 * minus operator. The compiler counts the number of parameters including 
 * the implicit this-pointer if available, then decides which operator function
 * was overloaded.
 *
 * In the example above, the unary minus operator may either be a static
 * function with one parameter or a member function with no parameter (besides
 * the implicit this-pointer every member function has).
 * 
 * The return type and paramter types of an operator functions may be freely
 * chosen, i.e. the operator== does not necessarily need to return a boolean.
 * To make the operator functions globally visible, a static import may be used.
 *
 * To make a distinction between the prefix and the postfix version of the
 * increment/decrement operators, a dummy parameter must be added to the
 * method signature of the postfix version, i.e. "operator++()" declares the
 * prefix increment operator while "operator++(int _i)" declares the postfix
 * increment operator.
 *
 *
 *
 * Introduction to autoconversion:
 *
 * With the introduction of Java 1.5, autoboxing became part of the language.
 * Autoboxing allows the compiler to wrap the primitive types (boolean, char, byte,
 * short, int, long, float, double) with an instance of the proper wrapper type
 * (found in the package java.lang.*). Unboxing performs the inverse operation
 * by extracting the primitive value from a wrapper instance.
 *
 * Going one step further would be to extend autoboxing conversions to any
 * type, not just the primitive ones. In the Java API, there are some functions
 * following a generic pattern, for instance: toString() and valueOf().
 *
 * If a class provides functions of the form "to<type>()" to perform a conversion
 * to the specified <type>, the compiler will automatically apply such a 
 * conversion, if all other means of conversion (including autoboxing) failed.
 * The "to<type>()" conversion function may either be a static function expecting
 * exactly one parameter or a member function expecting no parameters (besides
 * the implicit this pointer).
 *
 * Another way of specifying a conversion function is to provide a static 
 * "valueOf()" function expecting exactly one parameter. Last but not least any
 * constructor expecting exactly one parameter may be used for automatic 
 * conversion.
 *
 * Automatic conversions are not chained. This means that when the compiler 
 * tries to apply an autoconversion to a value, the source and target type of
 * the conversion function must match exactly.
 *
 * If multiple conversion functions can be applied for the same conversion, the
 * compiler will report an error.
 *
 * To provide a better control over which functions are subject for automatic
 * conversion, additional compiler flags were introduced:
 *    -Xasv      Allow Autoconversion with static valueOf
 *    -Xasto     Allow Autoconversion with static toX
 *    -Xato      Allow Autoconversion with toX
 *    -Xactor    Allow Autoconversion with constructor
 * 
 * There is also the option to use annotations to mark functions that should
 * serve as automatic conversion functions. This is done by placing the
 * annotation "@Autoconversion" in front of the conversion function. Usage of
 * autoconversion annotation has to be enabled with the -Xeaa compiler switch.
 */

import java.io.*;
import java.util.*;

import de.grogra.rgg.Library;

interface Manipulator
{
	void apply(PrintWriter p);
}

/*
 * An instance of this class represents a complex number.
 */
public class Complex
{
	// predefined complex representing the imaginary unit number
	public static final Complex I = new Complex(0, 1);
	
	public double r;	// real part
	public double i;	// imaginary part

	// initialise this complex number to 0
	public Complex()
	{
		this.r = 0;
		this.i = 0;
	}
	
	// initialise this complex number with a real number
	@de.grogra.xl.lang.ConversionConstructor
	public Complex(double r)
	{
		this.r = r;
		this.i = 0;
	}
	
	// initialise this complex number with a real and an imaginary number
	public Complex(double r, double i)
	{
		this.r = r;
		this.i = i;
	}
	
	// unary plus
	Complex operator+ ()
	{
		return this;
	}
	
	// unary minus
	Complex operator- ()
	{
		return new Complex(-r, -i);
	}
	
	// prefix increment
	Complex operator++ ()
	{
		r++;
		return this;
	}
	
	// postfix increment
	Complex operator++ (int _i)
	{
		Complex result = new Complex(r, i);
		r++;
		return result;
	}
	
	// prefix decrement
	Complex operator-- ()
	{
		r--;
		return this;
	}
	
	// postfix decrement
	Complex operator-- (int _i)
	{
		Complex result = new Complex(r, i);
		r--;
		return result;
	}
	
	// addition with assignment
	Complex operator+= (Complex c)
	{
		r += c.r;
		i += c.i;
		return this;
	}
	
	// subtraction with assignment
	Complex operator-= (Complex c)
	{
		r -= c.r;
		i -= c.i;
		return this;
	}
	
	// multiplication with assignment
	Complex operator*= (Complex c)
	{
		double nr = r * c.r - i * c.i;
		double ni = r * c.i + i * c.r;
		r = nr;
		i = ni;
		return this;
	}
	
	// division with assignment
	Complex operator/= (Complex c)
	{
		double d = c.r * c.r + c.i * c.i;
		double nr = r * c.r + i * c.i;
		double ni = -r * c.i + i * c.r;
		r = nr / d;
		i = ni / d;
		return this;
	}
	
	// binary addition
	static Complex operator+ (Complex left, Complex right)
	{
		return new Complex(left.r + right.r, left.i + right.i);
	}

	// binary subtraction
	static Complex operator- (Complex left, Complex right)
	{
		return new Complex(left.r - right.r, left.i - right.i);
	}

	// binary multiplication
	static Complex operator* (Complex left, Complex right)
	{
		return new Complex(
			left.r * right.r - left.i * right.i, 
			left.r * right.i + left.i * right.r);
	}

	// binary division
	static Complex operator/ (Complex left, Complex right)
	{
		double d = right.r * right.r + right.i * right.i;
		return new Complex(
			(left.r * right.r + left.i * right.i) / d,
			(-left.r * right.i + left.i * right.r) / d);
	}

	// check for equality
	static boolean operator== (Complex left, Complex right)
	{
		return (left.r == right.r) && (left.i == right.i);
	}

	// check for inequality
	static boolean operator!= (Complex left, Complex right)
	{
		return (left.r != right.r) || (left.i != right.i);
	}

	// autoconversion from int to Complex
	static Complex valueOf(int i)
	{
		i++;
		return new Complex(i);
	}

	// autoconversion to String
	public String toString()
	{
		return "Complex[r = " + r + ", i = " + i + "]";
	}

	// stream output operator
	static PrintWriter operator<< (PrintWriter p, Complex c)
	{
		p.print(c.toString());
		return p;
	}

	// provide stream output to XL-Console
	// use System.out instead of Library.out to redirect output to java console
	final static PrintWriter cout = new PrintWriter(Library.out);
	
	// implement the interface Manipulator to provide end-of-line
	final static Manipulator endl = new Manipulator() 
	{
		public void apply(PrintWriter p) 
		{
			p.print('\n');
			p.flush();
		}
	};
	
	// apply stream manipulator
	static PrintWriter operator<< (PrintWriter p, Manipulator m)
	{
		m.apply(p);
		return p;
	}

	// stream output operator
	static PrintWriter operator<< (PrintWriter p, String s)
	{
		p.print(s);
		return p;
	}

	public static void main (String[] args)
	{
		// create a new complex number the traditional way
		Complex a = new Complex(1, 2);
		// create a new complex number using operator overloading
		Complex b = 3 * a;
		// make use of imaginare unit and operator overloading
		Complex c = (a + b) * I;
		// use operator overloading without creation of temporaries
		Complex d = c / a;
		// make use of valueOf(int) autoconversion (note the 'int'!)
		Complex e = 3;
		// make use of ctor autoconversion (note the double)
		Complex f = 3.0;
		
		// output the traditional way using println()
		Library.out.println("a = " + a.r + "/" + a.i);
		// output the traditional way using toString()
		Library.out.println("b = " + a);
		// output using stream output operator
		cout << "c = " << c << endl;
		cout << "d = " << d << endl;
	}
}

