From 15b2d796e5f5fb1fcbdd63aebf8f4e2acb4704f3 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Wed, 5 Apr 2017 15:11:22 +0200
Subject: [PATCH] FastR Grid: implement L_pretty

---
 .../fastrGrid/FastRGridExternalLookup.java    |   2 +
 .../truffle/r/library/fastrGrid/LPretty.java  |  98 +++++++++++
 .../truffle/r/nodes/builtin/base/Pretty.java  | 145 +--------------
 .../truffle/r/runtime/PrettyIntevals.java     | 166 ++++++++++++++++++
 mx.fastr/copyrights/overrides                 |   2 +
 5 files changed, 272 insertions(+), 141 deletions(-)
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPretty.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrettyIntevals.java

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
index 1292153015..daea6bc681 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
@@ -89,6 +89,8 @@ public final class FastRGridExternalLookup {
                 return LConvert.create();
             case "L_validUnits":
                 return LValidUnit.create();
+            case "L_pretty":
+                return LPretty.create();
 
             // Viewport management
             case "L_upviewport":
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPretty.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPretty.java
new file mode 100644
index 0000000000..1ff6bb3f11
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPretty.java
@@ -0,0 +1,98 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.runtime.nmath.TOMS708.fabs;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.PrettyIntevals;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+public abstract class LPretty extends RExternalBuiltinNode.Arg1 {
+    static {
+        Casts casts = new Casts(LPretty.class);
+        casts.arg(0).mustBe(numericValue()).asDoubleVector();
+    }
+
+    public static LPretty create() {
+        return LPrettyNodeGen.create();
+    }
+
+    @Specialization
+    protected Object doPretty(RAbstractDoubleVector scale) {
+        double min = scale.getLength() > 0 ? scale.getDataAt(0) : 0;
+        double max = scale.getLength() > 1 ? scale.getDataAt(1) : 0;
+        boolean swap = max < min;
+        if (swap) {
+            min = max;
+            max = scale.getDataAt(0);
+        }
+
+        if (Double.isInfinite(min) || Double.isInfinite(max)) {
+            throw error(Message.GENERIC, String.format("infinite axis extents [GEPretty(%g,%g,5)]", min, max));
+        }
+
+        double[] ns = new double[]{min};
+        double[] nu = new double[]{max};
+        int[] ndiv = new int[]{5};
+        double unit = PrettyIntevals.pretty(getErrorContext(), ns, nu, new int[]{5}, 1, 0.25, new double[]{.8, 1.7}, 2, false);
+
+        if (nu[0] >= ns[0] + 1) {
+            if (ns[0] * unit < min - 1e-7 * unit) {
+                ns[0]++;
+            }
+            if (nu[0] > ns[0] + 1 && nu[0] * unit > max + 1e-7 * unit) {
+                nu[0]--;
+            }
+            ndiv[0] = (int) (nu[0] - ns[0]);
+        }
+        min = ns[0] * unit;
+        max = nu[0] * unit;
+
+        if (swap) {
+            double temp = min;
+            min = max;
+            max = temp;
+        }
+        return createAtVector(min, max, ndiv[0]);
+    }
+
+    private static RAbstractDoubleVector createAtVector(double axp0, double axp1, double axp2) {
+        /*
+         * Create an 'at = ...' vector for axis(.) i.e., the vector of tick mark locations, when
+         * none has been specified (= default).
+         *
+         * axp[0:2] = (x1, x2, nInt), where x1..x2 are the extreme tick marks {unless in log case,
+         * where nInt \in {1,2,3 ; -1,-2,....} and the `nint' argument is used *instead*.} The
+         * resulting REAL vector must have length >= 1, ideally >= 2
+         *
+         * FastR Notes: we only implement case where logflag == false.
+         */
+        /* --- linear axis --- Only use axp[] arg. */
+        int n = (int) (fabs(axp2) + 0.25); /* >= 0 */
+        int dn = Math.max(1, n);
+        double rng = axp1 - axp0;
+        double small = fabs(rng) / (100. * dn);
+        double[] at = new double[n + 1];
+        for (int i = 0; i <= n; i++) {
+            at[i] = axp0 + ((double) i / dn) * rng;
+            if (fabs(at[i]) < small) {
+                at[i] = 0;
+            }
+        }
+        return RDataFactory.createDoubleVector(at, RDataFactory.COMPLETE_VECTOR);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Pretty.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Pretty.java
index 52f90c316f..01bdd569f5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Pretty.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Pretty.java
@@ -22,6 +22,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.PrettyIntevals;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -46,6 +47,8 @@ public abstract class Pretty extends RBuiltinNode {
         casts.arg("eps.correct").defaultError(RError.Message.GENERIC, "'eps.correct' must be 0, 1, or 2").asIntegerVector().findFirst().mustNotBeNA().mustBe(and(gte0(), lte(2)));
     }
 
+    public abstract RList execute(double l, double u, int n, int minN, double shrinkSml, RAbstractDoubleVector hi, int epsCorrect);
+
     @Specialization
     protected RList pretty(double l, double u, int n, int minN, double shrinkSml, RAbstractDoubleVector hi, int epsCorrect) {
         double hi0 = hi.getDataAt(0);
@@ -60,151 +63,11 @@ public abstract class Pretty extends RBuiltinNode {
         double[] lo = new double[]{l};
         double[] up = new double[]{u};
         int[] ndiv = new int[]{n};
-        rPretty(lo, up, ndiv, minN, shrinkSml, hi.materialize().getDataWithoutCopying(), epsCorrect, true);
+        PrettyIntevals.pretty(getErrorContext(), lo, up, ndiv, minN, shrinkSml, hi.materialize().getDataWithoutCopying(), epsCorrect, true);
         Object[] data = new Object[3];
         data[0] = lo[0];
         data[1] = up[0];
         data[2] = ndiv[0];
         return RDataFactory.createList(data, NAMES);
     }
-
-    double rPretty(double[] lo, double[] up, int[] ndiv, int minN,
-                    double shrinkSml, double[] highUFact,
-                    int epsCorrection, boolean returnBounds) {
-        /*
-         * From version 0.65 on, we had rounding_eps := 1e-5, before, r..eps = 0 1e-7 is consistent
-         * with seq.default()
-         */
-        double roundingEps = 1e-7;
-
-        double h = highUFact[0];
-        double h5 = highUFact[1];
-
-        double dx;
-        double cell;
-        double unit;
-        double base;
-        double uu;
-        double ns;
-        double nu;
-        int k;
-        boolean iSmall;
-
-        dx = up[0] - lo[0];
-        /* cell := "scale" here */
-        if (dx == 0 && up[0] == 0) { /* up == lo == 0 */
-            cell = 1;
-            iSmall = true;
-        } else {
-            cell = Math.max(Math.abs(lo[0]), Math.abs(up[0]));
-            /* uu = upper bound on cell/unit */
-            // uu = (1 + (h5 >= 1.5 * h + .5)) ? 1 / (1 + h) : 1.5 / (1 + h5);
-            // How can above expression ever be zero?
-            uu = 1 / (1 + h);
-            /* added times 3, as several calculations here */
-            iSmall = dx < cell * uu * Math.max(1, ndiv[0]) * RRuntime.EPSILON * 3;
-        }
-
-        /* OLD: cell = FLT_EPSILON+ dx / ndiv[0]; FLT_EPSILON = 1.192e-07 */
-        if (iSmall) {
-            if (cell > 10) {
-                cell = 9 + cell / 10;
-            }
-            cell *= shrinkSml;
-            if (minN > 1) {
-                cell /= minN;
-            }
-        } else {
-            cell = dx;
-            if (ndiv[0] > 1) {
-                cell /= ndiv[0];
-            }
-        }
-
-        if (cell < 20 * Double.MIN_VALUE) {
-            warning(RError.Message.GENERIC, "Internal(pretty()): very small range.. corrected");
-            cell = 20 * Double.MIN_VALUE;
-        } else if (cell * 10 > Double.MAX_VALUE) {
-            warning(RError.Message.GENERIC, "Internal(pretty()): very large range.. corrected");
-            cell = .1 * Double.MAX_VALUE;
-        }
-        /*
-         * NB: the power can be negative and this relies on exact calculation, which glibc's exp10
-         * does not achieve
-         */
-        base = Math.pow(10.0, Math.floor(Math.log10(cell))); /* base <= cell < 10*base */
-
-        /*
-         * unit : from { 1,2,5,10 } * base such that |u - cell| is small, favoring larger (if h > 1,
-         * else smaller) u values; favor '5' more than '2' if h5 > h (default h5 = .5 + 1.5 h)
-         */
-        unit = base;
-        if ((uu = 2 * base) - cell < h * (cell - unit)) {
-            unit = uu;
-            if ((uu = 5 * base) - cell < h5 * (cell - unit)) {
-                unit = uu;
-                if ((uu = 10 * base) - cell < h * (cell - unit)) {
-                    unit = uu;
-                }
-            }
-        }
-        /*
-         * Result: c := cell, u := unit, b := base c in [ 1, (2+ h) /(1+h) ] b ==> u= b c in ( (2+
-         * h)/(1+h), (5+2h5)/(1+h5)] b ==> u= 2b c in ( (5+2h)/(1+h), (10+5h) /(1+h) ] b ==> u= 5b c
-         * in ((10+5h)/(1+h), 10 ) b ==> u=10b
-         *
-         * ===> 2/5 *(2+h)/(1+h) <= c/u <= (2+h)/(1+h)
-         */
-
-        ns = Math.floor(lo[0] / unit + roundingEps);
-        nu = Math.ceil(up[0] / unit - roundingEps);
-        if (epsCorrection != 0 && (epsCorrection > 1 || !iSmall)) {
-            if (lo[0] != 0.) {
-                lo[0] *= (1 - RRuntime.EPSILON);
-            } else {
-                lo[0] = -Double.MIN_VALUE;
-            }
-            if (up[0] != 0.) {
-                up[0] *= (1 + RRuntime.EPSILON);
-            } else {
-                up[0] = +Double.MIN_VALUE;
-            }
-        }
-
-        while (ns * unit > lo[0] + roundingEps * unit) {
-            ns--;
-        }
-
-        while (nu * unit < up[0] - roundingEps * unit) {
-            nu++;
-        }
-
-        k = (int) (0.5 + nu - ns);
-        if (k < minN) {
-            /* ensure that nu - ns == min_n */
-            k = minN - k;
-            if (ns >= 0.) {
-                nu += k / 2;
-                ns -= k / 2 + k % 2; /* ==> nu-ns = old(nu-ns) + min_n -k = min_n */
-            } else {
-                ns -= k / 2;
-                nu += k / 2 + k % 2;
-            }
-            ndiv[0] = minN;
-        } else {
-            ndiv[0] = k;
-        }
-        if (returnBounds) { /* if()'s to ensure that result covers original range */
-            if (ns * unit < lo[0]) {
-                lo[0] = ns * unit;
-            }
-            if (nu * unit > up[0]) {
-                up[0] = nu * unit;
-            }
-        } else {
-            lo[0] = ns;
-            up[0] = nu;
-        }
-        return unit;
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrettyIntevals.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrettyIntevals.java
new file mode 100644
index 0000000000..9ff10b704e
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrettyIntevals.java
@@ -0,0 +1,166 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2014,  The R Core Team
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.runtime;
+
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * Constructs m "pretty" values which cover the given interval. This code is used in both built-in
+ * {@code pretty} and the grid package.
+ */
+public final class PrettyIntevals {
+    private PrettyIntevals() {
+        // only static members
+    }
+
+    // transcribed from pretty.c
+
+    public static double pretty(RBaseNode errorCtx, double[] lo, double[] up, int[] ndiv, int minN,
+                    double shrinkSml, double[] highUFact,
+                    int epsCorrection, boolean returnBounds) {
+        /*
+         * From version 0.65 on, we had rounding_eps := 1e-5, before, r..eps = 0 1e-7 is consistent
+         * with seq.default()
+         */
+        double roundingEps = 1e-7;
+
+        double h = highUFact[0];
+        double h5 = highUFact[1];
+
+        double dx;
+        double cell;
+        double unit;
+        double base;
+        double uu;
+        double ns;
+        double nu;
+        int k;
+        boolean iSmall;
+
+        dx = up[0] - lo[0];
+        /* cell := "scale" here */
+        if (dx == 0 && up[0] == 0) { /* up == lo == 0 */
+            cell = 1;
+            iSmall = true;
+        } else {
+            cell = Math.max(Math.abs(lo[0]), Math.abs(up[0]));
+            /* uu = upper bound on cell/unit */
+            // uu = (1 + (h5 >= 1.5 * h + .5)) ? 1 / (1 + h) : 1.5 / (1 + h5);
+            // How can above expression ever be zero?
+            uu = 1 / (1 + h);
+            /* added times 3, as several calculations here */
+            iSmall = dx < cell * uu * Math.max(1, ndiv[0]) * RRuntime.EPSILON * 3;
+        }
+
+        /* OLD: cell = FLT_EPSILON+ dx / ndiv[0]; FLT_EPSILON = 1.192e-07 */
+        if (iSmall) {
+            if (cell > 10) {
+                cell = 9 + cell / 10;
+            }
+            cell *= shrinkSml;
+            if (minN > 1) {
+                cell /= minN;
+            }
+        } else {
+            cell = dx;
+            if (ndiv[0] > 1) {
+                cell /= ndiv[0];
+            }
+        }
+
+        if (cell < 20 * Double.MIN_VALUE) {
+            RError.warning(errorCtx, RError.Message.GENERIC, "Internal(pretty()): very small range.. corrected");
+            cell = 20 * Double.MIN_VALUE;
+        } else if (cell * 10 > Double.MAX_VALUE) {
+            RError.warning(errorCtx, RError.Message.GENERIC, "Internal(pretty()): very large range.. corrected");
+            cell = .1 * Double.MAX_VALUE;
+        }
+        /*
+         * NB: the power can be negative and this relies on exact calculation, which glibc's exp10
+         * does not achieve
+         */
+        base = Math.pow(10.0, Math.floor(Math.log10(cell))); /* base <= cell < 10*base */
+
+        /*
+         * unit : from { 1,2,5,10 } * base such that |u - cell| is small, favoring larger (if h > 1,
+         * else smaller) u values; favor '5' more than '2' if h5 > h (default h5 = .5 + 1.5 h)
+         */
+        unit = base;
+        if ((uu = 2 * base) - cell < h * (cell - unit)) {
+            unit = uu;
+            if ((uu = 5 * base) - cell < h5 * (cell - unit)) {
+                unit = uu;
+                if ((uu = 10 * base) - cell < h * (cell - unit)) {
+                    unit = uu;
+                }
+            }
+        }
+        /*
+         * Result: c := cell, u := unit, b := base c in [ 1, (2+ h) /(1+h) ] b ==> u= b c in ( (2+
+         * h)/(1+h), (5+2h5)/(1+h5)] b ==> u= 2b c in ( (5+2h)/(1+h), (10+5h) /(1+h) ] b ==> u= 5b c
+         * in ((10+5h)/(1+h), 10 ) b ==> u=10b
+         *
+         * ===> 2/5 *(2+h)/(1+h) <= c/u <= (2+h)/(1+h)
+         */
+
+        ns = Math.floor(lo[0] / unit + roundingEps);
+        nu = Math.ceil(up[0] / unit - roundingEps);
+        if (epsCorrection != 0 && (epsCorrection > 1 || !iSmall)) {
+            if (lo[0] != 0.) {
+                lo[0] *= (1 - RRuntime.EPSILON);
+            } else {
+                lo[0] = -Double.MIN_VALUE;
+            }
+            if (up[0] != 0.) {
+                up[0] *= (1 + RRuntime.EPSILON);
+            } else {
+                up[0] = +Double.MIN_VALUE;
+            }
+        }
+
+        while (ns * unit > lo[0] + roundingEps * unit) {
+            ns--;
+        }
+
+        while (nu * unit < up[0] - roundingEps * unit) {
+            nu++;
+        }
+
+        k = (int) (0.5 + nu - ns);
+        if (k < minN) {
+            /* ensure that nu - ns == min_n */
+            k = minN - k;
+            if (ns >= 0.) {
+                nu += k / 2;
+                ns -= k / 2 + k % 2; /* ==> nu-ns = old(nu-ns) + min_n -k = min_n */
+            } else {
+                ns -= k / 2;
+                nu += k / 2 + k % 2;
+            }
+            ndiv[0] = minN;
+        } else {
+            ndiv[0] = k;
+        }
+        if (returnBounds) { /* if()'s to ensure that result covers original range */
+            if (ns * unit < lo[0]) {
+                lo[0] = ns * unit;
+            }
+            if (nu * unit > up[0]) {
+                up[0] = nu * unit;
+            }
+        } else {
+            lo[0] = ns;
+            up[0] = nu;
+        }
+        return unit;
+    }
+}
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index a72c103a9a..d5600cf28a 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -185,6 +185,7 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/L
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Merge.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Pretty.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrettyIntevals.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java,purdue.copyright
@@ -744,6 +745,7 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/p
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/Deriv.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/DerivVisitor.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPretty.java,gnu_r_murrel_core.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRectBounds.java,gnu_r_murrel_core.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircleBounds.java,gnu_r_murrel_core.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLocnBounds.java,gnu_r_murrel_core.copyright
-- 
GitLab