diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R index e0ebabafddce019be410544ca68bb4ccc6a5cf70..c3b62475f676a8d386a147e273b64f69e1201fa9 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/model.R @@ -494,21 +494,29 @@ TermCode <- function(formula, callIdx, varIndex) { } # gets the formula as parameter and returns the same formula, where -# dot symbol is replaced with (a+b+c+...) where a,b,c.. are framenames. -ExpandDots <- function(x) { +# the dot symbol is replaced with the 'replacement' +replaceDots <- function(x, replacement) { if (is.symbol(x)) { if (identical(x, quote(`.`))) { - return(parse(text=paste(framenames, collapse="+"))[[1]]) + return(replacement) } return(x) } for (i in seq_along(x)) { - x[[i]] <- ExpandDots(x[[i]]); + x[[i]] <- replaceDots(x[[i]], replacement); } x } +# gets the formula as parameter and returns the same formula, where +# the dot symbol is replaced with (a+b+c+...) where a,b,c.. are framenames. +# Note: this version is not equivalent of the GnuR C version as it expects +# variable names as the second argument. replaceDots is closer to the GnuR C version. +ExpandDots <- function(x, framenames) { + replaceDots(x, parse(text=paste(framenames, collapse="+"))[[1]]); +} + # PUBLIC: termsform # @@ -666,7 +674,7 @@ termsform <- function (x, specials, data, keep.order, allowDotAsName) { # Step 6: Fix up the formula by substituting for dot, which should be # the framenames joined by + if (haveDot) { - x <- ExpandDots(x) + x <- ExpandDots(x, framenames) } attr(x, "order") <- ord @@ -1247,6 +1255,43 @@ modelmatrix <- function(formula, modelframe) { x } +# ============================================================= +# Implementation of updateform +# PUBLIC updateform +# +# Replaces all occurences of dot symbol '.' in the lsh/rhs of 'new' formula with +# lhs/rhs of the 'old' formula. The result is stripped off of all attributes and +# gets .Environment attribute from the 'old' formula. +updateform <- function(old, new) { + is.formula <- function (x) is.language(x) && x[[1L]] == quote(`~`); + if (!is.formula(old) || !is.formula(new)) { + error("formula expected") + } + if (length(old) == 3) { + lhs <- old[[2L]] + rhs <- old[[3L]] + # We now check that new formula has a valid lhs. If it doesn't, + # we add one and set it to the rhs of the old formula. + if (length(new) == 2) { + tmp <- new[[2L]] + new[[2L]] <- lhs + new[[3L]] <- tmp + } + # Note: we are probably getting away with not parenthesizing thanks to R + new[[2L]] <- replaceDots(new[[2L]], lhs) + new[[3L]] <- replaceDots(new[[3L]], rhs) + } else { + # the old formula has no lhs, we only expand rhs + if (length(new) == 3) { + new[[3L]] <- replaceDots(new[[3L]], old[[2L]]) + } else { + new[[2L]] <- replaceDots(new[[2L]], old[[2L]]) + } + } + attributes(new) <- NULL + attr(new, '.Environment') <- attr(old, '.Environment') + new +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java index 58e6992ce24d022fa4076ff2641d538567d5afba..4452a2d5b3060ece2953d3eabba624407d443a33 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java @@ -404,7 +404,6 @@ public class ForeignFunctions { case "binomial_dev_resids": case "rWishart": case "Cdist": - case "updateform": case "mvfft": case "nextn": case "r2dtable": @@ -446,8 +445,12 @@ public class ForeignFunctions { case "d2x2xk": return new UnimplementedExternal(name); + case "updateform": + return getExternalModelBuiltinNode("updateform"); + case "Cdqrls": return new RInternalCodeBuiltinNode(RContext.getInstance(), "stats", RInternalCode.loadSourceRelativeTo(StatsUtil.class, "lm.R"), "Cdqrls"); + case "dnorm": return StatsFunctionsFactory.Function3_1NodeGen.create(new Dnorm4()); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java index cf4cdca50cb5a10f779dbdcbcef25a57ed40dda5..f1b6e6148446abd8e77b68c416aebae36269d46e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java @@ -61,6 +61,9 @@ public final class RInternalCodeBuiltinNode extends RExternalBuiltinNode { CompilerDirectives.transferToInterpreterAndInvalidate(); RInternalCode internalCode = RInternalCode.lookup(context, basePackage, code); this.function = internalCode.lookupFunction(functionName); + if (this.function == null) { + throw RInternalError.shouldNotReachHere("Could not load RInternalCodeBuiltin function '" + functionName + "'."); + } } return call.execute(frame, actualArgs.getSignature(), actualArgs.getArguments(), function, functionName, null); 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 2f39c38c369eff2a241461fe81b3f3058fc9b96e..4ac9392678c23e2ee8dd10d7292da68a79e0d4a5 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 @@ -111569,6 +111569,42 @@ In qnorm(c(0.1, 0.9, 0.5, 1.00001, 0.99999), 100, c(20, 1)) : NaNs produced #{ round(100*sd(c(1,2))^2) } [1] 50 +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(. ~ u+v, log(.) ~ .:q) +log(.) ~ u:q + v:q + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(. ~ u+v, x + . ~ y:. + log(.)) +x + . ~ log(u + v) + y:u + y:v + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(. ~ u+v, ~ . + x2) +. ~ u + v + x2 + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ u+v, log(.) ~ .:q) +log(x) ~ u:q + v:q + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ u+v, x + . ~ y:. + log(.)) +x + x ~ log(u + v) + y:u + y:v + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ u+v, ~ . + x2) +x ~ u + v + x2 + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ y, log(.) ~ .:q) +log(x) ~ y:q + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ y, x + . ~ y:. + log(.)) +x + x ~ y + log(y) + +##com.oracle.truffle.r.test.library.stats.TestUpdateForm.basicTests +#update.formula(x ~ y, ~ . + x2) +x ~ y + x2 + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testInvalidName #f <- function(x) {<<<NEWLINE>>> `123t` <- x + 1<<<NEWLINE>>> print(`123t`)<<<NEWLINE>>> `123t`}<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>>n<<<NEWLINE>>>`123t`<<<NEWLINE>>>n<<<NEWLINE>>>n debugging in: f(5) diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestUpdateForm.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestUpdateForm.java new file mode 100644 index 0000000000000000000000000000000000000000..03fc6a7f434da9914635d112ba88fa96a7193891 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestUpdateForm.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.test.library.stats; + +import org.junit.Test; + +import com.oracle.truffle.r.test.TestBase; + +/** + * Tests the updateform external function called from R function update.formula, in FastR this + * function is implemented in R code. See file modelTests.R in the same directory for pure R tests + * testing only the FastR R code without the R wrappers from GnuR. When adding new test cases here, + * consider adding them to modelTests.R too. + */ +public class TestUpdateForm extends TestBase { + private static final String[] OLD_FORMULAE = new String[]{"x ~ y", ". ~ u+v", "x ~ u+v"}; + private static final String[] NEW_FORMULAE = new String[]{"~ . + x2", "log(.) ~ .:q", "x + . ~ y:. + log(.)"}; + + @Test + public void basicTests() { + assertEval(template("update.formula(%0, %1)", OLD_FORMULAE, NEW_FORMULAE)); + } +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R index 6fad3bc8dbd278793259a4daa352a4f4e6a0e757..b53b5e876fb411fe0535773b6b2e3d9a19afad4e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/modelTests.R @@ -47,10 +47,11 @@ modelmatrixdefault <- eval(parse(text=body)) # check function compares the results check <- function(expected, actual, name) { if (try(identical(expected, actual)) != TRUE) { - cat(name, ": FAIL\n") + cat(name, ": FAIL expected:\n\n") print(expected) cat("\n>>>>>>>>>actual:\n\n") print(actual) + cat("\n-------------\n") } else { cat(name, ": OK\n") } } @@ -120,3 +121,27 @@ check(terms.formula(t, data=mtcars), termsform(t, NULL, mtcars, FALSE, FALSE), " t <- cyl~mufun(mpg)+. print(t) check(terms.formula(t, specials=c('myfun'), data=mtcars), termsform(t, c('myfun'), mtcars, FALSE, FALSE), "termsform with specials and expandDots") + + +# ------------------------------------ +# tests for update formula + +body <- deparse(update.formula) +idx <- which(grepl(".Call", body)) +body[[idx]] <- gsub("C_updateform,", "", gsub(".Call", "updateform", body[[idx]])) +updateformula <- eval(parse(text=body)) + +test.update.formula <- function(old, new) { + print(old); + print(new); + check(update.formula(old, new), updateformula(old, new), "update.formula test") +} + +test.update.formula(y ~ x, ~ . + x2) +test.update.formula(y ~ x, log(.) ~ . ) +test.update.formula(. ~ u+v, res ~ . ) +test.update.formula(~ u+v, res ~ . ) +test.update.formula(~ u+v, ~ . ) +test.update.formula(~ u+v, . ~ . ) +test.update.formula(~ u+v, ~ x*. ) +test.update.formula(~ u+v, ~ x:. )