
import static org.junit.Assert.*;
import org.junit.Test;
// import java.util.*;

/** Test.
 * @author Jaanus
 */
public class ComplexTest {

   /** double numbers less than DELTA are considered zero */
   public static final double DELTA = 0.000001;

   @Test (timeout=1000)
   public void testConstructor() {
      Complex k1 = new Complex (-5., -81.);
      assertNotNull ("constructor of -5-81i returns null", k1);
      assertFalse ("constructor of -5-81i returns zero", k1.isZero());
      Complex k2 = new Complex (0., 350.);
      assertNotNull ("constructor returns null", k2);
      assertNotSame ("different numbers must be different objects", k2, k1);
      Complex k3 = new Complex (0., 0.);
      assertNotNull ("zero is not null", k3);
   }

   @Test (timeout=1000)
   public void testGetReIm() {
      Complex k1 = new Complex (3., 7.);
      double re = k1.getRe();
      assertEquals ("3+7i has wrong real part", 3., re, DELTA);
      k1 = new Complex (0., 0.);
      re = k1.getRe();
      assertEquals ("zero has wrong real part", 0., re, DELTA);
      k1 = new Complex (-456., 200.);
      re = k1.getRe();
      assertEquals ("-456+200i has wrong real part", -456., re, DELTA);
      k1 = new Complex (3., 7.);
      double im = k1.getIm();
      assertEquals ("3+7i has wrong imaginary part", 7., im, DELTA);
      k1 = new Complex (0., 0.);
      im = k1.getIm();
      assertEquals ("zero has wrong imaginary part", 0., im, DELTA);
      k1 = new Complex (-456., 200.);
      im = k1.getIm();
      assertEquals ("-456+200i has wrong imaginary part", 200., im, DELTA);
   }

   @Test (timeout=1000)
   public void testToString() {
      String s = new Complex (1., 4.).toString();
      assertTrue ("String <" + s + "> does not represent 1+4i", 
         (s.indexOf('1') < s.indexOf('4')) && (s.indexOf('1') >= 0)
          && (s.indexOf('1') < s.indexOf('i')));
      s = new Complex (-1., 5.).toString();
      assertTrue ("String <" + s + "> does not contain a minus", 
         s.indexOf('-') >= 0);
   } 

