diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java index ace7e51fb5f2b138103065be13d4b763cc99eaf8..fa35843bb2de35cfebb7f1769f2e83b97353c753 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.runtime.RBuiltinKind.*; import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.utilities.*; @@ -44,6 +45,7 @@ public abstract class PMinMax extends RBuiltinNode { @Child private CastToVectorNode castVector; @Child private CastIntegerNode castInteger; @Child private CastDoubleNode castDouble; + @Child private CastStringNode castString; @Child private PrecedenceNode precedenceNode = PrecedenceNodeFactory.create(null, null); private final ReduceSemantics semantics; private final NACheck na = NACheck.create(); @@ -90,6 +92,14 @@ public abstract class PMinMax extends RBuiltinNode { return castDouble; } + private CastNode getStringCastNode() { + if (castString == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + castString = insert(CastStringNodeFactory.create(null, true, true, true, false)); + } + return castString; + } + private int convertToVectorAndEnableNACheck(VirtualFrame frame, RArgsValuesAndNames args, CastNode castNode) { int length = 0; Object[] argValues = args.getValues(); @@ -118,7 +128,7 @@ public abstract class PMinMax extends RBuiltinNode { boolean warningAdded = false; for (int i = 0; i < maxLength; i++) { int result = semantics.getIntStart(); - for (int j = 0; j < args.length(); j++) { + for (int j = 0; j < argValues.length; j++) { RAbstractIntVector vec = (RAbstractIntVector) argValues[j]; if (vec.getLength() < maxLength && !warningAdded) { RError.warning(RError.Message.ARG_RECYCYLED); @@ -158,7 +168,7 @@ public abstract class PMinMax extends RBuiltinNode { boolean warningAdded = false; for (int i = 0; i < maxLength; i++) { double result = semantics.getDoubleStart(); - for (int j = 0; j < args.length(); j++) { + for (int j = 0; j < argValues.length; j++) { RAbstractDoubleVector vec = (RAbstractDoubleVector) argValues[j]; if (vec.getLength() < maxLength && !warningAdded) { RError.warning(RError.Message.ARG_RECYCYLED); @@ -182,7 +192,75 @@ public abstract class PMinMax extends RBuiltinNode { } } - // TODO implement support for strings + @SlowPath + private boolean doStringVectorMultiElem(Object[] argValues, byte naRm, int offset, int ind, int maxLength, boolean warning, String[] data) { + boolean warningAdded = warning; + RAbstractStringVector vec = (RAbstractStringVector) argValues[offset]; + if (vec.getLength() < maxLength && !warningAdded) { + RError.warning(RError.Message.ARG_RECYCYLED); + warningAdded = true; + } + String result = vec.getDataAt(ind % vec.getLength()); + na.enable(result); + if (naRm == RRuntime.LOGICAL_TRUE) { + if (na.check(result)) { + // the following is meant to eliminate leading NA-s + if (offset == argValues.length - 1) { + // last element - all other are NAs + data[ind] = semantics.getStringStart(); + } else { + return doStringVectorMultiElem(argValues, naRm, offset + 1, ind, maxLength, warningAdded, data); + } + return warningAdded; + } + } else { + if (na.check(result)) { + data[ind] = result; + return warningAdded; + } + } + // when we reach here, it means that we have already seen one non-NA element + assert !RRuntime.isNA(result); + for (int i = offset + 1; i < argValues.length; ++i) { + vec = (RAbstractStringVector) argValues[i]; + if (vec.getLength() < maxLength && !warningAdded) { + RError.warning(RError.Message.ARG_RECYCYLED); + warningAdded = true; + } + + String current = vec.getDataAt(ind % vec.getLength()); + na.enable(current); + if (na.check(current)) { + if (naRm == RRuntime.LOGICAL_TRUE) { + // skip NA-s + continue; + } else { + data[ind] = RRuntime.STRING_NA; + return warningAdded; + } + } else { + result = op.op(result, current); + } + } + data[ind] = result; + return warningAdded; + } + + @Specialization(guards = "isStringPrecedence") + protected RStringVector pMinMaxString(VirtualFrame frame, byte naRm, RArgsValuesAndNames args) { + int maxLength = convertToVectorAndEnableNACheck(frame, args, getStringCastNode()); + if (lengthProfile.profile(maxLength == 0)) { + return RDataFactory.createEmptyStringVector(); + } else { + String[] data = new String[maxLength]; + Object[] argValues = args.getValues(); + boolean warningAdded = false; + for (int i = 0; i < maxLength; i++) { + warningAdded = doStringVectorMultiElem(argValues, naRm, 0, i, maxLength, warningAdded, data); + } + return RDataFactory.createStringVector(data, na.neverSeenNA() || naRm == RRuntime.LOGICAL_TRUE); + } + } @SuppressWarnings("unused") @Specialization(guards = "isComplexPrecedence") diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java index ebdfb19762d71002a4aa9aede6419ed713b75532..816f7b1a582cc451b2748b5ab326378d919de53f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java @@ -343,7 +343,7 @@ public abstract class UnaryArithmeticReduceNode extends UnaryNode { @SlowPath private String doStringVectorMultiElem(RStringVector operand, byte naRm, int offset) { - String result = operand.getDataAt(0); + String result = operand.getDataAt(offset); na.enable(result); if (naRm == RRuntime.LOGICAL_TRUE) { if (na.check(result)) { @@ -362,7 +362,7 @@ public abstract class UnaryArithmeticReduceNode extends UnaryNode { } // when we reach here, it means that we have already seen one non-NA element assert !RRuntime.isNA(result); - for (int i = 1; i < operand.getLength(); ++i) { + for (int i = offset + 1; i < operand.getLength(); ++i) { String current = operand.getDataAt(i); na.enable(current); if (na.check(current)) { 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 91d4ff05c9223c12fca304eb14b542a348f964b5..3dcc90774a0b10c9397c695e04c22ee4f8e75e33 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 @@ -9028,6 +9028,14 @@ Error in max(42 + (0+42i), 7 + (0+7i)) : Warning message: In max(NULL) : no non-missing arguments to max; returning -Inf +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testMaximum +#{ max(as.character(NA), as.character(NA), "42", "7", na.rm=TRUE) } +[1] "7" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testMaximum +#{ max(as.character(NA), as.character(NA), "42", na.rm=TRUE) } +[1] "42" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testMaximum #{ max(as.character(NA), as.character(NA), na.rm=FALSE) } [1] NA @@ -9822,6 +9830,37 @@ Error in pmax(7 + (0+42i)) : invalid input type #{ pmax(as.raw(42)) } Error in pmax(as.raw(42)) : invalid input type +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", "7"), c("42", "1")) } +[1] "42" "7" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", "7"), c("42", as.character(NA))) } +[1] "42" NA + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", "7"), c("42", as.character(NA)), na.rm=TRUE) } +[1] "42" "7" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", "7"), character()) } +character(0) + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", "7", "8"), c("1"), c("42", "1")) } +[1] "42" "7" "8" +Warning message: +In pmax(c("1", "7", "8"), c("1"), c("42", "1")) : + an argument will be fractionally recycled + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", as.character(NA)), c("42", "1"), na.rm=TRUE) } +[1] "42" "1" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax +#{ pmax(c("1", as.character(NA)), c(as.character(NA), as.character(NA)), c("42", "1"), na.rm=TRUE) } +[1] "42" "1" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMax #{ pmax(c(1, 7), c(42, 1)) } [1] 42 7 @@ -9888,6 +9927,37 @@ Error in pmin(7 + (0+42i)) : invalid input type #{ pmin(as.raw(42)) } Error in pmin(as.raw(42)) : invalid input type +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", "7"), c("42", "1")) } +[1] "1" "1" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", "7"), c("42", as.character(NA))) } +[1] "1" NA + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", "7"), c("42", as.character(NA)), na.rm=TRUE) } +[1] "1" "7" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", "7"), character()) } +character(0) + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", "7", "8"), c("1"), c("42", "1")) } +[1] "1" "1" "1" +Warning message: +In pmin(c("1", "7", "8"), c("1"), c("42", "1")) : + an argument will be fractionally recycled + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", as.character(NA)), c("42", "1"), na.rm=TRUE) } +[1] "1" "1" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin +#{ pmin(c("1", as.character(NA)), c(as.character(NA), as.character(NA)), c("42", "1"), na.rm=TRUE) } +[1] "1" "1" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testPMin #{ pmin(c(1, 7), c(42, 1)) } [1] 1 1 diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java index afc37847d9dd9f63951c2bceae372e0dcb4fd685..2c3e6892a33403aff6f367080be47b3b11e913a1 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java @@ -10123,6 +10123,16 @@ public class AllTests extends TestBase { assertEval("{ max(\"42\", as.character(NA), \"7\", na.rm=FALSE) }"); } + @Test + public void TestSimpleBuiltins_testMaximum_5e6479a2a6452ee70ccf9c41cef980b1() { + assertEval("{ max(as.character(NA), as.character(NA), \"42\", na.rm=TRUE) }"); + } + + @Test + public void TestSimpleBuiltins_testMaximum_ff3b6fdb41d5de0a5783d292d154bd0a() { + assertEval("{ max(as.character(NA), as.character(NA), \"42\", \"7\", na.rm=TRUE) }"); + } + @Test public void TestSimpleBuiltins_testMaximum_f5adf3eb65411f16da79fcd519585f41() { assertEval("{ max(123, NA, TRUE, 12, FALSE, na.rm=TRUE) }"); @@ -11133,6 +11143,36 @@ public class AllTests extends TestBase { assertEval("{ pmax(c(1, 7), c(42, as.double(NA)), na.rm=TRUE) }"); } + @Test + public void TestSimpleBuiltins_testPMax_0ad681abd0347fc3f329a5de4446750e() { + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", \"1\")) }"); + } + + @Test + public void TestSimpleBuiltins_testPMax_b03d9fc7b1b74e088e80e50d516b1e27() { + assertEval("{ pmax(c(\"1\", \"7\"), character()) }"); + } + + @Test + public void TestSimpleBuiltins_testPMax_280f0aa34b8a627b370c19b2685fcdef() { + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", as.character(NA))) }"); + } + + @Test + public void TestSimpleBuiltins_testPMax_bad8ae5eb0c03b007b5430b14b0d1c96() { + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", as.character(NA)), na.rm=TRUE) }"); + } + + @Test + public void TestSimpleBuiltins_testPMax_4a39be88715bd7849ef8a84628a2bfbd() { + assertEval("{ pmax(c(\"1\", as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + } + + @Test + public void TestSimpleBuiltins_testPMax_ef454dbb5881c936173763ac86ab6816() { + assertEval("{ pmax(c(\"1\", as.character(NA)), c(as.character(NA), as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + } + @Test public void TestSimpleBuiltins_testPMax_835849fdf0d1b6da7aa2e34dd879e955() { assertEval("{ pmax(c(FALSE, TRUE), c(TRUE, FALSE)) }"); @@ -11168,6 +11208,11 @@ public class AllTests extends TestBase { assertEvalWarning("{ pmax(c(1, 7, 8), c(1), c(42, 1)) }"); } + @Test + public void TestSimpleBuiltins_testPMax_7e7482b7816a663ffb8ae05e759fc8c2() { + assertEvalWarning("{ pmax(c(\"1\", \"7\", \"8\"), c(\"1\"), c(\"42\", \"1\")) }"); + } + @Test public void TestSimpleBuiltins_testPMin_ed673aa55e7a1c2a8d40c87bcb4e1ada() { assertEval("{ pmin(c(1L, 7L), c(42L, 1L)) }"); @@ -11208,6 +11253,36 @@ public class AllTests extends TestBase { assertEval("{ pmin(c(1, 7), c(42, as.double(NA)), na.rm=TRUE) }"); } + @Test + public void TestSimpleBuiltins_testPMin_cba510383b0da53141c20db44e4130cb() { + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", \"1\")) }"); + } + + @Test + public void TestSimpleBuiltins_testPMin_6c548f27b81ae28280508da7fb1d59d0() { + assertEval("{ pmin(c(\"1\", \"7\"), character()) }"); + } + + @Test + public void TestSimpleBuiltins_testPMin_80e247d9adaff94718babba7fd798c59() { + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", as.character(NA))) }"); + } + + @Test + public void TestSimpleBuiltins_testPMin_9370e766db410333698ba931ad44da9b() { + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", as.character(NA)), na.rm=TRUE) }"); + } + + @Test + public void TestSimpleBuiltins_testPMin_9f6669bbf3ddb91ddbbb94b07b857347() { + assertEval("{ pmin(c(\"1\", as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + } + + @Test + public void TestSimpleBuiltins_testPMin_046977a3522a36099d402b3c7dd515cd() { + assertEval("{ pmin(c(\"1\", as.character(NA)), c(as.character(NA), as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + } + @Test public void TestSimpleBuiltins_testPMin_af8dffea1ee168a9cdae131b7ac14b57() { assertEval("{ pmin(c(FALSE, TRUE), c(TRUE, FALSE)) }"); @@ -11243,6 +11318,11 @@ public class AllTests extends TestBase { assertEvalWarning("{ pmin(c(1, 7, 8), c(1), c(42, 1)) }"); } + @Test + public void TestSimpleBuiltins_testPMin_c56e1872347158ba0ec8a2002fec7de9() { + assertEvalWarning("{ pmin(c(\"1\", \"7\", \"8\"), c(\"1\"), c(\"42\", \"1\")) }"); + } + @Test public void TestSimpleBuiltins_testParen_499acebd19ac76555ed92ca7ecc3ec53() { assertEval("{ a = array(1,c(3,3,3)); (a[1,2,3] = 3) }"); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java index 4b3895c0806b0ab9b84b6589ef6d4061e2febdb5..33ab8e03d5e629b52e54ee78eb726134666ccf63 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java @@ -32,6 +32,14 @@ public class TestSimpleBuiltins extends TestBase { assertEval("{ pmax(c(1, 7), c(42, as.double(NA))) }"); assertEval("{ pmax(c(1, 7), c(42, as.double(NA)), na.rm=TRUE) }"); + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", \"1\")) }"); + assertEval("{ pmax(c(\"1\", \"7\"), character()) }"); + assertEvalWarning("{ pmax(c(\"1\", \"7\", \"8\"), c(\"1\"), c(\"42\", \"1\")) }"); + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", as.character(NA))) }"); + assertEval("{ pmax(c(\"1\", \"7\"), c(\"42\", as.character(NA)), na.rm=TRUE) }"); + assertEval("{ pmax(c(\"1\", as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + assertEval("{ pmax(c(\"1\", as.character(NA)), c(as.character(NA), as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + assertEval("{ pmax(c(FALSE, TRUE), c(TRUE, FALSE)) }"); assertEval("{ pmax(c(FALSE, TRUE), logical()) }"); assertEval("{ pmax(c(FALSE, TRUE), c(FALSE, NA)) }"); @@ -54,6 +62,14 @@ public class TestSimpleBuiltins extends TestBase { assertEval("{ pmin(c(1, 7), c(42, as.double(NA))) }"); assertEval("{ pmin(c(1, 7), c(42, as.double(NA)), na.rm=TRUE) }"); + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", \"1\")) }"); + assertEval("{ pmin(c(\"1\", \"7\"), character()) }"); + assertEvalWarning("{ pmin(c(\"1\", \"7\", \"8\"), c(\"1\"), c(\"42\", \"1\")) }"); + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", as.character(NA))) }"); + assertEval("{ pmin(c(\"1\", \"7\"), c(\"42\", as.character(NA)), na.rm=TRUE) }"); + assertEval("{ pmin(c(\"1\", as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + assertEval("{ pmin(c(\"1\", as.character(NA)), c(as.character(NA), as.character(NA)), c(\"42\", \"1\"), na.rm=TRUE) }"); + assertEval("{ pmin(c(FALSE, TRUE), c(TRUE, FALSE)) }"); assertEval("{ pmin(c(FALSE, TRUE), logical()) }"); assertEval("{ pmin(c(FALSE, TRUE), c(FALSE, NA)) }"); @@ -263,6 +279,9 @@ public class TestSimpleBuiltins extends TestBase { assertEval("{ max(\"42\", as.character(NA), \"7\", na.rm=TRUE) }"); assertEval("{ max(\"42\", as.character(NA), \"7\", na.rm=FALSE) }"); + assertEval("{ max(as.character(NA), as.character(NA), \"42\", na.rm=TRUE) }"); + assertEval("{ max(as.character(NA), as.character(NA), \"42\", \"7\", na.rm=TRUE) }"); + assertEval("{ max(123, NA, TRUE, 12, FALSE, na.rm=TRUE) }"); assertEval("{ max(123, NA, TRUE, 12, FALSE, na.rm=FALSE) }"); assertEval("{ max(123, NA, TRUE, 12, FALSE) }");