import java.util.*;

/**
 * Complex numbers. Basic operations.
 * @author Jaanus P&ouml;ial
 * @version 0.7
 */
public class Complex {

   /** numbers less than EPSILON are considered to be zero */
   private final static double EPSILON = 0.0000001;

   /** real part of the complex number */
   private double re = 0.0;

   /** imaginary part of the complex number */
   private double im = 0.0;

   /** Constructor from the pair of double values.
    * @param r real part
    * @param i imaginary part
    */
   public Complex (double r, double i) {
      this.re = r;
      this.im = i;
   }

   /** Real part of the complex number.
    * @return real part
    */
   public double getRe() {
      return this.re;
   }

   /** Imaginary part of the complex number. 
    * @return imaginary part
    */
   public double getIm() {
      return this.im;
   }

   /** Test whether the real number is zero.
    * @param num real number
    * @return true if the number is close to zero
    *    (absolute value of num does not exceed EPSILON)
    */
   private static boolean isNaught (double num) {
      return Math.abs (num) <= Math.abs (EPSILON);
   }

   /** Conversion of the complex number to the string.
    * @return a string of form "a+bi", "-a+bi", "a-bi" or "-a-bi" 
    * (without any brackets)
    */
   @Override
   public String toString() {
      double a = this.getRe();
      double b = this.getIm();
      if (EPSILON > 0) {
         long jark = Math.round (1./EPSILON);
         a = Math.rint (a*jark)/jark;
         b = Math.rint (b*jark)/jark;
      }
      String rS = String.valueOf (a);
      String iS = String.valueOf (b);
      return rS + (iS.startsWith ("-") ? "" : "+") + iS + "i";
   }

   /** Conversion from the string to the complex number. 
    * Reverse to <code>toString</code> method.
    * @throws IllegalArgumentException if string s does not represent 
    *     a complex number (defined by the <code>toString</code> method)
    * @param s string of form produced by the <code>toString</code> method
    * @return a complex number represented by string s
    */
   public static Complex valueOf (String s) {
      double a = 0.0;
      double b = 0.0;
      StringTokenizer st = new StringTokenizer (s, "+-i", true);
      if (st.hasMoreTokens()) {
         String sa = st.nextToken().trim();
         if (st.hasMoreTokens()) {
            if (sa.equals ("+")) sa = st.nextToken().trim();
            if (sa.equals ("-")) sa = "-" + st.nextToken().trim();
            if (sa.equals ("i")) throw new IllegalArgumentException
               (s + " is not a complex number");
         } 
         a = Double.parseDouble (sa);
         if (st.hasMoreTokens()) {
            String sb = st.nextToken().trim();
            if (st.hasMoreTokens()) {
               if (sb.equals ("+")) sb = st.nextToken().trim();
               if (sb.equals ("-")) sb = "-" + st.nextToken().trim();
            }
            b = Double.parseDouble (sb);
         }
         if (st.hasMoreTokens()) {
            String si = st.nextToken().trim();
            if (!si.equals ("i"))
               throw new IllegalArgumentException
                  (s + " is not a complex number");
            if (st.hasMoreTokens())
               throw new IllegalArgumentException
                  (s + " is not a complex number");
         } else
            throw new IllegalArgumentException 
               (s + " is not a complex number");
      } else 
         throw new IllegalArgumentException (s + " is not a complex number");
      return new Complex (a, b);
   }

   /** Clone of the complex number.
    * @return independent clone of <code>this</code>
    */
   @Override
   public Object clone() throws CloneNotSupportedException {
      return new Complex (getRe(), getIm());
   }

   /** Test whether the complex number is zero. 
    * @return true if the real part and the imaginary part 
    *    are both (close to) zero
    */
   public boolean isZero() {
      return isNaught (this.getRe()) && isNaught (this.getIm());
   }

   /** Conjugate of the complex number. Expressed by the formula 
    *     conjugate(a+bi) = a-bi
    * @return conjugate of <code>this</code>
    */
   public Complex conjugate() {
      return new Complex (this.getRe(), -this.getIm());
   }

   /** Opposite of the complex number. Expressed by the formula 
    *    opposite(a+bi) = -a-bi
    * @return complex number <code>-this</code>
    */
   public Complex opposite() {
      return new Complex (-this.getRe(), -this.getIm());
   }

   /** Sum of complex numbers. Expressed by the formula 
    *    (a+bi) + (c+di) = (a+c) + (b+d)i
    * @param k addend (c+di)
    * @return complex number <code>this+k</code>
    */
   public Complex plus (Complex k) {
      return new Complex (this.getRe()+k.getRe(),
         this.getIm()+k.getIm());
   }

