package function.builtin.math;

import static testutil.TestUtilities.assertIsErrorWithMessage;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;

import org.junit.Test;

import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import function.builtin.math.MODULO.ModulusNotPositiveException;
import sexpression.LispNumber;
import testutil.SymbolAndFunctionCleaner;

public class MODULOTest extends SymbolAndFunctionCleaner {

    @Test
    public void mod() {
        String input = "(mod 5 3)";

        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
    }

    @Test
    public void modulo() {
        String input = "(modulo 11 7)";

        assertSExpressionsMatch(new LispNumber("4"), evaluateString(input));
    }

    @Test
    public void moduloSymbol() {
        String input = "(% 8 5)";

        assertSExpressionsMatch(new LispNumber("3"), evaluateString(input));
    }

    @Test
    public void dividendGreaterThanDivisor() {
        String input = "(mod 21 19)";

        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
    }

    @Test
    public void dividendLessThanDivisor() {
        String input = "(mod 5 239)";

        assertSExpressionsMatch(new LispNumber("5"), evaluateString(input));
    }

    @Test
    public void dividendEqualToDivisor() {
        String input = "(mod 5 5)";

        assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
    }

    @Test
    public void dividendMultipleOfDivisor() {
        String input = "(mod 20 5)";

        assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
    }

    @Test
    public void divisorOfOne() {
        String input = "(mod 5 1)";

        assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
    }

    @Test
    public void dividendOfZero() {
        String input = "(mod 0 2309)";

        assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
    }

    @Test
    public void negativeDividend() {
        String input = "(mod -23 25)";

        assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
    }

    @Test(expected = ModulusNotPositiveException.class)
    public void negativeDivisor() {
        evaluateString("(mod 5 -10)");
    }

    @Test(expected = ModulusNotPositiveException.class)
    public void divisorOfZero() {
        evaluateString("(mod 5 0)");
    }

    @Test(expected = BadArgumentTypeException.class)
    public void modWithNonNumber() {
        evaluateString("(mod 'a 'b)");
    }

    @Test(expected = TooFewArgumentsException.class)
    public void modWithTooFewArguments() {
        evaluateString("(mod 1)");
    }

    @Test(expected = TooManyArgumentsException.class)
    public void modWithTooManyArguments() {
        evaluateString("(mod 1 2 3)");
    }

    @Test
    public void moduloNotPositiveException_HasCorrectAttributes() {
        assertIsErrorWithMessage(new ModulusNotPositiveException());
    }
}
