From b17d4f2c3876e0789953b52dd0fc264fc6b98447 Mon Sep 17 00:00:00 2001
From: Adam Welc <adam.welc@oracle.com>
Date: Thu, 6 Nov 2014 11:55:57 +0100
Subject: [PATCH] Factors are now represented by a separate internal type.

---
 .../r/nodes/builtin/base/AsCharacter.java     |  34 +++++
 .../r/nodes/builtin/base/AsInteger.java       |   7 +
 .../nodes/builtin/base/DynLoadFunctions.java  |   4 +-
 .../r/nodes/builtin/base/Inherits.java        |   6 +-
 .../r/nodes/builtin/base/LaFunctions.java     |   2 +-
 .../r/nodes/builtin/base/Tabulate.java        |   8 +-
 .../truffle/r/nodes/builtin/base/UnClass.java |  17 ++-
 .../r/nodes/builtin/base/UpdateAttr.java      |  12 +-
 .../nodes/builtin/base/UpdateAttributes.java  |   2 +-
 .../r/nodes/builtin/base/UpdateClass.java     |   7 +-
 .../r/nodes/builtin/base/UpdateLevels.java    |  19 ++-
 .../r/nodes/builtin/base/UpdateOldClass.java  |   4 +-
 .../oracle/truffle/r/nodes/RProxyNode.java    |  13 ++
 .../com/oracle/truffle/r/nodes/RTypes.java    |   4 +-
 .../nodes/access/array/ArrayPositionCast.java |   5 +
 .../r/nodes/function/WrapArgumentNode.java    |   6 +
 .../r/nodes/unary/CastIntegerNode.java        |   5 +
 .../truffle/r/nodes/unary/InheritsNode.java   |   4 +-
 .../truffle/r/nodes/unary/PrecedenceNode.java |   5 +
 .../truffle/r/nodes/unary/TypeofNode.java     |  10 ++
 .../com/oracle/truffle/r/runtime/RError.java  |   3 +-
 .../oracle/truffle/r/runtime/RRuntime.java    |   1 +
 .../oracle/truffle/r/runtime/RSerialize.java  |   2 +-
 .../truffle/r/runtime/data/RDataFactory.java  |   4 +
 .../truffle/r/runtime/data/RDataFrame.java    |   2 +-
 .../truffle/r/runtime/data/RFactor.java       | 144 ++++++++++++++++++
 .../truffle/r/runtime/data/RVector.java       |  19 ++-
 .../truffle/r/test/ExpectedTestOutput.test    |  38 +++++
 .../oracle/truffle/r/test/all/AllTests.java   |  45 ++++++
 .../r/test/simple/TestSimpleBuiltins.java     |  12 ++
 30 files changed, 410 insertions(+), 34 deletions(-)
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
index e8f4dc36a2..b841764a4e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
@@ -24,9 +24,12 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
+import java.util.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.Node.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.unary.*;
@@ -40,6 +43,7 @@ public abstract class AsCharacter extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
     @Child private DispatchedCallNode dcn;
