/**
 * Tests for BalancedTernary class.
 */
package balancedTernary;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

/**
 * @author David Matuszek
 *
 */
public class BalancedTernaryTest {
    private BalancedTernary bt;
    
    /**
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {
        // Not needed
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#BalancedTernary(java.lang.String)}.
     */
    @Test
    public void testBalancedTernary_String() {
        bt = new BalancedTernary("1N11");
        assertEquals(22, bt.intValue());
        
        bt = new BalancedTernary("N1NN");
        assertEquals(-22, bt.intValue());

        bt = new BalancedTernary("0");
        assertEquals(0, bt.intValue());
        
        bt = new BalancedTernary("000");
        assertEquals(0, bt.intValue());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#BalancedTernary(int)}.
     */
    @Test
    public void testBalancedTernary_Int() {
        bt = new BalancedTernary(69);
        assertEquals("10NN0", bt.value());
        
        bt = new BalancedTernary(22);
        assertEquals("1N11", bt.value());
        
        bt = new BalancedTernary(0);
        assertEquals("0", bt.value());
        
        bt = new BalancedTernary(-22);
        assertEquals("N1NN", bt.value());
        
        bt = new BalancedTernary(1000000);
        assertEquals(1000000, new BalancedTernary(bt.value()).intValue());
        
        bt = new BalancedTernary(-1000000);
        assertEquals(-1000000, new BalancedTernary(bt.value()).intValue());
        
        bt = new BalancedTernary(Integer.MAX_VALUE);
        assertEquals(Integer.MAX_VALUE, new BalancedTernary(bt.value()).intValue());
        
        bt = new BalancedTernary(Integer.MAX_VALUE);
        assertEquals(Integer.MAX_VALUE, new BalancedTernary(bt.value()).intValue());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#value()}.
     */
    @Test
    public void testValue() {
        bt = new BalancedTernary(2000);
        assertEquals("10N1N01N", bt.value());
        
        bt = new BalancedTernary(300);
        assertEquals("11N010", bt.value());
        
        bt = new BalancedTernary(0);
        assertEquals("0", bt.value());
        
        bt = new BalancedTernary(-1000);
        assertEquals("NN0N00N", bt.value());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#toString()}.
     */
    @Test
    public void testToString() {
        bt = new BalancedTernary(2000);
        assertEquals("10,N1N,01N", bt.toString());
        
        bt = new BalancedTernary(300);
        assertEquals("11N,010", bt.toString());
        
        bt = new BalancedTernary(0);
        assertEquals("0", bt.toString());
        
        bt = new BalancedTernary(-1000);
        assertEquals("N,N0N,00N", bt.toString());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#add(balancedTernary.BalancedTernary)}.
     */
    @Test
    public void testAdd() {
        BalancedTernary[] bt1 = makeNumbers(-10, -10, -10,   0, 0,  0, 10, 20,  50,   80);
        BalancedTernary[] bt2 = makeNumbers(-10,   0,  10, -10, 0, 10, 15, 25, -12, -100);
        BalancedTernary[] bt3 = makeNumbers(-20, -10,   0, -10, 0, 10, 25, 45,  38,  -20);
        
        for (int i = 0; i < bt1.length; i++) {
            assertEquals(bt3[i], bt1[i].add(bt2[i]));
        }
        
        // Make sure addends are not changed
        BalancedTernary THIRTY = new BalancedTernary(30);
        BalancedTernary FIFTY = new BalancedTernary(50);
        BalancedTernary EIGHTY = new BalancedTernary(80);
        BalancedTernary sum = FIFTY.add(THIRTY);
        assertEquals(EIGHTY, sum);
        assertEquals(50, FIFTY.intValue());
        assertEquals("1010", THIRTY.value());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#subtract(balancedTernary.BalancedTernary)}.
     */
    @Test
    public void testSubtract() {
        BalancedTernary[] bt1 = makeNumbers(-10, -10, -10,   0, 0,   0, 10, 20,  50,   80);
        BalancedTernary[] bt2 = makeNumbers(-10,   0,  10, -10, 0,  10, 15, 25, -12, -100);
        BalancedTernary[] bt3 = makeNumbers(  0, -10, -20,  10, 0, -10, -5, -5,  62,  180);
        
        for (int i = 0; i < bt1.length; i++) {
            assertEquals(bt3[i], bt1[i].subtract(bt2[i]));
        }
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#multiply(balancedTernary.BalancedTernary)}.
     */
    @Test
    public void testMultiply() {
        BalancedTernary[] bt1 = makeNumbers(-10, -10,  -10,   0, 0,   0,  10,  20,   50);
        BalancedTernary[] bt2 = makeNumbers(-10,   0,   10, -10, 0,  10,  15,  25,  -10);
        BalancedTernary[] bt3 = makeNumbers(100,   0, -100,   0, 0,   0, 150, 500, -500);
        
        for (int i = 0; i < bt1.length; i++) {
            assertEquals(bt3[i], bt1[i].multiply(bt2[i]));
        }
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#divide(balancedTernary.BalancedTernary)}.
     */
    @Test
    public void testDivide() {
        BalancedTernary[] bt1 = makeNumbers(-10, 200, -300, 400, -500, 20, 20, -20, -20);
        BalancedTernary[] bt2 = makeNumbers(-10,  10,  -10, -10,   10,  3, -3,   3,  -3);
        BalancedTernary[] bt3 = makeNumbers(  1,  20,   30, -40,  -50,  6, -6,  -6,   6);
        
        for (int i = 0; i < bt1.length; i++) {
            assertEquals(bt3[i], bt1[i].divide(bt2[i]));
        }
    }


    /**
     * Test method for {@link balancedTernary.BalancedTernary#divide(balancedTernary.BalancedTernary)}.
     */
    @Test(expected=ArithmeticException.class)
    public void testDivideByZero() {
        new BalancedTernary(5).divide(new BalancedTernary(0));
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#remainder(balancedTernary.BalancedTernary)}.
     */
    @Test
    public void testRemainder() {
        BalancedTernary[] bt1 = makeNumbers(-10, 200, -300, 400, -500, 20, 20, -20, -20);
        BalancedTernary[] bt2 = makeNumbers(-10,  10,  -10, -10,   10,  3, -3,   3,  -3);
        BalancedTernary[] bt3 = makeNumbers(  0,   0,    0,   0,    0,  2,  2,  -2,  -2);
        
        for (int i = 0; i < bt1.length; i++) {
            assertEquals(bt3[i], bt1[i].remainder(bt2[i]));
        }
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#remainder(balancedTernary.BalancedTernary)}.
     */
    @Test(expected=ArithmeticException.class)
    public void testRemainderOfDivideByZero() {
        new BalancedTernary(5).remainder(new BalancedTernary(0));
    }
    
    /**
     * The input values to an operation should not themselves be changed.
     */
    @Test
    public void testForChangesInOperands() {
        BalancedTernary THIRTY = new BalancedTernary(30);
        BalancedTernary FIFTY = new BalancedTernary(50);
        BalancedTernary EIGHTY = new BalancedTernary(80);
        FIFTY.add(EIGHTY).subtract(THIRTY).multiply(EIGHTY).divide(THIRTY).remainder(FIFTY);
        assertEquals(30, THIRTY.intValue());
        assertEquals(50, FIFTY.intValue());
        assertEquals(80, EIGHTY.intValue());
        assertEquals("1010", THIRTY.value());
        assertEquals("1N0NN", FIFTY.value());
        assertEquals("1000N", EIGHTY.value());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#equals(java.lang.Object)}.
     */
    @Test
    public void testEqualsObject() {
        assertTrue(new BalancedTernary(25).equals(new BalancedTernary(25)));
        assertFalse(new BalancedTernary(25).equals(new BalancedTernary(35)));
        try {
            assertFalse(new BalancedTernary(25).equals("20"));
        }
        catch (ClassCastException e) {
            
        }
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#abs()}.
     */
    @Test
    public void testAbs() {
        assertEquals(new BalancedTernary(10), new BalancedTernary(-10).abs());
        assertEquals(new BalancedTernary(10), new BalancedTernary(10).abs());
        assertEquals(new BalancedTernary(0), new BalancedTernary(0).abs());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#signum()}.
     */
    @Test
    public void testSignum() {
        assertEquals(-1, new BalancedTernary(-50).signum());
        assertEquals(0, new BalancedTernary(0).signum());
        assertEquals(1, new BalancedTernary(50).signum());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#signum()}.
     */
    @Test
    public void testSignum2() {
        assertEquals("BalancedTernary class does not extend Number class.",
                     new BalancedTernary(-1), new BalancedTernary(-50).signum());
        assertEquals(new BalancedTernary(0), new BalancedTernary(0).signum());
        assertEquals(new BalancedTernary(1), new BalancedTernary(50).signum());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#byteValue()}.
     */
    @Test
    public void testByteValue() {
        assertEquals(10, new BalancedTernary(10).byteValue());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#shortValue()}.
     */
    @Test
    public void testShortValue() {
        assertEquals(10, new BalancedTernary(10).shortValue());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#longValue()}.
     */
    @Test
    public void testLongValue() {
        assertEquals(10, new BalancedTernary(10).longValue());
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#floatValue()}.
     */
    @Test
    public void testFloatValue() {
        assertEquals(10.0, new BalancedTernary(10).floatValue(), 0.000001);
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#doubleValue()}.
     */
    @Test
    public void testDoubleValue() {
        assertEquals(10.0, new BalancedTernary(10).doubleValue(), 0.000001);
    }

    /**
     * Test method for {@link balancedTernary.BalancedTernary#compareTo(Object)}.
     */
    @Test
    public void testCompareTo() {
        BalancedTernary TEN = new BalancedTernary(10);
        BalancedTernary FIVE = new BalancedTernary(5);
        assertTrue(TEN.compareTo(FIVE) > 0);
        assertTrue(TEN.compareTo(TEN) == 0);
        assertTrue(FIVE.compareTo(TEN) < 0);
    }

    /**
     * Given an array or parameter list of integers, return the corresponding
     * array of BalancedTernary numbers.
     * 
     * @param decimal The integers to be converted.
     * @return The corresponding balanced ternary numbers.
     */
    private BalancedTernary[] makeNumbers(int... decimal) {
        BalancedTernary[] ternary = new BalancedTernary[decimal.length];
            for (int i = 0; i < decimal.length; i++) {
                ternary[i] = new BalancedTernary(decimal[i]);
            }
        return ternary;
    }
}
