/*
 * @(#)XComplex.java    2.4.0   26 July 2005
 *
 * Copyright 2005
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs;

import edu.neu.ccs.util.*;

import java.awt.geom.*;
import java.util.*;
import java.text.ParseException;

/**
 * <p>Class <code>XComplex</code> extends <code>XPoint2D</code> in
 * order to implement the operations of complex numbers.</p>
 *
 * <p>This class provides arithmetic operations both as member
 * methods and static methods.  The following policies prevail.</p>
 *
 * <ul>
 *   <li>Arguments to methods are never changed.</li>
 *   <li>Member methods that return information do not modify
 *       the object <code>this</code>.</li>
 *   <li>Member methods that return <code>void</code> do modify
 *       the object <code>this</code> and try to avoid creation
 *       of intermediate helper objects if at all possible.</li>
 *   <li>Static methods whose return type is <code>XComplex</code>
 *       always construct a new <code>XComplex</code>object that
 *       is returned.</li>
 *   <li>An argument that is <code>null</code> is treated as if
 *       it were zero.</li>
 *   <li>An argument whether zero or <code>null</code> that would
 *       lead to division by zero will cause an ArithmeticException
 *       to be thrown.</li>
 * </ul>
 *
 * <p>The only exceptions to the above policies are with the static
 * method calls that do copy operations or data conversions.  These
 * method calls return <code>null</code> for <code>null</code>
 * arguments.</p>
 *
 * <p>This class provides the arithmetic operations <code>add</code>,
 * <code>subtract</code>, <code>multiply</code>, and <code>divide</code>
 * in four forms.  There are two member methods and two static methods.
 * In each pair, one has its arguments as <code>XComplex</code> objects
 * and the other as x,y coordinates.  This maximizes convenience since
 * it is not necessary to construct an object solely to pass its data
 * to one of the arithmetic operations.</p>
 *
 * <p>This class also provides operations that will efficiently add or
 * subtract the product of two complex numbers directly.  These methods
 * are used by the class <code>XPolynomialComplex</code>.</p>
 *
 * @author  Richard Rasala
 * @version 2.4.0
 * @since   2.4.0
 */
public class XComplex extends XPoint2D
{
    /**
     * <p>The standard error message for fromStringData for
     * <code>XComplex</code>.</p>
     *
     * <p>Replaces the error message used in the base class
     * <code>XPoint2D</code>.</p>
     */
    public static final String complexMessage =
        "\nXComplex Error: Data format must be\n"
        + "[...;...] or\n"
        + "[x=...;y=...]\n"
        + "where ... stands for\n"
        + "the x,y coordinate data\n";
    
    
    /** Helper constant for certain methods. */
    private static final double twopi = 2 * Math.PI;
    
    
    /** Constructs a default <code>XComplex</code> 0,0. */
    public XComplex() {}
    
    
    /** Constructs an <code>XComplex</code> x,0 using the
     * given <code>double</code> x.</p>
     *
     * @param x the x-coordinate of the complex number
     */
    public XComplex(double x) {
        setValue(x);
    }
    

    /**
     * Constructs an <code>XComplex</code> using the given
     * x,y <code>double</code> values.
     * 
     * @param x the x-coordinate of the complex number
     * @param y the y-coordinate of the complex number
     */
    public XComplex(double x, double y) {
        setValue(x, y);
    }
    