+    @Child private CastToVectorNode castVector;
 
     private void initCast() {
         if (castStringNode == null) {
@@ -48,6 +52,14 @@ public abstract class AsCharacter extends RBuiltinNode {
         }
     }
 
+    private RAbstractVector castVector(VirtualFrame frame, Object value) {
+        if (castVector == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castVector = insert(CastToVectorNodeFactory.create(null, false, false, false, false));
+        }
+        return (RAbstractVector) castVector.executeObject(frame, value);
+    }
+
     private String castString(VirtualFrame frame, int o) {
         initCast();
         return (String) castStringNode.executeString(frame, o);
@@ -141,6 +153,28 @@ public abstract class AsCharacter extends RBuiltinNode {
         }
     }
 
+    // TODO: this shold be handled by a generic function
+    @Specialization
+    protected Object doFactor(VirtualFrame frame, RFactor value) {
+        controlVisibility();
+        Object attr = value.getVector().getAttr(RRuntime.LEVELS_ATTR_KEY);
+        if (attr == null) {
+            return RNull.instance;
+        } else {
+            RAbstractStringVector vec = (RAbstractStringVector) castVector(frame, attr);
+            String[] data = new String[value.getLength()];
+            if (vec.getLength() == 0) {
+                Arrays.fill(data, RRuntime.STRING_NA);
+                return RDataFactory.createStringVector(data, RDataFactory.INCOMPLETE_VECTOR);
+            } else {
+                for (int i = 0; i < data.length; i++) {
+                    data[i] = vec.getDataAt(value.getVector().getDataAt(i) - 1);
+                }
+                return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
+            }
+        }
+    }
+
     protected boolean isObject(VirtualFrame frame, RAbstractVector vector) {
         return vector.isObject();
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
index 8febf62b73..ff874767f2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
@@ -130,4 +130,11 @@ public abstract class AsInteger extends RBuiltinNode {
         controlVisibility();
         return castIntVector(frame, vector);
     }
+
+    @Specialization
+    protected RIntVector asInteger(RFactor factor) {
+        controlVisibility();
+        return factor.getVector();
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
index dcd4e03591..a881407971 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
@@ -100,14 +100,14 @@ public class DynLoadFunctions {
                 data[i] = dllInfo;
             }
             RList result = RDataFactory.createList(data, RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR));
-            RVector.setClassAttr(result, RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS), null);
+            RVector.setClassAttr(result, RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS), null, null);
             return result;
         }
     }
 
     private static RList createDLLInfoList(Object[] data) {
         RList dllInfo = RDataFactory.createList(data, RDataFactory.createStringVector(DLLInfo.NAMES, RDataFactory.COMPLETE_VECTOR));
-        RVector.setClassAttr(dllInfo, RDataFactory.createStringVectorFromScalar(DLLINFO_CLASS), null);
+        RVector.setClassAttr(dllInfo, RDataFactory.createStringVectorFromScalar(DLLINFO_CLASS), null, null);
         return dllInfo;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
index d5d2b80f68..254cdf49e3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
@@ -58,19 +58,19 @@ public abstract class Inherits extends RBuiltinNode {
     }
 
     @SuppressWarnings("unused")
-    public boolean whichFalse(RAbstractVector x, RAbstractStringVector what, byte which) {
+    public boolean whichFalse(RAbstractContainer x, RAbstractStringVector what, byte which) {
         return which != RRuntime.LOGICAL_TRUE;
     }
 
     @Specialization(guards = "whichFalse")
-    protected byte doInherits(VirtualFrame frame, RAbstractVector x, RAbstractStringVector what, @SuppressWarnings("unused") byte which) {
+    protected byte doInherits(VirtualFrame frame, RAbstractContainer x, RAbstractStringVector what, @SuppressWarnings("unused") byte which) {
         return initInheritsNode().execute(frame, x, what);
     }
 
     @TruffleBoundary
     // map operations lead to recursion resulting in compilation failure
     @Specialization(guards = "!whichFalse")
-    protected Object doesInherit(RAbstractVector x, RAbstractStringVector what, @SuppressWarnings("unused") byte which) {
+    protected Object doesInherit(RAbstractContainer x, RAbstractStringVector what, @SuppressWarnings("unused") byte which) {
         Map<String, Integer> classToPos = InheritsNode.initClassToPos(x);
         int[] result = new int[what.getLength()];
         for (int i = 0; i < what.getLength(); ++i) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
index bbc832a386..efa2d560ff 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
@@ -314,7 +314,7 @@ public class LaFunctions {
             RDoubleVector modulusVec = RDataFactory.createDoubleVectorFromScalar(modulus);
             modulusVec.setAttr("logarithm", useLogIn);
             RList result = RDataFactory.createList(new Object[]{modulusVec, sign}, NAMES_VECTOR);
-            RList.setClassAttr(result, DET_CLASS, null);
+            RList.setClassAttr(result, DET_CLASS, null, null);
             return result;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
index 06a8471fff..8a03dfbcab 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
@@ -50,6 +50,12 @@ public abstract class Tabulate extends RBuiltinNode {
         return RDataFactory.createIntVector(ans, RDataFactory.COMPLETE_VECTOR);
     }
 
+    @Specialization(guards = {"isValidNBin"})
+    @TruffleBoundary
+    public RIntVector tabulate(RFactor bin, int nBins) {
+        return tabulate(bin.getVector(), nBins);
+    }
+
     @SuppressWarnings("unused")
     @Specialization
     public RIntVector tabulate(Object bin, int nBins) {
@@ -57,7 +63,7 @@ public abstract class Tabulate extends RBuiltinNode {
         throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_INPUT);
     }
 
-    protected boolean isValidNBin(@SuppressWarnings("unused") RAbstractIntVector bin, int nBins) {
+    protected boolean isValidNBin(@SuppressWarnings("unused") RAbstractContainer bin, int nBins) {
         if (RRuntime.isNA(nBins) || nBins < 0) {
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "nbin");
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
index 0af23a5b2e..eb31cd844a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
@@ -15,8 +15,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.utilities.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RDataFrame;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -36,7 +35,7 @@ public abstract class UnClass extends RBuiltinNode {
             if (resultVector.isShared()) {
                 resultVector = resultVector.copy();
             }
-            return RVector.setClassAttr(resultVector, null, null);
+            return RVector.setClassAttr(resultVector, null, null, null);
         }
         return arg;
     }
@@ -49,7 +48,17 @@ public abstract class UnClass extends RBuiltinNode {
         if (resultFrame.isShared()) {
             resultFrame = resultFrame.copy();
         }
-        return RVector.setClassAttr(resultFrame.getVector(), null, arg);
+        return RVector.setClassAttr(resultFrame.getVector(), null, arg, null);
     }
 
+    @Specialization
+    @TruffleBoundary
+    protected Object unClass(RFactor arg) {
+        controlVisibility();
+        RFactor resultFrame = arg;
+        if (resultFrame.isShared()) {
+            resultFrame = resultFrame.copy();
+        }
+        return RVector.setClassAttr(resultFrame.getVector(), null, null, arg);
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index 2fd0112738..69d3d49b10 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -99,23 +99,25 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode {
         } else if (name.equals(RRuntime.DIMNAMES_ATTR_KEY)) {
             return updateDimNames(frame, resultVector, value);
         } else if (name.equals(RRuntime.CLASS_ATTR_KEY)) {
-            return RVector.setClassAttr(resultVector, null, container.getElementClass() == RVector.class ? container : null);
+            return RVector.setClassAttr(resultVector, null, container.getElementClass() == RDataFrame.class ? container : null, container.getElementClass() == RFactor.class ? container : null);
         } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
             resultVector.setRowNames(null);
         } else if (resultVector.getAttributes() != null) {
             resultVector.getAttributes().remove(name);
         }
         // return frame if it's one, otherwise return the vector
-        return container.getElementClass() == RVector.class ? container : resultVector;
+        return container.getElementClass() == RDataFrame.class ? container : resultVector;
     }
 
     @TruffleBoundary
     public static RAbstractContainer setClassAttrFromObject(RVector resultVector, RAbstractContainer container, Object value, SourceSection sourceSection) {
         if (value instanceof RStringVector) {
-            return RVector.setClassAttr(resultVector, (RStringVector) value, container.getElementClass() == RVector.class ? container : null);
+            return RVector.setClassAttr(resultVector, (RStringVector) value, container.getElementClass() == RDataFrame.class ? container : null,
+                            container.getElementClass() == RFactor.class ? container : null);
         }
         if (value instanceof String) {
-            return RVector.setClassAttr(resultVector, RDataFactory.createStringVector((String) value), container.getElementClass() == RVector.class ? container : null);
+            return RVector.setClassAttr(resultVector, RDataFactory.createStringVector((String) value), container.getElementClass() == RDataFrame.class ? container : null,
+                            container.getElementClass() == RFactor.class ? container : null);
         }
         throw RError.error(sourceSection, RError.Message.SET_INVALID_CLASS_ATTR);
     }
@@ -143,7 +145,7 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode {
             resultVector.setAttr(name, value);
         }
         // return frame if it's one, otherwise return the vector
-        return container.getElementClass() == RVector.class ? container : resultVector;
+        return container.getElementClass() == RDataFrame.class ? container : resultVector;
     }
 
     @Specialization(guards = "!nullValue")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
index 467b7c5017..2dac8d6ef4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
@@ -174,7 +174,7 @@ public abstract class UpdateAttributes extends RInvisibleBuiltinNode {
                 }
             } else if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
                 if (value == RNull.instance) {
-                    RVector.setClassAttr(resultVector, null, container.getElementClass() == RVector.class ? container : null);
+                    RVector.setClassAttr(resultVector, null, container.getElementClass() == RDataFrame.class ? container : null, container.getElementClass() == RFactor.class ? container : null);
                 } else {
                     UpdateAttr.setClassAttrFromObject(resultVector, container, value, getEncapsulatingSourceSection());
                 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
index c41ff936aa..b3675f366d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -61,7 +61,7 @@ public abstract class UpdateClass extends RBuiltinNode {
     protected Object setClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         controlVisibility();
         RVector resultVector = arg.materializeNonSharedVector();
-        return RVector.setClassAttr(resultVector, null, arg.getElementClass() == RVector.class ? arg : null);
+        return RVector.setClassAttr(resultVector, null, arg.getElementClass() == RDataFrame.class ? arg : null, arg.getElementClass() == RFactor.class ? arg : null);
     }
 
     @Specialization
@@ -102,7 +102,8 @@ public abstract class UpdateClass extends RBuiltinNode {
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.NOT_ARRAY_UPDATE_CLASS);
         }
 
-        return RVector.setClassAttr(resultVector, RDataFactory.createStringVector(className), arg.getElementClass() == RVector.class ? arg : null);
+        return RVector.setClassAttr(resultVector, RDataFactory.createStringVector(className), arg.getElementClass() == RDataFrame.class ? arg : null, arg.getElementClass() == RFactor.class ? arg
+                        : null);
     }
 
     @Specialization
@@ -110,7 +111,7 @@ public abstract class UpdateClass extends RBuiltinNode {
     protected Object setClass(RAbstractContainer arg, RStringVector className) {
         controlVisibility();
         RVector resultVector = arg.materializeNonSharedVector();
-        return RVector.setClassAttr(resultVector, className, arg.getElementClass() == RVector.class ? arg : null);
+        return RVector.setClassAttr(resultVector, className, arg.getElementClass() == RDataFrame.class ? arg : null, arg.getElementClass() == RFactor.class ? arg : null);
     }
 
     public Object setClass(RFunction arg, @SuppressWarnings("unused") Object className) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
index 9a3c1b075e..b39f7f345d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
@@ -15,8 +15,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -41,6 +40,22 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode {
         RVector v = vector.materialize();
         v.setLevels(levels);
         return v;
+    }
+
+    @Specialization
+    @TruffleBoundary
+    protected RFactor updateLevels(RFactor factor, @SuppressWarnings("unused") RNull levels) {
+        controlVisibility();
+        factor.getVector().setLevels(null);
+        return factor;
+    }
 
+    @Specialization
+    @TruffleBoundary
+    protected RFactor updateLevels(RFactor factor, Object levels) {
+        controlVisibility();
+        factor.getVector().setLevels(levels);
+        return factor;
     }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
index 65dc054bb3..c9d49853b9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
@@ -76,7 +76,7 @@ public abstract class UpdateOldClass extends RInvisibleBuiltinNode {
     protected Object setOldClass(RAbstractContainer arg, RStringVector className) {
         controlVisibility();
         RVector resultVector = arg.materializeNonSharedVector();
-        return RVector.setClassAttr(resultVector, className, arg.getElementClass() == RVector.class ? arg : null);
+        return RVector.setClassAttr(resultVector, className, arg.getElementClass() == RDataFrame.class ? arg : null, arg.getElementClass() == RFactor.class ? arg : null);
     }
 
     @Specialization
@@ -84,7 +84,7 @@ public abstract class UpdateOldClass extends RInvisibleBuiltinNode {
     protected Object setOldClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         controlVisibility();
         RVector resultVector = arg.materializeNonSharedVector();
-        return RVector.setClassAttr(resultVector, null, arg.getElementClass() == RVector.class ? arg : null);
+        return RVector.setClassAttr(resultVector, null, arg.getElementClass() == RDataFrame.class ? arg : null, arg.getElementClass() == RFactor.class ? arg : null);
     }
 
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java
index 5e3cdef138..335b598f5f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java
@@ -46,6 +46,10 @@ public abstract class RProxyNode extends RNode {
         return dataFrame;
     }
 
+    protected RFactor proxyFactor(RFactor factor) {
+        return factor;
+    }
+
     @Specialization
     protected RNull wrap(RNull x) {
         return proxy(x);
@@ -208,6 +212,15 @@ public abstract class RProxyNode extends RNode {
         return proxyDataFrame(x);
     }
 
+    @Specialization
+    protected RFactor wrap(RFactor x) {
+        return proxy(x);
+    }
+
+    protected RFactor proxy(RFactor x) {
+        return proxyFactor(x);
+    }
+
     @Specialization
     protected RMissing wrap(RMissing x) {
         return proxy(x);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java
index d0821bb765..06d4f6a547 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java
@@ -40,8 +40,8 @@ import com.oracle.truffle.r.runtime.env.*;
 @TypeSystem({boolean.class, byte.class, int.class, double.class, RRaw.class, RComplex.class, String.class, RIntSequence.class, RDoubleSequence.class, RIntVector.class, RDoubleVector.class,
                 RRawVector.class, RComplexVector.class, RStringVector.class, RLogicalVector.class, RFunction.class, RNull.class, RMissing.class, REnvironment.class, RExpression.class,
                 RConnection.class, MaterializedFrame.class, FrameSlot.class, RAbstractIntVector.class, RAbstractDoubleVector.class, RAbstractLogicalVector.class, RAbstractComplexVector.class,
-                RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RDataFrame.class, RSymbol.class, RPromise.class, RLanguage.class, RPairList.class,
-                RFormula.class, RAbstractContainer.class, RArgsValuesAndNames.class, RType.class, Object[].class})
+                RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RDataFrame.class, RFactor.class, RSymbol.class, RPromise.class, RLanguage.class,
+                RPairList.class, RFormula.class, RAbstractContainer.class, RArgsValuesAndNames.class, RType.class, Object[].class})
 public class RTypes {
 
     @TypeCheck
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/ArrayPositionCast.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/ArrayPositionCast.java
index d2e24a2694..6904958bc3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/ArrayPositionCast.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/ArrayPositionCast.java
@@ -266,6 +266,11 @@ public abstract class ArrayPositionCast extends ArrayPositionsCastBase {
             return castInteger.executeCast(frame, operand);
         }
 
+        @Specialization
+        protected Object doFactor(VirtualFrame frame, RAbstractContainer container, RFactor factor) {
+            return convertOperatorRecursive(frame, container, factor.getVector());
+        }
+
         @Specialization
         protected RList doList(RAbstractContainer container, RList operand) {
             return operand;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentNode.java
index b66b6d8d0b..4ee1159015 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentNode.java
@@ -70,6 +70,12 @@ public abstract class WrapArgumentNode extends RProxyNode {
         return dataFrame;
     }
 
+    @Override
+    protected RFactor proxyFactor(RFactor factor) {
+        proxyVector(factor.getVector());
+        return factor;
+    }
+
     public abstract RNode getOperand();
 
     public static WrapArgumentNode create(RNode operand, boolean modeChange) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
index 1a903880cc..9986440352 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
@@ -412,6 +412,11 @@ public abstract class CastIntegerNode extends CastNode {
         return ret;
     }
 
+    @Specialization
+    protected RIntVector doFactor(RFactor factor) {
+        return factor.getVector();
+    }
+
     private RError cannotCoerceListError() {
         throw RError.error(this.getSourceSection(), RError.Message.LIST_COERCION, "integer");
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
index e1fc7ff143..15c0e4ee65 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
@@ -42,7 +42,7 @@ public abstract class InheritsNode extends BinaryNode {
 
     // map operations lead to recursion resulting in compilation failure
     @Specialization
-    protected Object doesInherit(RAbstractVector x, RAbstractStringVector what) {
+    protected Object doesInherit(RAbstractContainer x, RAbstractStringVector what) {
         Map<String, Integer> classToPos = initClassToPos(x);
         for (int i = 0; i < what.getLength(); ++i) {
             if (classToPos.get(what.getDataAt(i)) != null) {
@@ -53,7 +53,7 @@ public abstract class InheritsNode extends BinaryNode {
     }
 
     @TruffleBoundary
-    public static Map<String, Integer> initClassToPos(RAbstractVector x) {
+    public static Map<String, Integer> initClassToPos(RAbstractContainer x) {
         RStringVector klass = x.getClassHierarchy();
 
         // Create a mapping for elements to their respective positions
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
index 4f1f70e368..42aa8f6b56 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
@@ -161,6 +161,11 @@ public abstract class PrecedenceNode extends UnaryNode {
         return EXPRESSION_PRECEDENCE;
     }
 
+    @Specialization
+    protected int doFactor(RFactor val, byte recursive) {
+        return INT_PRECEDENCE;
+    }
+
     protected boolean isRecursive(RList val, byte recursive) {
         return recursive == RRuntime.LOGICAL_TRUE;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/TypeofNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/TypeofNode.java
index 3a58e82ef7..43c1b04356 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/TypeofNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/TypeofNode.java
@@ -162,6 +162,16 @@ public abstract class TypeofNode extends UnaryNode {
         return RType.Integer;
     }
 
+    @Specialization
+    protected RType typeof(RDataFrame frame) {
+        return RType.List;
+    }
+
+    @Specialization
+    protected RType typeof(RFactor factor) {
+        return RType.Integer;
+    }
+
     public static boolean isFunctionBuiltin(RFunction fun) {
         return fun.isBuiltin();
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index aee0e82ae3..2e7305b88d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -552,7 +552,8 @@ public final class RError extends RuntimeException {
         UNEXPECTED("unexpected '%s' in \"%s\""),
         FIRST_ELEMENT_USED("first element used of '%s' argument"),
         MUST_BE_COERCIBLE_INTEGER("argument must be coercible to non-negative integer"),
-        DEFAULT_METHOD_NOT_IMPLEMENTED_FOR_TYPE("default method not implemented for type '%s'");
+        DEFAULT_METHOD_NOT_IMPLEMENTED_FOR_TYPE("default method not implemented for type '%s'"),
+        ADDING_INVALID_CLASS("adding class \"%s\" to an invalid object");
 
         public final String message;
         private final boolean hasArgs;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
index b7e737ffc7..69f26f12cc 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
@@ -93,6 +93,7 @@ public class RRuntime {
     public static final String DIMNAMES_LIST_ELEMENT_NAME_PREFIX = "$dimnames";
 
     public static final String CLASS_ATTR_KEY = "class";
+    public static final String FACTOR_ATTR_KEY = "factor";
     public static final String PREVIOUS_ATTR_KEY = "previous";
     public static final String ROWNAMES_ATTR_KEY = "row.names";
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index 55062856a5..290652e0f5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -362,7 +362,7 @@ public class RSerialize {
                     } else if (tag.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
                         vec.setRowNames(car);
                     } else if (tag.equals(RRuntime.CLASS_ATTR_KEY)) {
-                        RVector.setClassAttr(vec, (RStringVector) car, null);
+                        RVector.setClassAttr(vec, (RStringVector) car, null, null);
                     } else {
                         vec.setAttr(tag, car);
                     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 085c73acad..c5ea07be92 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -300,6 +300,10 @@ public final class RDataFactory {
         return traceDataCreated(new RExpression(list));
     }
 
+    public static RFactor createFactor(RIntVector vector) {
+        return traceDataCreated(new RFactor(vector));
+    }
+
     public static RVector createObjectVector(Object[] data, boolean completeVector) {
         if (data.length < 1) {
             return null;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
index 234e6cd0b7..d008396f97 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
@@ -78,7 +78,7 @@ public final class RDataFrame implements RShareable, RAbstractContainer {
 
     @Override
     public Class<?> getElementClass() {
-        return RVector.class;
+        return RDataFrame.class;
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
new file mode 100644
index 0000000000..f58a701846
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2014, 2014, 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.runtime.data;
+
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public final class RFactor implements RShareable, RAbstractContainer {
+
+    private RIntVector vector;
+
+    public RFactor(RIntVector vector) {
+        this.vector = vector;
+    }
+
+    public RIntVector getVector() {
+        return vector;
+    }
+
+    @Override
+    public int getLength() {
+        return vector.getLength();
+    }
+
+    @Override
+    public void markNonTemporary() {
+        vector.markNonTemporary();
+    }
+
+    @Override
+    public boolean isTemporary() {
+        return vector.isTemporary();
+    }
+
+    @Override
+    public boolean isShared() {
+        return vector.isShared();
+    }
+
+    @Override
+    public RVector makeShared() {
+        return vector.makeShared();
+    }
+
+    @Override
+    public RFactor copy() {
+        return RDataFactory.createFactor((RIntVector) vector.copy());
+    }
+
+    @Override
+    public RAttributes getAttributes() {
+        return vector.getAttributes();
+    }
+
+    @Override
+    public int[] getDimensions() {
+        return vector.getDimensions();
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return RFactor.class;
+    }
+
+    @Override
+    public RVector materializeNonSharedVector() {
+        if (isShared()) {
+            vector = (RIntVector) vector.copy();
+            vector.markNonTemporary();
+        }
+        return vector;
+    }
+
+    @Override
+    public Object getDataAtAsObject(int index) {
+        return vector.getDataAtAsObject(index);
+    }
+
+    @Override
+    public Object getNames() {
+        return vector.getNames();
+    }
+
+    @Override
+    public RList getDimNames() {
+        return vector.getDimNames();
+    }
+
+    @Override
+    public Object getRowNames() {
+        return vector.getRowNames();
+    }
+
+    @Override
+    public RStringVector getClassHierarchy() {
+        return vector.getClassHierarchy();
+    }
+
+    @Override
+    public boolean isObject() {
+        return true;
+    }
+
+    public RAttributes initAttributes() {
+        return vector.initAttributes();
+    }
+
+    @Override
+    public RShareable materializeToShareable() {
+        return this;
+    }
+
+    public int getElementIndexByName(String name) {
+        return vector.getElementIndexByName(name);
+    }
+
+    public int getElementIndexByNameInexact(String name) {
+        return vector.getElementIndexByNameInexact(name);
+    }
+
+    public void setLevels(Object newLevels) {
+        vector.setLevels(newLevels);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index a1d351e9e2..6579249bf6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -180,7 +180,7 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV
         } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
             setRowNames(value);
         } else if (name.equals(RRuntime.CLASS_ATTR_KEY)) {
-            setClassAttr(this, (RStringVector) value, null);
+            setClassAttr(this, (RStringVector) value, null, null);
         } else {
             attributes.put(name, value);
         }
@@ -204,7 +204,7 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV
             } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
                 setRowNames(null);
             } else if (name.equals(RRuntime.CLASS_ATTR_KEY)) {
-                setClassAttr(this, (RStringVector) null, null);
+                setClassAttr(this, (RStringVector) null, null, null);
             } else {
                 attributes.remove(name);
             }
@@ -399,7 +399,7 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV
         }
     }
 
-    public static RAbstractContainer setClassAttr(RVector vector, RStringVector classAttr, RAbstractContainer enclosingDataFrame) {
+    public static RAbstractContainer setClassAttr(RVector vector, RStringVector classAttr, RAbstractContainer enclosingDataFrame, RAbstractContainer enclosingFactor) {
         if (vector.attributes == null && classAttr != null && classAttr.getLength() != 0) {
             vector.initAttributes();
         }
@@ -418,6 +418,19 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV
                         // it's a data frame now
                         return RDataFactory.createDataFrame(vector);
                     }
+                } else if (RType.Factor.getName().equals(classAttr.getDataAt(i))) {
+                    if (vector.getElementClass() != RInt.class) {
+                        // TODO: add source section
+                        throw RError.error(null, RError.Message.ADDING_INVALID_CLASS, "factor");
+                    }
+                    vector.putAttribute(RRuntime.CLASS_ATTR_KEY, classAttr);
+                    if (enclosingFactor != null) {
+                        // was a factor and still is a factor
+                        return enclosingFactor;
+                    } else {
+                        // it's a factor now
+                        return RDataFactory.createFactor((RIntVector) vector);
+                    }
                 }
             }
             vector.putAttribute(RRuntime.CLASS_ATTR_KEY, classAttr);
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 bd88572dd4..6f9e43f335 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
@@ -8269,6 +8269,28 @@ x
 [1,] 10+0i -4+0i
 [2,] -2+0i  0+0i
 
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-c("1","2","3"); class(x)<-"factor"; x }
+Error in class(x) <- "factor" :
+  adding class "factor" to an invalid object
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-c(1,2,3); class(x)<-"factor"; x }
+Error in class(x) <- "factor" :
+  adding class "factor" to an invalid object
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-c(1L,2L,3L); class(x)<-"factor"; x }
+Error in print.factor(1:3) : replacement has length zero
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a")); attr(x, "levels")<-NULL; as.character(x) }
+NULL
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a")); attr(x, "levels")<-character(); as.character(x) }
+[1] NA NA NA
+
 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
 #{data = c(1,2,2,3,1,2,3,3,1,2,3,3,1);fdata<-factor(data);levels(fdata) = c('I','II','III');print(fdata);}
  [1] I   II  II  III I   II  III III I   II  III III I
@@ -8510,6 +8532,14 @@ NULL
 #{ inherits(new.env(), "try-error") }
 [1] FALSE
 
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testInherits
+#{ x<-data.frame(c(1,2)); inherits(x, "data.frame") }
+[1] TRUE
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testInherits
+#{ x<-factor("a", "b", "a"); inherits(x, "factor") }
+[1] TRUE
+
 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testInherits
 #{x <- 10; inherits(x, "a") ;}
 [1] FALSE
@@ -14224,6 +14254,14 @@ Error in typeof(...) : unused arguments (2, 3, 4)
 #{ typeof(typeof(NULL)) }
 [1] "character"
 
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testTypeOf
+#{ x<-data.frame(c("a", "b", "a")); typeof(x) }
+[1] "list"
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testTypeOf
+#{ x<-factor(c("a", "b", "a")); typeof(x) }
+[1] "integer"
+
 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUnlist
 #{ names(unlist(list(a=list(b=list("1"))))) }
 [1] "a.b"
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 88e8c8372a..c50e14f06b 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
@@ -8053,6 +8053,31 @@ public class AllTests extends TestBase {
         assertEval("{ses <- c(\"low\", \"middle\", \"low\", \"low\", \"low\", \"low\", \"middle\", \"low\", \"middle\", \"middle\", \"middle\", \"middle\", \"middle\", \"high\", \"high\", \"low\", \"middle\", \"middle\", \"low\", \"high\"); ses.f.bad.order <- factor(ses); is.factor(ses.f.bad.order);levels(ses.f.bad.order);ses.f <- factor(ses, levels = c(\"low\", \"middle\", \"high\"));ses.order <- ordered(ses, levels = c(\"low\", \"middle\", \"high\"));print(ses.order); } ");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testFactor_6287c53e3c5f805807ac27e404a05595() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-NULL; as.character(x) }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_6057e63e95871f7ed5c00eb75931ac57() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-character(); as.character(x) }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_2ef7de52def309425a9b70965111f004() {
+        assertEvalError("{ x<-c(1,2,3); class(x)<-\"factor\"; x }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_7a9b1db57fd6e717cb6a5a4289638383() {
+        assertEvalError("{ x<-c(\"1\",\"2\",\"3\"); class(x)<-\"factor\"; x }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_78f60bd3f5d980d4b71b67fa44964e85() {
+        assertEvalError("{ x<-c(1L,2L,3L); class(x)<-\"factor\"; x }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testFileListing_9646bfd3fb553824f1f54cc5d04b8219() {
         assertEval("{ list.files(\"test/r/simple/data/tree1\") }");
@@ -8418,6 +8443,16 @@ public class AllTests extends TestBase {
         assertEval("{ inherits(new.env(), \"try-error\") }");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testInherits_04a77c3cfe28848e28a68ba564162427() {
+        assertEval("{ x<-data.frame(c(1,2)); inherits(x, \"data.frame\") }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testInherits_20d6545f67c653e20d3b612df41b64c2() {
+        assertEval("{ x<-factor(\"a\", \"b\", \"a\"); inherits(x, \"factor\") }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testInheritsIgnore_d0dc6389c924878311546ba61d753a22() {
         assertEval("{x <- 10;class(x) <- c(\"a\", \"b\");inherits(x, 2, c(TRUE)) ;}");
@@ -14918,6 +14953,16 @@ public class AllTests extends TestBase {
         assertEval("{ f <- function(...) typeof(...); f(1, 2, 3, 4)}");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testTypeOf_d63eb623f4c13b74dc9704bb857ca416() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); typeof(x) }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testTypeOf_4b90c824616e773167165340b6cfea37() {
+        assertEval("{ x<-data.frame(c(\"a\", \"b\", \"a\")); typeof(x) }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testTypeOfIgnore_847d333bfb40729281acd0b949d4c097() {
         assertEval("{ f <- function(...) typeof(...); f()}");
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 1faa2e5c7c..0d91359ab0 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
@@ -1592,6 +1592,9 @@ public class TestSimpleBuiltins extends TestBase {
         assertEval("{ f <- function(...) typeof(...); f(1, 2)}");
         assertEval("{ f <- function(...) typeof(...); f(1, 2, 3)}");
         assertEval("{ f <- function(...) typeof(...); f(1, 2, 3, 4)}");
+
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); typeof(x) }");
+        assertEval("{ x<-data.frame(c(\"a\", \"b\", \"a\")); typeof(x) }");
     }
 
     @Test
@@ -3291,6 +3294,9 @@ public class TestSimpleBuiltins extends TestBase {
         assertEval("{x <- 10;class(x) <- c(\"a\", \"b\");inherits(x, \"a\", c(TRUE)) ;}");
         assertEval("{ inherits(NULL, \"try-error\") }");
         assertEval("{ inherits(new.env(), \"try-error\") }");
+
+        assertEval("{ x<-data.frame(c(1,2)); inherits(x, \"data.frame\") }");
+        assertEval("{ x<-factor(\"a\", \"b\", \"a\"); inherits(x, \"factor\") }");
     }
 
     @Test
@@ -3871,6 +3877,12 @@ public class TestSimpleBuiltins extends TestBase {
         // Checkstyle: stop line length check
         assertEval("{ses <- c(\"low\", \"middle\", \"low\", \"low\", \"low\", \"low\", \"middle\", \"low\", \"middle\", \"middle\", \"middle\", \"middle\", \"middle\", \"high\", \"high\", \"low\", \"middle\", \"middle\", \"low\", \"high\"); ses.f.bad.order <- factor(ses); is.factor(ses.f.bad.order);levels(ses.f.bad.order);ses.f <- factor(ses, levels = c(\"low\", \"middle\", \"high\"));ses.order <- ordered(ses, levels = c(\"low\", \"middle\", \"high\"));print(ses.order); } ");
         // Checkstyle: resume line length check
+
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-NULL; as.character(x) }");
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-character(); as.character(x) }");
+        assertEvalError("{ x<-c(1,2,3); class(x)<-\"factor\"; x }");
+        assertEvalError("{ x<-c(\"1\",\"2\",\"3\"); class(x)<-\"factor\"; x }");
+        assertEvalError("{ x<-c(1L,2L,3L); class(x)<-\"factor\"; x }");
     }
 
     @Test
-- 
GitLab