Add remainder function

This commit is contained in:
Mike Cifelli 2018-01-13 08:52:29 -05:00
parent 6a4d19517e
commit 735f1d584a
4 changed files with 152 additions and 8 deletions

View File

@ -0,0 +1,35 @@
package function.builtin.math;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import function.builtin.math.DIVIDE.DivideByZeroException;
import sexpression.Cons;
import sexpression.LispNumber;
import sexpression.SExpression;
@FunctionNames({ "REM", "REMAINDER" })
public class REMAINDER extends LispFunction {
private ArgumentValidator argumentValidator;
public REMAINDER(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
LispNumber dividend = (LispNumber) argumentList.getFirst();
LispNumber divisor = (LispNumber) ((Cons) argumentList.getRest()).getFirst();
try {
return new LispNumber(dividend.getValue().remainder(divisor.getValue()));
} catch (ArithmeticException e) {
throw new DivideByZeroException();
}
}
}

View File

@ -29,6 +29,7 @@ import function.builtin.math.MINUS;
import function.builtin.math.MODULO; import function.builtin.math.MODULO;
import function.builtin.math.MULTIPLY; import function.builtin.math.MULTIPLY;
import function.builtin.math.PLUS; import function.builtin.math.PLUS;
import function.builtin.math.REMAINDER;
import function.builtin.predicate.ATOM; import function.builtin.predicate.ATOM;
import function.builtin.predicate.EQ; import function.builtin.predicate.EQ;
import function.builtin.predicate.EQUAL; import function.builtin.predicate.EQUAL;
@ -98,6 +99,7 @@ public class FunctionTable {
allBuiltIns.add(PROGN.class); allBuiltIns.add(PROGN.class);
allBuiltIns.add(QUOTE.class); allBuiltIns.add(QUOTE.class);
allBuiltIns.add(RECUR.class); allBuiltIns.add(RECUR.class);
allBuiltIns.add(REMAINDER.class);
allBuiltIns.add(REST.class); allBuiltIns.add(REST.class);
allBuiltIns.add(SET.class); allBuiltIns.add(SET.class);
allBuiltIns.add(SETQ.class); allBuiltIns.add(SETQ.class);

View File

@ -1,5 +1,6 @@
package function.builtin.math; package function.builtin.math;
import static testutil.TestUtilities.assertIsErrorWithMessage;
import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.evaluateString;
@ -85,17 +86,13 @@ public class MODULOTest extends SymbolAndFunctionCleaner {
} }
@Test(expected = ModulusNotPositiveException.class) @Test(expected = ModulusNotPositiveException.class)
public void divisorOfZero() { public void negativeDivisor() {
String input = "(mod 5 0)"; evaluateString("(mod 5 -10)");
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
} }
@Test(expected = ModulusNotPositiveException.class) @Test(expected = ModulusNotPositiveException.class)
public void negativeDivisor() { public void divisorOfZero() {
String input = "(mod 5 -10)"; evaluateString("(mod 5 0)");
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
} }
@Test(expected = BadArgumentTypeException.class) @Test(expected = BadArgumentTypeException.class)
@ -112,4 +109,9 @@ public class MODULOTest extends SymbolAndFunctionCleaner {
public void modWithTooManyArguments() { public void modWithTooManyArguments() {
evaluateString("(mod 1 2 3)"); evaluateString("(mod 1 2 3)");
} }
@Test
public void moduloNotPositiveException_HasCorrectAttributes() {
assertIsErrorWithMessage(new ModulusNotPositiveException());
}
} }

View File

@ -0,0 +1,105 @@
package function.builtin.math;
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.DIVIDE.DivideByZeroException;
import sexpression.LispNumber;
import testutil.SymbolAndFunctionCleaner;
public class REMAINDERTest extends SymbolAndFunctionCleaner {
@Test
public void rem() {
String input = "(rem 5 3)";
assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
}
@Test
public void remainder() {
String input = "(remainder 11 7)";
assertSExpressionsMatch(new LispNumber("4"), evaluateString(input));
}
@Test
public void dividendGreaterThanDivisor() {
String input = "(rem 21 19)";
assertSExpressionsMatch(new LispNumber("2"), evaluateString(input));
}
@Test
public void dividendLessThanDivisor() {
String input = "(rem 5 239)";
assertSExpressionsMatch(new LispNumber("5"), evaluateString(input));
}
@Test
public void dividendEqualToDivisor() {
String input = "(rem 5 5)";
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
}
@Test
public void dividendMultipleOfDivisor() {
String input = "(rem 20 5)";
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
}
@Test
public void divisorOfOne() {
String input = "(rem 5 1)";
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
}
@Test
public void dividendOfZero() {
String input = "(rem 0 2309)";
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
}
@Test
public void negativeDividend() {
String input = "(rem -23 25)";
assertSExpressionsMatch(new LispNumber("-23"), evaluateString(input));
}
public void negativeDivisor() {
String input = "(rem 5 -11)";
assertSExpressionsMatch(new LispNumber("0"), evaluateString(input));
}
@Test(expected = DivideByZeroException.class)
public void divisorOfZero() {
evaluateString("(rem 5 0)");
}
@Test(expected = BadArgumentTypeException.class)
public void remWithNonNumber() {
evaluateString("(rem 'a 'b)");
}
@Test(expected = TooFewArgumentsException.class)
public void remWithTooFewArguments() {
evaluateString("(rem 1)");
}
@Test(expected = TooManyArgumentsException.class)
public void remWithTooManyArguments() {
evaluateString("(rem 1 2 3)");
}
}