    /**
     * <p>Constructs an <code>XComplex</code> using a copy of
     * the data in the given <code>Point2D</code>.</p>
     * 
     * <p>If the given point is <code>null</code>,
     * then initializes this complex number to 0,0.</p>
     *
     * @param p an existing point whose data will be copied
     */
    public XComplex(Point2D p) {
        setValue(p);
    }
    
    
    /**
     * Constructs an <code>XComplex</code> using the pair of values
     * in the given array of <code>double</code> which must be of
     * size 2.</p>
     *
     * <p>If the given array is <code>null</code> or not of size 2,
     * then initializes this complex number to 0,0.</p>
     *
     * @param data the array of size 2 with the x,y data
     */
    public XComplex(double[] data) {
        setValue(data);
    }
    
    
    /**
     * Constructs an <code>XComplex</code> using the pair of values
     * in the given array of <code>float</code> which must be of
     * size 2.</p>
     *
     * <p>If the given array is <code>null</code> or not of size 2,
     * then initializes this complex number to 0,0.</p>
     *
     * @param data the array of size 2 with the x,y data
     */
    public XComplex(float[] data) {
        setValue(data);
    }
    
    
    /**
     * Constructs an <code>XComplex</code> object from
     * a <code>String</code> representation of the data state.
     * 
     * @param data <code>String</code> representation of the data state
     * @throws ParseException if the data is malformed
     */
    public XComplex(String data) throws ParseException {
        fromStringData(data);
    }
    
    
    /**
     * <p>Returns a human readable <code>String</code> representing
     * the data state of this <code>XComplex</code> as an annotated
     * string.</p>
     *
     * <p><code>XComplex[x=...;y=...]</code></p>
     *
     * <p>where the dots stand for the x,y coordinate data.</p>
     *
     * <p>Remark: The related method <code>toStringData()</code> is
     * inherited as is from class <code>XPoint2D</code>.</p>
     */
    public String toString() {
        return "XComplex[x=" + x + ";y=" + y + "]";
    }
    
    
    /**
     * <p>Returns a human readable <code>String</code> representing
     * the data state of the complex number z as an annotated
     * string.</p>
     *
     * <p><code>XComplex[x=...;y=...]</code></p>
     *
     * <p>where the dots stand for the x,y coordinate data of z.</p>
     *
     * <p>Returns</p>
     *
     * <p><code>XComplex[x=0;y=0]</code></p>
     *
     * <p>if z is <code>null</code>.</p>
     *
     * @param z the explicit argument
     */
    public static String toString(XComplex z) {
        return (z == null)
            ? "XComplex[x=0;y=0]"
            : z.toString();
    }
    
    
    /**
     * <p>Returns a human readable <code>String</code> representing
     * the data state of the complex number z as a simple string.</p>
     *
     * <p><code>[...;...]</code></p>
     *
     * <p>where the dots stand for the x,y coordinate data of z.</p>
     *
     * <p>Returns</p>
     *
     * <p><code>[0;0]</code></p>
     *
     * <p>if z is <code>null</code>.</p>
     *
     * @param z the explicit argument
     */
    public static String toStringData(XComplex z) {
        return (z == null)
            ? "[0;0]"
            : z.toStringData();
    }
    
    
    /**
     * <p>Defines the data state for this <code>XComplex</code> object 
     * from a <code>String</code> representation of the data state.</p>
     * 
     * <p>Uses the same algorithm as the corresponding method in the
     * base class <code>XPoint2D</code> but uses an error message that
     * is specific to this class.  Therefore, expects data as an x,y
     * pair in the form [x;y].  Does not attempt to parse expressions
     * involving complex numbers and does not use the mathematician's
     * symbol <i>i</i> for <i>sqrt(-1)</i>.</p> 
     *
     * <p>Fires property change VALUE.</p>
     *
     * @param data <code>String</code> representation of the data state
     * @throws ParseException if the data is malformed
     */
    public void fromStringData(String data)
        throws ParseException
    {
        if (data == null)
            throw new ParseException(complexMessage, -1);

        String[] strings = Strings.decode(data);
            
        if (strings == null)
            throw new ParseException(complexMessage, -1);
        
        if (strings.length != 2)
            throw new ParseException(complexMessage, -1);
        
        String[] names  = Strings.getNames (strings);
        String[] values = Strings.getValues(strings);
        
        // check for valid names before parsing double values
        // to permit specific error messages
        
        if ((Arrays.equals(names, BLANK)) || (Arrays.equals(names, XY)))
            names = XY;
        else
            throw new ParseException(complexMessage, -1);
        
        // parse double values
        
        double[] result = null;
        
        try {
            result = Strings.stringsToDoubles(values);
        }
        catch (ParseException ex) {
            throw Strings.makeAdjustedParseException(ex, "XComplex", names); 
        }
        
        setValue(result[0], result[1]);
    }
    
    
    