   @Test (timeout=1000)
   public void testValueOf() {
      Complex f = new Complex (2., 5.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (-17.55, 10.77);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (0., 0.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (3., -2.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (-3., -5.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (3., -1.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (-3., 0.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (3., 1.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
      f = new Complex (0., -5.);
      assertEquals ("valueOf must read back what toString outputs. ",
         f, Complex.valueOf (f.toString()));
   }

   @Test (timeout=1000)
   public void testEquals() {
      Complex k1 = new Complex (3., 7.);
      Complex k2 = new Complex (3., -9.);
      Complex k3 = new Complex (3., 7.);
      assertTrue ("3+7i must be equal to 3+7i", k1.equals (k3));
      assertFalse ("3+7i must not be equal to 3-9i" , k1.equals (k2));
      Complex k4 = null;
      try {
         k4 = (Complex)k3.clone();
      } catch (CloneNotSupportedException e) {};
      assertTrue ("identical numbers must be equal", k1.equals(k1));
      assertTrue ("independent instances of the same number must be equal",
         k3.equals(k1));
      assertTrue ("clone must be equal to original", k3.equals(k4));
      assertFalse ("clone must not be identical to original", k3 == k4);
      assertFalse ("different numbers must not be equal", k1.equals(k2));
      assertTrue ("zero must satisfy isZero", new Complex (0., 0.).isZero());
      assertTrue ("number created from parts must be equal to original", 
         k2.equals (new Complex (k2.getRe(), k2.getIm())));
      Complex k5 = new Complex (3.000000000000001, 7.000000000000001);
      assertTrue ("cannot compare real numbers using == operator", 
         k5.equals (k1));
   }

   @Test (timeout=1000)
   public void testIsZero() {
      Complex k1 = new Complex (0., 0.);
      assertTrue ("0+0i must be zero", k1.isZero());
      k1 = new Complex (0., 1.);
      assertFalse ("0-1i must not be zero", k1.isZero());
   }

   @Test (timeout=1000)
   public void testClone() {
      Complex f1 = new Complex (2., 5.);
      Complex f2 = null;
      try {
         f2 = (Complex)f1.clone();
      } catch (CloneNotSupportedException e){};
      assertNotSame ("clone must differ from original", f2, f1);
      assertEquals ("clone must be equal to original", f1, f2);
      assertEquals ("clone must be equal to original", f2, f1);
      f1 = f2.plus(f2);
      assertEquals ("clone must be independent from original",
         new Complex (2., 5.), f2);
      f1 = new Complex (0., 0.);
      try {
         f2 = (Complex)f1.clone();
      } catch (CloneNotSupportedException e) {};
      assertNotSame ("clone must differ from original", f2, f1);
      assertEquals ("clone must be equal to original", f1, f2);
   }

   @Test (timeout=1000)
   public void testOpposite() {
      Complex f1 = new Complex (1., 6.);
      Complex f2 = f1.opposite();
      assertEquals ("Wrong opposite: 1+6i", new Complex (-1., -6.), f2);
      assertEquals ("Do not change the argument of opposite",
         new Complex (1., 6.), f1);
      f1 = new Complex (-4., 75.);
      f2 = f1.opposite();
      assertEquals ("Wrong opposite: -4+75i", new Complex (4., -75.), f2);
      f1 = new Complex (0., 0.);
      f2 = f1.opposite();
      assertEquals ("zero must be neutral to opposite", f1, f2);
   }

   @Test (timeout=1000)
   public void testConjugate() {
      Complex f1 = new Complex (1., 6.);
      Complex f2 = f1.conjugate();
      assertEquals ("Wrong conjugate: 1+6i", new Complex (1., -6.), f2);
      assertEquals ("Do not change the argument of conjugate",
         new Complex (1., 6.), f1);
      f1 = new Complex (-4., -75.);
      f2 = f1.conjugate();
      assertEquals ("Wrong conjugate: -4-75i", new Complex (-4., 75.), f2);
      f1 = new Complex (12340., 0.);
      f2 = f1.conjugate();
      assertEquals ("real number must be neutral to conjugate", f1, f2);
   }

   @Test (timeout=1000)
   public void testPlus() { 
      Complex f1 = new Complex (2., 5.);
      Complex f2 = new Complex (4., 15.);
      Complex sum = f1.plus(f2);
      assertEquals ("Wrong sum: <" + f1 + "> + <" + f2 + ">", 
         new Complex (6., 20.), sum);
      sum = f1.plus (f1);
      assertEquals ("plus must not change the arguments!", new Complex (2., 5.),
         f1);
      f1 = new Complex (-1., 25.);
      f2 = new Complex (1., -25.);
      sum = f1.plus(f2);
      assertEquals ("Wrong sum: <" + f1 + "> + <" + f2 + ">",
         new Complex (0., 0.), sum);
      f1 = new Complex (1., -22.);
      f2 = new Complex (0., 0.);
      sum = f1.plus (f2);
      assertEquals ("Wrong sum: <" + f1 + "> + <" + f2 + ">",
         new Complex (1., -22.), sum);
      f1 = new Complex (0., 0.);
      f2 = new Complex (-1., 41.);
      sum = f1.plus (f2);
      assertEquals ("Wrong sum: <" + f1 + "> + <" + f2 + ">",
         new Complex (-1., 41.), sum);
      f1 = new Complex (-2., -5.);
      f2 = new Complex (-1., -6.);
      sum = f1.plus(f2);
      assertEquals ("Wrong sum: <" + f1 + "> + <" + f2 + ">",
         new Complex (-3., -11.), sum);
   }

   @Test (timeout=1000)
   public void testMinus() {
      Complex f1 = new Complex (2., 5.);
      Complex f2 = new Complex (4., 15.);
      Complex dif = f1.minus(f2);
      assertEquals ("Wrong difference: <" + f1 + "> - <" + f2 + ">",
         new Complex (-2., -10.), dif);
      dif = f1.minus (f1);
      assertEquals ("plus must not change the arguments!", new Complex (2., 5.),
         f1);
      assertEquals ("Wrong difference: <" + f1 + "> - <" + f1 + ">",
         new Complex (0., 0.), dif);
      f1 = new Complex (4., 25.);
      f2 = new Complex (1., 5.);
      dif = f1.minus(f2);
      assertEquals ("Wrong difference: <" + f1 + "> - <" + f2 + ">",
         new Complex (3., 20.), dif);
   }

   @Test (timeout=1000)
   public void testTimes() {
      Complex f1 = new Complex (4., -3.);
      Complex f2 = new Complex (-24., 7.);
      Complex prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (-75., 100.), prd);
      f1 = new Complex (1., -1.);
      f2 = new Complex (3., 7.);
      prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (10., 4.), prd);
      Complex pr2 = prd.times (prd);
      assertEquals ("Wrong prd.times(prd) for 10+4i",
         new Complex (84., 80.), pr2);
      assertEquals ("Do not change the arguments of the product",
         new Complex (10., 4.), prd);
      f1 = new Complex (0., 0.);
      f2 = new Complex (2., 3.);
      prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (0., 0.), prd);
      f1 = new Complex (1., 0.);
      f2 = new Complex (2., 3.);
      prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (2., 3.), prd);
      f2 = new Complex (0., 0.);
      f1 = new Complex (2., 3.);
      prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (0., 0.), prd);
      f2 = new Complex (1., 0.);
      f1 = new Complex (2., 3.);
      prd = f1.times (f2);
      assertEquals ("Wrong product: <" + f1 + "> * <" + f2 + ">",
         new Complex (2., 3.), prd);
   } 

   @Test (timeout=1000)
   public void testModule() {
      Complex k1 = new Complex (3., 4.);
      double mm = k1.module();
      assertEquals ("wrong module for 3+4i", 5., mm, DELTA);
      k1 = new Complex (0., 0.);
      mm = k1.module();
      assertEquals ("wrong module for zero", 0., mm, DELTA);
      k1 = new Complex (-1., 0.);
      mm = k1.module();
      assertEquals ("wrong module for -1+0i", 1., mm, DELTA);
      k1 = new Complex (0., 8.);
      mm = k1.module();
      assertEquals ("wrong module for 0+8i", 8., mm, DELTA);
   }

   @Test (timeout=1000)
   public void testInverse() {
      Complex f1 = new Complex (0.3, -0.4);
      Complex f2 = f1.inverse();
      assertEquals ("Wrong inverse: 0.3-0.4i ", new Complex (1.2, 1.6), f2);
      assertEquals ("Do not change the argument of inverse",
         new Complex (0.3, -0.4), f1);
      f1 = new Complex (0.6, 0.8);
      f2 = f1.inverse();
      assertEquals ("Wrong inverse: 0.6+0.8i", new Complex (0.6, -0.8), f2);
      f1 = new Complex (1., 0);
      f2 = f1.inverse();
      assertEquals ("1 must be neutral to inverse", f1, f2);
   }

   @Test (expected=RuntimeException.class)
   public void testZeroInverse() {
      Complex f = new Complex (0., 0.);
      f.inverse();
   }

   @Test (timeout=1000)
   public void testDivideBy() {
      Complex k1 = new Complex (3.0, 7.0);
      Complex k2 = new Complex (3.000000000000001, 7.000000000000001);
      assertTrue ("cannot compare real numbers using == operator",
         k1.equals (k2));
      Complex f1 = new Complex (-75., 100.);
      Complex f2 = new Complex (-24., 7.);
      Complex qt = f1.divideBy (f2);
      assertEquals ("Wrong quotient: <" + f1 + "> / <" + f2 + ">",
         new Complex (4., -3.), qt);
      qt = f1.divideBy (f1);
      assertEquals ("Do not change the arguments of the quotient",
         new Complex (-75., 100.), f1);
      assertEquals ("Wrong quotient: <" + f1 + "> / <" + f1 + ">",
         new Complex (1., 0.), qt);
      f1 = new Complex (10., 4.);
      f2 = new Complex (1., -1.);
      qt = f1.divideBy (f2);
      assertEquals ("Wrong quotient: <" + f1 + "> / <" + f2 + ">",
         new Complex (3., 7.), qt);
   }

   @Test (expected=RuntimeException.class)
   public void testDivideByZero() {
      Complex f1 = new Complex (1., 5.);
      Complex f2 = new Complex (0., 0.);
      f1.divideBy (f2);
   }

   /** kas module ja nurk on koosk6las? */
   boolean consistent (Complex c) {
      double p = c.angle();
      if (Math.abs(p) >= 2.*Math.PI)
         return false;
      double m = c.module();
      Complex k = new Complex (m*Math.cos(p), m*Math.sin(p));
      return k.equals(c);
   } // consistent

   /** liitmise, lahutamise, vastandarvu ja kaaskompleksi koosk6la */
   boolean consistent1 (Complex c) {
      if (!(c.plus(c.opposite()).isZero()))
         return false;
      if (!(c.equals(c.opposite().opposite())))
         return false;
      if (!(c.minus(c).isZero()))
         return false;
      if (!(new Complex(c.minus(c.conjugate()).getRe(), 
         c.plus(c.conjugate()).getIm()).isZero()))
         return false;
      return true;
   } // consistent1

   /** korrutamise, jagamise ja p66rdarvu consistent */
   boolean consistent2 (Complex c) {
      Complex yks = new Complex (1., 0.);
      if (!(c.divideBy(c).equals(yks)))
         return false;
      if (!(c.times(c.inverse()).equals(yks)))
         return false;
      if (!(yks.divideBy(c.inverse()).equals(c)))
         return false;
      if (!(c.inverse().inverse().equals(c)))
         return false;
      return true;
   } // consistent2

   @Test (timeout=1000)
   public void testAngle() {  // testime k6iki nelja veerandit
      Complex k1 = new Complex (3.0, 7.0);
      Complex k2 = new Complex (3.000000000000001, 7.000000000000001);
      assertTrue ("cannot compare real numbers using == operator", 
         k1.equals (k2));
      assertTrue ("error if re>0, im>0: 1.8+6.4i", 
         consistent (new Complex (1.8, 6.4)));
      assertTrue ("error if re>0, im<0: 3.2-8.1i", 
         consistent (new Complex (3.2, -8.1)));
      assertTrue ("error if re<0, im<0: -5.6-7.2i", 
         consistent (new Complex (-5.6, -7.2)));
      assertTrue ("error if re<0, im>0: -4.67+2.5i", 
         consistent (new Complex (-4.67, 2.5)));
      assertTrue ("real numbers are not exact! sqrt(2.)", 
         new Complex (2., 0.).equals (new Complex
         (new Complex (Math.sqrt(2.), -Math.sqrt(2.)).module(), 0.)));
      assertTrue ("real numbers are not exact! sqrt(25.)", 
         new Complex (5., 0.).equals (new Complex
         (new Complex (-3., -4.).module(), 0.)));
   } // testAngle

   @Test (expected=RuntimeException.class)
   public void testZeroAngle() {
      new Complex (0., 0.).angle();
   }

   @Test (timeout=1000)
   public void testConsistency() {
      assertTrue ("1+1i is not ok + -", consistent1 (new Complex (1., 1.)));
      assertTrue ("0+0i is not ok + -", consistent1 (new Complex (0., 0.)));
      assertTrue ("2-3i is not ok + -", consistent1 (new Complex (2., -3.)));
      assertTrue ("-5-1i is not ok + - ", consistent1 (new Complex (-5., -1.)));
      assertTrue ("5-4i is not ok * /", consistent2 (new Complex (5., -4.)));
      assertTrue ("-1+0i is not ok * /", consistent2 (new Complex (-1., 0.)));
      assertTrue ("-100-130i is not ok * /", 
         consistent2 (new Complex (-100., -130.)));
   }

}

