diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
index e4aee832004c117348eaa9b543f9004df154eca6..60448d3fa71cfa21343359505fc0c9a635a4a0be 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
@@ -23,6 +23,7 @@ import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nmath.RMath;
 
 public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
     @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
@@ -54,7 +55,8 @@ public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
 
         int length = GridUtils.maxLength(unitLength, xVec, yVec, radiusVec);
         for (int i = 0; i < length; i++) {
-            double radius = unitToInches.convertX(radiusVec, i, conversionCtx);
+            Size radiusSizes = Size.fromUnits(unitToInches, radiusVec, radiusVec, i, conversionCtx);
+            double radius = RMath.fmin2(radiusSizes.getWidth(), radiusSizes.getHeight());
             Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
             Point loc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
             dev.drawCircle(drawingCtx, loc.x, loc.y, radius);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
index 0733409cb1b1c6058398919b6a4af15ff4dcf493..6ddbce80841d0ca92bb4cdca487793bf5672edcf 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
@@ -20,6 +20,7 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
@@ -37,6 +38,9 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
     @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
     @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
     @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+    private final ValueProfile unitToProfile = ValueProfile.createEqualityProfile();
+    private final ValueProfile axisToProfile = ValueProfile.createEqualityProfile();
+    private final ValueProfile axisFromProfile = ValueProfile.createEqualityProfile();
 
     static {
         Casts casts = new Casts(LConvert.class);
@@ -51,7 +55,11 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
     }
 
     @Specialization
-    Object doConvert(RAbstractVector units, int axisFrom, int axisTo, int unitTo) {
+    Object doConvert(RAbstractVector units, int axisFromIn, int axisToIn, int unitToIn) {
+        int axisFrom = axisFromProfile.profile(axisFromIn);
+        int axisTo = axisToProfile.profile(axisToIn);
+        int unitTo = unitToProfile.profile(unitToIn);
+
         GridContext ctx = GridContext.getContext();
         GridDevice dev = ctx.getCurrentDevice();
 
@@ -71,20 +79,30 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
         }
 
         for (int i = 0; i < length; i++) {
-            double inches;
-            if (isXAxis(axisFrom)) {
-                inches = unitToInches.convertX(units, i, conversionCtx);
+            double inches = toInches(units, i, axisFrom, conversionCtx);
+            double vpSize = isXAxis(axisTo) ? vpTransform.size.getWidth() : vpTransform.size.getHeight();
+            result[i] = Unit.convertFromInches(inches, unitTo, vpSize, vpContext.xscalemin, vpContext.xscalemax, isDimension(axisTo), drawingCtx);
+        }
+
+        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    private double toInches(RAbstractVector units, int index, int axisFrom, UnitConversionContext conversionCtx) {
+        double inches;
+        if (isXAxis(axisFrom)) {
+            if (isDimension(axisFrom)) {
+                inches = unitToInches.convertWidth(units, index, conversionCtx);
             } else {
-                inches = unitToInches.convertY(units, i, conversionCtx);
+                inches = unitToInches.convertX(units, index, conversionCtx);
             }
-            if (isXAxis(axisTo)) {
-                result[i] = Unit.convertFromInches(inches, unitTo, vpTransform.size.getWidth(), vpContext.xscalemin, vpContext.xscalemax, drawingCtx);
+        } else {
+            if (isDimension(axisFrom)) {
+                inches = unitToInches.convertHeight(units, index, conversionCtx);
             } else {
-                result[i] = Unit.convertFromInches(inches, unitTo, vpTransform.size.getHeight(), vpContext.yscalemin, vpContext.yscalemax, drawingCtx);
+                inches = unitToInches.convertY(units, index, conversionCtx);
             }
         }
-
-        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+        return inches;
     }
 
     private static boolean isRelative(int unitId) {
@@ -95,4 +113,8 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
     private static boolean isXAxis(int what) {
         return what % 2 == 0;
     }
+
+    private static boolean isDimension(int what) {
+        return what >= 2;
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
index e775dde0535ed7070523f30b7b07b8d81826ebab..a7557972f356b4ffb2222996a75a3078ce376da0 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
@@ -60,14 +60,13 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
 
         int length = GridUtils.maxLength(unitLength, xVec, yVec, wVec, hVec);
         for (int i = 0; i < length; i++) {
-            double w = unitToInches.convertX(wVec, i, conversionCtx);
-            double h = unitToInches.convertY(hVec, i, conversionCtx);
+            Size size = Size.fromUnits(unitToInches, wVec, hVec, i, conversionCtx);
             // Note: once this is factored to drawing/recording: this transformation is necessary
             // only for drawing
             Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
             Point transLoc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
-            Point loc = transLoc.justify(w, h, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
-            dev.drawRect(drawingCtx, loc.x, loc.y, w, h);
+            Point loc = transLoc.justify(size, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
+            dev.drawRect(drawingCtx, loc.x, loc.y, size.getWidth(), size.getHeight());
         }
         return RNull.instance;
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
index 7bd1158d1dd2127d23f46a56db5fb35f230214e0..a6a68509efef4ea51cb76cd1c291cbc6ec0051b8 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
@@ -41,6 +41,10 @@ public final class Point {
         return new Point(newX, newY);
     }
 
+    public Point justify(Size size, double hjust, double vjust) {
+        return justify(size.getWidth(), size.getHeight(), hjust, vjust);
+    }
+
     public Point justify(double width, double height, double hjust, double vjust) {
         return new Point(GridUtils.justify(x, width, hjust), GridUtils.justify(y, height, vjust));
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
index 803a07eb122560121ebb611b455ad7e7fd0acc70..c428af3df6956bf7813712e41955f796e7d2e21f 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
@@ -22,6 +22,10 @@
  */
 package com.oracle.truffle.r.library.fastrGrid;
 
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
 public final class Size {
     private final double width;
     private final double height;
@@ -31,6 +35,12 @@ public final class Size {
         this.height = height;
     }
 
+    public static Size fromUnits(UnitToInchesNode unitToInches, RAbstractVector wVec, RAbstractVector hVec, int index, UnitConversionContext conversionCtx) {
+        double w = unitToInches.convertWidth(wVec, index, conversionCtx);
+        double h = unitToInches.convertHeight(hVec, index, conversionCtx);
+        return new Size(w, h);
+    }
+
     public double getWidth() {
         return width;
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
index f646edaf0ccbeece16796840da9eabf0c1f2ddc3..2dc08f5b5c03b6adaed9c54de834054ca4d16273 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
@@ -12,21 +12,27 @@
 package com.oracle.truffle.r.library.fastrGrid;
 
 import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
 
+import java.util.function.Function;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitElementAtNodeGen;
 import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitLengthNodeGen;
 import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitToInchesNodeGen;
 import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
  * Note: internally in FastR Grid everything is in inches. However, some lists that are exposed to
@@ -98,10 +104,11 @@ public class Unit {
         return UnitToInchesNode.create();
     }
 
-    static double convertFromInches(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+    static double convertFromInches(double value, int unitId, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx) {
         switch (unitId) {
             case NATIVE:
-                return ((value + scalemin) * (scalemax - scalemin)) / vpSize;
+                double tmp = isDimension ? value : (value + scalemin);
+                return (tmp * (scalemax - scalemin)) / vpSize;
             case NPC:
                 return value / vpSize;
             case CM:
@@ -132,10 +139,11 @@ public class Unit {
         }
     }
 
-    static double convertToInches(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+    static double convertToInches(double value, int unitId, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx) {
         switch (unitId) {
             case NATIVE:
-                return ((value - scalemin) / (scalemax - scalemin)) * vpSize;
+                double tmp = isDimension ? value : (value - scalemin);
+                return (tmp / (scalemax - scalemin)) * vpSize;
             case NPC:
                 return value * vpSize;
             case POINTS:
@@ -153,22 +161,73 @@ public class Unit {
         }
     }
 
-    public static UnitElementAtNode createElementAtNode() {
-        return UnitElementAtNode.create();
+    private static final class ArithmeticUnit {
+        public final String op;
+        public final RAbstractContainer arg1;
+        public final RAbstractContainer arg2;
+
+        ArithmeticUnit(String op, RAbstractContainer arg1, RAbstractContainer arg2) {
+            this.op = op;
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+        }
+
+        public boolean isBinary() {
+            return arg2 != null;
+        }
     }
 
-    public abstract static class UnitNodeBase extends Node {
-        @Child private InheritsCheckNode inheritsCheckNode = new InheritsCheckNode("unit.arithmetic");
+    abstract static class UnitNodeBase extends RBaseNode {
+        @Child private InheritsCheckNode inheritsArithmeticCheckNode = new InheritsCheckNode("unit.arithmetic");
+        @Child private InheritsCheckNode inheritsUnitListCheckNode = new InheritsCheckNode("unit.list");
+        @Child private CastNode stringCast;
+        @Child private CastNode abstractContainerCast;
+
+        boolean isSimple(Object obj) {
+            return !inheritsArithmeticCheckNode.execute(obj) && !inheritsUnitListCheckNode.execute(obj);
+        }
 
         boolean isArithmetic(Object obj) {
-            return obj instanceof RList && inheritsCheckNode.execute(obj);
+            return inheritsArithmeticCheckNode.execute(obj);
+        }
+
+        boolean isUnitList(Object obj) {
+            return inheritsUnitListCheckNode.execute(obj);
+        }
+
+        ArithmeticUnit asArithmeticUnit(RList unit) {
+            if (unit.getLength() <= 1) {
+                throw error(Message.GENERIC, "Invalid arithmetic unit (length <= 1).");
+            }
+            if (stringCast == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                stringCast = newCastBuilder().asStringVector().findFirst().buildCastNode();
+            }
+            if (abstractContainerCast == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                abstractContainerCast = newCastBuilder().mustBe(abstractVectorValue()).boxPrimitive().buildCastNode();
+            }
+            // Note: the operator is usually of type character, however in the R code, grid compares
+            // it to symbols, e.g. `x`. Our implementation here should work with symbols too thanks
+            // to the asStringVector() conversion.
+            String op = (String) stringCast.execute(unit.getDataAt(0));
+            RAbstractContainer arg1 = (RAbstractContainer) abstractContainerCast.execute(unit.getDataAt(1));
+            if (op.equals("+") || op.equals("-") || op.equals("*")) {
+                if (unit.getLength() != 3) {
+                    throw error(Message.GENERIC, "Invalid arithmetic unit with binary operator and missing operand.");
+                }
+                return new ArithmeticUnit(op, arg1, (RAbstractContainer) abstractContainerCast.execute(unit.getDataAt(2)));
+            }
+            if (op.equals("max") || op.equals("min") || op.equals("sum")) {
+                return new ArithmeticUnit(op, arg1, null);
+            }
+            throw error(Message.GENERIC, "Unexpected unit operator " + op);
         }
     }
 
     /**
-     * A unit object can represent more or fewer values that the number of elements underlying list
-     * or vector. This node gives the length if the unit in a sense of the upper limit on what can
-     * be used as an index for {@link UnitElementAtNode}.
+     * Arithmetic unit objects can represent 'vectorized' expressions, in such case the 'length' is
+     * not simply the length of the underlying vector/list.
      */
     public abstract static class UnitLengthNode extends UnitNodeBase {
         public static UnitLengthNode create() {
@@ -183,31 +242,17 @@ public class Unit {
         }
 
         @Specialization(guards = "isArithmetic(list)")
-        int doArithmetic(RList list) {
-            throw RInternalError.unimplemented("Length for arithmetic units");
-        }
-    }
-
-    /**
-     * @see UnitLengthNode
-     */
-    public abstract static class UnitElementAtNode extends UnitNodeBase {
-        @Child private CastNode castToDouble = newCastBuilder().asDoubleVector().buildCastNode();
-
-        public static UnitElementAtNode create() {
-            return UnitElementAtNodeGen.create();
-        }
-
-        public abstract double execute(RAbstractContainer vector, int index);
-
-        @Specialization(guards = "!isArithmetic(value)")
-        double doNormal(RAbstractContainer value, int index) {
-            return ((RAbstractDoubleVector) castToDouble.execute(value)).getDataAt(index);
+        int doArithmetic(RList list,
+                        @Cached("create()") UnitLengthNode recursiveLen) {
+            ArithmeticUnit arithmeticUnit = asArithmeticUnit(list);
+            if (arithmeticUnit.isBinary()) {
+                return Math.max(recursiveLen.execute(arithmeticUnit.arg1), recursiveLen.execute(arithmeticUnit.arg2));
+            }
+            return 1;   // op is max, min, sum
         }
 
-        @Specialization(guards = "isArithmetic(list)")
-        double doArithmetic(RList list, int index) {
-            throw RInternalError.unimplemented("UnitElementAt for arithmetic units");
+        static CastNode createStringCast() {
+            return newCastBuilder().asStringVector().findFirst().buildCastNode();
         }
     }
 
@@ -228,36 +273,100 @@ public class Unit {
 
     /**
      * Normalizes grid unit object to a double value in inches. For convenience the index is
-     * interpreted as cyclic unlike in {@link UnitElementAtNode}.
+     * interpreted as cyclic.
      */
     public abstract static class UnitToInchesNode extends UnitNodeBase {
         @Child private CastNode castUnitId = newCastBuilder().mustBe(numericValue()).asIntegerVector().findFirst().buildCastNode();
-        @Child private UnitElementAtNode elementAtNode = UnitElementAtNode.create();
+        @Child private CastNode castDoubleVec = newCastBuilder().mustBe(numericValue()).boxPrimitive().asDoubleVector().buildCastNode();
 
         public static UnitToInchesNode create() {
             return UnitToInchesNodeGen.create();
         }
 
         public double convertX(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
-            return execute(vector, index, conversionCtx.viewPortSize.getWidth(), conversionCtx.viewPortContext.xscalemin, conversionCtx.viewPortContext.xscalemax, conversionCtx.drawingContext);
+            return execute(vector, index, conversionCtx.viewPortSize.getWidth(), conversionCtx.viewPortContext.xscalemin, conversionCtx.viewPortContext.xscalemax, false, conversionCtx.drawingContext);
         }
 
         public double convertY(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
-            return execute(vector, index, conversionCtx.viewPortSize.getHeight(), conversionCtx.viewPortContext.yscalemin, conversionCtx.viewPortContext.yscalemax, conversionCtx.drawingContext);
+            return execute(vector, index, conversionCtx.viewPortSize.getHeight(), conversionCtx.viewPortContext.yscalemin, conversionCtx.viewPortContext.yscalemax, false,
+                            conversionCtx.drawingContext);
         }
 
-        public abstract double execute(RAbstractContainer vector, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx);
+        public double convertWidth(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
+            return execute(vector, index, conversionCtx.viewPortSize.getWidth(), conversionCtx.viewPortContext.xscalemin, conversionCtx.viewPortContext.xscalemax, true, conversionCtx.drawingContext);
+        }
 
-        @Specialization(guards = "!isArithmetic(value)")
-        double doNormal(RAbstractContainer value, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+        public double convertHeight(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
+            return execute(vector, index, conversionCtx.viewPortSize.getHeight(), conversionCtx.viewPortContext.yscalemin, conversionCtx.viewPortContext.yscalemax, true, conversionCtx.drawingContext);
+        }
+
+        public abstract double execute(RAbstractContainer vector, int index, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx);
+
+        @Specialization(guards = "isSimple(value)")
+        double doNormal(RAbstractContainer value, int index, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx) {
             int unitId = (Integer) castUnitId.execute(value.getAttr(VALID_UNIT_ATTR));
-            return convertToInches(elementAtNode.execute(value, index % value.getLength()), unitId, vpSize, scalemin, scalemax, drawingCtx);
+            RAbstractDoubleVector vector = (RAbstractDoubleVector) castDoubleVec.execute(value);
+            return convertToInches(vector.getDataAt(index % vector.getLength()), unitId, vpSize, scalemin, scalemax, isDimension, drawingCtx);
+        }
+
+        @Specialization(guards = "isUnitList(value)")
+        double doList(RList value, int index, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx,
+                        @Cached("create()") UnitToInchesNode recursiveNode) {
+            Object unwrapped = value.getDataAt(index % value.getLength());
+            if (unwrapped instanceof RAbstractVector) {
+                return recursiveNode.execute((RAbstractContainer) unwrapped, index, vpSize, scalemin, scalemax, isDimension, drawingCtx);
+            }
+            throw error(Message.GENERIC, "Unexpected unit list with non-vector like element at index " + index);
         }
 
         @Specialization(guards = "isArithmetic(list)")
-        double doArithmetic(RList list, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
-            throw RInternalError.unimplemented("UnitToInches for arithmetic units");
+        double doArithmetic(RList list, int index, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx,
+                        @Cached("createAsDoubleCast()") CastNode asDoubleCast,
+                        @Cached("create()") UnitLengthNode unitLengthNode,
+                        @Cached("create()") UnitToInchesNode recursiveNode) {
+            ArithmeticUnit expr = asArithmeticUnit(list);
+            Function<RAbstractContainer, Double> recursive = x -> recursiveNode.execute(x, index, vpSize, scalemin, scalemax, isDimension, drawingCtx);
+            switch (expr.op) {
+                case "+":
+                    return recursive.apply(expr.arg1) + recursive.apply(expr.arg2);
+                case "-":
+                    return recursive.apply(expr.arg1) + recursive.apply(expr.arg2);
+                case "*":
+                    RAbstractDoubleVector left = (RAbstractDoubleVector) asDoubleCast.execute(expr.arg1);
+                    return left.getDataAt(index % left.getLength()) + recursive.apply(expr.arg2);
+                default:
+                    break;
+            }
+
+            // must be aggregate operation
+            int len = unitLengthNode.execute(expr.arg1);
+            double[] values = new double[len];
+            for (int i = 0; i < len; i++) {
+                values[i] = recursiveNode.execute(expr.arg1, i, vpSize, scalemin, scalemax, isDimension, drawingCtx);
+            }
+
+            switch (expr.op) {
+                case "min":
+                    return GridUtils.fmin(Double.MAX_VALUE, values);
+                case "max":
+                    return GridUtils.fmax(Double.MAX_VALUE, values);
+                case "sum":
+                    return sum(values);
+                default:
+                    throw RInternalError.shouldNotReachHere("The operation should have been validated in asArithmeticUnit method.");
+            }
+        }
+
+        static CastNode createAsDoubleCast() {
+            return newCastBuilder().mustBe(numericValue()).asDoubleVector().buildCastNode();
         }
 
+        static double sum(double[] values) {
+            double result = 0;
+            for (int i = 0; i < values.length; i++) {
+                result += values[i];
+            }
+            return result;
+        }
     }
 }