Repository: jena Updated Branches: refs/heads/JENA-507 [created] 4a42ee2a1
Implement factorial and log functions (JENA-507) Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/33a8f8de Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/33a8f8de Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/33a8f8de Branch: refs/heads/JENA-507 Commit: 33a8f8deea0e41c9fefafe2bcdaf19c49868e2a5 Parents: 66c27e4 Author: Rob Vesse <[email protected]> Authored: Thu Oct 9 12:06:29 2014 +0100 Committer: Rob Vesse <[email protected]> Committed: Thu Oct 9 12:06:29 2014 +0100 ---------------------------------------------------------------------- .../sparql/function/library/leviathan/cube.java | 2 +- .../function/library/leviathan/factorial.java | 40 +++++++ .../sparql/function/library/leviathan/log.java | 44 ++++++++ .../sparql/expr/TestLeviathanFunctions.java | 105 +++++++++++++++---- 4 files changed, 170 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/33a8f8de/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cube.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cube.java b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cube.java index d94f928..0b78822 100644 --- a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cube.java +++ b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cube.java @@ -38,7 +38,7 @@ public class cube extends FunctionBase1 { double dec = v.getDecimal().doubleValue() ; return NodeValue.makeDecimal( Math.pow(dec, 3d)) ; case OP_FLOAT: - // TODO Should squaring a float keep it a float? + // TODO Should cubing a float keep it a float? case OP_DOUBLE: return NodeValue.makeDouble( Math.pow(v.getDouble(), 3d) ) ; default: http://git-wip-us.apache.org/repos/asf/jena/blob/33a8f8de/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/factorial.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/factorial.java b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/factorial.java new file mode 100644 index 0000000..8fea35b --- /dev/null +++ b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/factorial.java @@ -0,0 +1,40 @@ +package com.hp.hpl.jena.sparql.function.library.leviathan; + +import java.math.BigInteger; + +import com.hp.hpl.jena.sparql.expr.ExprEvalException; +import com.hp.hpl.jena.sparql.expr.NodeValue; +import com.hp.hpl.jena.sparql.expr.nodevalue.XSDFuncOp; +import com.hp.hpl.jena.sparql.function.FunctionBase1; + +public class factorial extends FunctionBase1 { + + @Override + public NodeValue exec(NodeValue v) { + // Don't care about the return value, will just error if the thing isn't + // a numeric + XSDFuncOp.classifyNumeric("factorial", v); + + BigInteger i = v.getInteger(); + + switch (i.compareTo(BigInteger.ZERO)) { + case 0: + // 0! is 1 + return NodeValue.makeInteger(BigInteger.ONE); + case -1: + // Negative factorial is error + throw new ExprEvalException("Cannot evaluate a negative factorial"); + case 1: + BigInteger res = i.add(BigInteger.ZERO); + i = i.subtract(BigInteger.ONE); + while (i.compareTo(BigInteger.ZERO) != 0) { + res = res.multiply(i); + i = i.subtract(BigInteger.ONE); + } + return NodeValue.makeInteger(res); + default: + throw new ExprEvalException("Unexpecte comparison result"); + } + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/33a8f8de/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/log.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/log.java b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/log.java new file mode 100644 index 0000000..c45bd77 --- /dev/null +++ b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/log.java @@ -0,0 +1,44 @@ +package com.hp.hpl.jena.sparql.function.library.leviathan; + +import java.util.List; + +import com.hp.hpl.jena.query.QueryBuildException; +import com.hp.hpl.jena.sparql.expr.ExprEvalException; +import com.hp.hpl.jena.sparql.expr.ExprList; +import com.hp.hpl.jena.sparql.expr.NodeValue; +import com.hp.hpl.jena.sparql.expr.nodevalue.XSDFuncOp; +import com.hp.hpl.jena.sparql.function.FunctionBase; +import com.hp.hpl.jena.sparql.util.Utils; + +public class log extends FunctionBase { + + @Override + public NodeValue exec(List<NodeValue> args) { + if (args.size() < 1 || args.size() > 2) + throw new ExprEvalException("Invalid number of arguments"); + + // Don't care about the return value, will just error if the thing isn't + // a numeric + NodeValue v = args.get(0); + XSDFuncOp.classifyNumeric("log", v); + + if (args.size() == 1) { + // Log base 10 + return NodeValue.makeDouble(Math.log10(v.getDouble())); + } else { + // Log with arbitrary base + // See http://en.wikipedia.org/wiki/List_of_logarithmic_identities#Changing_the_base + NodeValue base = args.get(1); + XSDFuncOp.classifyNumeric("log", base); + + return NodeValue.makeDouble(Math.log10(v.getDouble()) / Math.log10(base.getDouble())); + } + } + + @Override + public void checkBuild(String uri, ExprList args) { + if (args.size() < 1 || args.size() > 2) + throw new QueryBuildException("Function '" + Utils.className(this) + "' takes one/two argument(s)"); + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/33a8f8de/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java b/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java index 7c90c74..ba5c53c 100644 --- a/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java +++ b/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java @@ -18,20 +18,20 @@ package com.hp.hpl.jena.sparql.expr; -import org.apache.jena.atlas.junit.BaseTest ; -import org.junit.AfterClass ; -import org.junit.BeforeClass ; -import org.junit.Test ; - -import com.hp.hpl.jena.graph.Node ; -import com.hp.hpl.jena.shared.PrefixMapping ; -import com.hp.hpl.jena.shared.impl.PrefixMappingImpl ; -import com.hp.hpl.jena.sparql.ARQConstants ; -import com.hp.hpl.jena.sparql.function.FunctionEnvBase ; -import com.hp.hpl.jena.sparql.function.library.leviathan.LeviathanConstants ; -import com.hp.hpl.jena.sparql.serializer.SerializationContext ; -import com.hp.hpl.jena.sparql.util.ExprUtils ; -import com.hp.hpl.jena.sparql.util.NodeFactoryExtra ; +import org.apache.jena.atlas.junit.BaseTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.hp.hpl.jena.graph.Node; +import com.hp.hpl.jena.shared.PrefixMapping; +import com.hp.hpl.jena.shared.impl.PrefixMappingImpl; +import com.hp.hpl.jena.sparql.ARQConstants; +import com.hp.hpl.jena.sparql.function.FunctionEnvBase; +import com.hp.hpl.jena.sparql.function.library.leviathan.LeviathanConstants; +import com.hp.hpl.jena.sparql.serializer.SerializationContext; +import com.hp.hpl.jena.sparql.util.ExprUtils; +import com.hp.hpl.jena.sparql.util.NodeFactoryExtra; public class TestLeviathanFunctions extends BaseTest { @@ -90,24 +90,84 @@ public class TestLeviathanFunctions extends BaseTest { public void e_01() { test("lfn:e(2)", NodeFactoryExtra.doubleToNode(Math.exp(2d))); } - + @Test public void pow_01() { test("lfn:pow(2, 4)", "16"); } - + @Test public void pow_02() { test("lfn:pow(0.5, 3)", "0.125"); } + + @Test + public void factorial_01() { + test("lfn:factorial(0)", "1"); + } + + @Test + public void factorial_02() { + test("lfn:factorial(1)", "1"); + } + + @Test + public void factorial_03() { + test("lfn:factorial(3)", "6"); + } + + @Test + public void factorial_04() { + test("lfn:factorial(5)", "120"); + } + + @Test(expected = ExprEvalException.class) + public void factorial_05() { + testError("lfn:factorial(-1)"); + } + + @Test(expected = ExprEvalException.class) + public void factorial_06() { + testError("lfn:factorial(5.4)"); + } + + @Test + public void log_01() { + test("lfn:log(1)", "0"); + } + + @Test + public void log_02() { + test("lfn:log(10)", "1"); + } + + @Test + public void log_03() { + test("lfn:log(-1)", NodeFactoryExtra.doubleToNode(Double.NaN)); + } + + @Test + public void log_04() { + test("lfn:log(4, 2)", "2"); + } + + @Test + public void log_05() { + test("lfn:log(4, 16)", "0.5"); + } - private static void test(String string, String result) { + @Test + public void log_06() { + test("lfn:log(16, 4)", "2"); + } + + private static void test(String exprString, String result) { Node r = NodeFactoryExtra.parseNode(result); - test(string, r); + test(exprString, r); } - private static void test(String string, Node result) { - Expr expr = ExprUtils.parse(string, pmap); + private static void test(String exprString, Node result) { + Expr expr = ExprUtils.parse(exprString, pmap); NodeValue nv = expr.eval(null, new FunctionEnvBase()); NodeValue nvr = NodeValue.makeNode(result); @@ -116,4 +176,9 @@ public class TestLeviathanFunctions extends BaseTest { assertTrue("Not same value: Expected: " + nvr + " : Actual = " + nv, NodeValue.sameAs(nvr, nv)); } + private static void testError(String exprString) { + Expr expr = ExprUtils.parse(exprString, pmap); + expr.eval(null, new FunctionEnvBase()); + } + }
