
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.)));
    }

}
