diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java index da39fe9bdec0bf28a7259cf9f31fb90fe5b65302..2ca65bc6427b9c391f9faf31de90e40cd2c9bb8e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java @@ -14,33 +14,30 @@ import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE_SUMMARY; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RComplex; -import com.oracle.truffle.r.runtime.data.RDataFactory; -import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; +import com.oracle.truffle.r.runtime.data.nodes.VectorAccess; +import com.oracle.truffle.r.runtime.data.nodes.VectorAccess.SequentialIterator; import com.oracle.truffle.r.runtime.ops.BinaryArithmetic; -import com.oracle.truffle.r.runtime.ops.na.NACheck; +@ImportStatic(RType.class) @RBuiltin(name = "prod", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE_SUMMARY) public abstract class Prod extends RBuiltinNode.Arg2 { - // TODO: handle multiple arguments, handle na.rm - static { - Casts.noCasts(Prod.class); + Casts casts = new Casts(Prod.class); + casts.arg("na.rm").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(Predef.toBoolean()); } @Override @@ -48,139 +45,154 @@ public abstract class Prod extends RBuiltinNode.Arg2 { return new Object[]{RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE}; } - @Child private Prod prodRecursive; - - public abstract Object executeObject(Object x); - @Child private BinaryArithmetic prod = BinaryArithmetic.MULTIPLY.createOperation(); - @Specialization - protected Object prod(RArgsValuesAndNames args) { - int argsLen = args.getLength(); - if (argsLen == 0) { - return 1d; + @ExplodeLoop + protected static boolean supports(RArgsValuesAndNames args, VectorAccess[] argAccess) { + if (args.getLength() != argAccess.length) { + return false; } - if (prodRecursive == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - prodRecursive = insert(ProdNodeGen.create()); + for (int i = 0; i < argAccess.length; i++) { + if (!argAccess[i].supports(args.getArgument(i))) { + return false; + } } - Object ret = 1d; - if (argsLen > 0) { - double prodReal; - double prodImg; - boolean complex; - if (ret instanceof RComplex) { - RComplex c = (RComplex) ret; - prodReal = c.getRealPart(); - prodImg = c.getImaginaryPart(); - complex = true; - } else { - prodReal = (Double) ret; - prodImg = 0d; - complex = false; + return true; + } + + protected static VectorAccess[] createAccess(RArgsValuesAndNames args, RType topmostType) { + VectorAccess[] result = new VectorAccess[args.getLength()]; + for (int i = 0; i < result.length; i++) { + VectorAccess access = VectorAccess.create(args.getArgument(i)); + if (access == null) { + return null; } - for (int i = 0; i < argsLen; i++) { - Object aProd = prodRecursive.executeObject(args.getArgument(i)); - double aProdReal; - double aProdImg; - if (aProd instanceof RComplex) { - RComplex c = (RComplex) aProd; - if (RRuntime.isNA(c)) { - return c; - } - aProdReal = c.getRealPart(); - aProdImg = c.getImaginaryPart(); - complex = true; - } else { - aProdReal = (Double) aProd; - aProdImg = 0d; - if (RRuntime.isNA(aProdReal)) { - return aProd; - } - } - if (complex) { - RComplex c = prod.op(prodReal, prodImg, aProdReal, aProdImg); - prodReal = c.getRealPart(); - prodImg = c.getImaginaryPart(); - } else { - prodReal = prod.op(prodReal, aProdReal); - } + RType type = access.getType(); + if (type != RType.Null && type != RType.Logical && type != RType.Integer && type != RType.Double && type != topmostType) { + return null; } - ret = complex ? RComplex.valueOf(prodReal, prodImg) : prodReal; + result[i] = access; } - return ret; + return result; } - private final ValueProfile intVecProfile = ValueProfile.createClassProfile(); - private final NACheck naCheck = NACheck.create(); - - @Specialization - protected double prod(RAbstractDoubleVector x) { - RAbstractDoubleVector profiledVec = intVecProfile.profile(x); - double product = 1; - naCheck.enable(x); - for (int k = 0; k < profiledVec.getLength(); k++) { - double value = profiledVec.getDataAt(k); - if (naCheck.check(value)) { - return RRuntime.DOUBLE_NA; + @Specialization(guards = {"argAccess != null", "supports(args, argAccess)", "naRm == cachedNaRm"}) + @ExplodeLoop + protected double prodDoubleCached(RArgsValuesAndNames args, @SuppressWarnings("unused") boolean naRm, + @Cached("naRm") boolean cachedNaRm, + @Cached("createAccess(args, Double)") VectorAccess[] argAccess) { + double value = 1; + for (int i = 0; i < argAccess.length; i++) { + VectorAccess access = argAccess[i]; + double element = prodDouble(args.getArgument(i), access, cachedNaRm); + if (!cachedNaRm && access.na.check(element)) { + return element; } - product = prod.op(product, value); + value *= element; } - return product; + return value; } - @Specialization - protected double prod(RAbstractIntVector x) { - RAbstractIntVector profiledVec = intVecProfile.profile(x); - double product = 1; - naCheck.enable(x); - for (int k = 0; k < profiledVec.getLength(); k++) { - int data = profiledVec.getDataAt(k); - if (naCheck.check(data)) { - return RRuntime.DOUBLE_NA; + @Specialization(guards = {"argAccess != null", "supports(args, argAccess)", "naRm == cachedNaRm"}) + @ExplodeLoop + protected RComplex prodComplexCached(RArgsValuesAndNames args, @SuppressWarnings("unused") boolean naRm, + @Cached("naRm") boolean cachedNaRm, + @Cached("createAccess(args, Complex)") VectorAccess[] argAccess) { + RComplex value = RComplex.valueOf(1, 0); + for (int i = 0; i < argAccess.length; i++) { + VectorAccess access = argAccess[i]; + RComplex element = prodComplex(args.getArgument(i), access, cachedNaRm); + if (!cachedNaRm && access.na.check(element)) { + return element; } - product = prod.op(product, data); + value = prod.op(value.getRealPart(), value.getImaginaryPart(), element.getRealPart(), element.getImaginaryPart()); } - return product; + return value; } - @Specialization - protected double prod(RAbstractLogicalVector x) { - RAbstractLogicalVector profiledVec = intVecProfile.profile(x); - double product = 1; - naCheck.enable(x); - for (int k = 0; k < profiledVec.getLength(); k++) { - byte value = profiledVec.getDataAt(k); - if (naCheck.check(value)) { - return RRuntime.DOUBLE_NA; + @Specialization(replaces = {"prodDoubleCached", "prodComplexCached"}) + protected Object prodGeneric(RArgsValuesAndNames args, boolean naRm) { + int length = args.getLength(); + double value = 1; + int i = 0; + for (; i < length; i++) { + Object arg = args.getArgument(i); + VectorAccess access = VectorAccess.createSlowPath(arg); + if (access == null) { + break; + } + RType type = access.getType(); + if (type != RType.Null && type != RType.Logical && type != RType.Integer && type != RType.Double) { + break; + } + double element = prodDouble(arg, access, naRm); + if (!naRm && access.na.check(element)) { + return element; } - product = prod.op(product, value); + value *= element; } - return product; + if (i == length) { + return value; + } + RComplex complexValue = RComplex.valueOf(1, 0); + for (; i < length; i++) { + Object arg = args.getArgument(i); + VectorAccess access = VectorAccess.createSlowPath(arg); + if (access == null) { + break; + } + RType type = access.getType(); + if (!type.isNumeric() && type != RType.Null) { + break; + } + RComplex element = prodComplex(arg, access, naRm); + if (!naRm && access.na.check(element)) { + return element; + } + complexValue = prod.op(complexValue.getRealPart(), complexValue.getImaginaryPart(), element.getRealPart(), element.getImaginaryPart()); + } + if (i == length) { + return complexValue; + } + throw error(RError.Message.INVALID_TYPE_ARGUMENT, Predef.typeName().apply(args.getArgument(i))); } - @Specialization - protected RComplex prod(RAbstractComplexVector x) { - RAbstractComplexVector profiledVec = intVecProfile.profile(x); - RComplex product = RDataFactory.createComplexRealOne(); - naCheck.enable(x); - for (int k = 0; k < profiledVec.getLength(); k++) { - RComplex a = profiledVec.getDataAt(k); - if (naCheck.check(a)) { - return a; + protected static double prodDouble(Object v, VectorAccess access, boolean naRm) { + try (SequentialIterator iter = access.access(v)) { + double value = 1; + while (access.next(iter)) { + double element = access.getDouble(iter); + if (access.na.check(element)) { + if (!naRm) { + return RRuntime.DOUBLE_NA; + } + } else { + value *= element; + } } - product = prod.op(product.getRealPart(), product.getImaginaryPart(), a.getRealPart(), a.getImaginaryPart()); + return value; } - return product; } - @Specialization - protected double prod(@SuppressWarnings("unused") RNull n) { - return 1d; + protected RComplex prodComplex(Object v, VectorAccess access, boolean naRm) { + try (SequentialIterator iter = access.access(v)) { + RComplex value = RComplex.valueOf(1, 0); + while (access.next(iter)) { + RComplex element = access.getComplex(iter); + if (access.na.check(element)) { + if (!naRm) { + return element; + } + } else { + value = prod.op(value.getRealPart(), value.getImaginaryPart(), element.getRealPart(), element.getImaginaryPart()); + } + } + return value; + } } @Fallback - protected Object prod(Object o) { - throw error(RError.Message.INVALID_TYPE_ARGUMENT, Predef.typeName().apply(o)); + protected Object prod(Object v, @SuppressWarnings("unused") Object naRm) { + throw error(RError.Message.INVALID_TYPE_ARGUMENT, Predef.typeName().apply(v)); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index c015d5ee754805752a2d98c3a36440bb74ef9c7a..c58c83079b2017abcc5d19c3c53d4119d648507c 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -48245,105 +48245,237 @@ sex -1.65487 0.483 0.38527 11.74 1.0 0.00061 frailty(id, dist = 't', c 20.33 13.9 0.12000 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#prod() +#prod(1) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#prod(complex()) -[1] 1+0i +#prod(1,2,3,4) +[1] 24 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#prod(numeric()) +#prod(1,2,3,4,na.rm=FALSE) +[1] 24 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1,2,3,4,na.rm=TRUE) +[1] 24 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1,FALSE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1,FALSE,na.rm=FALSE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1,FALSE,na.rm=TRUE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1,na.rm=FALSE) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{ foo <- function(...) prod(...); foo(); } +#prod(1,na.rm=TRUE) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod('a')} -Error in prod("a") : invalid 'type' (character) of argument +#prod(1L,NA,5+3i) +[1] NA ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod()} -[1] 1 +#prod(1L,NA,5+3i,na.rm=FALSE) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(1L,NA,5+3i,na.rm=TRUE) +[1] 5+3i ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(2+3i,42)} -[1] 84+126i +#prod(2,TRUE) +[1] 2 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(2+3i,42+5i)} -[1] 69+136i +#prod(2,TRUE,na.rm=FALSE) +[1] 2 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(2+3i,c())} -[1] 2+3i +#prod(2,TRUE,na.rm=TRUE) +[1] 2 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(42,2+3i)} -[1] 84+126i +#prod(4+0i,6,NA) +[1] NA ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(NULL)} +#prod(4+0i,6,NA,na.rm=FALSE) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(4+0i,6,NA,na.rm=TRUE) +[1] 24+0i + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(4L) +[1] 4 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(4L,na.rm=FALSE) +[1] 4 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(4L,na.rm=TRUE) +[1] 4 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(FALSE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(FALSE,na.rm=FALSE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(FALSE,na.rm=TRUE) +[1] 0 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA,c(1,2,3)) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA,c(1,2,3),na.rm=FALSE) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA,c(1,2,3),na.rm=TRUE) +[1] 6 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA_integer_) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA_integer_,na.rm=FALSE) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(NA_integer_,na.rm=TRUE) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c())} +#prod(TRUE) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(),c())} +#prod(TRUE,na.rm=FALSE) [1] 1 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(1+2i))} -[1] 1+2i +#prod(TRUE,na.rm=TRUE) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(1,2,NA),NA) +[1] NA + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(1,2,NA),NA,na.rm=FALSE) +[1] NA ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(1+2i, 2+3i))} -[1] -4+7i +#prod(c(1,2,NA),NA,na.rm=TRUE) +[1] 2 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(1+2i,1+3i,1+45i))} -[1] -230-220i +#prod(c(2,4)) +[1] 8 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(1,2,3,4,5))} -[1] 120 +#prod(c(2,4),na.rm=FALSE) +[1] 8 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(2,4))} +#prod(c(2,4),na.rm=TRUE) [1] 8 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(2,4,3))} +#prod(c(2,4,3)) [1] 24 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(TRUE, FALSE))} -[1] 0 +#prod(c(2,4,3),na.rm=FALSE) +[1] 24 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(c(TRUE, TRUE))} -[1] 1 +#prod(c(2,4,3),na.rm=TRUE) +[1] 24 ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# -#{prod(list())} -Error in prod(list()) : invalid 'type' (list) of argument +#prod(c(2,4,NA)) +[1] NA -##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProdNa# -#{prod(c(1,2,3,4,5,NA),FALSE)} +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(2,4,NA),na.rm=FALSE) [1] NA -##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProdNa# -#{prod(c(2,4,3,NA),TRUE)} +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(2,4,NA),na.rm=TRUE) +[1] 8 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(NA,2L,4L)) [1] NA -##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProdNa# -#{prod(c(2,4,NA))} +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(NA,2L,4L),na.rm=FALSE) [1] NA +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(c(NA,2L,4L),na.rm=TRUE) +[1] 8 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(complex(),numeric()) +[1] 1+0i + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(complex(),numeric(),na.rm=FALSE) +[1] 1+0i + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(complex(),numeric(),na.rm=TRUE) +[1] 1+0i + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric()) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric(),na.rm=FALSE) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric(),na.rm=TRUE) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric(),numeric()) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric(),numeric(),na.rm=FALSE) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#prod(numeric(),numeric(),na.rm=TRUE) +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testProd# +#{ foo <- function(...) prod(...); foo(); } +[1] 1 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_prod.testprod1# #argv <- list(9L);prod(argv[[1]]); [1] 9 diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_prod.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_prod.java index 42a9f0ef49eb81f41d325bbc09fcefceac17ba45..387c17b835001191ea2a0c0ac737ea68aecb02c2 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_prod.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_prod.java @@ -72,36 +72,13 @@ public class TestBuiltin_prod extends TestBase { assertEval("argv <- list(numeric(0));prod(argv[[1]]);"); } + private static final String[] VALUES = {"FALSE", "TRUE", "1,FALSE", "2,TRUE", "c(2,4)", "c(2,4,3)", "c(2,4,NA)", "c(NA,2L,4L)", "1", "4L", "NA_integer_", "NA,c(1,2,3)", "c(1,2,NA),NA", "1,2,3,4", + "1L,NA,5+3i", "4+0i,6,NA", "numeric(),numeric()", "numeric()", "complex(),numeric()"}; + private static final String[] OPTIONS = {"", ",na.rm=TRUE", ",na.rm=FALSE"}; + @Test public void testProd() { - assertEval("{prod(c(2,4))}"); - assertEval("{prod(c(2,4,3))}"); - assertEval("{prod(c(1,2,3,4,5))}"); - assertEval("{prod(c(1+2i))}"); - assertEval("{prod(c(1+2i, 2+3i))}"); - assertEval("{prod(c(1+2i,1+3i,1+45i))}"); - assertEval("{prod(c(TRUE, TRUE))}"); - assertEval("{prod(c(TRUE, FALSE))}"); - assertEval("{prod()}"); - assertEval("{prod(NULL)}"); - assertEval("{prod(c())}"); - assertEval("{prod(c(),c())}"); - assertEval("{prod(2+3i,c())}"); - assertEval("{prod(2+3i,42+5i)}"); - assertEval("{prod(2+3i,42)}"); - assertEval("{prod(42,2+3i)}"); - assertEval("{prod('a')}"); - assertEval("{prod(list())}"); - assertEval("prod()"); - assertEval("prod(numeric())"); - assertEval("prod(complex())"); + assertEval(template("prod(%0%1)", VALUES, OPTIONS)); assertEval("{ foo <- function(...) prod(...); foo(); }"); } - - @Test - public void testProdNa() { - assertEval("{prod(c(2,4,NA))}"); - assertEval("{prod(c(2,4,3,NA),TRUE)}"); - assertEval("{prod(c(1,2,3,4,5,NA),FALSE)}"); - } }