   /** Product of complex numbers. Expressed by the formula
    *  (a+bi) * (c+di) = (ac-bd) + (ad+bc)i
    * @param k factor (c+di)
    * @return complex number <code>this*k</code>
    */
   public Complex times (Complex k) {
      return new Complex (this.getRe()*k.getRe()-this.getIm()*k.getIm(),
         this.getRe()*k.getIm()+this.getIm()*k.getRe());
   }

   /** Inverse of the complex number. Expressed by the formula
    *     1/(a+bi) = a/(a*a+b*b) + ((-b)/(a*a+b*b))i
    * @return complex number <code>1/this</code>
    */
   public Complex inverse() {
      double rs = this.getRe()*this.getRe() + this.getIm()*this.getIm();
      if (isNaught (rs))
         throw new ArithmeticException ("/ by zero");
      return new Complex (this.getRe()/rs, -this.getIm()/rs);
   }

   /** Difference of complex numbers. Expressed as addition to the opposite.
    * @param k subtrahend
    * @return complex number <code>this-k</code>
    */
   public Complex minus (Complex k) {
      return this.plus (k.opposite());
   }

   /** Quotient of complex numbers. Expressed as multiplication to the inverse.
    * @param k divisor
    * @return complex number <code>this/k</code>
    */
   public Complex divideBy (Complex k) {
      return this.times (k.inverse());
   }

   /** Equality test of complex numbers. Difference of equal numbers
    *     is (close to) zero.
    * @param ko second complex number
    * @return logical value of the expression <code>this.equals(ko)</code>
    */
   @Override
   public boolean equals (Object ko) {
      if (!(ko instanceof Complex))
         return false;
      return ((Complex)ko).minus (this).isZero();
   }

   /** Integer hashCode has to be the same for equal objects.
    * @return hashcode
    */
   @Override
   public int hashCode() {
      int v1 = (int) (Double.doubleToLongBits (getRe())>>32);
      int v2 = (int) (Double.doubleToLongBits (getIm())>>31);
      int v3 = (int) (v1^v2)>>1;
      return ((v3<0)?-v3:v3);
   }

   /** Module of the complex number. Expressed by the formula 
    *     module(a+bi) = Math.sqrt(a*a+b*b)
    * @return module of <code>this</code> (module is a real number)
    */
   public double module() {
      return Math.sqrt (this.getRe()*this.getRe() + this.getIm()*this.getIm());
   }

   /** Polar angle of the complex number (principal value) in radians.
    * Defined by the connection  a+bi = m*cos(p) + (m*sin(p))i ,
    *    where m is the module and p is the polar angle of complex number a+bi.
    *    In Java p=Math.atan2(b,a)
    * @return polar angle p of <code>this</code> (in radians)
    */
   public double angle() {
      if (this.isZero())
         throw new ArithmeticException ("angle is undefined for zero");
      double a = this.getRe();
      double b = this.getIm();
      return Math.atan2 (b,a);
   }

   /** Main method for testing purposes. 
    * @param arg command line parameters
    */
   public static void main (String[] arg) {
      Complex arv1 = new Complex (-1., 1.);
      if (arg.length > 0)
         arv1 = valueOf (arg[0]);
      System.out.println ("first: " + arv1.toString());
      System.out.println ("real: " + arv1.getRe());
      System.out.println ("imag: " + arv1.getIm());
      System.out.println ("isZero: " + arv1.isZero());
      System.out.println ("conjugate: " + arv1.conjugate());
      System.out.println ("opposite: " + arv1.opposite());
      System.out.println ("hashCode: " + arv1.hashCode());
      Complex res = null;
      try {
         res = (Complex)arv1.clone();
      } catch (CloneNotSupportedException e) {};
      System.out.println ("clone equals to original: " + res.equals (arv1));
      System.out.println ("clone is not the same object: " + (res!=arv1));
      System.out.println ("hashCode: " + res.hashCode());
      res = valueOf (arv1.toString());
      System.out.println ("string conversion equals to original: " 
         + res.equals (arv1));
      Complex arv2 = new Complex (1., -2.);
      if (arg.length > 1)
         arv2 = valueOf (arg[1]);
      System.out.println ("second: " + arv2.toString());
      System.out.println ("hashCode: " + arv2.hashCode());
      System.out.println ("equals: " + arv1.equals (arv2));
      res = arv1.plus (arv2);
      System.out.println ("plus: " + res);
      System.out.println ("times: " + arv1.times (arv2));
      System.out.println ("minus: " + arv1.minus (arv2));
      double mm = arv1.module();
      System.out.println ("module: " + mm);
      double nn = arv1.angle();
      System.out.println ("angle: " + nn);
      System.out.println ("back from polar: " + new Complex (mm*Math.cos (nn),
         mm*Math.sin (nn)));
      System.out.println ("inverse: " + arv1.inverse());
      System.out.println ("divideBy: " + arv1.divideBy (arv2));
   }

}