    /** <p>Sets this complex number to x,0 using the
     * given <code>double</code> x.</p>
     *
     * @param x the x-coordinate of the complex number
     */
    public final void setValue(double x) {
        setValue(x, 0);
    }
    
    
    /**
     * <p>If <code>z</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new copy of <code>z</code> via the constructor
     * call <code>new&nbsp;XComplex(z)</code>.</p>
     *
     * <p>This method avoids making a new object when none is necessary.</p>
     *
     * @param z the explicit argument
     */
    public static XComplex copy(XComplex z) {
        return (z == null)
            ? null
            : new XComplex(z);
    }
    
    
    /**
     * <p>If <code>data</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>XComplex</code> via the constructor call
     * <code>new&nbsp;XComplex(data)</code>.</p>
     *
     * <p>This method avoids making a new object when none is necessary.</p>
     *
     * @param data the array of size 2 with the x,y data
     */
    public static XComplex copyData(double[] data) {
        return (data == null)
            ? null
            : new XComplex(data);
    }
    
    
    /**
     * <p>If <code>data</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>XComplex</code> via the constructor call
     * <code>new&nbsp;XComplex(data)</code>.</p>
     *
     * <p>This method avoids making a new object when none is necessary.</p>
     *
     * @param data the array of size 2 with the x,y data
     */
    public static XComplex copyData(float[] data) {
        return (data == null)
            ? null
            : new XComplex(data);
    }
    
    
    /**
     * <p>If <code>z</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>double[]</code> whose elements are
     * x and y components of z.</p>
     *
     * <p>This method avoids making a new object when none is necessary.</p>
     *
     * @param z the XComplex argument to copy
     */
    public static double[] toData(XComplex z) {
        return (z == null)
            ? null
            : new double[] { z.x, z.y };
    }
    
    
    /**
     * <p>If <code>array</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>XComplex[]</code> that uses the method
     * <code>copy(XComplex)</code> to copy the array items.</p>
     *
     * <p>This method avoids making a new objects when none are necessary.</p>
     *
     * @param array the XComplex array to copy
     */
    public static XComplex[] copy(XComplex[] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        XComplex[] result = new XComplex[length];
        
        for (int i = 0; i < length; i++)
            result[i] = copy(array[i]);
        
        return result;
    }
    
    
    /**
     * <p>If <code>array</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>XComplex[]</code> that uses the method
     * <code>copyData(double[])</code> to copy the array items.</p>
     *
     * <p>This method avoids making new objects when none are necessary.</p>
     *
     * @param array the double[][] array to copy
     */
    public static XComplex[] copyData(double[][] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        XComplex[] result = new XComplex[length];
        
        for (int i = 0; i < length; i++)
            result[i] = copyData(array[i]);
        
        return result;
    }
    
    
    /**
     * <p>If <code>array</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>XComplex[]</code> that uses the method
     * <code>copyData(float[])</code> to copy the array items.</p>
     *
     * <p>This method avoids making new objects when none are necessary.</p>
     *
     * @param array the float[][] array to copy
     */
    public static XComplex[] copyData(float[][] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        XComplex[] result = new XComplex[length];
        
        for (int i = 0; i < length; i++)
            result[i] = copyData(array[i]);
        
        return result;
    }
    
    
    /**
     * <p>If <code>array</code> is <code>null</code> returns <code>null</code>
     * otherwise returns a new <code>double[][]</code> whose elements are
     * obtained by using <code>toData(XComplex)</code> on the array items.</p>
     *
     * <p>This method avoids making new objects when none are necessary.</p>
     *
     * @param array the XComplex array argument to copy
     */
    public static double[][] toData(XComplex[] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        double[][] result = new double[length][];
        
        for (int i = 0; i < length; i++)
            result[i] = toData(array[i]);
        
        return result;
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(-this)</code>:
     * in coordinates [x;y] is replaced by [-x;-y].</p>
     *
     * <p>Fires property change: VALUE.</p>
     */
    public final void negate() {
        setValue(-x, -y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is the negation
     * of the given z.</p>
     *
     * <p>Returns zero if z is <code>null</code>.</p>
     *
     * @param z the explicit argument
     */
    public static XComplex negate(XComplex z) {
        return (z == null)
            ? new XComplex()
            : new XComplex(- z.x, - z.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(s * this)</code>:
     * in coordinates [x;y] is replaced by [s*x;s*y].</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param s the scale argument
     */
    public final void scale(double s) {
        setValue(s * x, s * y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>s * z</code>.</p>
     *
     * <p>Returns zero if z is <code>null</code>.</p>
     *
     * @param s the scale argument
     * @param z the complex argument
     */
    public static XComplex scale(double s, XComplex z) {
        return (z == null)
            ? new XComplex()
            : new XComplex(s * z.x, s * z.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to its complex conjugate:
     * in coordinates [x;y] is replaced by [x;-y].</p>
     *
     * <p>Fires property change: VALUE.</p>
     */
    public final void conjugate() {
        setValue(x, -y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is the complex
     * conjugate of the given z: in coordinates, if z is [x;y], then the
     * new value returned is [x;-y].</p>
     *
     * <p>Returns zero if z is <code>null</code>.</p>
     *
     * @param z the explicit argument
     */
    public static XComplex conjugate(XComplex z) {
        return (z == null)
            ? new XComplex()
            : new XComplex(z.x, - z.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to its multiplicative inverse which
     * is equal to its conjugate divided by the value of its radius
     * squared: in coordinates [x;y] is replaced by [x/rr;-y/rr]
     * where rr=x*x+y*y.</p>
     *
     * <p>Throws ArithmeticException if <code>this</code> is zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @throws ArithmeticException if <code>this</code> is zero
     */
    public final void invert() {
        if ((x == 0) && (y == 0))
            throw new ArithmeticException
                ("Zero divisor in XComplex.invert");
        
        double rr = x * x + y * y;
        
        setValue(x/rr, - y/rr);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is the
     * the multiplicative inverse of the given z.</p>
     *
     * <p>The multiplicative inverse is the conjugate divided by
     * the radius squared.</p>
     *
     * <p>Throws ArithmeticException if z is <code>null</code>
     * or zero.</p>
     *
     * @param z the explicit argument
     * @throws ArithmeticException if z is <code>null</code> or zero
     */
    public static XComplex invert(XComplex z) {
        if (isZero(z))
            throw new ArithmeticException
                ("Zero divisor in XComplex.invert");
        
        double rr = z.x * z.x + z.y * z.y;
        double xx =   z.x / rr;
        double yy = - z.y / rr;
        
        return new XComplex(xx, yy);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this + w)</code>.</p>
     *
     * <p>Does nothing if w is <code>null</code> or zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param w the explicit argument
     */
    public final void add(XComplex w) {
        if (isZero(w))
            return;
        
        add(w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this + w)</code>
     * where w is the complex with coordinates x,y.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x the x-coordinate of the complex number w
     * @param y the y-coordinate of the complex number w
     */
    public final void add(double x, double y) {
        setValue(this.x + x, this.y + y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z + w</code>.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public static XComplex add(XComplex z, XComplex w) {
        if (isZero(z))
            return new XComplex(w);
        
        if (isZero(w))
            return new XComplex(z);
        
        return add(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z + w</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     */
    public static XComplex add
        (double x1, double y1, double x2, double y2)
    {
        return new XComplex(x1 + x2, y1 + y2);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this - w)</code>.</p>
     *
     * <p>Does nothing if w is <code>null</code> or zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param w the explicit argument
     */
    public final void subtract(XComplex w) {
        if (isZero(w))
            return;
        
        subtract(w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this - w)</code>
     * where w is the complex with coordinates x,y.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x the x-coordinate of the complex number w
     * @param y the y-coordinate of the complex number w
     */
    public final void subtract(double x, double y) {
        setValue(this.x - x, this.y - y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z - w</code>.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public static XComplex subtract(XComplex z, XComplex w) {
        if (isZero(z))
            return negate(w);
        
        if (isZero(w))
            return new XComplex(z);
        
        return subtract(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z - w</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     */
    public static XComplex subtract
        (double x1, double y1, double x2, double y2)
    {
        return new XComplex(x1 - x2, y1 - y2);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this * w)</code>.</p>
     *
     * <p>If w is <code>null</code> or zero, sets <code>this</code> to zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param w the explicit argument
     */
    public final void multiply(XComplex w) {
        if (isZero(w))
            setValue(0, 0);
        else
            multiply(w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this * w)</code>
     * where w is the complex with coordinates x,y.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x the x-coordinate of the complex number w
     * @param y the y-coordinate of the complex number w
     */
    public final void multiply(double x, double y) {
        double xx = this.x * x - this.y * y;
        double yy = this.x * y + this.y * x;
        
        setValue(xx, yy);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z * w</code>.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public static XComplex multiply(XComplex z, XComplex w) {
        if (isZero(z) || isZero(w))
            return new XComplex();
        
        return multiply(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z * w</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     */
    public static XComplex multiply
        (double x1, double y1, double x2, double y2)
    {
        double xx = x1 * x2 - y1 * y2;
        double yy = x1 * y2 + y1 * x2;
        
        return new XComplex(xx, yy);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this / w)</code>.</p>
     *
     * <p>Throws ArithmeticException on division by zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param w the explicit argument
     * @throws ArithmeticException on division by zero
     */
    public final void divide(XComplex w) {
        if (isZero(w))
            throw new ArithmeticException
                ("Zero divisor in XComplex.divide");
        
        divide(w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this / w)</code>
     * where w is the complex with coordinates x,y.</p>
     *
     * <p>Throws ArithmeticException on division by zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x the x-coordinate of the complex number w
     * @param y the y-coordinate of the complex number w
     * @throws ArithmeticException on division by zero
     */
    public final void divide(double x, double y) {
        if ((x == 0) && (y == 0))
            throw new ArithmeticException
                ("Zero divisor in XComplex.divide");
        
        // multiply by the inverse
        
        double rr = x * x + y * y;
        double xx =   x/rr;
        double yy = - y/rr;
        
        multiply(xx, yy);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z / w</code>.</p>
     *
     * <p>Throws ArithmeticException on division by zero.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     * @throws ArithmeticException on division by zero
     */
    public static XComplex divide(XComplex z, XComplex w) {
        if (isZero(w))
            throw new ArithmeticException
                ("Zero divisor in XComplex.divide");
        
        if (isZero(z))
            return new XComplex();
        
        return divide(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Returns a new <code>XComplex</code> whose value is
     * <code>z / w</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * <p>Throws ArithmeticException on division by zero.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     * @throws ArithmeticException on division by zero
     */
    public static XComplex divide
        (double x1, double y1, double x2, double y2)
    {
        if ((x2 == 0) && (y2 == 0))
            throw new ArithmeticException
                ("Zero divisor in XComplex.divide");
        
        if ((x1 == 0) && (y1 == 0))
            return new XComplex();
        
        // multiply by the inverse
        
        double rr = x2 * x2 + y2 * y2;
        double xx =   x2/rr;
        double yy = - y2/rr;
        
        return multiply(x1, y1, xx, yy);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this + z * w)</code>.</p>
     *
     * <p>Does nothing if z or w is <code>null</code> or zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public final void addProduct(XComplex z, XComplex w) {
        if (isZero(z) || isZero(w))
            return;
        
        addProduct(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this + z * w)</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     */
    public final void addProduct
        (double x1, double y1, double x2, double y2)
    {
        double xx = x1 * x2 - y1 * y2;
        double yy = x1 * y2 + y1 * x2;
        
        setValue(this.x + xx, this.y + yy);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this - z * w)</code>.</p>
     *
     * <p>Does nothing if z or w is <code>null</code> or zero.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public final void subtractProduct(XComplex z, XComplex w) {
        if (isZero(z) || isZero(w))
            return;
        
        subtractProduct(z.x, z.y, w.x, w.y);
    }
    
    
    /**
     * <p>Sets <code>this</code> to <code>(this - z * w)</code>
     * where z is the complex with coordinates x1,y1
     * and   w is the complex with coordinates x2,y2.</p>
     *
     * <p>Fires property change: VALUE.</p>
     *
     * @param x1 the x-coordinate of the complex number z
     * @param y1 the y-coordinate of the complex number z
     * @param x2 the x-coordinate of the complex number w
     * @param y2 the y-coordinate of the complex number w
     */
    public final void subtractProduct
        (double x1, double y1, double x2, double y2)
    {
        double xx = x1 * x2 - y1 * y2;
        double yy = x1 * y2 + y1 * x2;
        
        setValue(this.x - xx, this.y - yy);
    }
    
    
    /**
     * <p>Returns the complex number whose coordinates are
     * <code>x=radius*Math.cos(radians)</code> and
     * <code>y=radius*Math.sin(radians)</code>.</p>
     *
     * @param radius  the distance of the complex from the origin
     * @param radians the angle in radians of the complex from the x-axis
     */
    public static XComplex makeUsingRadiusAndRadians
        (double radius, double radians)
    {
        if (radius == 0)
            return new XComplex();
        
        double x = Math.cos(radians);
        double y = Math.sin(radians);
        
        return new XComplex(radius * x, radius * y);
    }
    
    
    /**
     * <p>Returns the complex number whose coordinates are
     * <code>x=radius*MathUtilities.cosdeg(degrees)</code> and
     * <code>y=radius*MathUtilities.sindeg(degrees)</code>.</p>
     *
     * @param radius  the distance of the complex from the origin
     * @param degrees the angle in degrees of the complex from the x-axis
     */
    public static XComplex makeUsingRadiusAndDegrees
        (double radius, double degrees)
    {
        if (radius == 0)
            return new XComplex();
        
        double x = MathUtilities.cosdeg(degrees);
        double y = MathUtilities.sindeg(degrees);
        
        return new XComplex(radius * x, radius * y);
    }
    
    
    /**
     * <p>Returns the real part of <code>this</code>
     * using notation familiar to mathematicians;
     * this is the same value as returned by the
     * inherited method <code>getX()</code>.</p>
     */
    public final double Re() {
        return x;
    }
    
    
    /**
     * <p>Returns the real part of the complex number z
     * using notation familiar to mathematicians.</p>
     *
     * <p>Returns zero if z is <code>null</code>;
     * otherwise, returns <code>z.getX()</code>.</p>
     *
     * @param z the explicit argument
     */
    public static double Re(XComplex z) {
        return (z == null)
            ? 0
            : z.x;
    }
    
    
    /**
     * <p>Returns the imaginary part of <code>this</code>
     * using notation familiar to mathematicians;
     * this is the same value as returned by the
     * inherited method <code>getY()</code>.</p>
     */
    public final double Im() {
        return y;
    }
    
    
    /**
     * <p>Returns the imaginary part of the complex number z
     * using notation familiar to mathematicians.</p>
     *
     * <p>Returns zero if z is <code>null</code>;
     * otherwise, returns <code>z.getY()</code>.</p>
     *
     * @param z the explicit argument
     */
    public static double Im(XComplex z) {
        return (z == null)
            ? 0
            : z.y;
    }
    
    
    /**
     * <p>Returns the absolute value of the complex number
     * <code>this</code>; this value is the same as the value
     * returned by the inherited method <code>radius()</code>,
     * that is, <code>sqrt(x*x+y*y)</code>.</p>
     *
     * <p>From the mathematician's point of view, <code>abs</code>
     * satisfies the important identity:</p>
     *
     * <p><code>abs(z * w) = abs(z) * abs(w)</code></p>
     *
     * <p>However, because of the square root, <code>abs</code> is
     * expensive to compute.  If you wish to simply obtain a rough
     * estimate of the size of a complex number, we suggest the
     * alternate method <code>maxabs</code>.</p>
     */
    public final double abs() {
        return radius();
    }
    
    
    /**
     * <p>Returns the absolute value of the complex number z.</p>
     *
     * <p>Returns 0 if z is <code>null</code>;
     * otherwise, returns <code>z.abs()</code>.</p>
     *
     * <p>From the mathematician's point of view, <code>abs</code>
     * satisfies the important identity:</p>
     *
     * <p><code>abs(z * w) = abs(z) * abs(w)</code></p>
     *
     * <p>However, because of the square root, <code>abs</code> is
     * expensive to compute.  If you wish to simply obtain a rough
     * estimate of the size of a complex number, we suggest the
     * alternate method <code>maxabs</code>.</p>
     *
     * @param z the explicit argument
     */
    public static double abs(XComplex z) {
        return (z == null)
            ? 0
            : z.radius();
    }
    
    
    /**
     * <p>Returns the maximum of the absolute value of the x,y
     * coordinates of the complex number <code>this</code>,
     * that is, <code>max(abs(x),abs(y))</code>.</p>
     *
     * <p>The relationship between the value of <code>abs</code> and
     * the value of <code>maxabs</code> is:</p>
     *
     * <p><pre>  maxabs &lt;= abs &lt;= sqrt(2) * maxabs</pre></p>
     *
     * <p>Therefore, <code>maxabs</code> provides a reasonably tight
     * estimate for <code>abs</code> without the cost of computing a
     * square root.</p>
     */
    public final double maxabs() {
        return Math.max(Math.abs(x), Math.abs(y));
    }
    
    
    /**
     * <p>Returns the maximum of the absolute value of the x,y
     * coordinates of the complex number z.</p>
     *
     * <p>Returns 0 if z is <code>null</code>;
     * otherwise, returns <code>z.maxabs()</code>.</p>
     *
     * @param z the explicit argument
     */
    public static double maxabs(XComplex z) {
        return (z == null)
            ? 0
            : z.maxabs();
    }
    
    
    /**
     * <p>Returns true if <code>this</code> is zero.</p>
     */
    public final boolean isZero() {
        return ((x == 0) && (y == 0));
    }
    
    
    /**
     * <p>Returns true if the given complex number z is <code>null</code>
     * or zero.</p>
     *
     * @param z the explicit argument
     */
    public static boolean isZero(XComplex z) {
        return (z == null)
            ? true
            : z.isZero();
    }
    
    
    /**
     * <p>Returns true if <code>this</code> is almost zero in the sense
     * that its <code>maxabs</code> is less than or equal to epsilon.</p>
     *
     * <p>Replaces epsilon with its absolute value before testing.</p>
     *
     * @param epsilon the measure of closeness to zero
     */
    public final boolean isAlmostZero(double epsilon) {
        epsilon = Math.abs(epsilon);
        
        return (maxabs() <= epsilon);
    }
    
    
    /**
     * <p>Returns true if the given complex number z is <code>null</code>
     * or is almost zero relative to epsilon.</p>
     *
     * @param z the explicit argument
     */
    public static boolean isAlmostZero(XComplex z, double epsilon) {
        return (z == null)
            ? true
            : z.isAlmostZero(epsilon);
    }
    
    
    /**
     * <p>Returns true if the given complex number w is <code>null</code>
     * and <code>this</code> is zero or if the given complex number w is
     * non-<code>null</code> and <code>this</code> and w have equal x,y
     * coordinates.</p>
     *
     * @param w the explicit argument
     */
    public final boolean isEqualTo(XComplex w) {
        return (w == null)
            ? isZero()
            : ((x == w.x) && (y == w.y));
    }
    
    
    /**
     * <p>Returns true if both arguments are <code>null</code>,
     * or if one argument is <code>null</code> and the other is zero,
     * or if the two arguments have equal x,y coordinates.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     */
    public static boolean isEqualTo(XComplex z, XComplex w) {
        if ((z == null) && (w == null))
            return true;
        
        if (z == null)
            return w.isZero();
        
        if (w == null)
            return z.isZero();
        
        return ((z.x == w.x) && (z.y == w.y));
    }
    
    
    /**
     * <p>Returns true if the given complex number w is <code>null</code>
     * and <code>this</code> is almost zero relative to epsilon or if the
     * given complex number w is non-<code>null</code> and the maximum of
     * the absolute value of the differences in the x,y coordinates of w
     * and <code>this</code> is less than or equal to epsilon.</p>
     *
     * <p>Replaces epsilon with its absolute value before testing.</p>
     *
     * @param w the explicit argument
     * @param epsilon the measure of closeness to zero
     */
    public final boolean isAlmostEqualTo(XComplex w, double epsilon) {
        if (w == null)
            return isAlmostZero(epsilon);
        
        epsilon = Math.abs(epsilon);
        
        return (Math.max(Math.abs(x - w.x), Math.abs(y - w.y)) <= epsilon);
    }
    
    
    /**
     * <p>Returns true if both arguments are <code>null</code>,
     * or if one argument is <code>null</code> and the other is almost zero,
     * or if the two arguments have almost equal x,y coordinates.</p>
     *
     * <p>Replaces epsilon with its absolute value before testing.</p>
     *
     * @param z the LHS argument
     * @param w the RHS argument
     * @param epsilon the measure of closeness to zero
     */
    public static boolean isAlmostEqualTo(XComplex z, XComplex w, double epsilon) {
        if ((z == null) && (w == null))
            return true;
        
        if (z == null)
            return w.isAlmostZero(epsilon);
        
        if (w == null)
            return z.isAlmostZero(epsilon);
        
        epsilon = Math.abs(epsilon);
        
        return (Math.max(Math.abs(z.x - w.x), Math.abs(z.y - w.y)) <= epsilon);
    }
    
    
    /**
     * <p>Returns the complex exponential of the given z.</p>
     *
     * <p>If z has coordinates [x;y], then returns the complex with
     * coordinates <code>[exp(x)*cos(y);exp(x)*sin(y)]</code.</p>
     *
     * <p>This is a formula proved in the theory of complex functions.</p>
     *
     * <p>Returns the complex number 1 if z is <code>null</code>.</p>
     *
     * @param z the explicit argument
     */
    public static XComplex exp(XComplex z) {
        if (z == null)
            return new XComplex(1);
        
        double rr = (z.x == 0)
            ? 1
            : Math.exp(z.x);
        
        if (z.y == 0)
            return new XComplex(rr);
        
        double xx = rr * Math.cos(z.y);
        double yy = rr * Math.sin(z.y);
        
        return new XComplex(xx, yy);
    }
    
    
    /**
     * <p>Returns the branch of the logarithm of z that is determined by
     * the constraint that the imaginary part is between 0 and 2*pi,
     * that is, <code>0&lt;=Im(log(z))&lt;2*pi</code>.</p>
     *
     * <p>If z has coordinates [x;y], then returns the complex with
     * coordinates <code>[Math.log(radius);radians]</code> where
     * <code>radius=z.radius()</code> and
     * <code>radians=z.angleInRadians()</code>.</p>
     *
     * <p>Throws ArithmeticException if z is <code>null</code> or zero.</p>
     *
     * <p>This is a formula proved in the theory of complex functions.</p>
     *
     * @param z the explicit argument
     * @throws ArithmeticException if z is <code>null</code> or zero
     */
    public static XComplex log(XComplex z) {
        if (isZero(z))
            throw new ArithmeticException
                ("Zero argument in XComplex.log");
        
        double radius  = z.radius();
        double radians = z.angleInRadians();
        
        return new XComplex(Math.log(radius), radians);
    }
    
    
    /**
     * <p>Returns one of the two complex square roots of z, specifically,
     * the one whose angle in radians is between 0 and pi.</p>
     *
     * <p>Returns zero if z is <code>null</code> or zero.</p>
     *
     * @param w the explicit argument
     */
    public static XComplex sqrt(XComplex z) {
        if (isZero(z))
            return new XComplex();
        
        double radius  = z.radius();
        double radians = z.angleInRadians();
        
        return makeUsingRadiusAndRadians(Math.sqrt(radius), radians / 2);
    }
    
    
    /**
     * <p>Returns the n-th power of z, <code>z<sup>n</sup></code>, using
     * the given integer exponent n.</p>
     *
     * <p>Computes using ordinary complex arithmetic.</p>
     *
     * <p>Throws ArithmeticException if z is <code>null</code> or if
     * n is negative and z is zero.</p>
     * 
     * <p>Note: Departs from the convention of the Java <code>Math</code>
     * class by calling the method <code>power</code> rather than simply
     * <code>pow</code>.</p>
     *
     * @param z the explicit argument
     * @param n the exponent
     * @throws ArithmeticException as explained above
     */
    public static XComplex power(XComplex z, int n) {
        if (z == null)
            throw new ArithmeticException
                ("Null argument in XComplex.power");
        
        if ((n < 0) && (z.x == 0) && (z.y == 0))
            throw new ArithmeticException
                ("Negative exponent and zero argument in XComplex.power");
        
        XComplex result = new XComplex(1);
        
        if (n == 0)
            return result;
        
        // Make n positive
        // Copy or invert z as needed so z may be modified later
        
        if (n > 0) {
            z = new XComplex(z);
        }
        else {
            n = -n;
            z = invert(z);
        }
        
        // Loop invariant: The final result is the product of the current
        // result and the yet to be completed part of z-to-the-power-n.
        
        // Loop termination: n decreases in each loop cycle; when n == 0,
        // result holds the final value.
        
        while (n > 0) {
            if ((n % 2) != 0) {
                // add one factor of z to result and decrease
                // the exponent n by one
                
                result.multiply(z);
                n--;
            }
            else {
                // square z and reduce the exponent n by half
                // result is unchanged in this step
                
                z.multiply(z);
                n /= 2;
            }
        }
        
        return result;
    }
    
    
    /**
     * <p>Returns the d-th power of z, <code>z<sup>d</sup></code>, using
     * the given double exponent d.</p>
     *
     * <p>Computes <code>z<sup>d</sup></code> via the formula
     * <code>exp(d*log(z))</code>.  Note that, since log is a complex
     * function with multiple branches and our rule for log arbitrarily
     * selects one such branch, the value of <code>z<sup>d</sup></code>
     * computed in this fashion is also to some degree arbitrary.</p>
     *
     * <p>This method may be used to compute an n-th root.  If n is a
     * positive integer then <code>generalPower(z,1.0/n)</code> computes
     * one of the n-th roots of z.  One can obtain the other n-th roots
     * of z by multiplying this root by any one of the corresponding
     * "n-th roots of unity" that may be computed by using the method
     * <code>rootsOfUnity</code>.</p>
     *
     * <p>Throws ArithmeticException if z is <code>null</code> or zero.</p>
     * 
     * @param z the explicit argument
     * @param d the exponent
     * @throws ArithmeticException if z is <code>null</code> or zero
     */
    public static XComplex generalPower(XComplex z, double d) {
        if (z == null)
            throw new ArithmeticException
                ("Null argument in XComplex.generalPower");
        
        if ((z.x == 0) && (z.y == 0))
            throw new ArithmeticException
                ("Zero argument in XComplex.generalPower");
        
        if (d == 0)
            return new XComplex(1);
        
        return exp(scale(d, log(z)));
    }
    
    
    /**
     * <p>Returns the w-th power of z, <code>z<sup>w</sup></code>, using
     * the given complex exponent w.</p>
     *
     * <p>Computes <code>z<sup>w</sup></code> via the formula
     * <code>exp(w*log(z))</code>.  Note that, since log is a complex
     * function with multiple branches and our rule for log arbitrarily
     * selects one such branch, the value of <code>z<sup>w</sup></code>
     * computed in this fashion is also to some degree arbitrary.</p>
     *
     * <p>Throws ArithmeticException if z is <code>null</code> or zero.</p>
     * 
     * @param z the explicit argument
     * @param w the exponent
     * @throws ArithmeticException if z is <code>null</code> or zero
     */
    public static XComplex generalPower(XComplex z, XComplex w) {
        if (z == null)
            throw new ArithmeticException
                ("Null argument in XComplex.generalPower");
        
        if ((z.x == 0) && (z.y == 0))
            throw new ArithmeticException
                ("Zero argument in XComplex.generalPower");
        
        if (isZero(w))
            return new XComplex(1);
        
        return exp(multiply(w, log(z)));
    }
    
    
    /**
     * <p>Returns the k-th root among the n-th roots of unity for
     * <code>0&lt;=k&lt;n</code>.</p>
     *
     * <p>For convenience, the condition on k, <code>0&lt;=k&lt;n</code>,
     * is <i>not</i> checked but the roots returned will be repeated for
     * values of k that fall outside this range (up to roundoff error).</p>
     *
     * <p>Computes the complex number of radius 1 and angle in radians of
     * <code>(2*pi*k)/n</code>.</p>
     *
     * <p>Throws ArithmeticException if n&lt;=0.</p>
     * 
     * @param n the positive root specifier n that determines the n-th root
     * @param k a root selector with 0&lt;=k&lt;n
     * @throws ArithmeticException if n&lt;=0
     */
    public static XComplex rootOfUnity(int n, int k) {
        if (n <= 0)
            throw new ArithmeticException
                ("n-th root specifier is zero or negative in XComplex.rootOfUnity");
        
        return makeUsingRadiusAndRadians(1, (twopi * k) / n);
    }
    
    
    /**
     * <p>Returns an array of size n with the n-th roots of unity.</p>
     *
     * <p>Throws ArithmeticException if n&lt;=0.</p>
     * 
     * @param n the positive root specifier n that determines the n-th root
     * @throws ArithmeticException if n&lt;=0
     */
    public static XComplex[] rootsOfUnity(int n) {
        if (n <= 0)
            throw new ArithmeticException
                ("n-th root specifier is zero or negative in XComplex.rootsOfUnity");
        
        XComplex[] result = new XComplex[n];
        
        for (int k = 0; k < n; k++)
            result[k] = makeUsingRadiusAndRadians(1, (twopi * k) / n);
        
        return result;
    }
    
}
 
