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 69d3d49b10821db3909ce52d7e07a13a352c5264..9cc09ec1e4d84395cd5dea0798e1d1c1645ca325 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
@@ -102,6 +102,8 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode {
             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 (name.equals(RRuntime.LEVELS_ATTR_KEY)) {
+            resultVector.setLevels(null);
         } else if (resultVector.getAttributes() != null) {
             resultVector.getAttributes().remove(name);
         }
@@ -140,6 +142,8 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode {
             return setClassAttrFromObject(resultVector, container, value, getEncapsulatingSourceSection());
         } else if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
             resultVector.setRowNames(castVector(frame, value));
+        } else if (name.equals(RRuntime.LEVELS_ATTR_KEY)) {
+            resultVector.setLevels(castVector(frame, value));
         } else {
             // generic attribute
             resultVector.setAttr(name, value);
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 2dac8d6ef4276fed1818457c4e39bf0d1a6321e7..e057dd8e85058fa8143487c7c56b1901aef861b5 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
@@ -179,11 +179,9 @@ public abstract class UpdateAttributes extends RInvisibleBuiltinNode {
                     UpdateAttr.setClassAttrFromObject(resultVector, container, value, getEncapsulatingSourceSection());
                 }
             } else if (attrName.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
-                if (value == RNull.instance) {
-                    resultVector.setRowNames(null);
-                } else {
-                    resultVector.setRowNames(castVector(virtualFrame, value));
-                }
+                resultVector.setRowNames(castVector(virtualFrame, value));
+            } else if (attrName.equals(RRuntime.LEVELS_ATTR_KEY)) {
+                resultVector.setLevels(castVector(virtualFrame, value));
             } else {
                 if (value == RNull.instance) {
                     resultVector.removeAttr(attrName);
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 b39f7f345d3a1a6ac8eb05fbca3d8800073cea24..47e1c683d5fb66e1d58d4dc5f5020d9bef174a66 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
@@ -11,8 +11,11 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode;
+import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.*;
@@ -24,6 +27,16 @@ import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 // 2nd parameter is "value", but should not be matched against, so ""
 public abstract class UpdateLevels extends RInvisibleBuiltinNode {
 
+    @Child private CastToVectorNode castVector;
+
+    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);
+    }
+
     @Specialization
     @TruffleBoundary
     protected RAbstractVector updateLevels(RAbstractVector vector, @SuppressWarnings("unused") RNull levels) {
@@ -34,11 +47,10 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode {
     }
 
     @Specialization
-    @TruffleBoundary
-    protected RAbstractVector updateLevels(RAbstractVector vector, Object levels) {
+    protected RAbstractVector updateLevels(VirtualFrame frame, RAbstractVector vector, Object levels) {
         controlVisibility();
         RVector v = vector.materialize();
-        v.setLevels(levels);
+        v.setLevels(castVector(frame, levels));
         return v;
     }
 
@@ -52,9 +64,9 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode {
 
     @Specialization
     @TruffleBoundary
-    protected RFactor updateLevels(RFactor factor, Object levels) {
+    protected RFactor updateLevels(VirtualFrame frame, RFactor factor, Object levels) {
         controlVisibility();
-        factor.getVector().setLevels(levels);
+        factor.getVector().setLevels(castVector(frame, levels));
         return factor;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java
index 261a69b958f4e6f901aa0b54e1cc0bc0194b5bac..8f09abde45a867940c74e2b3971ac9a1328dbc5e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java
@@ -158,6 +158,10 @@ public abstract class RNode extends Node {
         return RTypesGen.RTYPES.expectRDataFrame(execute(frame));
     }
 
+    public RFactor executeRFactor(VirtualFrame frame) throws UnexpectedResultException {
+        return RTypesGen.RTYPES.expectRFactor(execute(frame));
+    }
+
     public RSymbol executeRSymbol(VirtualFrame frame) throws UnexpectedResultException {
         return RTypesGen.RTYPES.expectRSymbol(execute(frame));
     }
@@ -190,7 +194,7 @@ public abstract class RNode extends Node {
         return RTypesGen.RTYPES.expectRType(execute(frame));
     }
 
-    public static boolean areSameLength(RAbstractVector a, RAbstractVector b) {
+    public static boolean areSameLength(RAbstractContainer a, RAbstractContainer b) {
         return a.getLength() == b.getLength();
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
index 35d65aaf9e88ea41afe39e5cd0d9b0d8fb455df9..a6b85084e2b0933f0ebfad322aa1ce9c45c349c4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
@@ -60,6 +60,8 @@ public abstract class ConstantNode extends RNode implements VisibilityController
             return new ConstantVectorNode((RAbstractVector) value);
         } else if (value instanceof RDataFrame) {
             return new ConstantDataFrameNode((RDataFrame) value);
+        } else if (value instanceof RFactor) {
+            return new ConstantFactorNode((RFactor) value);
         } else if (value instanceof RRaw) {
             return new ConstantRawNode((RRaw) value);
         } else if (value instanceof RFunction) {
@@ -304,6 +306,27 @@ public abstract class ConstantNode extends RNode implements VisibilityController
         }
     }
 
+    private static final class ConstantFactorNode extends ConstantNode {
+
+        private final RFactor factor;
+
+        public ConstantFactorNode(RFactor factor) {
+            this.factor = factor;
+        }
+
+        @Override
+        public RFactor executeRFactor(VirtualFrame frame) {
+            controlVisibility();
+            return factor;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            controlVisibility();
+            return factor;
+        }
+    }
+
     private static final class ConstantRawNode extends ConstantNode {
 
         private final RRaw data;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
index b80217f68c4c03d3253c78571d06e6d9d8bb2f5d..d8c732f9d642aef5e1f54539446aa27a8299a6a0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
@@ -701,6 +701,78 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         throw RError.error(getEncapsulatingSourceSection(), RError.Message.NON_CONFORMABLE_ARRAYS);
     }
 
+    // factor and scalar
+
+    @Specialization(guards = "!isEq")
+    protected RLogicalVector doFactorOp(RFactor left, Object right) {
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.NOT_MEANINGFUL_FOR_FACTORS, logic.opName());
+    }
+
+    @Specialization(guards = "!isEq")
+    protected RLogicalVector doFactorOp(Object left, RFactor right) {
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.NOT_MEANINGFUL_FOR_FACTORS, logic.opName());
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, int right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.intToString(right, false), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(int left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.intToString(left, false), true);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, double right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.doubleToString(right), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(double left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.doubleToString(left), true);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, byte right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.logicalToString(right), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(byte left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.logicalToString(left), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, String right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), right, false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(String left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), left, true);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, RComplex right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.complexToString(right), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RComplex left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.complexToString(left), true);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RFactor left, RRaw right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(left, leftNACheck), RRuntime.rawToString(right), false);
+    }
+
+    @Specialization(guards = "isEq")
+    protected RLogicalVector doFactorOp(RRaw left, RFactor right) {
+        return performStringVectorOp(RClosures.createFactorToStringVector(right, leftNACheck), RRuntime.rawToString(left), true);
+    }
+
     protected static boolean differentDimensions(RAbstractVector left, RAbstractVector right) {
         if (!left.hasDimensions() || !right.hasDimensions()) {
             return false;
@@ -791,6 +863,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         return performStringVectorOpSameLength(left, RClosures.createIntToStringVector(right, rightNACheck));
     }
 
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doIntVectorDifferentLength(RAbstractIntVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createIntToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doIntVectorSameLength(RAbstractIntVector left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createIntToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doIntVectorDifferentLength(RFactor left, RAbstractIntVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createIntToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doIntVectorSameLength(RFactor left, RAbstractIntVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createIntToStringVector(right, rightNACheck));
+    }
+
     @Specialization(guards = "!areSameLength")
     protected RLogicalVector doIntVectorDifferentLength(RAbstractIntVector left, RComplexVector right) {
         return performComplexVectorOpDifferentLength(RClosures.createIntToComplexVector(left, leftNACheck), right);
@@ -883,6 +975,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         return performStringVectorOpSameLength(left, RClosures.createDoubleToStringVector(right, rightNACheck));
     }
 
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doDoubleVectorDifferentLength(RAbstractDoubleVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createDoubleToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doDoubleVectorSameLength(RAbstractDoubleVector left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createDoubleToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doDoubleVectorDifferentLength(RFactor left, RAbstractDoubleVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createDoubleToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doDoubleVectorSameLength(RFactor left, RAbstractDoubleVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createDoubleToStringVector(right, rightNACheck));
+    }
+
     @Specialization(guards = "!areSameLength")
     protected RLogicalVector doDoubleVectorDifferentLength(RAbstractDoubleVector left, RComplexVector right) {
         return performComplexVectorOpDifferentLength(RClosures.createDoubleToComplexVector(left, leftNACheck), right);
@@ -955,6 +1067,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         return performStringVectorOpSameLength(left, RClosures.createLogicalToStringVector(right, rightNACheck));
     }
 
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doLogicalVectorDifferentLength(RAbstractLogicalVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createLogicalToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doLogicalVectorSameLength(RAbstractLogicalVector left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createLogicalToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, leftNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doLogicalVectorDifferentLength(RFactor left, RAbstractLogicalVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createLogicalToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doLogicalVectorSameLength(RFactor left, RAbstractLogicalVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, leftNACheck), RClosures.createLogicalToStringVector(right, rightNACheck));
+    }
+
     @Specialization(guards = "!areSameLength")
     protected RLogicalVector doLogicalVectorDifferentLength(RAbstractLogicalVector left, RComplexVector right) {
         return performComplexVectorOpDifferentLength(RClosures.createLogicalToComplexVector(left, leftNACheck), right);
@@ -1007,6 +1139,26 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         return performStringVectorOpSameLength(left, right);
     }
 
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RStringVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(left, RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RStringVector left, RFactor right) {
+        return performStringVectorOpSameLength(left, RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RFactor left, RStringVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), right);
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RFactor left, RStringVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), right);
+    }
+
     @Specialization(guards = "!areSameLength")
     protected RLogicalVector doStringVectorDifferentLength(RStringVector left, RAbstractComplexVector right) {
         return performStringVectorOpDifferentLength(left, RClosures.createComplexToStringVector(right, rightNACheck));
@@ -1047,6 +1199,58 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
         return performStringVectorOpSameLength(RClosures.createRawToStringVector(left, leftNACheck), right);
     }
 
+    // factor and vectors
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RFactor left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RFactor left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RFactor left, RAbstractComplexVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createComplexToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RFactor left, RAbstractComplexVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createComplexToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RAbstractComplexVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createComplexToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RAbstractComplexVector left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createComplexToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLength(RFactor left, RRawVector right) {
+        return performStringVectorOpDifferentLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createRawToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLength(RFactor left, RRawVector right) {
+        return performStringVectorOpSameLength(RClosures.createFactorToStringVector(left, rightNACheck), RClosures.createRawToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"!areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorDifferentLengthRRawVector(RRawVector left, RFactor right) {
+        return performStringVectorOpDifferentLength(RClosures.createRawToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
+    @Specialization(guards = {"areSameLength", "isEq"})
+    protected RLogicalVector doStringVectorSameLengthRRawVector(RRawVector left, RFactor right) {
+        return performStringVectorOpSameLength(RClosures.createRawToStringVector(left, leftNACheck), RClosures.createFactorToStringVector(right, rightNACheck));
+    }
+
     // complex vector and vectors
 
     @Specialization(guards = "!areSameLength")
@@ -1134,6 +1338,18 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
 
     // guards
 
+    public boolean isEq(RFactor left, RFactor right) {
+        return logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual;
+    }
+
+    public boolean isEq(RFactor left, Object right) {
+        return logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual;
+    }
+
+    public boolean isEq(Object left, RFactor right) {
+        return !(logic instanceof BinaryCompare.Equal || logic instanceof BinaryCompare.NotEqual);
+    }
+
     private boolean isVectorizedLogicalOp() {
         return !(logic instanceof BinaryLogic.And || logic instanceof BinaryLogic.Or);
     }
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 2e7305b88d64ee05f7369d610f0a48c5685742ef..17570328329ca7fca247dd8577f97e853c7036a6 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
@@ -553,7 +553,9 @@ public final class RError extends RuntimeException {
         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'"),
-        ADDING_INVALID_CLASS("adding class \"%s\" to an invalid object");
+        ADDING_INVALID_CLASS("adding class \"%s\" to an invalid object"),
+        IS_NA_TO_NON_VECTOR("is.na() applied to non-(list or vector) of type '%s'"),
+        NOT_MEANINGFUL_FOR_FACTORS("%s not meaningful for factors");
 
         public final String message;
         private final boolean hasArgs;
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 6579249bf68bad3afb2bea4b37d09f7ba368ad27..f4a3e81da5c94fa07805b1fa1f5d3920c82baa19 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
@@ -214,6 +214,7 @@ public abstract class RVector extends RBounded implements RShareable, RAbstractV
         }
     }
 
+    @TruffleBoundary
     public final void setLevels(Object newLevels) {
         if (attributes != null && newLevels == null) {
             // whether it's one dimensional array or not, assigning null always removes the "Levels"
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
index 2dfd14c197005f9bad72fd05df1c43567eb8d944..32b7d1c26275edecf13b3f7f6db3ce9af049c68e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.runtime.data.closures;
 
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 public class RClosures {
@@ -101,4 +102,10 @@ public class RClosures {
         return new RComplexToStringVectorClosure(vector, check);
     }
 
+    // Factor to
+
+    public static RAbstractStringVector createFactorToStringVector(RFactor factor, NACheck check) {
+        return new RFactorToStringVectorClosure(factor, check);
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
index 22fc5f2164def5f8c60bafabda94c0163c0039da..0a614dc224f5f5cafc84402517093fa10a96b075 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
@@ -100,7 +100,7 @@ public abstract class BinaryCompare extends BooleanOperation {
         super(commutative, false);
     }
 
-    private static final class NotEqual extends BinaryCompare {
+    public static final class NotEqual extends BinaryCompare {
 
         public NotEqual() {
             super(true);
@@ -144,7 +144,7 @@ public abstract class BinaryCompare extends BooleanOperation {
         }
     }
 
-    private static final class Equal extends BinaryCompare {
+    public static final class Equal extends BinaryCompare {
 
         public Equal() {
             super(true);
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 4c1ccfdf8c12d6f6bf890a50e93c33bec3edaa71..01212e5a0ae5b8a82a1f0dd113410a7cc972307f 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
@@ -8332,6 +8332,35 @@ NULL
 #{ 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
+#{ x<-factor(c("a", "b", "a")); x == "a" }
+[1]  TRUE FALSE  TRUE
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a")); x == c("a", "b") }
+[1] TRUE TRUE TRUE
+Warning messages:
+1: In is.na(e1) | is.na(e2) :
+  longer object length is not a multiple of shorter object length
+2: In `==.default`(x, c("a", "b")) :
+  longer object length is not a multiple of shorter object length
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a")); x > "a" }
+[1] NA NA NA
+Warning message:
+In Ops.factor(x, "a") : > not meaningful for factors
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a")); x > c("a", "b") }
+[1] NA NA NA
+Warning message:
+In Ops.factor(x, c("a", "b")) : > not meaningful for factors
+
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFactor
+#{ x<-factor(c("a", "b", "a", "c")); x == c("a", "b") }
+[1]  TRUE  TRUE  TRUE FALSE
+
 ##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
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 d0877e00e11531382d33c697fb23ffc48ada1ce3..654d31fc5c2e6e6b619836ea9b036fdeec436be5 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
@@ -8113,6 +8113,16 @@ public class AllTests extends TestBase {
         assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); attr(x, \"levels\")<-character(); as.character(x) }");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testFactor_696be2cb6d37235d8e5aa08b6de78b44() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); x == \"a\" }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_6b857dc0c28485c71e998f91ad719e79() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\", \"c\")); x == c(\"a\", \"b\") }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testFactor_2ef7de52def309425a9b70965111f004() {
         assertEvalError("{ x<-c(1,2,3); class(x)<-\"factor\"; x }");
@@ -8128,6 +8138,21 @@ public class AllTests extends TestBase {
         assertEvalError("{ x<-c(1L,2L,3L); class(x)<-\"factor\"; x }");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testFactor_8e866be378d6495f8d649996dcb5bb3c() {
+        assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > \"a\" }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_7cd2b27121f6c77b417a436d60108819() {
+        assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > c(\"a\", \"b\") }");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testFactor_61bfd366e4db68e9bda18fe2c3cc87f2() {
+        assertEvalWarning("{ x<-factor(c(\"a\", \"b\", \"a\")); x == c(\"a\", \"b\") }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testFileListing_9646bfd3fb553824f1f54cc5d04b8219() {
         assertEval("{ list.files(\"test/r/simple/data/tree1\") }");
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 5dd75c552a11ce6c653d2c4f29b6e33b42e8b4ba..2a70539c45a9eed50f090eb6bfa79bcc9fe47222 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
@@ -3894,6 +3894,13 @@ public class TestSimpleBuiltins extends TestBase {
         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 }");
+
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); x == \"a\" }");
+        assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > \"a\" }");
+
+        assertEvalWarning("{ x<-factor(c(\"a\", \"b\", \"a\")); x == c(\"a\", \"b\") }");
+        assertEvalError("{ x<-factor(c(\"a\", \"b\", \"a\")); x > c(\"a\", \"b\") }");
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\", \"c\")); x == c(\"a\", \"b\") }");
     }
 
     @Test