From bc7087e774f43e5879522d7be49dfe371941af6c Mon Sep 17 00:00:00 2001
From: bourgesl <bourges.laurent@gmail.com>
Date: Fri, 29 Oct 2021 00:17:16 +0200
Subject: [PATCH] removed float-variant + cleanup

---
 pom.xml                                       |   43 -
 src/main/java/net/jafama/CmnFastMath.java     | 2134 ------------
 src/main/java/net/jafama/DoubleWrapper.java   |   24 -
 src/main/java/net/jafama/FastMath.java        | 2975 -----------------
 src/main/java/net/jafama/IntWrapper.java      |   24 -
 src/main/java/net/jafama/LICENSE-2.0.txt      |  202 --
 src/main/java/net/jafama/NumbersUtils.java    | 2660 ---------------
 src/main/java/net/jafama/README.txt           |  290 --
 .../sun/java2d/marlin/ArrayCacheByte.java     |    5 +-
 .../sun/java2d/marlin/ArrayCacheDouble.java   |    5 +-
 .../sun/java2d/marlin/ArrayCacheFloat.java    |  269 --
 .../java/sun/java2d/marlin/ArrayCacheInt.java |    5 +-
 .../sun/java2d/marlin/ArrayCacheIntClean.java |    5 +-
 .../java2d/marlin/CollinearSimplifier.java    |   39 +-
 src/main/java/sun/java2d/marlin/Curve.java    |  164 +-
 .../java2d/marlin/DCollinearSimplifier.java   |  158 -
 src/main/java/sun/java2d/marlin/DCurve.java   |  264 --
 src/main/java/sun/java2d/marlin/DDasher.java  | 1136 -------
 src/main/java/sun/java2d/marlin/DHelpers.java | 1020 ------
 .../java2d/marlin/DMarlinRenderingEngine.java |   78 +-
 .../sun/java2d/marlin/DPathSimplifier.java    |  165 -
 .../java/sun/java2d/marlin/DRenderer.java     | 1578 ---------
 .../sun/java2d/marlin/DRendererContext.java   |  291 --
 src/main/java/sun/java2d/marlin/DStroker.java | 1388 --------
 .../marlin/DTransformingPathConsumer2D.java   | 1389 --------
 src/main/java/sun/java2d/marlin/Dasher.java   |  367 +-
 src/main/java/sun/java2d/marlin/Helpers.java  |  399 ++-
 .../sun/java2d/marlin/IRendererContext.java   |   36 -
 .../java/sun/java2d/marlin/MarlinCache.java   |    4 +-
 .../sun/java2d/marlin/MarlinProperties.java   |    5 +-
 .../sun/java2d/marlin/MarlinRenderer.java     |   30 -
 .../java2d/marlin/MarlinRenderingEngine.java  | 1227 -------
 .../java2d/marlin/MarlinTileGenerator.java    |   29 +-
 .../java/sun/java2d/marlin/MergeSort.java     |    1 +
 .../sun/java2d/marlin/PathSimplifier.java     |   46 +-
 src/main/java/sun/java2d/marlin/Renderer.java |  194 +-
 .../sun/java2d/marlin/RendererContext.java    |  107 +-
 src/main/java/sun/java2d/marlin/Stroker.java  |  529 ++-
 .../marlin/TransformingPathConsumer2D.java    |  488 ++-
 src/main/java/test/ClipShapeTest.java         |   15 +-
 src/main/java/test/EndlessLoop.java           |    6 -
 .../services/sun.java2d.pipe.RenderingEngine  |    1 -
 .../java2d/marlin}/Version.properties         |    0
 src/test/java/ClipShapeTest.java              |   15 +-
 44 files changed, 1235 insertions(+), 18575 deletions(-)
 delete mode 100644 src/main/java/net/jafama/CmnFastMath.java
 delete mode 100644 src/main/java/net/jafama/DoubleWrapper.java
 delete mode 100644 src/main/java/net/jafama/FastMath.java
 delete mode 100644 src/main/java/net/jafama/IntWrapper.java
 delete mode 100644 src/main/java/net/jafama/LICENSE-2.0.txt
 delete mode 100644 src/main/java/net/jafama/NumbersUtils.java
 delete mode 100644 src/main/java/net/jafama/README.txt
 delete mode 100644 src/main/java/sun/java2d/marlin/ArrayCacheFloat.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DCollinearSimplifier.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DCurve.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DDasher.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DHelpers.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DPathSimplifier.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DRenderer.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DRendererContext.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DStroker.java
 delete mode 100644 src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java
 delete mode 100644 src/main/java/sun/java2d/marlin/IRendererContext.java
 delete mode 100644 src/main/java/sun/java2d/marlin/MarlinRenderer.java
 delete mode 100644 src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java
 rename src/main/resources/{org/marlin/pisces => sun/java2d/marlin}/Version.properties (100%)

diff --git a/pom.xml b/pom.xml
index d6f2957..9c721f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,27 +178,6 @@
                                 -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar</argLine>
                         </configuration>
                     </execution>
-                    <execution>
-                        <id>integration-test-float</id>
-                        <phase>integration-test</phase>
-                        <goals>
-                            <goal>integration-test</goal>
-                            <goal>verify</goal>
-                        </goals>
-                        <configuration>
-                            <skip>${integration.skip}</skip>
-                            <includes>
-                                <include>**/RunJUnitTest.java</include>
-                            </includes>
-                            <argLine>
-                                -Xms1g -Xmx1g
-                                -Dsun.java2d.renderer.log=true
-                                -Djava.util.logging.config.file=src/test/resources/logging.properties
-                                -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine
-                                -Xbootclasspath/a:${basedir}/target/${project.build.finalName}.jar
-                                -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar</argLine>
-                        </configuration>
-                    </execution>
 
                     <!-- RunJUnitLongTest -->
                     <execution>
@@ -223,28 +202,6 @@
                                 -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar</argLine>
                         </configuration>
                     </execution>
-                    <execution>
-                        <id>integration-long-test-float</id>
-                        <phase>integration-test</phase>
-                        <goals>
-                            <goal>integration-test</goal>
-                            <goal>verify</goal>
-                        </goals>
-                        <configuration>
-                            <skip>${integration.skip.long}</skip>
-                            <includes>
-                                <include>**/RunJUnitLongTest.java</include>
-                            </includes>
-                            <argLine>
-                                -Xms1g -Xmx1g
-                                -Dsun.java2d.renderer.log=true
-                                -DClipShapeTest.numTests=5000
-                                -Djava.util.logging.config.file=src/test/resources/logging.properties
-                                -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine
-                                -Xbootclasspath/a:${basedir}/target/${project.build.finalName}.jar
-                                -Xbootclasspath/p:${basedir}/target/${project.build.finalName}-sun-java2d.jar</argLine>
-                        </configuration>
-                    </execution>
                 </executions>
             </plugin>
         </plugins>
diff --git a/src/main/java/net/jafama/CmnFastMath.java b/src/main/java/net/jafama/CmnFastMath.java
deleted file mode 100644
index f819237..0000000
--- a/src/main/java/net/jafama/CmnFastMath.java
+++ /dev/null
@@ -1,2134 +0,0 @@
-/*
- * Copyright 2014-2015 Jeff Hain
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * =============================================================================
- * Notice of fdlibm package this program is partially derived from:
- *
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunSoft, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * =============================================================================
- */
-package net.jafama;
-
-/**
- * Stuffs for FastMath and StrictFastMath.
- */
-abstract class CmnFastMath {
-
-    /*
-     * For trigonometric functions, use of look-up tables and Taylor-Lagrange formula
-     * with 4 derivatives (more take longer to compute and don't add much accuracy,
-     * less require larger tables (which use more memory, take more time to initialize,
-     * and are slower to access (at least on the machine they were developed on))).
-     *
-     * For angles reduction of cos/sin/tan functions:
-     * - for small values, instead of reducing angles, and then computing the best index
-     *   for look-up tables, we compute this index right away, and use it for reduction,
-     * - for large values, treatments derived from fdlibm package are used, as done in
-     *   java.lang.Math. They are faster but still "slow", so if you work with
-     *   large numbers and need speed over accuracy for them, you might want to use
-     *   normalizeXXXFast treatments before your function, or modify cos/sin/tan
-     *   so that they call the fast normalization treatments instead of the accurate ones.
-     *   NB: If an angle is huge (like PI*1e20), in double precision format its last digits
-     *       are zeros, which most likely is not the case for the intended value, and doing
-     *       an accurate reduction on a very inaccurate value is most likely pointless.
-     *       But it gives some sort of coherence that could be needed in some cases.
-     *
-     * Multiplication on double appears to be about as fast (or not much slower) than call
-     * to <double_array>[<index>], and regrouping some doubles in a private class, to use
-     * index only once, does not seem to speed things up, so:
-     * - for uniformly tabulated values, to retrieve the parameter corresponding to
-     *   an index, we recompute it rather than using an array to store it,
-     * - for cos/sin, we recompute derivatives divided by (multiplied by inverse of)
-     *   factorial each time, rather than storing them in arrays.
-     *
-     * Lengths of look-up tables are usually of the form 2^n+1, for their values to be
-     * of the form (<a_constant> * k/2^n, k in 0 .. 2^n), so that particular values
-     * (PI/2, etc.) are "exactly" computed, as well as for other reasons.
-     *
-     * Tables are put in specific inner classes, to be lazily initialized.
-     * Always doing strict tables initialization, even if StrictFastMath delegates
-     * to StrictMath and doesn't use tables, which makes tables initialization a bit
-     * slower but code simpler.
-     * Using redefined pure Java treatments during tables initialization,
-     * instead of Math or StrictMath ones (even asin(double)), can be very slow,
-     * because class loading is likely not to be optimized.
-     *
-     * Most math treatments I could find on the web, including "fast" ones,
-     * usually take care of special cases (NaN, etc.) at the beginning, and
-     * then deal with the general case, which adds a useless overhead for the
-     * general (and common) case. In this class, special cases are only dealt
-     * with when needed, and if the general case does not already handle them.
-     */
-
-    /*
-     * Regarding strictfp-ness:
-     *
-     * Switching from/to strictfp has some overhead, so we try to only
-     * strictfp-ize when needed (or when clueless).
-     * Compile-time constants are computed in a FP-strict way, so no need
-     * to make this whole class strictfp.
-     */
-
-    //--------------------------------------------------------------------------
-    // CONFIGURATION
-    //--------------------------------------------------------------------------
-
-    /*
-     * FastMath
-     */
-
-    static final boolean FM_USE_JDK_MATH = getBooleanProperty("jafama.usejdk", false);
-
-    /**
-     * Used for both FastMath.log(double) and FastMath.log10(double).
-     */
-    static final boolean FM_USE_REDEFINED_LOG = getBooleanProperty("jafama.fastlog", false);
-
-    static final boolean FM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.fastsqrt", false);
-
-    /**
-     * Set it to true if FastMath.sqrt(double) is slow
-     * (more tables, but less calls to FastMath.sqrt(double)).
-     */
-    static final boolean FM_USE_POWTABS_FOR_ASIN = false;
-
-    /*
-     * StrictFastMath
-     */
-
-    static final boolean SFM_USE_JDK_MATH = getBooleanProperty("jafama.strict.usejdk", false);
-
-    /**
-     * Used for both StrictFastMath.log(double) and StrictFastMath.log10(double).
-     * True by default because the StrictMath implementations can be slow.
-     */
-    static final boolean SFM_USE_REDEFINED_LOG = getBooleanProperty("jafama.strict.fastlog", true);
-
-    static final boolean SFM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.strict.fastsqrt", false);
-
-    /**
-     * Set it to true if StrictFastMath.sqrt(double) is slow
-     * (more tables, but less calls to StrictFastMath.sqrt(double)).
-     */
-    static final boolean SFM_USE_POWTABS_FOR_ASIN = false;
-
-    /*
-     * Common to FastMath and StrictFastMath.
-     */
-
-    /**
-     * Using two pow tab can just make things barely faster,
-     * and could relatively hurt in case of cache-misses,
-     * especially for methods that otherwise wouldn't rely
-     * on any tab, so we don't use it.
-     */
-    static final boolean USE_TWO_POW_TAB = false;
-
-    /**
-     * Because on some architectures, some casts can be slow,
-     * especially for large values.
-     * Might make things a bit slower for latest architectures,
-     * but not as much as it makes them faster for older ones.
-     */
-    static final boolean ANTI_SLOW_CASTS = true;
-
-    /**
-     * If some methods get JIT-optimized, they might crash
-     * if they contain "(var == xxx)" with var being NaN
-     * (can happen with Java 6u29).
-     *
-     * The crash does not happen if we replace "==" with "<" or ">".
-     *
-     * Only the code that has been observed to trigger the bug
-     * has been modified.
-     */
-    static final boolean ANTI_JIT_OPTIM_CRASH_ON_NAN = true;
-
-    //--------------------------------------------------------------------------
-    // GENERAL CONSTANTS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Closest double approximation of e.
-     */
-    public static final double E = Math.E;
-
-    /**
-     * Closest double approximation of pi, which is inferior to mathematical pi:
-     * pi ~= 3.14159265358979323846...
-     * PI ~= 3.141592653589793
-     */
-    public static final double PI = Math.PI;
-
-    /**
-     * High double approximation of pi, which is further from pi
-     * than the low approximation PI:
-     *     pi ~= 3.14159265358979323846...
-     *     PI ~= 3.141592653589793
-     * PI_SUP ~= 3.1415926535897936
-     */
-    public static final double PI_SUP = Double.longBitsToDouble(Double.doubleToRawLongBits(Math.PI)+1);
-
-    static final double ONE_DIV_F2 = 1/2.0;
-    static final double ONE_DIV_F3 = 1/6.0;
-    static final double ONE_DIV_F4 = 1/24.0;
-
-    static final float TWO_POW_23_F = (float)NumbersUtils.twoPow(23);
-
-    static final double TWO_POW_24 = NumbersUtils.twoPow(24);
-    private static final double TWO_POW_N24 = NumbersUtils.twoPow(-24);
-
-    static final double TWO_POW_26 = NumbersUtils.twoPow(26);
-    static final double TWO_POW_N26 = NumbersUtils.twoPow(-26);
-
-    // First double value (from zero) such as (value+-1/value == value).
-    static final double TWO_POW_27 = NumbersUtils.twoPow(27);
-    static final double TWO_POW_N27 = NumbersUtils.twoPow(-27);
-
-    static final double TWO_POW_N28 = NumbersUtils.twoPow(-28);
-
-    static final double TWO_POW_52 = NumbersUtils.twoPow(52);
-
-    static final double TWO_POW_N55 = NumbersUtils.twoPow(-55);
-
-    static final double TWO_POW_66 = NumbersUtils.twoPow(66);
-
-    static final double TWO_POW_512 = NumbersUtils.twoPow(512);
-    static final double TWO_POW_N512 = NumbersUtils.twoPow(-512);
-
-    /**
-     * Double.MIN_NORMAL since Java 6.
-     */
-    static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308
-
-    // Not storing float/double mantissa size in constants,
-    // for 23 and 52 are shorter to read and more
-    // bitwise-explicit than some constant's name.
-
-    static final int MIN_DOUBLE_EXPONENT = -1074;
-    static final int MIN_DOUBLE_NORMAL_EXPONENT = -1022;
-    static final int MAX_DOUBLE_EXPONENT = 1023;
-
-    static final int MIN_FLOAT_NORMAL_EXPONENT = -126;
-    static final int MAX_FLOAT_EXPONENT = 127;
-
-    private static final double SQRT_2 = StrictMath.sqrt(2.0);
-
-    static final double LOG_2 = StrictMath.log(2.0);
-    static final double LOG_TWO_POW_27 = StrictMath.log(TWO_POW_27);
-    static final double LOG_DOUBLE_MAX_VALUE = StrictMath.log(Double.MAX_VALUE);
-
-    static final double INV_LOG_10 = 1.0/StrictMath.log(10.0);
-
-    static final double DOUBLE_BEFORE_60 = Double.longBitsToDouble(Double.doubleToRawLongBits(60.0)-1);
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS FOR NORMALIZATIONS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Table of constants for 1/(PI/2), 282 Hex digits (enough for normalizing doubles).
-     * 1/(PI/2) approximation = sum of TWO_OVER_PI_TAB[i]*2^(-24*(i+1)).
-     *
-     * double and not int, to avoid int-to-double cast during computations.
-     */
-    private static final double TWO_OVER_PI_TAB[] = {
-        0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
-        0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
-        0x424DD2, 0xe00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
-        0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
-        0x3991d6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8,
-        0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
-        0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5,
-        0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08,
-        0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3,
-        0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
-        0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B};
-
-    /*
-     * Constants for PI/2. Only the 23 most significant bits of each mantissa are used.
-     * 2*PI approximation = sum of TWOPI_TAB<i>.
-     */
-    private static final double PIO2_TAB0 = Double.longBitsToDouble(0x3FF921FB40000000L);
-    private static final double PIO2_TAB1 = Double.longBitsToDouble(0x3E74442D00000000L);
-    private static final double PIO2_TAB2 = Double.longBitsToDouble(0x3CF8469880000000L);
-    private static final double PIO2_TAB3 = Double.longBitsToDouble(0x3B78CC5160000000L);
-    private static final double PIO2_TAB4 = Double.longBitsToDouble(0x39F01B8380000000L);
-    private static final double PIO2_TAB5 = Double.longBitsToDouble(0x387A252040000000L);
-
-    static final double PIO2_INV = Double.longBitsToDouble(0x3FE45F306DC9C883L); // 6.36619772367581382433e-01 53 bits of 2/pi
-    static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2
-    static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI
-    static final double PI_INV = PIO2_INV/2;
-    static final double PI_HI = 2*PIO2_HI;
-    static final double PI_LO = 2*PIO2_LO;
-    static final double TWOPI_INV = PIO2_INV/4;
-    static final double TWOPI_HI = 4*PIO2_HI;
-    static final double TWOPI_LO = 4*PIO2_LO;
-
-    /**
-     * Bit = 0 where quadrant is encoded in remainder bits.
-     */
-    private static final long QUADRANT_BITS_0_MASK = 0xCFFFFFFFFFFFFFFFL;
-
-    /**
-     * Remainder bits where quadrant is encoded, 0 elsewhere.
-     */
-    private static final long QUADRANT_PLACE_BITS = 0x3000000000000000L;
-
-    /**
-     * fdlibm uses 2^19*PI/2 here.
-     * With 2^18*PI/2 we would be more accurate, for example when normalizing
-     * 822245.903631403, which is close to 2^19*PI/2, but we are still in
-     * our accuracy tolerance with fdlibm's value (but not 2^20*PI/2) so we
-     * stick to it, to help being faster than (Strict)Math for values in
-     * [2^18*PI/2,2^19*PI/2].
-     *
-     * For tests, can use a smaller value, for heavy remainder
-     * not to only be used with huge values.
-     */
-    static final double NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2 = StrictMath.pow(2.0,19.0)*(Math.PI/2);
-
-    /**
-     * 2*Math.PI, normalized into [-PI,PI], as returned by
-     * StrictMath.asin(StrictMath.sin(2*Math.PI))
-     * (asin behaves as identity for this).
-     *
-     * NB: NumbersUtils.minus2PI(2*Math.PI) returns  -2.449293598153844E-16,
-     * which is different due to not using an accurate enough definition of PI.
-     */
-    static final double TWO_MATH_PI_IN_MINUS_PI_PI = -2.4492935982947064E-16;
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR SIN AND COS
-    //--------------------------------------------------------------------------
-
-    static final int SIN_COS_TABS_SIZE = (1<<getTabSizePower(11)) + 1;
-    static final double SIN_COS_DELTA_HI = TWOPI_HI/(SIN_COS_TABS_SIZE-1);
-    static final double SIN_COS_DELTA_LO = TWOPI_LO/(SIN_COS_TABS_SIZE-1);
-    static final double SIN_COS_INDEXER = 1/(SIN_COS_DELTA_HI+SIN_COS_DELTA_LO);
-
-    static final class MyTSinCos {
-        static final double[] sinTab = new double[SIN_COS_TABS_SIZE];
-        static final double[] cosTab = new double[SIN_COS_TABS_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            final int SIN_COS_PI_INDEX = (SIN_COS_TABS_SIZE-1)/2;
-            final int SIN_COS_PI_MUL_2_INDEX = 2*SIN_COS_PI_INDEX;
-            final int SIN_COS_PI_MUL_0_5_INDEX = SIN_COS_PI_INDEX/2;
-            final int SIN_COS_PI_MUL_1_5_INDEX = 3*SIN_COS_PI_INDEX/2;
-            for (int i=0;i<SIN_COS_TABS_SIZE;i++) {
-                // angle: in [0,2*PI] (doesn't seem to help to have it in [-PI,PI]).
-                double angle = i * SIN_COS_DELTA_HI + i * SIN_COS_DELTA_LO;
-                double sinAngle = StrictMath.sin(angle);
-                double cosAngle = StrictMath.cos(angle);
-                // For indexes corresponding to zero cosine or sine, we make sure
-                // the value is zero and not an epsilon, since each value
-                // corresponds to sin-or-cos(i*PI/n), where PI is a more accurate
-                // definition of PI than Math.PI.
-                // This allows for a much better accuracy for results close to zero.
-                if (i == SIN_COS_PI_INDEX) {
-                    sinAngle = 0.0;
-                } else if (i == SIN_COS_PI_MUL_2_INDEX) {
-                    sinAngle = 0.0;
-                } else if (i == SIN_COS_PI_MUL_0_5_INDEX) {
-                    cosAngle = 0.0;
-                } else if (i == SIN_COS_PI_MUL_1_5_INDEX) {
-                    cosAngle = 0.0;
-                }
-                sinTab[i] = sinAngle;
-                cosTab[i] = cosAngle;
-            }
-        }
-    }
-
-    /**
-     * Max abs value for index-based reduction, above which we use regular angle normalization.
-     * This value must be < (Integer.MAX_VALUE / SIN_COS_INDEXER), to stay in range of int type.
-     * If too high, error gets larger because index-based reduction doesn't use an accurate
-     * enough definition of PI.
-     * If too low, and if we would be using remainder into [-PI,PI] instead of into [-PI/4,PI/4],
-     * error would get larger as well, because remainder would just provide a double, while
-     * index-based reduction is more accurate, using delta from index values and HI/LO values.
-     */
-    static final double SIN_COS_MAX_VALUE_FOR_INT_MODULO = ((Integer.MAX_VALUE>>9) / SIN_COS_INDEXER) * 0.99;
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR TAN
-    //--------------------------------------------------------------------------
-
-    // We use the following formula:
-    // 1) tan(-x) = -tan(x)
-    // 2) tan(x) = 1/tan(PI/2-x)
-    // ---> we only have to compute tan(x) on [0,A] with PI/4<=A<PI/2.
-
-    /**
-     * We use indexing past look-up tables, so that indexing information
-     * allows for fast recomputation of angle in [0,PI/2] range.
-     */
-    static final int TAN_VIRTUAL_TABS_SIZE = (1<<getTabSizePower(12)) + 1;
-
-    /**
-     * Must be >= 45deg, and supposed to be >= 51.4deg, as fdlibm code is not
-     * supposed to work with values inferior to that (51.4deg is about
-     * (PI/2-Double.longBitsToDouble(0x3FE5942800000000L))).
-     */
-    static final double TAN_MAX_VALUE_FOR_TABS = StrictMath.toRadians(77.0);
-
-    static final int TAN_TABS_SIZE = (int)((TAN_MAX_VALUE_FOR_TABS/(Math.PI/2)) * (TAN_VIRTUAL_TABS_SIZE-1)) + 1;
-    static final double TAN_DELTA_HI = PIO2_HI/(TAN_VIRTUAL_TABS_SIZE-1);
-    static final double TAN_DELTA_LO = PIO2_LO/(TAN_VIRTUAL_TABS_SIZE-1);
-    static final double TAN_INDEXER = 1/(TAN_DELTA_HI+TAN_DELTA_LO);
-
-    static final class MyTTan {
-        static final double[] tanTab = new double[TAN_TABS_SIZE];
-        static final double[] tanDer1DivF1Tab = new double[TAN_TABS_SIZE];
-        static final double[] tanDer2DivF2Tab = new double[TAN_TABS_SIZE];
-        static final double[] tanDer3DivF3Tab = new double[TAN_TABS_SIZE];
-        static final double[] tanDer4DivF4Tab = new double[TAN_TABS_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=0;i<TAN_TABS_SIZE;i++) {
-                // angle: in [0,TAN_MAX_VALUE_FOR_TABS].
-                double angle = i * TAN_DELTA_HI + i * TAN_DELTA_LO;
-                double sinAngle = StrictMath.sin(angle);
-                double cosAngle = StrictMath.cos(angle);
-                double cosAngleInv = 1/cosAngle;
-                double cosAngleInv2 = cosAngleInv*cosAngleInv;
-                double cosAngleInv3 = cosAngleInv2*cosAngleInv;
-                double cosAngleInv4 = cosAngleInv2*cosAngleInv2;
-                double cosAngleInv5 = cosAngleInv3*cosAngleInv2;
-                tanTab[i] = sinAngle * cosAngleInv;
-                tanDer1DivF1Tab[i] = cosAngleInv2;
-                tanDer2DivF2Tab[i] = ((2*sinAngle)*cosAngleInv3) * ONE_DIV_F2;
-                tanDer3DivF3Tab[i] = ((2*(1+2*sinAngle*sinAngle))*cosAngleInv4) * ONE_DIV_F3;
-                tanDer4DivF4Tab[i] = ((8*sinAngle*(2+sinAngle*sinAngle))*cosAngleInv5) * ONE_DIV_F4;
-            }
-        }
-    }
-
-    /**
-     * Max abs value for fast modulo, above which we use regular angle normalization.
-     * This value must be < (Integer.MAX_VALUE / TAN_INDEXER), to stay in range of int type.
-     * If too high, error gets larger because index-based reduction doesn't use an accurate
-     * enough definition of PI.
-     * If too low, error gets larger as well, because we use remainder into [-PI/2,PI/2],
-     * just provides a double, while index-based reduction is more accurate, using delta
-     * from index values and HI/LO values.
-     */
-    static final double TAN_MAX_VALUE_FOR_INT_MODULO = (((Integer.MAX_VALUE>>9) / TAN_INDEXER) * 0.99);
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR ACOS, ASIN
-    //--------------------------------------------------------------------------
-
-    // We use the following formula:
-    // 1) acos(x) = PI/2 - asin(x)
-    // 2) asin(-x) = -asin(x)
-    // ---> we only have to compute asin(x) on [0,1].
-    // For values not close to +-1, we use look-up tables;
-    // for values near +-1, we use code derived from fdlibm.
-
-    /**
-     * Supposed to be >= sin(77.2deg), as fdlibm code is supposed to work with values > 0.975,
-     * but seems to work well enough as long as value >= sin(25deg).
-     */
-    static final double ASIN_MAX_VALUE_FOR_TABS = StrictMath.sin(StrictMath.toRadians(73.0));
-
-    static final int ASIN_TABS_SIZE = (1<<getTabSizePower(13)) + 1;
-    static final double ASIN_DELTA = ASIN_MAX_VALUE_FOR_TABS/(ASIN_TABS_SIZE - 1);
-    static final double ASIN_INDEXER = 1/ASIN_DELTA;
-
-    static final class MyTAsin {
-        static final double[] asinTab = new double[ASIN_TABS_SIZE];
-        static final double[] asinDer1DivF1Tab = new double[ASIN_TABS_SIZE];
-        static final double[] asinDer2DivF2Tab = new double[ASIN_TABS_SIZE];
-        static final double[] asinDer3DivF3Tab = new double[ASIN_TABS_SIZE];
-        static final double[] asinDer4DivF4Tab = new double[ASIN_TABS_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=0;i<ASIN_TABS_SIZE;i++) {
-                // x: in [0,ASIN_MAX_VALUE_FOR_TABS].
-                double x = i * ASIN_DELTA;
-                double oneMinusXSqInv = 1/(1-x*x);
-                double oneMinusXSqInv0_5 = StrictMath.sqrt(oneMinusXSqInv);
-                double oneMinusXSqInv1_5 = oneMinusXSqInv0_5*oneMinusXSqInv;
-                double oneMinusXSqInv2_5 = oneMinusXSqInv1_5*oneMinusXSqInv;
-                double oneMinusXSqInv3_5 = oneMinusXSqInv2_5*oneMinusXSqInv;
-                asinTab[i] = StrictMath.asin(x);
-                asinDer1DivF1Tab[i] = oneMinusXSqInv0_5;
-                asinDer2DivF2Tab[i] = (x*oneMinusXSqInv1_5) * ONE_DIV_F2;
-                asinDer3DivF3Tab[i] = ((1+2*x*x)*oneMinusXSqInv2_5) * ONE_DIV_F3;
-                asinDer4DivF4Tab[i] = ((5+2*x*(2+x*(5-2*x)))*oneMinusXSqInv3_5) * ONE_DIV_F4;
-            }
-        }
-    }
-
-    static final double ASIN_MAX_VALUE_FOR_POWTABS = StrictMath.sin(StrictMath.toRadians(88.6));
-    static final int ASIN_POWTABS_POWER = 84;
-
-    static final double ASIN_POWTABS_ONE_DIV_MAX_VALUE = 1/ASIN_MAX_VALUE_FOR_POWTABS;
-    static final int ASIN_POWTABS_SIZE = (FM_USE_POWTABS_FOR_ASIN || SFM_USE_POWTABS_FOR_ASIN) ? (1<<getTabSizePower(12)) + 1 : 0;
-    static final int ASIN_POWTABS_SIZE_MINUS_ONE = ASIN_POWTABS_SIZE - 1;
-
-    static final class MyTAsinPow {
-        static final double[] asinParamPowTab = new double[ASIN_POWTABS_SIZE];
-        static final double[] asinPowTab = new double[ASIN_POWTABS_SIZE];
-        static final double[] asinDer1DivF1PowTab = new double[ASIN_POWTABS_SIZE];
-        static final double[] asinDer2DivF2PowTab = new double[ASIN_POWTABS_SIZE];
-        static final double[] asinDer3DivF3PowTab = new double[ASIN_POWTABS_SIZE];
-        static final double[] asinDer4DivF4PowTab = new double[ASIN_POWTABS_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            if (FM_USE_POWTABS_FOR_ASIN || SFM_USE_POWTABS_FOR_ASIN) {
-                for (int i=0;i<ASIN_POWTABS_SIZE;i++) {
-                    // x: in [0,ASIN_MAX_VALUE_FOR_POWTABS].
-                    double x = StrictMath.pow(i*(1.0/ASIN_POWTABS_SIZE_MINUS_ONE), 1.0/ASIN_POWTABS_POWER) * ASIN_MAX_VALUE_FOR_POWTABS;
-                    double oneMinusXSqInv = 1/(1-x*x);
-                    double oneMinusXSqInv0_5 = StrictMath.sqrt(oneMinusXSqInv);
-                    double oneMinusXSqInv1_5 = oneMinusXSqInv0_5*oneMinusXSqInv;
-                    double oneMinusXSqInv2_5 = oneMinusXSqInv1_5*oneMinusXSqInv;
-                    double oneMinusXSqInv3_5 = oneMinusXSqInv2_5*oneMinusXSqInv;
-                    asinParamPowTab[i] = x;
-                    asinPowTab[i] = StrictMath.asin(x);
-                    asinDer1DivF1PowTab[i] = oneMinusXSqInv0_5;
-                    asinDer2DivF2PowTab[i] = (x*oneMinusXSqInv1_5) * ONE_DIV_F2;
-                    asinDer3DivF3PowTab[i] = ((1+2*x*x)*oneMinusXSqInv2_5) * ONE_DIV_F3;
-                    asinDer4DivF4PowTab[i] = ((5+2*x*(2+x*(5-2*x)))*oneMinusXSqInv3_5) * ONE_DIV_F4;
-                }
-            }
-        }
-    }
-
-    static final double ASIN_PIO2_HI = Double.longBitsToDouble(0x3FF921FB54442D18L); // 1.57079632679489655800e+00
-    static final double ASIN_PIO2_LO = Double.longBitsToDouble(0x3C91A62633145C07L); // 6.12323399573676603587e-17
-    static final double ASIN_PS0 = Double.longBitsToDouble(0x3fc5555555555555L); //  1.66666666666666657415e-01
-    static final double ASIN_PS1 = Double.longBitsToDouble(0xbfd4d61203eb6f7dL); // -3.25565818622400915405e-01
-    static final double ASIN_PS2 = Double.longBitsToDouble(0x3fc9c1550e884455L); //  2.01212532134862925881e-01
-    static final double ASIN_PS3 = Double.longBitsToDouble(0xbfa48228b5688f3bL); // -4.00555345006794114027e-02
-    static final double ASIN_PS4 = Double.longBitsToDouble(0x3f49efe07501b288L); //  7.91534994289814532176e-04
-    static final double ASIN_PS5 = Double.longBitsToDouble(0x3f023de10dfdf709L); //  3.47933107596021167570e-05
-    static final double ASIN_QS1 = Double.longBitsToDouble(0xc0033a271c8a2d4bL); // -2.40339491173441421878e+00
-    static final double ASIN_QS2 = Double.longBitsToDouble(0x40002ae59c598ac8L); //  2.02094576023350569471e+00
-    static final double ASIN_QS3 = Double.longBitsToDouble(0xbfe6066c1b8d0159L); // -6.88283971605453293030e-01
-    static final double ASIN_QS4 = Double.longBitsToDouble(0x3fb3b8c5b12e9282L); //  7.70381505559019352791e-02
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR ATAN
-    //--------------------------------------------------------------------------
-
-    // We use the formula atan(-x) = -atan(x)
-    // ---> we only have to compute atan(x) on [0,+Infinity[.
-    // For values corresponding to angles not close to +-PI/2, we use look-up tables;
-    // for values corresponding to angles near +-PI/2, we use code derived from fdlibm.
-
-    /**
-     * Supposed to be >= tan(67.7deg), as fdlibm code is supposed to work with values > 2.4375.
-     */
-    static final double ATAN_MAX_VALUE_FOR_TABS = StrictMath.tan(StrictMath.toRadians(74.0));
-
-    static final int ATAN_TABS_SIZE = (1<<getTabSizePower(12)) + 1;
-    static final double ATAN_DELTA = ATAN_MAX_VALUE_FOR_TABS/(ATAN_TABS_SIZE - 1);
-    static final double ATAN_INDEXER = 1/ATAN_DELTA;
-
-    static final class MyTAtan {
-        static final double[] atanTab = new double[ATAN_TABS_SIZE];
-        static final double[] atanDer1DivF1Tab = new double[ATAN_TABS_SIZE];
-        static final double[] atanDer2DivF2Tab = new double[ATAN_TABS_SIZE];
-        static final double[] atanDer3DivF3Tab = new double[ATAN_TABS_SIZE];
-        static final double[] atanDer4DivF4Tab = new double[ATAN_TABS_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=0;i<ATAN_TABS_SIZE;i++) {
-                // x: in [0,ATAN_MAX_VALUE_FOR_TABS].
-                double x = i * ATAN_DELTA;
-                double onePlusXSqInv = 1/(1+x*x);
-                double onePlusXSqInv2 = onePlusXSqInv*onePlusXSqInv;
-                double onePlusXSqInv3 = onePlusXSqInv2*onePlusXSqInv;
-                double onePlusXSqInv4 = onePlusXSqInv2*onePlusXSqInv2;
-                atanTab[i] = StrictMath.atan(x);
-                atanDer1DivF1Tab[i] = onePlusXSqInv;
-                atanDer2DivF2Tab[i] = (-2*x*onePlusXSqInv2) * ONE_DIV_F2;
-                atanDer3DivF3Tab[i] = ((-2+6*x*x)*onePlusXSqInv3) * ONE_DIV_F3;
-                atanDer4DivF4Tab[i] = ((24*x*(1-x*x))*onePlusXSqInv4) * ONE_DIV_F4;
-            }
-        }
-    }
-
-    static final double ATAN_HI3 = Double.longBitsToDouble(0x3ff921fb54442d18L); // 1.57079632679489655800e+00 atan(inf)hi
-    static final double ATAN_LO3 = Double.longBitsToDouble(0x3c91a62633145c07L); // 6.12323399573676603587e-17 atan(inf)lo
-    static final double ATAN_AT0 = Double.longBitsToDouble(0x3fd555555555550dL); //  3.33333333333329318027e-01
-    static final double ATAN_AT1 = Double.longBitsToDouble(0xbfc999999998ebc4L); // -1.99999999998764832476e-01
-    static final double ATAN_AT2 = Double.longBitsToDouble(0x3fc24924920083ffL); //  1.42857142725034663711e-01
-    static final double ATAN_AT3 = Double.longBitsToDouble(0xbfbc71c6fe231671L); // -1.11111104054623557880e-01
-    static final double ATAN_AT4 = Double.longBitsToDouble(0x3fb745cdc54c206eL); //  9.09088713343650656196e-02
-    static final double ATAN_AT5 = Double.longBitsToDouble(0xbfb3b0f2af749a6dL); // -7.69187620504482999495e-02
-    static final double ATAN_AT6 = Double.longBitsToDouble(0x3fb10d66a0d03d51L); //  6.66107313738753120669e-02
-    static final double ATAN_AT7 = Double.longBitsToDouble(0xbfadde2d52defd9aL); // -5.83357013379057348645e-02
-    static final double ATAN_AT8 = Double.longBitsToDouble(0x3fa97b4b24760debL); //  4.97687799461593236017e-02
-    static final double ATAN_AT9 = Double.longBitsToDouble(0xbfa2b4442c6a6c2fL); // -3.65315727442169155270e-02
-    static final double ATAN_AT10 = Double.longBitsToDouble(0x3f90ad3ae322da11L); // 1.62858201153657823623e-02
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR TANH
-    //--------------------------------------------------------------------------
-
-    /**
-     * Constant found experimentally:
-     * StrictMath.tanh(TANH_1_THRESHOLD) = 1,
-     * StrictMath.tanh(nextDown(TANH_1_THRESHOLD)) = FastMath.tanh(nextDown(TANH_1_THRESHOLD)) < 1.
-     */
-    static final double TANH_1_THRESHOLD = 19.061547465398498;
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR ASINH AND ACOSH
-    //--------------------------------------------------------------------------
-
-    static final double ASINH_LOG1P_THRESHOLD = 0.04;
-
-    /**
-     * sqrt(x*x+-1) should yield higher threshold, but it's enough due to
-     * subsequent log.
-     */
-    static final double ASINH_ACOSH_SQRT_ELISION_THRESHOLD = (1<<24);
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR EXP AND EXPM1
-    //--------------------------------------------------------------------------
-
-    static final double EXP_OVERFLOW_LIMIT = Double.longBitsToDouble(0x40862E42FEFA39EFL); // 7.09782712893383973096e+02
-    static final double EXP_UNDERFLOW_LIMIT = Double.longBitsToDouble(0xC0874910D52D3051L); // -7.45133219101941108420e+02
-    static final int EXP_LO_DISTANCE_TO_ZERO_POT = 0;
-    static final int EXP_LO_DISTANCE_TO_ZERO = (1<<EXP_LO_DISTANCE_TO_ZERO_POT);
-    static final int EXP_LO_TAB_SIZE_POT = getTabSizePower(11);
-    static final int EXP_LO_TAB_SIZE = (1<<EXP_LO_TAB_SIZE_POT)+1;
-    static final int EXP_LO_TAB_MID_INDEX = ((EXP_LO_TAB_SIZE-1)/2);
-    static final int EXP_LO_INDEXING = EXP_LO_TAB_MID_INDEX/EXP_LO_DISTANCE_TO_ZERO;
-    static final int EXP_LO_INDEXING_DIV_SHIFT = EXP_LO_TAB_SIZE_POT-1-EXP_LO_DISTANCE_TO_ZERO_POT;
-
-    static final class MyTExp {
-        static final double[] expHiTab = new double[1+(int)EXP_OVERFLOW_LIMIT-(int)EXP_UNDERFLOW_LIMIT];
-        static final double[] expLoPosTab = new double[EXP_LO_TAB_SIZE];
-        static final double[] expLoNegTab = new double[EXP_LO_TAB_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=(int)EXP_UNDERFLOW_LIMIT;i<=(int)EXP_OVERFLOW_LIMIT;i++) {
-                expHiTab[i-(int)EXP_UNDERFLOW_LIMIT] = StrictMath.exp(i);
-            }
-            for (int i=0;i<EXP_LO_TAB_SIZE;i++) {
-                // x: in [-EXPM1_DISTANCE_TO_ZERO,EXPM1_DISTANCE_TO_ZERO].
-                double x = -EXP_LO_DISTANCE_TO_ZERO + i/(double)EXP_LO_INDEXING;
-                // exp(x)
-                expLoPosTab[i] = StrictMath.exp(x);
-                // 1-exp(-x), accurately computed
-                expLoNegTab[i] = -StrictMath.expm1(-x);
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR LOG AND LOG1P
-    //--------------------------------------------------------------------------
-
-    static final int LOG_BITS = getTabSizePower(12);
-    static final int LOG_TAB_SIZE = (1<<LOG_BITS);
-
-    static final class MyTLog {
-        static final double[] logXLogTab = new double[LOG_TAB_SIZE];
-        static final double[] logXTab = new double[LOG_TAB_SIZE];
-        static final double[] logXInvTab = new double[LOG_TAB_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=0;i<LOG_TAB_SIZE;i++) {
-                // Exact to use inverse of tab size, since it is a power of two.
-                double x = 1+i*(1.0/LOG_TAB_SIZE);
-                logXLogTab[i] = StrictMath.log(x);
-                logXTab[i] = x;
-                logXInvTab[i] = 1/x;
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // TABLE FOR POWERS OF TWO
-    //--------------------------------------------------------------------------
-
-    static final int TWO_POW_TAB_SIZE = USE_TWO_POW_TAB ? (MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT)+1 : 0;
-
-    static final class MyTTwoPow {
-        static final double[] twoPowTab = new double[TWO_POW_TAB_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            if (USE_TWO_POW_TAB) {
-                for (int i=MIN_DOUBLE_EXPONENT;i<=MAX_DOUBLE_EXPONENT;i++) {
-                    twoPowTab[i-MIN_DOUBLE_EXPONENT] = NumbersUtils.twoPow(i);
-                }
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR SQRT
-    //--------------------------------------------------------------------------
-
-    static final int SQRT_LO_BITS = getTabSizePower(12);
-    static final int SQRT_LO_TAB_SIZE = (1<<SQRT_LO_BITS);
-
-    static final class MyTSqrt {
-        static final double[] sqrtXSqrtHiTab = new double[MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT+1];
-        static final double[] sqrtXSqrtLoTab = new double[SQRT_LO_TAB_SIZE];
-        static final double[] sqrtSlopeHiTab = new double[MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT+1];
-        static final double[] sqrtSlopeLoTab = new double[SQRT_LO_TAB_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=MIN_DOUBLE_EXPONENT;i<=MAX_DOUBLE_EXPONENT;i++) {
-                double twoPowExpDiv2 = StrictMath.pow(2.0,i*0.5);
-                sqrtXSqrtHiTab[i-MIN_DOUBLE_EXPONENT] = twoPowExpDiv2 * 0.5; // Half sqrt, to avoid overflows.
-                sqrtSlopeHiTab[i-MIN_DOUBLE_EXPONENT] = 1/twoPowExpDiv2;
-            }
-            sqrtXSqrtLoTab[0] = 1.0;
-            sqrtSlopeLoTab[0] = 1.0;
-            final long SQRT_LO_MASK = (0x3FF0000000000000L | (0x000FFFFFFFFFFFFFL>>SQRT_LO_BITS));
-            for (int i=1;i<SQRT_LO_TAB_SIZE;i++) {
-                long xBits = SQRT_LO_MASK | (((long)(i-1))<<(52-SQRT_LO_BITS));
-                double sqrtX = StrictMath.sqrt(Double.longBitsToDouble(xBits));
-                sqrtXSqrtLoTab[i] = sqrtX;
-                sqrtSlopeLoTab[i] = 1/sqrtX;
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS AND TABLES FOR CBRT
-    //--------------------------------------------------------------------------
-
-    static final int CBRT_LO_BITS = getTabSizePower(12);
-    static final int CBRT_LO_TAB_SIZE = (1<<CBRT_LO_BITS);
-
-    // For CBRT_LO_BITS = 12:
-    // cbrtXCbrtLoTab[0] = 1.0.
-    // cbrtXCbrtLoTab[1] = cbrt(1. 000000000000 1111111111111111111111111111111111111111b)
-    // cbrtXCbrtLoTab[2] = cbrt(1. 000000000001 1111111111111111111111111111111111111111b)
-    // cbrtXCbrtLoTab[3] = cbrt(1. 000000000010 1111111111111111111111111111111111111111b)
-    // cbrtXCbrtLoTab[4] = cbrt(1. 000000000011 1111111111111111111111111111111111111111b)
-    // etc.
-    static final class MyTCbrt {
-        static final double[] cbrtXCbrtHiTab = new double[MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT+1];
-        static final double[] cbrtXCbrtLoTab = new double[CBRT_LO_TAB_SIZE];
-        static final double[] cbrtSlopeHiTab = new double[MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT+1];
-        static final double[] cbrtSlopeLoTab = new double[CBRT_LO_TAB_SIZE];
-        static {
-            init();
-        }
-        private static strictfp void init() {
-            for (int i=MIN_DOUBLE_EXPONENT;i<=MAX_DOUBLE_EXPONENT;i++) {
-                double twoPowExpDiv3 = StrictMath.pow(2.0,i*(1.0/3));
-                cbrtXCbrtHiTab[i-MIN_DOUBLE_EXPONENT] = twoPowExpDiv3 * 0.5; // Half cbrt, to avoid overflows.
-                cbrtSlopeHiTab[i-MIN_DOUBLE_EXPONENT] = (4.0/3)/(twoPowExpDiv3*twoPowExpDiv3);
-            }
-            cbrtXCbrtLoTab[0] = 1.0;
-            cbrtSlopeLoTab[0] = 1.0;
-            final long CBRT_LO_MASK = (0x3FF0000000000000L | (0x000FFFFFFFFFFFFFL>>CBRT_LO_BITS));
-            for (int i=1;i<CBRT_LO_TAB_SIZE;i++) {
-                long xBits = CBRT_LO_MASK | (((long)(i-1))<<(52-CBRT_LO_BITS));
-                double cbrtX = StrictMath.cbrt(Double.longBitsToDouble(xBits));
-                cbrtXCbrtLoTab[i] = cbrtX;
-                cbrtSlopeLoTab[i] = 1/(cbrtX*cbrtX);
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // CONSTANTS FOR HYPOT
-    //--------------------------------------------------------------------------
-
-    /**
-     * For using sqrt, to avoid overflow/underflow, we want values magnitude in
-     * [1/sqrt(Double.MAX_VALUE/n),sqrt(Double.MAX_VALUE/n)],
-     * n being the number of arguments.
-     *
-     * sqrt(Double.MAX_VALUE/2) = 9.480751908109176E153
-     * and
-     * sqrt(Double.MAX_VALUE/3) = 7.741001517595157E153
-     * so
-     * 2^511 = 6.7039039649712985E153
-     * works for both.
-     */
-    static final double HYPOT_MAX_MAG = NumbersUtils.twoPow(511);
-
-    /**
-     * Large enough to get a value's magnitude back into [2^-511,2^511]
-     * from Double.MIN_VALUE or Double.MAX_VALUE, and small enough
-     * not to get it across that range (considering a 2*53 tolerance
-     * due to only checking magnitude of min/max value, and scaling
-     * all values together).
-     */
-    static final double HYPOT_FACTOR = NumbersUtils.twoPow(750);
-
-    //--------------------------------------------------------------------------
-    // PUBLIC METHODS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Ensures that all look-up tables are initialized - otherwise they are
-     * initialized lazily.
-     */
-    public static void initTables() {
-        /*
-         * Taking care not to call init methods here, which would
-         * recompute tables each time (even though the computations
-         * should be identical, since done with strictfp and StrictMath).
-         */
-        int antiOptim = 0;
-        antiOptim += MyTSinCos.sinTab.length;
-        antiOptim += MyTTan.tanTab.length;
-        antiOptim += MyTAsin.asinTab.length;
-        antiOptim += MyTAsinPow.asinPowTab.length;
-        antiOptim += MyTAtan.atanTab.length;
-        antiOptim += MyTExp.expHiTab.length;
-        antiOptim += MyTLog.logXTab.length;
-        antiOptim += MyTTwoPow.twoPowTab.length;
-        antiOptim += MyTSqrt.sqrtXSqrtHiTab.length;
-        antiOptim += MyTCbrt.cbrtXCbrtHiTab.length;
-        if (StrictMath.cos((double)antiOptim) == 0.0) {
-            // Can't happen, cos is never +-0.0.
-            throw new AssertionError();
-        }
-    }
-
-    /*
-     * logarithms
-     */
-
-    /**
-     * @param value An integer value in [1,Integer.MAX_VALUE].
-     * @return The integer part of the logarithm, in base 2, of the specified value,
-     *         i.e. a result in [0,30]
-     * @throws IllegalArgumentException if the specified value is <= 0.
-     */
-    public static int log2(int value) {
-        return NumbersUtils.log2(value);
-    }
-
-    /**
-     * @param value An integer value in [1,Long.MAX_VALUE].
-     * @return The integer part of the logarithm, in base 2, of the specified value,
-     *         i.e. a result in [0,62]
-     * @throws IllegalArgumentException if the specified value is <= 0.
-     */
-    public static int log2(long value) {
-        return NumbersUtils.log2(value);
-    }
-
-    /*
-     * powers
-     */
-
-    /**
-     * Returns the exact result, provided it's in double range,
-     * i.e. if power is in [-1074,1023].
-     *
-     * @param power An int power.
-     * @return 2^power as a double, or +-Infinity in case of overflow.
-     */
-    public static double twoPow(int power) {
-        /*
-         * OK to have this method factored here even though it returns
-         * a floating point value, because it only does integer operations
-         * and only takes integer arguments, so should behave the same
-         * even if inlined into FP-wide context.
-         */
-        if (USE_TWO_POW_TAB) {
-            if (power >= MIN_DOUBLE_EXPONENT) {
-                if (power <= MAX_DOUBLE_EXPONENT) { // Normal or subnormal.
-                    return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT];
-                } else { // Overflow.
-                    return Double.POSITIVE_INFINITY;
-                }
-            } else { // Underflow.
-                return 0.0;
-            }
-        } else {
-            return NumbersUtils.twoPow(power);
-        }
-    }
-
-    /**
-     * @param value An int value.
-     * @return value*value.
-     */
-    public static int pow2(int value) {
-        return value*value;
-    }
-
-    /**
-     * @param value A long value.
-     * @return value*value.
-     */
-    public static long pow2(long value) {
-        return value*value;
-    }
-
-    /**
-     * @param value An int value.
-     * @return value*value*value.
-     */
-    public static int pow3(int value) {
-        return value*value*value;
-    }
-
-    /**
-     * @param value A long value.
-     * @return value*value*value.
-     */
-    public static long pow3(long value) {
-        return value*value*value;
-    }
-
-    /*
-     * absolute values
-     */
-
-    /**
-     * @param value An int value.
-     * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE.
-     */
-    public static int abs(int value) {
-        if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) {
-            return Math.abs(value);
-        }
-        return NumbersUtils.abs(value);
-    }
-
-    /**
-     * @param value A long value.
-     * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE.
-     */
-    public static long abs(long value) {
-        if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) {
-            return Math.abs(value);
-        }
-        return NumbersUtils.abs(value);
-    }
-
-    /*
-     * close values
-     */
-
-    /**
-     * @param value A long value.
-     * @return The specified value as int.
-     * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int toIntExact(long value) {
-        return NumbersUtils.asInt(value);
-    }
-
-    /**
-     * @param value A long value.
-     * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int toInt(long value) {
-        return NumbersUtils.toInt(value);
-    }
-
-    /*
-     * ranges
-     */
-
-    /**
-     * @param min An int value.
-     * @param max An int value.
-     * @param value An int value.
-     * @return minValue if value < minValue, maxValue if value > maxValue, value otherwise.
-     */
-    public static int toRange(int min, int max, int value) {
-        return NumbersUtils.toRange(min, max, value);
-    }
-
-    /**
-     * @param min A long value.
-     * @param max A long value.
-     * @param value A long value.
-     * @return min if value < min, max if value > max, value otherwise.
-     */
-    public static long toRange(long min, long max, long value) {
-        return NumbersUtils.toRange(min, max, value);
-    }
-
-    /*
-     * unary operators (increment,decrement,negate)
-     */
-
-    /**
-     * @param value An int value.
-     * @return The argument incremented by one.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in int range.
-     */
-    public static int incrementExact(int value) {
-        if (value == Integer.MAX_VALUE) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return value + 1;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument incremented by one.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in long range.
-     */
-    public static long incrementExact(long value) {
-        if (value == Long.MAX_VALUE) {
-            throw new ArithmeticException("long overflow");
-        }
-        return value + 1L;
-    }
-
-    /**
-     * @param value An int value.
-     * @return The argument incremented by one, or the argument
-     *         if the mathematical result is not in int range.
-     */
-    public static int incrementBounded(int value) {
-        if (value == Integer.MAX_VALUE) {
-            return value;
-        }
-        return value + 1;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument incremented by one, or the argument
-     *         if the mathematical result is not in long range.
-     */
-    public static long incrementBounded(long value) {
-        if (value == Long.MAX_VALUE) {
-            return value;
-        }
-        return value + 1L;
-    }
-
-    /**
-     * @param value An int value.
-     * @return The argument decremented by one.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in int range.
-     */
-    public static int decrementExact(int value) {
-        if (value == Integer.MIN_VALUE) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return value - 1;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument decremented by one.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in long range.
-     */
-    public static long decrementExact(long value) {
-        if (value == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
-        }
-        return value - 1L;
-    }
-
-    /**
-     * @param value An int value.
-     * @return The argument decremented by one, or the argument
-     *         if the mathematical result is not in int range.
-     */
-    public static int decrementBounded(int value) {
-        if (value == Integer.MIN_VALUE) {
-            return value;
-        }
-        return value - 1;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument decremented by one, or the argument
-     *         if the mathematical result is not in long range.
-     */
-    public static long decrementBounded(long value) {
-        if (value == Long.MIN_VALUE) {
-            return value;
-        }
-        return value - 1L;
-    }
-
-    /**
-     * @param value An int value.
-     * @return The argument negated.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in int range.
-     */
-    public static int negateExact(int value) {
-        if (value == Integer.MIN_VALUE) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return -value;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument negated.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in long range.
-     */
-    public static long negateExact(long value) {
-        if (value == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
-        }
-        return -value;
-    }
-
-    /**
-     * @param value An int value.
-     * @return The argument negated, or Integer.MAX_VALUE
-     *         if the argument is Integer.MIN_VALUE.
-     */
-    public static int negateBounded(int value) {
-        if (value == Integer.MIN_VALUE) {
-            return Integer.MAX_VALUE;
-        }
-        return -value;
-    }
-
-    /**
-     * @param value A long value.
-     * @return The argument negated, or Long.MAX_VALUE
-     *         if the argument is Long.MIN_VALUE.
-     */
-    public static long negateBounded(long value) {
-        if (value == Long.MIN_VALUE) {
-            return Long.MAX_VALUE;
-        }
-        return -value;
-    }
-
-    /*
-     * binary operators (+,-,*)
-     */
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a+b.
-     * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int addExact(int a, int b) {
-        return NumbersUtils.plusExact(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a+b.
-     * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long addExact(long a, long b) {
-        return NumbersUtils.plusExact(a, b);
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b.
-     */
-    public static int addBounded(int a, int b) {
-        return NumbersUtils.plusBounded(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b.
-     */
-    public static long addBounded(long a, long b) {
-        return NumbersUtils.plusBounded(a, b);
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a-b.
-     * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int subtractExact(int a, int b) {
-        return NumbersUtils.minusExact(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a-b.
-     * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long subtractExact(long a, long b) {
-        return NumbersUtils.minusExact(a, b);
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b.
-     */
-    public static int subtractBounded(int a, int b) {
-        return NumbersUtils.minusBounded(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b.
-     */
-    public static long subtractBounded(long a, long b) {
-        return NumbersUtils.minusBounded(a, b);
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a*b.
-     * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int multiplyExact(int a, int b) {
-        return NumbersUtils.timesExact(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b An int value.
-     * @return The mathematical result of a*b.
-     * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long multiplyExact(long a, int b) {
-        return NumbersUtils.timesExact(a, (long) b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a*b.
-     * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long multiplyExact(long a, long b) {
-        return NumbersUtils.timesExact(a, b);
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b.
-     */
-    public static int multiplyBounded(int a, int b) {
-        return NumbersUtils.timesBounded(a, b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b An int value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b.
-     */
-    public static long multiplyBounded(long a, int b) {
-        return NumbersUtils.timesBounded(a, (long) b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b.
-     */
-    public static long multiplyBounded(long a, long b) {
-        return NumbersUtils.timesBounded(a, b);
-    }
-
-    /**
-     * @param x An int value.
-     * @param y An int value.
-     * @return The mathematical product as a long.
-     */
-    public static long multiplyFull(int x, int y) {
-        return ((long) x) * ((long) y);
-    }
-
-    /**
-     * @param x A long value.
-     * @param y A long value.
-     * @return The most significant 64 bits of the 128-bit product of two 64-bit factors.
-     */
-    public static long multiplyHigh(long x, long y) {
-        if ((x|y) < 0) {
-            // Use technique from section 8-2 of Henry S. Warren, Jr.,
-            // Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174.
-            long x1 = (x >> 32);
-            long y1 = (y >> 32);
-            long x2 = (x & 0xFFFFFFFFL);
-            long y2 = (y & 0xFFFFFFFFL);
-            long z2 = x2 * y2;
-            long t = x1 * y2 + (z2 >>> 32);
-            long z1 = (t & 0xFFFFFFFFL) + x2 * y1;
-            long z0 = (t >> 32);
-            return x1 * y1 + z0 + (z1 >> 32);
-        } else {
-            // Use Karatsuba technique with two base 2^32 digits.
-            long x1 = (x >>> 32);
-            long y1 = (y >>> 32);
-            long x2 = (x & 0xFFFFFFFFL);
-            long y2 = (y & 0xFFFFFFFFL);
-            long A = x1 * y1;
-            long B = x2 * y2;
-            long C = (x1 + x2) * (y1 + y2);
-            long K = C - A - B;
-            return (((B >>> 32) + K) >>> 32) + A;
-        }
-    }
-
-    /*
-     * binary operators (/,%)
-     */
-
-    /**
-     * Returns the largest int <= dividend/divisor.
-     *
-     * Unlike "/" operator, which rounds towards 0, this division
-     * rounds towards -Infinity (which give different result
-     * when the exact result is negative).
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The largest int <= dividend/divisor, unless dividend is
-     *         Integer.MIN_VALUE and divisor is -1, in which case
-     *         Integer.MIN_VALUE is returned.
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static int floorDiv(int x, int y) {
-        int r = x / y;
-        // If the signs are different and modulo not zero, rounding down.
-        if (((x ^ y) < 0) && ((r * y) != x)) {
-            r--;
-        }
-        return r;
-    }
-
-    /**
-     * Returns the largest long <= dividend/divisor.
-     *
-     * Unlike "/" operator, which rounds towards 0, this division
-     * rounds towards -Infinity (which give different result
-     * when the exact result is negative).
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The largest long <= dividend/divisor, unless dividend is
-     *         Long.MIN_VALUE and divisor is -1, in which case
-     *         Long.MIN_VALUE is returned.
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static long floorDiv(long x, int y) {
-        return floorDiv(x, (long) y);
-    }
-
-    /**
-     * Returns the largest long <= dividend/divisor.
-     *
-     * Unlike "/" operator, which rounds towards 0, this division
-     * rounds towards -Infinity (which give different result
-     * when the exact result is negative).
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The largest long <= dividend/divisor, unless dividend is
-     *         Long.MIN_VALUE and divisor is -1, in which case
-     *         Long.MIN_VALUE is returned.
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static long floorDiv(long x, long y) {
-        long r = x / y;
-        // If the signs are different and modulo not zero, rounding down.
-        if (((x ^ y) < 0) && ((r * y) != x)) {
-            r--;
-        }
-        return r;
-    }
-
-    /**
-     * Returns the floor modulus, which is "x - floorDiv(x,y) * y",
-     * has the same sign as y, and is in ]-abs(y),abs(y)[.
-     *
-     * The relationship between floorMod and floorDiv is the same
-     * than between "%" and "/".
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)".
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static int floorMod(int x, int y) {
-        return x - floorDiv(x, y) * y;
-    }
-
-    /**
-     * Returns the floor modulus, which is "x - floorDiv(x,y) * y",
-     * has the same sign as y, and is in ]-abs(y),abs(y)[.
-     *
-     * The relationship between floorMod and floorDiv is the same
-     * than between "%" and "/".
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)".
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static int floorMod(long x, int y) {
-        // No overflow so can cast.
-        return (int) (x - floorDiv(x,y) * y);
-    }
-
-    /**
-     * Returns the floor modulus, which is "x - floorDiv(x,y) * y",
-     * has the same sign as y, and is in ]-abs(y),abs(y)[.
-     *
-     * The relationship between floorMod and floorDiv is the same
-     * than between "%" and "/".
-     *
-     * @param x The dividend.
-     * @param y The divisor.
-     * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)".
-     * @throws ArithmeticException if the divisor is zero.
-     */
-    public static long floorMod(long x, long y) {
-        return x - floorDiv(x, y) * y;
-    }
-
-    /*
-     * Non-redefined Math public values and treatments.
-     */
-
-    public static int min(int a, int b) {
-        return Math.min(a,b);
-    }
-
-    public static long min(long a, long b) {
-        return Math.min(a,b);
-    }
-
-    public static int max(int a, int b) {
-        return Math.max(a,b);
-    }
-
-    public static long max(long a, long b) {
-        return Math.max(a,b);
-    }
-
-    //--------------------------------------------------------------------------
-    // PACKAGE-PRIVATE METHODS
-    //--------------------------------------------------------------------------
-
-    /**
-     * @param power Must be in normal values range.
-     */
-    static double twoPowNormal(int power) {
-        if (USE_TWO_POW_TAB) {
-            return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT];
-        } else {
-            return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52);
-        }
-    }
-
-    /**
-     * @param power Must be in normal or subnormal values range.
-     */
-    static double twoPowNormalOrSubnormal(int power) {
-        if (USE_TWO_POW_TAB) {
-            return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT];
-        } else {
-            if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal.
-                return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT)));
-            } else { // Normal.
-                return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52);
-            }
-        }
-    }
-
-    static double atan2_pinf_yyy(double y) {
-        if (y == Double.POSITIVE_INFINITY) {
-            return Math.PI/4;
-        } else if (y == Double.NEGATIVE_INFINITY) {
-            return -Math.PI/4;
-        } else if (y > 0.0) {
-            return 0.0;
-        } else if (y < 0.0) {
-            return -0.0;
-        } else {
-            return Double.NaN;
-        }
-    }
-
-    static double atan2_ninf_yyy(double y) {
-        if (y == Double.POSITIVE_INFINITY) {
-            return 3*Math.PI/4;
-        } else if (y == Double.NEGATIVE_INFINITY) {
-            return -3*Math.PI/4;
-        } else if (y > 0.0) {
-            return Math.PI;
-        } else if (y < 0.0) {
-            return -Math.PI;
-        } else {
-            return Double.NaN;
-        }
-    }
-
-    static double atan2_yyy_zeroOrNaN(double y, double x) {
-        if (x == 0.0) {
-            if (y == 0.0) {
-                if (signFromBit_antiCyclic(x) < 0) {
-                    // x is -0.0
-                    return signFromBit_antiCyclic(y) * Math.PI;
-                } else {
-                    // +-0.0
-                    return y;
-                }
-            }
-            if (y > 0.0) {
-                return Math.PI/2;
-            } else if (y < 0.0) {
-                return -Math.PI/2;
-            } else {
-                return Double.NaN;
-            }
-        } else {
-            return Double.NaN;
-        }
-    }
-
-    /**
-     * At least one of the arguments must be NaN.
-     */
-    static double hypot_NaN(double xAbs, double yAbs) {
-        if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY)) {
-            return Double.POSITIVE_INFINITY;
-        } else {
-            return Double.NaN;
-        }
-    }
-
-    /**
-     * At least one of the arguments must be NaN.
-     */
-    static double hypot_NaN(double xAbs, double yAbs, double zAbs) {
-        if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY) || (zAbs == Double.POSITIVE_INFINITY)) {
-            return Double.POSITIVE_INFINITY;
-        } else {
-            return Double.NaN;
-        }
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @param remainder Must have 1 for 2nd and 3rd exponent bits, which is the
-     *        case for heavyRemPiO2 remainders (their absolute values are >=
-     *        Double.longBitsToDouble(0x3000000000000000L)
-     *        = 1.727233711018889E-77, and even if they were not, turning these
-     *        bits from 0 to 1 on decoding would not change the absolute error
-     *        much), and also works for +-Infinity or NaN encoding.
-     * @param quadrant Must be in [0,3].
-     * @return Bits holding remainder, and quadrant instead of
-     *         reamainder's 2nd and 3rd exponent bits.
-     */
-    static long encodeRemainderAndQuadrant(double remainder, int quadrant) {
-        final long bits = Double.doubleToRawLongBits(remainder);
-        return (bits&QUADRANT_BITS_0_MASK)|(((long)quadrant)<<60);
-    }
-
-    static double decodeRemainder(long bits) {
-        return Double.longBitsToDouble((bits&QUADRANT_BITS_0_MASK)|QUADRANT_PLACE_BITS);
-    }
-
-    static int decodeQuadrant(long bits) {
-        return ((int)(bits>>60))&3;
-    }
-
-    /*
-     * JDK-based remainders.
-     * Since a strict one for (% (PI/2)) is needed for heavyRemainderPiO2,
-     * we need it in this class.
-     * Then, for homogeneity, we put them all in this class.
-     * Then, to avoid code duplication for these slow-anyway methods,
-     * we just stick with strict versions, for both FastMath and StrictFastMath.
-     */
-
-    /**
-     * @param angle Angle, in radians.
-     * @return Remainder of (angle % (2*PI)), in [-PI,PI].
-     */
-    static strictfp double jdkRemainderTwoPi(double angle) {
-        final double sin = StrictMath.sin(angle);
-        final double cos = StrictMath.cos(angle);
-        return StrictMath.atan2(sin, cos);
-    }
-
-    /**
-     * @param angle Angle, in radians.
-     * @return Remainder of (angle % PI), in [-PI/2,PI/2].
-     */
-    static strictfp double jdkRemainderPi(double angle) {
-        final double sin = StrictMath.sin(angle);
-        final double cos = StrictMath.cos(angle);
-        /*
-         * Making sure atan2's result ends up in [-PI/2,PI/2],
-         * i.e. has maximum accuracy.
-         */
-        return StrictMath.atan2(sin, Math.abs(cos));
-    }
-
-    /**
-     * @param angle Angle, in radians.
-     * @return Bits of double corresponding to remainder of (angle % (PI/2)),
-     *         in [-PI/4,PI/4], with quadrant encoded in exponent bits.
-     */
-    static strictfp long jdkRemainderPiO2(double angle, boolean negateRem) {
-        final double sin = StrictMath.sin(angle);
-        final double cos = StrictMath.cos(angle);
-
-        /*
-         * Computing quadrant first, and then computing
-         * atan2, to make sure its result ends up in [-PI/4,PI/4],
-         * i.e. has maximum accuracy.
-         */
-
-        final int q;
-        final double sinForAtan2;
-        final double cosForAtan2;
-        if (cos >= (SQRT_2/2)) {
-            // [-PI/4,PI/4]
-            q = 0;
-            sinForAtan2 = sin;
-            cosForAtan2 = cos;
-        } else if (cos <= -(SQRT_2/2)) {
-            // [3*PI/4,5*PI/4]
-            q = 2;
-            sinForAtan2 = -sin;
-            cosForAtan2 = -cos;
-        } else if (sin > 0.0) {
-            // [PI/4,3*PI/4]
-            q = 1;
-            sinForAtan2 = -cos;
-            cosForAtan2 = sin;
-        } else {
-            // [5*PI/4,7*PI/4]
-            q = 3;
-            sinForAtan2 = cos;
-            cosForAtan2 = -sin;
-        }
-
-        double fw = StrictMath.atan2(sinForAtan2, cosForAtan2);
-
-        return encodeRemainderAndQuadrant(negateRem ? -fw : fw, q);
-    }
-
-    /*
-     * Our remainders implementations.
-     */
-
-    /**
-     * @param angle Angle, in radians. Must not be NaN nor +-Infinity.
-     * @return Remainder of (angle % (2*PI)), in [-PI,PI].
-     */
-    static strictfp double heavyRemainderTwoPi(double angle) {
-        final long remAndQuad = heavyRemainderPiO2(angle, false);
-        final double rem = decodeRemainder(remAndQuad);
-        final int q = decodeQuadrant(remAndQuad);
-        if (q == 0) {
-            return rem;
-        } else if (q == 1) {
-            return (rem + PIO2_LO) + PIO2_HI;
-        } else if (q == 2) {
-            if (rem < 0.0) {
-                return (rem + PI_LO) + PI_HI;
-            } else {
-                return (rem - PI_LO) - PI_HI;
-            }
-        } else {
-            return (rem - PIO2_LO) - PIO2_HI;
-        }
-    }
-
-    /**
-     * @param angle Angle, in radians. Must not be NaN nor +-Infinity.
-     * @return Remainder of (angle % PI), in [-PI/2,PI/2].
-     */
-    static strictfp double heavyRemainderPi(double angle) {
-        final long remAndQuad = heavyRemainderPiO2(angle, false);
-        final double rem = decodeRemainder(remAndQuad);
-        final int q = decodeQuadrant(remAndQuad);
-        if ((q&1) != 0) {
-            // q is 1 or 3
-            if (rem < 0.0) {
-                return (rem + PIO2_LO) + PIO2_HI;
-            } else {
-                return (rem - PIO2_LO) - PIO2_HI;
-            }
-        }
-        return rem;
-    }
-
-    /**
-     * Remainder using an accurate definition of PI.
-     * Derived from a fdlibm treatment called __kernel_rem_pio2.
-     *
-     * Not defining a non-strictfp version for FastMath, to avoid duplicating
-     * its long and messy code, and because it's slow anyway, and should be
-     * rarely used when speed matters.
-     *
-     * @param angle Angle, in radians. Must not be NaN nor +-Infinity.
-     * @param negateRem True if remainder must be negated before encoded into returned long.
-     * @return Bits of double corresponding to remainder of (angle % (PI/2)),
-     *         in [-PI/4,PI/4], with quadrant encoded in exponent bits.
-     */
-    static strictfp long heavyRemainderPiO2(double angle, boolean negateRem) {
-
-        /*
-         * fdlibm treatments unrolled, to avoid garbage and be OOME-free,
-         * corresponding to:
-         * 1) initial jk = 4 (precision = 3 = 64 bits (extended)),
-         *    which is more accurate than using precision = 2
-         *    (53 bits, double), even though we work with doubles
-         *    and use strictfp!
-         * 2) max lengths of 8 for f[], 6 for q[], fq[] and iq[].
-         * 3) at most one recomputation (one goto).
-         * These limitations were experimentally found to
-         * be sufficient for billions of random doubles
-         * of random magnitudes.
-         * For the rare cases that our unrolled treatments can't handle,
-         * we fall back to a JDK-based implementation.
-         */
-
-        int n,i,j,ih;
-        double fw;
-
-        /*
-         * Turning angle into 24-bits integer chunks.
-         * Done outside __kernel_rem_pio2, but we factor it inside our method.
-         */
-
-        // Reworking exponent to have a value < 2^24.
-        final long lx = Double.doubleToRawLongBits(angle);
-        final long exp = ((lx>>52)&0x7FF) - (1023+23);
-        double z = Double.longBitsToDouble(lx - (exp<<52));
-
-        double x0 = (double)(int)z;
-        z = (z-x0)*TWO_POW_24;
-        double x1 = (double)(int)z;
-        z = (z-x1)*TWO_POW_24;
-        double x2 = (double)(int)z;
-
-        final int e0 = (int)exp;
-        // in [1,3]
-        final int nx = (x2 == 0.0) ? ((x1 == 0.0) ? 1 : 2) : 3;
-
-        /*
-         *
-         */
-
-        double f0,f1,f2,f3,f4,f5,f6,f7;
-        double q0,q1,q2,q3,q4,q5;
-        int iq0,iq1,iq2,iq3,iq4,iq5;
-
-        int jk = 4;
-
-        int jx = nx-1;
-        int jv = Math.max(0,(e0-3)/24);
-        // In fdlibm, this is q0, but we prefer to use q0 for q[0].
-        int qZero = e0-24*(jv+1);
-
-        j = jv-jx;
-        if (jx == 0) {
-            f6 = 0.0;
-            f5 = 0.0;
-            f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0;
-            f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0;
-            f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0;
-            f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0;
-            f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0;
-
-            q0 = x0*f0;
-            q1 = x0*f1;
-            q2 = x0*f2;
-            q3 = x0*f3;
-            q4 = x0*f4;
-        } else if (jx == 1) {
-            f6 = 0.0;
-            f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0;
-            f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0;
-            f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0;
-            f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0;
-            f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0;
-            f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0;
-
-            q0 = x0*f1 + x1*f0;
-            q1 = x0*f2 + x1*f1;
-            q2 = x0*f3 + x1*f2;
-            q3 = x0*f4 + x1*f3;
-            q4 = x0*f5 + x1*f4;
-        } else { // jx == 2
-            f6 = (j >= -6) ? TWO_OVER_PI_TAB[j+6] : 0.0;
-            f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0;
-            f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0;
-            f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0;
-            f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0;
-            f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0;
-            f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0;
-
-            q0 = x0*f2 + x1*f1 + x2*f0;
-            q1 = x0*f3 + x1*f2 + x2*f1;
-            q2 = x0*f4 + x1*f3 + x2*f2;
-            q3 = x0*f5 + x1*f4 + x2*f3;
-            q4 = x0*f6 + x1*f5 + x2*f4;
-        }
-
-        double twoPowQZero = twoPowNormal(qZero);
-
-        int jz = jk;
-
-        /*
-         * Unrolling of first round.
-         */
-
-        z = q4;
-        fw = (double)(int)(TWO_POW_N24*z);
-        iq0 = (int)(z-TWO_POW_24*fw);
-        z = q3+fw;
-        fw = (double)(int)(TWO_POW_N24*z);
-        iq1 = (int)(z-TWO_POW_24*fw);
-        z = q2+fw;
-        fw = (double)(int)(TWO_POW_N24*z);
-        iq2 = (int)(z-TWO_POW_24*fw);
-        z = q1+fw;
-        fw = (double)(int)(TWO_POW_N24*z);
-        iq3 = (int)(z-TWO_POW_24*fw);
-        z = q0+fw;
-        iq4 = 0;
-        iq5 = 0;
-
-        z = (z*twoPowQZero) % 8.0;
-        n = (int)z;
-        z -= (double)n;
-
-        ih = 0;
-        if (qZero > 0) {
-            // Parentheses against code formatter bug.
-            i = (iq3>>(24-qZero));
-            n += i;
-            iq3 -= i<<(24-qZero);
-            ih = iq3>>(23-qZero);
-        } else if (qZero == 0) {
-            ih = iq3>>23;
-        } else if (z >= 0.5) {
-            ih = 2;
-        }
-
-        if (ih > 0) {
-            n += 1;
-            // carry = 1 is common case,
-            // so using it as initial value.
-            int carry = 1;
-            if (iq0 != 0) {
-                iq0 = 0x1000000 - iq0;
-                iq1 = 0xFFFFFF - iq1;
-                iq2 = 0xFFFFFF - iq2;
-                iq3 = 0xFFFFFF - iq3;
-            } else if (iq1 != 0) {
-                iq1 = 0x1000000 - iq1;
-                iq2 = 0xFFFFFF - iq2;
-                iq3 = 0xFFFFFF - iq3;
-            } else if (iq2 != 0) {
-                iq2 = 0x1000000 - iq2;
-                iq3 = 0xFFFFFF - iq3;
-            } else if (iq3 != 0) {
-                iq3 = 0x1000000 - iq3;
-            } else {
-                carry = 0;
-            }
-            if (qZero > 0) {
-                if (qZero == 1) {
-                    iq3 &= 0x7FFFFF;
-                } else if (qZero == 2) {
-                    iq3 &= 0x3FFFFF;
-                }
-            }
-            if (ih == 2) {
-                z = 1.0 - z;
-                if (carry != 0) {
-                    z -= twoPowQZero;
-                }
-            }
-        }
-
-        if (z == 0.0) {
-            if (iq3 == 0) {
-                // With random values of random magnitude,
-                // probability for this to happen seems lower than 1e-6.
-                // jz would be more than just incremented by one,
-                // which our unrolling doesn't support.
-                return jdkRemainderPiO2(angle, negateRem);
-            }
-            if (jx == 0) {
-                f5 = TWO_OVER_PI_TAB[jv+5];
-                q5 = x0*f5;
-            } else if (jx == 1) {
-                f6 = TWO_OVER_PI_TAB[jv+5];
-                q5 = x0*f6 + x1*f5;
-            } else { // jx == 2
-                f7 = TWO_OVER_PI_TAB[jv+5];
-                q5 = x0*f7 + x1*f6 + x2*f5;
-            }
-
-            jz++;
-
-            /*
-             * Unrolling of second round.
-             */
-
-            z = q5;
-            fw = (double)(int)(TWO_POW_N24*z);
-            iq0 = (int)(z-TWO_POW_24*fw);
-            z = q4+fw;
-            fw = (double)(int)(TWO_POW_N24*z);
-            iq1 = (int)(z-TWO_POW_24*fw);
-            z = q3+fw;
-            fw = (double)(int)(TWO_POW_N24*z);
-            iq2 = (int)(z-TWO_POW_24*fw);
-            z = q2+fw;
-            fw = (double)(int)(TWO_POW_N24*z);
-            iq3 = (int)(z-TWO_POW_24*fw);
-            z = q1+fw;
-            fw = (double)(int)(TWO_POW_N24*z);
-            iq4 = (int)(z-TWO_POW_24*fw);
-            z = q0+fw;
-            iq5 = 0;
-
-            z = (z*twoPowQZero) % 8.0;
-            n = (int)z;
-            z -= (double)n;
-
-            ih = 0;
-            if (qZero > 0) {
-                // Parentheses against code formatter bug.
-                i = (iq4>>(24-qZero));
-                n += i;
-                iq4 -= i<<(24-qZero);
-                ih = iq4>>(23-qZero);
-            } else if (qZero == 0) {
-                ih = iq4>>23;
-            } else if (z >= 0.5) {
-                ih = 2;
-            }
-
-            if (ih > 0) {
-                n += 1;
-                // carry = 1 is common case,
-                // so using it as initial value.
-                int carry = 1;
-                if (iq0 != 0) {
-                    iq0 = 0x1000000 - iq0;
-                    iq1 = 0xFFFFFF - iq1;
-                    iq2 = 0xFFFFFF - iq2;
-                    iq3 = 0xFFFFFF - iq3;
-                    iq4 = 0xFFFFFF - iq4;
-                } else if (iq1 != 0) {
-                    iq1 = 0x1000000 - iq1;
-                    iq2 = 0xFFFFFF - iq2;
-                    iq3 = 0xFFFFFF - iq3;
-                    iq4 = 0xFFFFFF - iq4;
-                } else if (iq2 != 0) {
-                    iq2 = 0x1000000 - iq2;
-                    iq3 = 0xFFFFFF - iq3;
-                    iq4 = 0xFFFFFF - iq4;
-                } else if (iq3 != 0) {
-                    iq3 = 0x1000000 - iq3;
-                    iq4 = 0xFFFFFF - iq4;
-                } else if (iq4 != 0) {
-                    iq4 = 0x1000000 - iq4;
-                } else {
-                    carry = 0;
-                }
-                if (qZero > 0) {
-                    if (qZero == 1) {
-                        iq4 &= 0x7FFFFF;
-                    } else if (qZero == 2) {
-                        iq4 &= 0x3FFFFF;
-                    }
-                }
-                if (ih == 2) {
-                    z = 1.0 - z;
-                    if (carry != 0) {
-                        z -= twoPowQZero;
-                    }
-                }
-            }
-
-            if (z == 0.0) {
-                if (iq4 == 0) {
-                    // Case not encountered in tests, but still handling it.
-                    // Would require a third loop unrolling.
-                    return jdkRemainderPiO2(angle, negateRem);
-                } else {
-                    // z == 0.0, and iq4 != 0,
-                    // so we remove 24 from qZero only once,
-                    // but since we no longer use qZero,
-                    // we just bother to multiply its 2-power
-                    // by 2^-24.
-                    jz--;
-                    twoPowQZero *= TWO_POW_N24;
-                }
-            } else {
-                // z != 0.0 at end of second round.
-            }
-        } else {
-            // z != 0.0 at end of first round.
-        }
-
-        /*
-         * After loop.
-         */
-
-        if (z != 0.0) {
-            z /= twoPowQZero;
-            if (z >= TWO_POW_24) {
-                fw = (double)(int)(TWO_POW_N24*z);
-                if (jz == jk) {
-                    iq4 = (int)(z-TWO_POW_24*fw);
-                    jz++; // jz to 5
-                    // Not using qZero anymore so not updating it.
-                    twoPowQZero *= TWO_POW_24;
-                    iq5 = (int)fw;
-                } else { // jz == jk+1 == 5
-                    // Case not encountered in tests, but still handling it.
-                    // Would require use of iq6, with jz = 6.
-                    return jdkRemainderPiO2(angle, negateRem);
-                }
-            } else {
-                if (jz == jk) {
-                    iq4 = (int)z;
-                } else { // jz == jk+1 == 5
-                    // Case not encountered in tests, but still handling it.
-                    iq5 = (int)z;
-                }
-            }
-        }
-
-        fw = twoPowQZero;
-
-        if (jz == 5) {
-            q5 = fw*(double)iq5;
-            fw *= TWO_POW_N24;
-        } else {
-            q5 = 0.0;
-        }
-        q4 = fw*(double)iq4;
-        fw *= TWO_POW_N24;
-        q3 = fw*(double)iq3;
-        fw *= TWO_POW_N24;
-        q2 = fw*(double)iq2;
-        fw *= TWO_POW_N24;
-        q1 = fw*(double)iq1;
-        fw *= TWO_POW_N24;
-        q0 = fw*(double)iq0;
-
-        /*
-         * We just use HI part of the result.
-         */
-
-        fw = PIO2_TAB0*q5;
-        fw += PIO2_TAB0*q4 + PIO2_TAB1*q5;
-        fw += PIO2_TAB0*q3 + PIO2_TAB1*q4 + PIO2_TAB2*q5;
-        fw += PIO2_TAB0*q2 + PIO2_TAB1*q3 + PIO2_TAB2*q4 + PIO2_TAB3*q5;
-        fw += PIO2_TAB0*q1 + PIO2_TAB1*q2 + PIO2_TAB2*q3 + PIO2_TAB3*q4 + PIO2_TAB4*q5;
-        fw += PIO2_TAB0*q0 + PIO2_TAB1*q1 + PIO2_TAB2*q2 + PIO2_TAB3*q3 + PIO2_TAB4*q4 + PIO2_TAB5*q5;
-
-        if ((ih != 0) ^ negateRem) {
-            fw = -fw;
-        }
-
-        return encodeRemainderAndQuadrant(fw, n&3);
-    }
-
-    //--------------------------------------------------------------------------
-    // PRIVATE METHODS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Redefined here, to avoid cyclic dependency with (Strict)FastMath.
-     *
-     * @param value A double value.
-     * @return -1 if sign bit is 1, 1 if sign bit is 0.
-     */
-    private static long signFromBit_antiCyclic(double value) {
-        // Returning a long, to avoid useless cast into int.
-        return ((Double.doubleToRawLongBits(value)>>62)|1);
-    }
-
-    private static boolean getBooleanProperty(
-            final String key,
-            boolean defaultValue) {
-        final String tmp = System.getProperty(key);
-        if (tmp != null) {
-            return Boolean.parseBoolean(tmp);
-        } else {
-            return defaultValue;
-        }
-    }
-
-    /**
-     * Use look-up tables size power through this method,
-     * to make sure is it small in case java.lang.Math
-     * is directly used.
-     */
-    private static int getTabSizePower(int tabSizePower) {
-        return (FM_USE_JDK_MATH && SFM_USE_JDK_MATH) ? Math.min(2, tabSizePower) : tabSizePower;
-    }
-}
diff --git a/src/main/java/net/jafama/DoubleWrapper.java b/src/main/java/net/jafama/DoubleWrapper.java
deleted file mode 100644
index a73de33..0000000
--- a/src/main/java/net/jafama/DoubleWrapper.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2012 Jeff Hain
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.jafama;
-
-public class DoubleWrapper {
-    public double value;
-    @Override
-    public String toString() {
-        return Double.toString(this.value);
-    }
-}
diff --git a/src/main/java/net/jafama/FastMath.java b/src/main/java/net/jafama/FastMath.java
deleted file mode 100644
index 483d56d..0000000
--- a/src/main/java/net/jafama/FastMath.java
+++ /dev/null
@@ -1,2975 +0,0 @@
-/*
- * Copyright 2012-2015 Jeff Hain
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * =============================================================================
- * Notice of fdlibm package this program is partially derived from:
- *
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunSoft, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * =============================================================================
- */
-package net.jafama;
-
-/**
- * Class providing math treatments that:
- * - are meant to be faster than java.lang.Math class equivalents (if any),
- * - are still somehow accurate and robust (handling of NaN and such),
- * - do not (or not directly) generate objects at run time (no "new").
- *
- * Other than optimized treatments, a valuable feature of this class is the
- * presence of angles normalization methods, derived from those used in
- * java.lang.Math (for which, sadly, no API is provided, letting everyone
- * with the terrible responsibility of writing their own ones).
- *
- * Non-redefined methods Math methods are also available, for easy replacement,
- * even though for some of them, such as for incrementExact, you might want to
- * stick to Math versions to benefit from eventual JVM intrinsics.
- *
- * Use of look-up tables: around 1 Mo total, and initialized lazily or on first
- * call to initTables().
- *
- * Depending on JVM, or JVM options, these treatments can actually be slower
- * than Math ones.
- * In particular, they can be slower if not optimized by the JIT, which you
- * can see with -Xint JVM option.
- * Another cause of slowness can be cache-misses on look-up tables.
- * Also, look-up tables initialization typically takes multiple hundreds of
- * milliseconds (and is about twice slower in J6 than in J5, and in J7 than in
- * J6, possibly due to intrinsifications preventing optimizations such as use
- * of hardware sqrt, and Math delegating to StrictMath with JIT optimizations
- * not yet up during class load).
- * As a result, you might want to make these treatments not use tables,
- * and delegate to corresponding Math methods, when they are available in the
- * lowest supported Java version, by using the appropriate property (see below).
- *
- * Methods with same signature than Math ones, are meant to return
- * "good" approximations on all range.
- * Methods terminating with "Fast" are meant to return "good" approximation
- * on a reduced range only.
- * Methods terminating with "Quick" are meant to be quick, but do not
- * return a good approximation, and might only work on a reduced range.
- *
- * Properties:
- *
- * - jafama.usejdk (boolean, default is false):
- *   If true, for redefined methods, as well as their "Fast" or "Quick"
- *   terminated counterparts, instead of using redefined computations,
- *   delegating to Math, when available in required Java version.
- *
- * - jafama.fastlog (boolean, default is false):
- *   If true, using redefined computations for log(double) and
- *   log10(double), else they delegate to Math.log(double) and
- *   Math.log10(double).
- *   False by default because Math.log(double) and Math.log10(double)
- *   seem usually fast (redefined log(double) might be even faster,
- *   but is less accurate).
- *
- * - jafama.fastsqrt (boolean, default is false):
- *   If true, using redefined computation for sqrt(double),
- *   else it delegates to Math.sqrt(double).
- *   False by default because Math.sqrt(double) seems usually fast.
- */
-public final class FastMath extends CmnFastMath {
-
-    //--------------------------------------------------------------------------
-    // CONFIGURATION
-    //--------------------------------------------------------------------------
-
-    private static final boolean USE_JDK_MATH = FM_USE_JDK_MATH;
-
-    private static final boolean USE_REDEFINED_LOG = FM_USE_REDEFINED_LOG;
-
-    private static final boolean USE_REDEFINED_SQRT = FM_USE_REDEFINED_SQRT;
-
-    private static final boolean USE_POWTABS_FOR_ASIN = FM_USE_POWTABS_FOR_ASIN;
-
-    //--------------------------------------------------------------------------
-    // PUBLIC METHODS
-    //--------------------------------------------------------------------------
-
-    /*
-     * trigonometry
-     */
-
-    /**
-     * @param angle Angle in radians.
-     * @return Angle sine.
-     */
-    public static double sin(double angle) {
-        if (USE_JDK_MATH) {
-            return Math.sin(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) {
-            if (false) {
-                // Can give very bad relative error near PI (mod 2*PI).
-                angle = remainderTwoPi(angle);
-                if (angle < 0.0) {
-                    angle = -angle;
-                    negateResult = !negateResult;
-                }
-            } else {
-                final long remAndQuad = remainderPiO2(angle);
-                angle = decodeRemainder(remAndQuad);
-                final double sin;
-                final int q = decodeQuadrant(remAndQuad);
-                if (q == 0) {
-                    sin = sin(angle);
-                } else if (q == 1) {
-                    sin = cos(angle);
-                } else if (q == 2) {
-                    sin = -sin(angle);
-                } else {
-                    sin = -cos(angle);
-                }
-                return (negateResult ? -sin : sin);
-            }
-        }
-        // index: possibly outside tables range.
-        int index = (int)(angle * SIN_COS_INDEXER + 0.5);
-        double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO;
-        // Making sure index is within tables range.
-        // Last value of each table is the same than first,
-        // so we ignore it (tabs size minus one) for modulo.
-        index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1)
-        double indexSin = MyTSinCos.sinTab[index];
-        double indexCos = MyTSinCos.cosTab[index];
-        double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4)));
-        return negateResult ? -result : result;
-    }
-
-    /**
-     * Quick sin, with accuracy of about 1.6e-3 (PI/<look-up tabs size>)
-     * for |angle| < 6588395.0 (Integer.MAX_VALUE * (2*PI/<look-up tabs size>) - 2)
-     * (- 2 due to removing PI/2 before using cosine tab),
-     * and no accuracy at all for larger values.
-     *
-     * @param angle Angle in radians.
-     * @return Angle sine.
-     */
-    public static double sinQuick(double angle) {
-        if (USE_JDK_MATH) {
-            return Math.sin(angle);
-        }
-        return MyTSinCos.cosTab[((int)(Math.abs(angle-Math.PI/2) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)];
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return Angle cosine.
-     */
-    public static double cos(double angle) {
-        if (USE_JDK_MATH) {
-            return Math.cos(angle);
-        }
-        angle = Math.abs(angle);
-        if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) {
-            if (false) {
-                // Can give very bad relative error near PI (mod 2*PI).
-                angle = remainderTwoPi(angle);
-                if (angle < 0.0) {
-                    angle = -angle;
-                }
-            } else {
-                final long remAndQuad = remainderPiO2(angle);
-                angle = decodeRemainder(remAndQuad);
-                final double cos;
-                final int q = decodeQuadrant(remAndQuad);
-                if (q == 0) {
-                    cos = cos(angle);
-                } else if (q == 1) {
-                    cos = -sin(angle);
-                } else if (q == 2) {
-                    cos = -cos(angle);
-                } else {
-                    cos = sin(angle);
-                }
-                return cos;
-            }
-        }
-        // index: possibly outside tables range.
-        int index = (int)(angle * SIN_COS_INDEXER + 0.5);
-        double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO;
-        // Making sure index is within tables range.
-        // Last value of each table is the same than first,
-        // so we ignore it (tabs size minus one) for modulo.
-        index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1)
-        double indexCos = MyTSinCos.cosTab[index];
-        double indexSin = MyTSinCos.sinTab[index];
-        return indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4)));
-    }
-
-    /**
-     * Quick cos, with accuracy of about 1.6e-3 (PI/<look-up tabs size>)
-     * for |angle| < 6588397.0 (Integer.MAX_VALUE * (2*PI/<look-up tabs size>)),
-     * and no accuracy at all for larger values.
-     *
-     * @param angle Angle in radians.
-     * @return Angle cosine.
-     */
-    public static double cosQuick(double angle) {
-        if (USE_JDK_MATH) {
-            return Math.cos(angle);
-        }
-        return MyTSinCos.cosTab[((int)(Math.abs(angle) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)];
-    }
-
-    /**
-     * Computes sine and cosine together.
-     *
-     * @param angle Angle in radians.
-     * @param cosine (out) Angle cosine.
-     * @return Angle sine.
-     */
-    public static double sinAndCos(double angle, DoubleWrapper cosine) {
-        if (USE_JDK_MATH) {
-            cosine.value = Math.cos(angle);
-            return Math.sin(angle);
-        }
-        // Using the same algorithm than sin(double) method,
-        // and computing also cosine at the end.
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) {
-            if (false) {
-                // Can give very bad relative error near PI (mod 2*PI).
-                angle = remainderTwoPi(angle);
-                if (angle < 0.0) {
-                    angle = -angle;
-                    negateResult = !negateResult;
-                }
-            } else {
-                final long remAndQuad = remainderPiO2(angle);
-                angle = decodeRemainder(remAndQuad);
-                final double sin;
-                final int q = decodeQuadrant(remAndQuad);
-                if (q == 0) {
-                    sin = sin(angle);
-                    cosine.value = cos(angle);
-                } else if (q == 1) {
-                    sin = cos(angle);
-                    cosine.value = -sin(angle);
-                } else if (q == 2) {
-                    sin = -sin(angle);
-                    cosine.value = -cos(angle);
-                } else {
-                    sin = -cos(angle);
-                    cosine.value = sin(angle);
-                }
-                return (negateResult ? -sin : sin);
-            }
-        }
-        int index = (int)(angle * SIN_COS_INDEXER + 0.5);
-        double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO;
-        index &= (SIN_COS_TABS_SIZE-2); // index % (SIN_COS_TABS_SIZE-1)
-        double indexSin = MyTSinCos.sinTab[index];
-        double indexCos = MyTSinCos.cosTab[index];
-        // Could factor some multiplications (delta * factorials), but then is less accurate.
-        cosine.value = indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4)));
-        double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4)));
-        return negateResult ? -result : result;
-    }
-
-    /**
-     * Can have very bad relative error near +-PI/2,
-     * but of the same magnitude than the relative delta between
-     * StrictMath.tan(PI/2) and StrictMath.tan(nextDown(PI/2)).
-     *
-     * @param angle Angle in radians.
-     * @return Angle tangent.
-     */
-    public static double tan(double angle) {
-        if (USE_JDK_MATH) {
-            return Math.tan(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle > TAN_MAX_VALUE_FOR_INT_MODULO) {
-            angle = remainderPi(angle);
-            if (angle < 0.0) {
-                angle = -angle;
-                negateResult = !negateResult;
-            }
-        }
-        // index: possibly outside tables range.
-        int index = (int)(angle * TAN_INDEXER + 0.5);
-        double delta = (angle - index * TAN_DELTA_HI) - index * TAN_DELTA_LO;
-        // Making sure index is within tables range.
-        // index modulo PI, i.e. 2*(virtual tab size minus one).
-        index &= (2*(TAN_VIRTUAL_TABS_SIZE-1)-1); // index % (2*(TAN_VIRTUAL_TABS_SIZE-1))
-        // Here, index is in [0,2*(TAN_VIRTUAL_TABS_SIZE-1)-1], i.e. indicates an angle in [0,PI[.
-        if (index > (TAN_VIRTUAL_TABS_SIZE-1)) {
-            index = (2*(TAN_VIRTUAL_TABS_SIZE-1)) - index;
-            delta = -delta;
-            negateResult = !negateResult;
-        }
-        double result;
-        if (index < TAN_TABS_SIZE) {
-            result = MyTTan.tanTab[index]
-                    + delta * (MyTTan.tanDer1DivF1Tab[index]
-                            + delta * (MyTTan.tanDer2DivF2Tab[index]
-                                    + delta * (MyTTan.tanDer3DivF3Tab[index]
-                                            + delta * MyTTan.tanDer4DivF4Tab[index])));
-        } else { // angle in ]TAN_MAX_VALUE_FOR_TABS,TAN_MAX_VALUE_FOR_INT_MODULO], or angle is NaN
-            // Using tan(angle) == 1/tan(PI/2-angle) formula: changing angle (index and delta), and inverting.
-            index = (TAN_VIRTUAL_TABS_SIZE-1) - index;
-            result = 1/(MyTTan.tanTab[index]
-                    - delta * (MyTTan.tanDer1DivF1Tab[index]
-                            - delta * (MyTTan.tanDer2DivF2Tab[index]
-                                    - delta * (MyTTan.tanDer3DivF3Tab[index]
-                                            - delta * MyTTan.tanDer4DivF4Tab[index]))));
-        }
-        return negateResult ? -result : result;
-    }
-
-    /**
-     * @param value Value in [-1,1].
-     * @return Value arcsine, in radians, in [-PI/2,PI/2].
-     */
-    public static double asin(double value) {
-        if (USE_JDK_MATH) {
-            return Math.asin(value);
-        }
-        boolean negateResult = false;
-        if (value < 0.0) {
-            value = -value;
-            negateResult = true;
-        }
-        if (value <= ASIN_MAX_VALUE_FOR_TABS) {
-            int index = (int)(value * ASIN_INDEXER + 0.5);
-            double delta = value - index * ASIN_DELTA;
-            double result = MyTAsin.asinTab[index]
-                    + delta * (MyTAsin.asinDer1DivF1Tab[index]
-                            + delta * (MyTAsin.asinDer2DivF2Tab[index]
-                                    + delta * (MyTAsin.asinDer3DivF3Tab[index]
-                                            + delta * MyTAsin.asinDer4DivF4Tab[index])));
-            return negateResult ? -result : result;
-        } else if (USE_POWTABS_FOR_ASIN && (value <= ASIN_MAX_VALUE_FOR_POWTABS)) {
-            int index = (int)(powFast(value * ASIN_POWTABS_ONE_DIV_MAX_VALUE, ASIN_POWTABS_POWER) * ASIN_POWTABS_SIZE_MINUS_ONE + 0.5);
-            double delta = value - MyTAsinPow.asinParamPowTab[index];
-            double result = MyTAsinPow.asinPowTab[index]
-                    + delta * (MyTAsinPow.asinDer1DivF1PowTab[index]
-                            + delta * (MyTAsinPow.asinDer2DivF2PowTab[index]
-                                    + delta * (MyTAsinPow.asinDer3DivF3PowTab[index]
-                                            + delta * MyTAsinPow.asinDer4DivF4PowTab[index])));
-            return negateResult ? -result : result;
-        } else { // value > ASIN_MAX_VALUE_FOR_TABS, or value is NaN
-            // This part is derived from fdlibm.
-            if (value < 1.0) {
-                double t = (1.0 - value)*0.5;
-                double p = t*(ASIN_PS0+t*(ASIN_PS1+t*(ASIN_PS2+t*(ASIN_PS3+t*(ASIN_PS4+t*ASIN_PS5)))));
-                double q = 1.0+t*(ASIN_QS1+t*(ASIN_QS2+t*(ASIN_QS3+t*ASIN_QS4)));
-                double s = sqrt(t);
-                double z = s+s*(p/q);
-                double result = ASIN_PIO2_HI-((z+z)-ASIN_PIO2_LO);
-                return negateResult ? -result : result;
-            } else { // value >= 1.0, or value is NaN
-                if (value == 1.0) {
-                    return negateResult ? -Math.PI/2 : Math.PI/2;
-                } else {
-                    return Double.NaN;
-                }
-            }
-        }
-    }
-
-    /**
-     * If value is not NaN and is outside [-1,1] range, closest value in this range is used.
-     *
-     * @param value Value in [-1,1].
-     * @return Value arcsine, in radians, in [-PI/2,PI/2].
-     */
-    public static double asinInRange(double value) {
-        if (value <= -1.0) {
-            return -Math.PI/2;
-        } else if (value >= 1.0) {
-            return Math.PI/2;
-        } else {
-            return asin(value);
-        }
-    }
-
-    /**
-     * @param value Value in [-1,1].
-     * @return Value arccosine, in radians, in [0,PI].
-     */
-    public static double acos(double value) {
-        if (USE_JDK_MATH) {
-            return Math.acos(value);
-        }
-        return Math.PI/2 - asin(value);
-    }
-
-    /**
-     * If value is not NaN and is outside [-1,1] range,
-     * closest value in this range is used.
-     *
-     * @param value Value in [-1,1].
-     * @return Value arccosine, in radians, in [0,PI].
-     */
-    public static double acosInRange(double value) {
-        if (value <= -1.0) {
-            return Math.PI;
-        } else if (value >= 1.0) {
-            return 0.0;
-        } else {
-            return acos(value);
-        }
-    }
-
-    /**
-     * @param value A double value.
-     * @return Value arctangent, in radians, in [-PI/2,PI/2].
-     */
-    public static double atan(double value) {
-        if (USE_JDK_MATH) {
-            return Math.atan(value);
-        }
-        boolean negateResult = false;
-        if (value < 0.0) {
-            value = -value;
-            negateResult = true;
-        }
-        if (value == 1.0) {
-            // We want "exact" result for 1.0.
-            return negateResult ? -Math.PI/4 : Math.PI/4;
-        } else if (value <= ATAN_MAX_VALUE_FOR_TABS) {
-            int index = (int)(value * ATAN_INDEXER + 0.5);
-            double delta = value - index * ATAN_DELTA;
-            double result = MyTAtan.atanTab[index]
-                    + delta * (MyTAtan.atanDer1DivF1Tab[index]
-                            + delta * (MyTAtan.atanDer2DivF2Tab[index]
-                                    + delta * (MyTAtan.atanDer3DivF3Tab[index]
-                                            + delta * MyTAtan.atanDer4DivF4Tab[index])));
-            return negateResult ? -result : result;
-        } else { // value > ATAN_MAX_VALUE_FOR_TABS, or value is NaN
-            // This part is derived from fdlibm.
-            if (value < TWO_POW_66) {
-                double x = -1/value;
-                double x2 = x*x;
-                double x4 = x2*x2;
-                double s1 = x2*(ATAN_AT0+x4*(ATAN_AT2+x4*(ATAN_AT4+x4*(ATAN_AT6+x4*(ATAN_AT8+x4*ATAN_AT10)))));
-                double s2 = x4*(ATAN_AT1+x4*(ATAN_AT3+x4*(ATAN_AT5+x4*(ATAN_AT7+x4*ATAN_AT9))));
-                double result = ATAN_HI3-((x*(s1+s2)-ATAN_LO3)-x);
-                return negateResult ? -result : result;
-            } else { // value >= 2^66, or value is NaN
-                if (value != value) {
-                    return Double.NaN;
-                } else {
-                    return negateResult ? -Math.PI/2 : Math.PI/2;
-                }
-            }
-        }
-    }
-
-    /**
-     * For special values for which multiple conventions could be adopted,
-     * behaves like Math.atan2(double,double).
-     *
-     * @param y Coordinate on y axis.
-     * @param x Coordinate on x axis.
-     * @return Angle from x axis positive side to (x,y) position, in radians, in [-PI,PI].
-     *         Angle measure is positive when going from x axis to y axis (positive sides).
-     */
-    public static double atan2(double y, double x) {
-        if (USE_JDK_MATH) {
-            return Math.atan2(y,x);
-        }
-        /*
-         * Using sub-methods, to make method lighter for general case,
-         * and to avoid JIT-optimization crash on NaN.
-         */
-        if (x > 0.0) {
-            if (y == 0.0) {
-                // +-0.0
-                return y;
-            }
-            if (x == Double.POSITIVE_INFINITY) {
-                return atan2_pinf_yyy(y);
-            } else {
-                return atan(y/x);
-            }
-        } else if (x < 0.0) {
-            if (y == 0.0) {
-                return signFromBit(y) * Math.PI;
-            }
-            if (x == Double.NEGATIVE_INFINITY) {
-                return atan2_ninf_yyy(y);
-            } else if (y > 0.0) {
-                return Math.PI/2 - atan(x/y);
-            } else if (y < 0.0) {
-                return -Math.PI/2 - atan(x/y);
-            } else {
-                return Double.NaN;
-            }
-        } else {
-            return atan2_yyy_zeroOrNaN(y, x);
-        }
-    }
-
-    /**
-     * Gives same result as Math.toRadians for some particular values
-     * like 90.0, 180.0 or 360.0, but is faster (no division).
-     *
-     * @param angdeg Angle value in degrees.
-     * @return Angle value in radians.
-     */
-    public static double toRadians(double angdeg) {
-        if (USE_JDK_MATH) {
-            return Math.toRadians(angdeg);
-        }
-        return angdeg * (Math.PI/180);
-    }
-
-    /**
-     * Gives same result as Math.toDegrees for some particular values
-     * like Math.PI/2, Math.PI or 2*Math.PI, but is faster (no division).
-     *
-     * @param angrad Angle value in radians.
-     * @return Angle value in degrees.
-     */
-    public static double toDegrees(double angrad) {
-        if (USE_JDK_MATH) {
-            return Math.toDegrees(angrad);
-        }
-        return angrad * (180/Math.PI);
-    }
-
-    /**
-     * @param sign Sign of the angle: true for positive, false for negative.
-     * @param degrees Degrees, in [0,180].
-     * @param minutes Minutes, in [0,59].
-     * @param seconds Seconds, in [0.0,60.0[.
-     * @return Angle in radians.
-     */
-    public static double toRadians(boolean sign, int degrees, int minutes, double seconds) {
-        return toRadians(toDegrees(sign, degrees, minutes, seconds));
-    }
-
-    /**
-     * @param sign Sign of the angle: true for positive, false for negative.
-     * @param degrees Degrees, in [0,180].
-     * @param minutes Minutes, in [0,59].
-     * @param seconds Seconds, in [0.0,60.0[.
-     * @return Angle in degrees.
-     */
-    public static double toDegrees(boolean sign, int degrees, int minutes, double seconds) {
-        double signFactor = sign ? 1.0 : -1.0;
-        return signFactor * (degrees + (1.0/60)*(minutes + (1.0/60)*seconds));
-    }
-
-    /**
-     * @param angrad Angle in radians.
-     * @param degrees (out) Degrees, in [0,180].
-     * @param minutes (out) Minutes, in [0,59].
-     * @param seconds (out) Seconds, in [0.0,60.0[.
-     * @return true if the resulting angle in [-180deg,180deg] is positive, false if it is negative.
-     */
-    public static boolean toDMS(double angrad, IntWrapper degrees, IntWrapper minutes, DoubleWrapper seconds) {
-        // Computing longitude DMS.
-        double tmp = toDegrees(normalizeMinusPiPi(angrad));
-        boolean isNeg = (tmp < 0.0);
-        if (isNeg) {
-            tmp = -tmp;
-        }
-        degrees.value = (int)tmp;
-        tmp = (tmp-degrees.value)*60.0;
-        minutes.value = (int)tmp;
-        seconds.value = Math.min((tmp-minutes.value)*60.0,DOUBLE_BEFORE_60);
-        return !isNeg;
-    }
-
-    /**
-     * NB: Since 2*Math.PI < 2*PI, a span of 2*Math.PI does not mean full angular range.
-     * ex.: isInClockwiseDomain(0.0, 2*Math.PI, -1e-20) returns false.
-     * ---> For full angular range, use a span > 2*Math.PI, like 2*PI_SUP constant of this class.
-     *
-     * @param startAngRad An angle, in radians.
-     * @param angSpanRad An angular span, >= 0.0, in radians.
-     * @param angRad An angle, in radians.
-     * @return true if angRad is in the clockwise angular domain going from startAngRad, over angSpanRad,
-     *         extremities included, false otherwise.
-     */
-    public static boolean isInClockwiseDomain(double startAngRad, double angSpanRad, double angRad) {
-        if (Math.abs(angRad) < -TWO_MATH_PI_IN_MINUS_PI_PI) {
-            // special case for angular values of small magnitude
-            if (angSpanRad <= 2*Math.PI) {
-                if (angSpanRad < 0.0) {
-                    // empty domain
-                    return false;
-                }
-                // angSpanRad is in [0,2*PI]
-                startAngRad = normalizeMinusPiPi(startAngRad);
-                double endAngRad = normalizeMinusPiPi(startAngRad + angSpanRad);
-                if (startAngRad <= endAngRad) {
-                    return (angRad >= startAngRad) && (angRad <= endAngRad);
-                } else {
-                    return (angRad >= startAngRad) || (angRad <= endAngRad);
-                }
-            } else { // angSpanRad > 2*Math.PI, or is NaN
-                return (angSpanRad == angSpanRad);
-            }
-        } else {
-            // general case
-            return (normalizeZeroTwoPi(angRad - startAngRad) <= angSpanRad);
-        }
-    }
-
-    /*
-     * hyperbolic trigonometry
-     */
-
-    /**
-     * Some properties of sinh(x) = (exp(x)-exp(-x))/2:
-     * 1) defined on ]-Infinity,+Infinity[
-     * 2) result in ]-Infinity,+Infinity[
-     * 3) sinh(x) = -sinh(-x) (implies sinh(0) = 0)
-     * 4) sinh(epsilon) ~= epsilon
-     * 5) lim(sinh(x),x->+Infinity) = +Infinity
-     *    (y increasing exponentially faster than x)
-     * 6) reaches +Infinity (double overflow) for x >= 710.475860073944,
-     *    i.e. a bit further than exp(x)
-     *
-     * @param value A double value.
-     * @return Value hyperbolic sine.
-     */
-    public static double sinh(double value) {
-        if (USE_JDK_MATH) {
-            return Math.sinh(value);
-        }
-        // sinh(x) = (exp(x)-exp(-x))/2
-        double h;
-        if (value < 0.0) {
-            value = -value;
-            h = -0.5;
-        } else {
-            h = 0.5;
-        }
-        if (value < 22.0) {
-            if (value < TWO_POW_N28) {
-                return (h < 0.0) ? -value : value;
-            } else {
-                // sinh(x)
-                // = (exp(x)-exp(-x))/2
-                // = (exp(x)-1/exp(x))/2
-                // = (expm1(x) + 1 - 1/(expm1(x)+1))/2
-                // = (expm1(x) + (expm1(x)+1)/(expm1(x)+1) - 1/(expm1(x)+1))/2
-                // = (expm1(x) + expm1(x)/(expm1(x)+1))/2
-                double t = expm1(value);
-                // Might be more accurate, if value < 1: return h*((t+t)-t*t/(t+1.0)).
-                return h * (t + t/(t+1.0));
-            }
-        } else if (value < LOG_DOUBLE_MAX_VALUE) {
-            return h * exp(value);
-        } else {
-            double t = exp(value*0.5);
-            return (h*t)*t;
-        }
-    }
-
-    /**
-     * Some properties of cosh(x) = (exp(x)+exp(-x))/2:
-     * 1) defined on ]-Infinity,+Infinity[
-     * 2) result in [1,+Infinity[
-     * 3) cosh(0) = 1
-     * 4) cosh(x) = cosh(-x)
-     * 5) lim(cosh(x),x->+Infinity) = +Infinity
-     *    (y increasing exponentially faster than x)
-     * 6) reaches +Infinity (double overflow) for x >= 710.475860073944,
-     *    i.e. a bit further than exp(x)
-     *
-     * @param value A double value.
-     * @return Value hyperbolic cosine.
-     */
-    public static double cosh(double value) {
-        if (USE_JDK_MATH) {
-            return Math.cosh(value);
-        }
-        // cosh(x) = (exp(x)+exp(-x))/2
-        if (value < 0.0) {
-            value = -value;
-        }
-        if (value < LOG_TWO_POW_27) {
-            if (value < TWO_POW_N27) {
-                // cosh(x)
-                // = (exp(x)+exp(-x))/2
-                // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2
-                // = 1+x^2/2!+x^4/4!+...
-                // For value of x small in magnitude, the sum of the terms does not add to 1.
-                return 1;
-            } else {
-                // cosh(x)
-                // = (exp(x)+exp(-x))/2
-                // = (exp(x)+1/exp(x))/2
-                double t = exp(value);
-                return 0.5 * (t+1/t);
-            }
-        } else if (value < LOG_DOUBLE_MAX_VALUE) {
-            return 0.5 * exp(value);
-        } else {
-            double t = exp(value*0.5);
-            return (0.5*t)*t;
-        }
-    }
-
-    /**
-     * Much more accurate than cosh(value)-1,
-     * for arguments (and results) close to zero.
-     *
-     * coshm1(-0.0) = -0.0, for homogeneity with
-     * acosh1p(-0.0) = -0.0.
-     *
-     * @param value A double value.
-     * @return Value hyperbolic cosine, minus 1.
-     */
-    public static double coshm1(double value) {
-        // cosh(x)-1 = (exp(x)+exp(-x))/2 - 1
-        if (value < 0.0) {
-            value = -value;
-        }
-        if (value < LOG_TWO_POW_27) {
-            if (value < TWO_POW_N27) {
-                if (value == 0.0) {
-                    // +-0.0
-                    return value;
-                }
-                // Using (expm1(x)+expm1(-x))/2
-                // is not accurate for tiny values,
-                // for expm1 results are of higher
-                // magnitude than the result and
-                // of different signs, such as their
-                // sum is not accurate.
-                // cosh(x) - 1
-                // = (exp(x)+exp(-x))/2 - 1
-                // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 - 1
-                // = x^2/2!+x^4/4!+...
-                // ~= x^2 * (1/2 + x^2 * 1/24)
-                //  = x^2 * 0.5 (since x < 2^-27)
-                return 0.5 * value*value;
-            } else {
-                // cosh(x) - 1
-                // = (exp(x)+exp(-x))/2 - 1
-                // = (exp(x)-1+exp(-x)-1)/2
-                // = (expm1(x)+expm1(-x))/2
-                return 0.5 * (expm1(value)+expm1(-value));
-            }
-        } else if (value < LOG_DOUBLE_MAX_VALUE) {
-            return 0.5 * exp(value) - 1.0;
-        } else {
-            // No need to subtract 1 from result.
-            double t = exp(value*0.5);
-            return (0.5*t)*t;
-        }
-    }
-
-    /**
-     * Computes hyperbolic sine and hyperbolic cosine together.
-     *
-     * @param value A double value.
-     * @param hcosine (out) Value hyperbolic cosine.
-     * @return Value hyperbolic sine.
-     */
-    public static double sinhAndCosh(double value, DoubleWrapper hcosine) {
-        if (USE_JDK_MATH) {
-            hcosine.value = Math.cosh(value);
-            return Math.sinh(value);
-        }
-        // Mixup of sinh and cosh treatments: if you modify them,
-        // you might want to also modify this.
-        double h;
-        if (value < 0.0) {
-            value = -value;
-            h = -0.5;
-        } else {
-            h = 0.5;
-        }
-        final double hsine;
-        // LOG_TWO_POW_27 = 18.714973875118524
-        if (value < LOG_TWO_POW_27) { // test from cosh
-            // sinh
-            if (value < TWO_POW_N28) {
-                hsine = (h < 0.0) ? -value : value;
-            } else {
-                double t = expm1(value);
-                hsine = h * (t + t/(t+1.0));
-            }
-            // cosh
-            if (value < TWO_POW_N27) {
-                hcosine.value = 1;
-            } else {
-                double t = exp(value);
-                hcosine.value = 0.5 * (t+1/t);
-            }
-        } else if (value < 22.0) { // test from sinh
-            // Here, value is in [18.714973875118524,22.0[.
-            double t = expm1(value);
-            hsine = h * (t + t/(t+1.0));
-            hcosine.value = 0.5 * (t+1.0);
-        } else {
-            if (value < LOG_DOUBLE_MAX_VALUE) {
-                hsine = h * exp(value);
-            } else {
-                double t = exp(value*0.5);
-                hsine = (h*t)*t;
-            }
-            hcosine.value = Math.abs(hsine);
-        }
-        return hsine;
-    }
-
-    /**
-     * Some properties of tanh(x) = sinh(x)/cosh(x) = (exp(2*x)-1)/(exp(2*x)+1):
-     * 1) defined on ]-Infinity,+Infinity[
-     * 2) result in ]-1,1[
-     * 3) tanh(x) = -tanh(-x) (implies tanh(0) = 0)
-     * 4) tanh(epsilon) ~= epsilon
-     * 5) lim(tanh(x),x->+Infinity) = 1
-     * 6) reaches 1 (double loss of precision) for x = 19.061547465398498
-     *
-     * @param value A double value.
-     * @return Value hyperbolic tangent.
-     */
-    public static double tanh(double value) {
-        if (USE_JDK_MATH) {
-            return Math.tanh(value);
-        }
-        // tanh(x) = sinh(x)/cosh(x)
-        //         = (exp(x)-exp(-x))/(exp(x)+exp(-x))
-        //         = (exp(2*x)-1)/(exp(2*x)+1)
-        boolean negateResult = false;
-        if (value < 0.0) {
-            value = -value;
-            negateResult = true;
-        }
-        double z;
-        if (value < TANH_1_THRESHOLD) {
-            if (value < TWO_POW_N55) {
-                return negateResult ? -value*(1.0-value) : value*(1.0+value);
-            } else if (value >= 1) {
-                z = 1.0-2.0/(expm1(value+value)+2.0);
-            } else {
-                double t = expm1(-(value+value));
-                z = -t/(t+2.0);
-            }
-        } else {
-            z = (value != value) ? Double.NaN : 1.0;
-        }
-        return negateResult ? -z : z;
-    }
-
-    /**
-     * Some properties of asinh(x) = log(x + sqrt(x^2 + 1))
-     * 1) defined on ]-Infinity,+Infinity[
-     * 2) result in ]-Infinity,+Infinity[
-     * 3) asinh(x) = -asinh(-x) (implies asinh(0) = 0)
-     * 4) asinh(epsilon) ~= epsilon
-     * 5) lim(asinh(x),x->+Infinity) = +Infinity
-     *    (y increasing logarithmically slower than x)
-     *
-     * @param value A double value.
-     * @return Value hyperbolic arcsine.
-     */
-    public static double asinh(double value) {
-        // asinh(x) = log(x + sqrt(x^2 + 1))
-        boolean negateResult = false;
-        if (value < 0.0) {
-            value = -value;
-            negateResult = true;
-        }
-        double result;
-        // (about) smallest possible for
-        // non-log1p case to be accurate.
-        if (value < ASINH_LOG1P_THRESHOLD) {
-            // Around this range, FDLIBM uses
-            // log1p(value+value*value/(1+sqrt(value*value+1))),
-            // but it's slower, so we don't use it.
-            /*
-             * If x is close to zero, log argument is close to 1,
-             * so to avoid precision loss we use log1p(double),
-             * with
-             * (1+x)^p = 1 + p * x + (p*(p-1))/2! * x^2 + (p*(p-1)*(p-2))/3! * x^3 + ...
-             * (1+x)^p = 1 + p * x * (1 + (p-1)/2 * x * (1 + (p-2)/3 * x + ...)
-             * (1+x)^0.5 = 1 + 0.5 * x * (1 + (0.5-1)/2 * x * (1 + (0.5-2)/3 * x + ...)
-             * (1+x^2)^0.5 = 1 + 0.5 * x^2 * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...)
-             * x + (1+x^2)^0.5 = 1 + x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...))
-             * so
-             * asinh(x) = log1p(x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...)))
-             */
-            final double x = value;
-            final double x2 = x*x;
-            // Enough terms for good accuracy,
-            // given our threshold.
-            final double argLog1p = (x *
-                    (1 + 0.5 * x
-                            * (1 + (0.5-1)/2 * x2
-                                    * (1 + (0.5-2)/3 * x2
-                                            * (1 + (0.5-3)/4 * x2
-                                                    * (1 + (0.5-4)/5 * x2
-                                                    ))))));
-            result = log1p(argLog1p);
-        } else if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) {
-            // Around this range, FDLIBM uses
-            // log(2*value+1/(value+sqrt(value*value+1))),
-            // but it involves an additional division
-            // so we don't use it.
-            result = log(value + sqrt(value*value + 1.0));
-        } else {
-            // log(2*value) would overflow for value > Double.MAX_VALUE/2,
-            // so we compute otherwise.
-            result = LOG_2 + log(value);
-        }
-        return negateResult ? -result : result;
-    }
-
-    /**
-     * Some properties of acosh(x) = log(x + sqrt(x^2 - 1)):
-     * 1) defined on [1,+Infinity[
-     * 2) result in ]0,+Infinity[ (by convention, since cosh(x) = cosh(-x))
-     * 3) acosh(1) = 0
-     * 4) acosh(1+epsilon) ~= log(1 + sqrt(2*epsilon)) ~= sqrt(2*epsilon)
-     * 5) lim(acosh(x),x->+Infinity) = +Infinity
-     *    (y increasing logarithmically slower than x)
-     *
-     * @param value A double value.
-     * @return Value hyperbolic arccosine.
-     */
-    public static double acosh(double value) {
-        if (!(value > 1.0)) {
-            // NaN, or value <= 1
-            if (ANTI_JIT_OPTIM_CRASH_ON_NAN) {
-                return (value < 1.0) ? Double.NaN : value - 1.0;
-            } else {
-                return (value == 1.0) ? 0.0 : Double.NaN;
-            }
-        }
-        double result;
-        if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) {
-            // Around this range, FDLIBM uses
-            // log(2*value-1/(value+sqrt(value*value-1))),
-            // but it involves an additional division
-            // so we don't use it.
-            result = log(value + sqrt(value*value - 1.0));
-        } else {
-            // log(2*value) would overflow for value > Double.MAX_VALUE/2,
-            // so we compute otherwise.
-            result = LOG_2 + log(value);
-        }
-        return result;
-    }
-
-    /**
-     * Much more accurate than acosh(1+value),
-     * for arguments (and results) close to zero.
-     *
-     * acosh1p(-0.0) = -0.0, for homogeneity with
-     * sqrt(-0.0) = -0.0, which looks about the same
-     * near 0.
-     *
-     * @param value A double value.
-     * @return Hyperbolic arccosine of (1+value).
-     */
-    public static double acosh1p(double value) {
-        if (!(value > 0.0)) {
-            // NaN, or value <= 0.
-            // If value is -0.0, returning -0.0.
-            if (ANTI_JIT_OPTIM_CRASH_ON_NAN) {
-                return (value < 0.0) ? Double.NaN : value;
-            } else {
-                return (value == 0.0) ? value : Double.NaN;
-            }
-        }
-        double result;
-        if (value < (ASINH_ACOSH_SQRT_ELISION_THRESHOLD-1)) {
-            // acosh(1+x)
-            // = log((1+x) + sqrt((1+x)^2 - 1))
-            // = log(1 + x + sqrt(1 + 2*x + x^2 - 1))
-            // = log1p(x + sqrt(2*x + x^2))
-            // = log1p(x + sqrt(x * (2 + x))
-            result = log1p(value + sqrt(value * (2 + value)));
-        } else {
-            result = LOG_2 + log(1+value);
-        }
-        return result;
-    }
-
-    /**
-     * Some properties of atanh(x) = log((1+x)/(1-x))/2:
-     * 1) defined on ]-1,1[
-     * 2) result in ]-Infinity,+Infinity[
-     * 3) atanh(-1) = -Infinity (by continuity)
-     * 4) atanh(1) = +Infinity (by continuity)
-     * 5) atanh(epsilon) ~= epsilon
-     * 6) lim(atanh(x),x->1) = +Infinity
-     *
-     * @param value A double value.
-     * @return Value hyperbolic arctangent.
-     */
-    public static double atanh(double value) {
-        boolean negateResult = false;
-        if (value < 0.0) {
-            value = -value;
-            negateResult = true;
-        }
-        double result;
-        if (!(value < 1.0)) {
-            // NaN, or value >= 1
-            if (ANTI_JIT_OPTIM_CRASH_ON_NAN) {
-                result = (value > 1.0) ? Double.NaN : Double.POSITIVE_INFINITY + value;
-            } else {
-                result = (value == 1.0) ? Double.POSITIVE_INFINITY : Double.NaN;
-            }
-        } else {
-            // For value < 0.5, FDLIBM uses
-            // 0.5 * log1p((value+value) + (value+value)*value/(1-value)),
-            // instead, but this is good enough for us.
-            // atanh(x)
-            // = log((1+x)/(1-x))/2
-            // = log((1-x+2x)/(1-x))/2
-            // = log1p(2x/(1-x))/2
-            result = 0.5 * log1p((value+value)/(1.0-value));
-        }
-        return negateResult ? -result : result;
-    }
-
-    /*
-     * exponentials
-     */
-
-    /**
-     * @param value A double value.
-     * @return e^value.
-     */
-    public static double exp(double value) {
-        if (USE_JDK_MATH) {
-            return Math.exp(value);
-        }
-        // exp(x) = exp([x])*exp(y)
-        // with [x] the integer part of x, and y = x-[x]
-        // ===>
-        // We find an approximation of y, called z.
-        // ===>
-        // exp(x) = exp([x])*(exp(z)*exp(epsilon))
-        // with epsilon = y - z
-        // ===>
-        // We have exp([x]) and exp(z) pre-computed in tables, we "just" have to compute exp(epsilon).
-        //
-        // We use the same indexing (cast to int) to compute x integer part and the
-        // table index corresponding to z, to avoid two int casts.
-        // Also, to optimize index multiplication and division, we use powers of two,
-        // so that we can do it with bits shifts.
-
-        if (value > EXP_OVERFLOW_LIMIT) {
-            return Double.POSITIVE_INFINITY;
-        } else if (!(value >= EXP_UNDERFLOW_LIMIT)) {
-            return (value != value) ? Double.NaN : 0.0;
-        }
-
-        final int indexes = (int)(value*EXP_LO_INDEXING);
-
-        final int valueInt;
-        if (indexes >= 0) {
-            valueInt = (indexes>>EXP_LO_INDEXING_DIV_SHIFT);
-        } else {
-            valueInt = -((-indexes)>>EXP_LO_INDEXING_DIV_SHIFT);
-        }
-        final double hiTerm = MyTExp.expHiTab[valueInt-(int)EXP_UNDERFLOW_LIMIT];
-
-        final int zIndex = indexes - (valueInt<<EXP_LO_INDEXING_DIV_SHIFT);
-        final double y = (value-valueInt);
-        final double z = zIndex*(1.0/EXP_LO_INDEXING);
-        final double eps = y-z;
-        final double expZ = MyTExp.expLoPosTab[zIndex+EXP_LO_TAB_MID_INDEX];
-        final double expEps = (1+eps*(1+eps*(1.0/2+eps*(1.0/6+eps*(1.0/24)))));
-        final double loTerm = expZ * expEps;
-
-        return hiTerm * loTerm;
-    }
-
-    /**
-     * Quick exp, with a max relative error of about 2.94e-2 for |value| < 700.0 or so,
-     * and no accuracy at all outside this range.
-     * Derived from a note by Nicol N. Schraudolph, IDSIA, 1998.
-     *
-     * @param value A double value.
-     * @return e^value.
-     */
-    public static double expQuick(double value) {
-        if (USE_JDK_MATH) {
-            return Math.exp(value);
-        }
-        /*
-         * Cast of double values, even in long range, into long, is slower than
-         * from double to int for values in int range, and then from int to long.
-         * For that reason, we only work with integer values in int range
-         * (corresponding to the 32 first bits of the long, containing sign,
-         * exponent, and highest significant bits of double's mantissa),
-         * and cast twice.
-         *
-         * Constants determined empirically, using a random-based metaheuristic.
-         * Should be possible to find better ones.
-         */
-        return Double.longBitsToDouble(((long)(int)(1512775.3952 * value + 1.0726481222E9))<<32);
-    }
-
-    /**
-     * Much more accurate than exp(value)-1,
-     * for arguments (and results) close to zero.
-     *
-     * @param value A double value.
-     * @return e^value-1.
-     */
-    public static double expm1(double value) {
-        if (USE_JDK_MATH) {
-            return Math.expm1(value);
-        }
-        // If value is far from zero, we use exp(value)-1.
-        //
-        // If value is close to zero, we use the following formula:
-        // exp(value)-1
-        // = exp(valueApprox)*exp(epsilon)-1
-        // = exp(valueApprox)*(exp(epsilon)-exp(-valueApprox))
-        // = exp(valueApprox)*(1+epsilon+epsilon^2/2!+...-exp(-valueApprox))
-        // = exp(valueApprox)*((1-exp(-valueApprox))+epsilon+epsilon^2/2!+...)
-        // exp(valueApprox) and exp(-valueApprox) being stored in tables.
-
-        if (Math.abs(value) < EXP_LO_DISTANCE_TO_ZERO) {
-            // Taking int part instead of rounding, which takes too long.
-            int i = (int)(value*EXP_LO_INDEXING);
-            double delta = value-i*(1.0/EXP_LO_INDEXING);
-            return MyTExp.expLoPosTab[i+EXP_LO_TAB_MID_INDEX]*(MyTExp.expLoNegTab[i+EXP_LO_TAB_MID_INDEX]+delta*(1+delta*(1.0/2+delta*(1.0/6+delta*(1.0/24+delta*(1.0/120))))));
-        } else {
-            return exp(value)-1;
-        }
-    }
-
-    /*
-     * logarithms
-     */
-
-    /**
-     * @param value A double value.
-     * @return Value logarithm (base e).
-     */
-    public static double log(double value) {
-        if (USE_JDK_MATH || (!USE_REDEFINED_LOG)) {
-            return Math.log(value);
-        }
-        if (value > 0.0) {
-            if (value == Double.POSITIVE_INFINITY) {
-                return Double.POSITIVE_INFINITY;
-            }
-
-            // For normal values not close to 1.0, we use the following formula:
-            // log(value)
-            // = log(2^exponent*1.mantissa)
-            // = log(2^exponent) + log(1.mantissa)
-            // = exponent * log(2) + log(1.mantissa)
-            // = exponent * log(2) + log(1.mantissaApprox) + log(1.mantissa/1.mantissaApprox)
-            // = exponent * log(2) + log(1.mantissaApprox) + log(1+epsilon)
-            // = exponent * log(2) + log(1.mantissaApprox) + epsilon-epsilon^2/2+epsilon^3/3-epsilon^4/4+...
-            // with:
-            // 1.mantissaApprox <= 1.mantissa,
-            // log(1.mantissaApprox) in table,
-            // epsilon = (1.mantissa/1.mantissaApprox)-1
-            //
-            // To avoid bad relative error for small results,
-            // values close to 1.0 are treated aside, with the formula:
-            // log(x) = z*(2+z^2*((2.0/3)+z^2*((2.0/5))+z^2*((2.0/7))+...)))
-            // with z=(x-1)/(x+1)
-
-            double h;
-            if (value > 0.95) {
-                if (value < 1.14) {
-                    double z = (value-1.0)/(value+1.0);
-                    double z2 = z*z;
-                    return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11)))))));
-                }
-                h = 0.0;
-            } else if (value < DOUBLE_MIN_NORMAL) {
-                // Ensuring value is normal.
-                value *= TWO_POW_52;
-                // log(x*2^52)
-                // = log(x)-ln(2^52)
-                // = log(x)-52*ln(2)
-                h = -52*LOG_2;
-            } else {
-                h = 0.0;
-            }
-
-            int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32);
-            int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT;
-            // Getting the first LOG_BITS bits of the mantissa.
-            int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS));
-
-            // 1.mantissa/1.mantissaApprox - 1
-            double z = (value * twoPowNormalOrSubnormal(-valueExp)) * MyTLog.logXInvTab[xIndex] - 1;
-
-            z *= (1-z*((1.0/2)-z*((1.0/3))));
-
-            return h + valueExp * LOG_2 + (MyTLog.logXLogTab[xIndex] + z);
-
-        } else if (value == 0.0) {
-            return Double.NEGATIVE_INFINITY;
-        } else { // value < 0.0, or value is NaN
-            return Double.NaN;
-        }
-    }
-
-    /**
-     * Quick log, with a max relative error of about 1.9e-3
-     * for values in ]Double.MIN_NORMAL,+Infinity[, and
-     * worse accuracy outside this range.
-     *
-     * @param value A double value, in ]0,+Infinity[ (strictly positive and finite).
-     * @return Value logarithm (base e).
-     */
-    public static double logQuick(double value) {
-        if (USE_JDK_MATH) {
-            return Math.log(value);
-        }
-        /*
-         * Inverse of Schraudolph's method for exp, is very inaccurate near 1,
-         * and not that fast (even using floats), especially with added if's
-         * to deal with values near 1, so we don't use it, and use a simplified
-         * version of our log's redefined algorithm.
-         */
-
-        // Simplified version of log's redefined algorithm:
-        // log(value) ~= exponent * log(2) + log(1.mantissaApprox)
-
-        double h;
-        if (value > 0.87) {
-            if (value < 1.16) {
-                return 2.0 * (value-1.0)/(value+1.0);
-            }
-            h = 0.0;
-        } else if (value < DOUBLE_MIN_NORMAL) {
-            value *= TWO_POW_52;
-            h = -52*LOG_2;
-        } else {
-            h = 0.0;
-        }
-
-        int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32);
-        int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT;
-        int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS));
-
-        return h + valueExp * LOG_2 + MyTLog.logXLogTab[xIndex];
-    }
-
-    /**
-     * @param value A double value.
-     * @return Value logarithm (base 10).
-     */
-    public static double log10(double value) {
-        if (USE_JDK_MATH || (!USE_REDEFINED_LOG)) {
-            return Math.log10(value);
-        }
-        // INV_LOG_10 is < 1, but there is no risk of log(double)
-        // overflow (positive or negative) while the end result shouldn't,
-        // since log(Double.MIN_VALUE) and log(Double.MAX_VALUE) have
-        // magnitudes of just a few hundreds.
-        return log(value) * INV_LOG_10;
-    }
-
-    /**
-     * Much more accurate than log(1+value),
-     * for arguments (and results) close to zero.
-     *
-     * @param value A double value.
-     * @return Logarithm (base e) of (1+value).
-     */
-    public static double log1p(double value) {
-        if (USE_JDK_MATH) {
-            return Math.log1p(value);
-        }
-        if (false) {
-            // This also works. Simpler but a bit slower.
-            if (value == Double.POSITIVE_INFINITY) {
-                return Double.POSITIVE_INFINITY;
-            }
-            double valuePlusOne = 1+value;
-            if (valuePlusOne == 1.0) {
-                return value;
-            } else {
-                return log(valuePlusOne)*(value/(valuePlusOne-1.0));
-            }
-        }
-        if (value > -1.0) {
-            if (value == Double.POSITIVE_INFINITY) {
-                return Double.POSITIVE_INFINITY;
-            }
-
-            // ln'(x) = 1/x
-            // so
-            // log(x+epsilon) ~= log(x) + epsilon/x
-            //
-            // Let u be 1+value rounded:
-            // 1+value = u+epsilon
-            //
-            // log(1+value)
-            // = log(u+epsilon)
-            // ~= log(u) + epsilon/value
-            // We compute log(u) as done in log(double), and then add the corrective term.
-
-            double valuePlusOne = 1.0+value;
-            if (valuePlusOne == 1.0) {
-                return value;
-            } else if (Math.abs(value) < 0.15) {
-                double z = value/(value+2.0);
-                double z2 = z*z;
-                return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11)))))));
-            }
-
-            int valuePlusOneBitsHi = (int)(Double.doubleToRawLongBits(valuePlusOne)>>32) & 0x7FFFFFFF;
-            int valuePlusOneExp = (valuePlusOneBitsHi>>20)-MAX_DOUBLE_EXPONENT;
-            // Getting the first LOG_BITS bits of the mantissa.
-            int xIndex = ((valuePlusOneBitsHi<<12)>>>(32-LOG_BITS));
-
-            // 1.mantissa/1.mantissaApprox - 1
-            double z = (valuePlusOne * twoPowNormalOrSubnormal(-valuePlusOneExp)) * MyTLog.logXInvTab[xIndex] - 1;
-
-            z *= (1-z*((1.0/2)-z*(1.0/3)));
-
-            // Adding epsilon/valuePlusOne to z,
-            // with
-            // epsilon = value - (valuePlusOne-1)
-            // (valuePlusOne + epsilon ~= 1+value (not rounded))
-
-            return valuePlusOneExp * LOG_2 + MyTLog.logXLogTab[xIndex] + (z + (value - (valuePlusOne-1))/valuePlusOne);
-        } else if (value == -1.0) {
-            return Double.NEGATIVE_INFINITY;
-        } else { // value < -1.0, or value is NaN
-            return Double.NaN;
-        }
-    }
-
-    /*
-     * powers
-     */
-
-    /**
-     * 1e-13ish accuracy or better on whole double range.
-     *
-     * @param value A double value.
-     * @param power A power.
-     * @return value^power.
-     */
-    public static double pow(double value, double power) {
-        if (USE_JDK_MATH) {
-            return Math.pow(value,power);
-        }
-        if (power == 0.0) {
-            return 1.0;
-        } else if (power == 1.0) {
-            return value;
-        }
-        if (value <= 0.0) {
-            // powerInfo: 0 if not integer, 1 if even integer, -1 if odd integer
-            int powerInfo;
-            if (Math.abs(power) >= (TWO_POW_52*2)) {
-                // The binary digit just before comma is outside mantissa,
-                // thus it is always 0: power is an even integer.
-                powerInfo = 1;
-            } else {
-                // If power's magnitude permits, we cast into int instead of into long,
-                // as it is faster.
-                if (Math.abs(power) <= (double)Integer.MAX_VALUE) {
-                    int powerAsInt = (int)power;
-                    if (power == (double)powerAsInt) {
-                        powerInfo = ((powerAsInt & 1) == 0) ? 1 : -1;
-                    } else { // power is not an integer (and not NaN, due to test against Integer.MAX_VALUE)
-                        powerInfo = 0;
-                    }
-                } else {
-                    long powerAsLong = (long)power;
-                    if (power == (double)powerAsLong) {
-                        powerInfo = ((powerAsLong & 1) == 0) ? 1 : -1;
-                    } else { // power is not an integer, or is NaN
-                        if (power != power) {
-                            return Double.NaN;
-                        }
-                        powerInfo = 0;
-                    }
-                }
-            }
-
-            if (value == 0.0) {
-                if (power < 0.0) {
-                    return (powerInfo < 0) ? 1/value : Double.POSITIVE_INFINITY;
-                } else { // power > 0.0 (0 and NaN cases already treated)
-                    return (powerInfo < 0) ? value : 0.0;
-                }
-            } else { // value < 0.0
-                if (value == Double.NEGATIVE_INFINITY) {
-                    if (powerInfo < 0) { // power odd integer
-                        return (power < 0.0) ? -0.0 : Double.NEGATIVE_INFINITY;
-                    } else { // power even integer, or not an integer
-                        return (power < 0.0) ? 0.0 : Double.POSITIVE_INFINITY;
-                    }
-                } else {
-                    return (powerInfo == 0) ? Double.NaN : powerInfo * exp(power*log(-value));
-                }
-            }
-        } else { // value > 0.0, or value is NaN
-            return exp(power*log(value));
-        }
-    }
-
-    /**
-     * Quick pow, with a max relative error of about 1e-2
-     * for value >= Double.MIN_NORMAL and 1e-10 < |value^power| < 1e10,
-     * of about 6e-2 for value >= Double.MIN_NORMAL and 1e-40 < |value^power| < 1e40,
-     * and worse accuracy otherwise.
-     *
-     * @param value A double value, in ]0,+Infinity[ (strictly positive and finite).
-     * @param power A double value.
-     * @return value^power.
-     */
-    public static double powQuick(double value, double power) {
-        if (USE_JDK_MATH) {
-            return Math.pow(value,power);
-        }
-        return exp(power*logQuick(value));
-    }
-
-    /**
-     * This treatment is somehow accurate for low values of |power|,
-     * and for |power*getExponent(value)| < 1023 or so (to stay away
-     * from double extreme magnitudes (large and small)).
-     *
-     * @param value A double value.
-     * @param power A power.
-     * @return value^power.
-     */
-    public static double powFast(double value, int power) {
-        if (USE_JDK_MATH) {
-            return Math.pow(value,power);
-        }
-        if (power < 3) {
-            if (power < 0) {
-                // Opposite of Integer.MIN_VALUE does not exist as int.
-                if (power == Integer.MIN_VALUE) {
-                    // Integer.MAX_VALUE = -(power+1)
-                    return 1.0/(powFast(value,Integer.MAX_VALUE) * value);
-                } else {
-                    return 1.0/powFast(value,-power);
-                }
-            } else {
-                // Here, power is in [0,2].
-                if (power == 2) { // Most common case first.
-                    return value * value;
-                } else if (power == 0) {
-                    return 1.0;
-                } else { // power == 1
-                    return value;
-                }
-            }
-        } else { // power >= 4
-            double oddRemains = 1.0;
-            // If power <= 5, faster to finish outside the loop.
-            while (power > 5) {
-                // Test if power is odd.
-                if ((power & 1) != 0) {
-                    oddRemains *= value;
-                }
-                value *= value;
-                power >>= 1; // power = power / 2
-            }
-            // Here, power is in [3,5].
-            if (power == 3) {
-                return oddRemains * value * value * value;
-            } else { // power in [4,5].
-                double v2 = value * value;
-                if (power == 4) {
-                    return oddRemains * v2 * v2;
-                } else { // power == 5
-                    return oddRemains * v2 * v2 * value;
-                }
-            }
-        }
-    }
-
-    /**
-     * @param value A float value.
-     * @return value*value.
-     */
-    public static float pow2(float value) {
-        return value*value;
-    }
-
-    /**
-     * @param value A double value.
-     * @return value*value.
-     */
-    public static double pow2(double value) {
-        return value*value;
-    }
-
-    /**
-     * @param value A float value.
-     * @return value*value*value.
-     */
-    public static float pow3(float value) {
-        return value*value*value;
-    }
-
-    /**
-     * @param value A double value.
-     * @return value*value*value.
-     */
-    public static double pow3(double value) {
-        return value*value*value;
-    }
-
-    /*
-     * roots
-     */
-
-    /**
-     * @param value A double value.
-     * @return Value square root.
-     */
-    public static double sqrt(double value) {
-        if (USE_JDK_MATH || (!USE_REDEFINED_SQRT)) {
-            return Math.sqrt(value);
-        }
-        // See cbrt for comments, sqrt uses the same ideas.
-
-        if (!(value > 0.0)) { // value <= 0.0, or value is NaN
-            if (ANTI_JIT_OPTIM_CRASH_ON_NAN) {
-                return (value < 0.0) ? Double.NaN : value;
-            } else {
-                return (value == 0.0) ? value : Double.NaN;
-            }
-        } else if (value == Double.POSITIVE_INFINITY) {
-            return Double.POSITIVE_INFINITY;
-        }
-
-        double h;
-        if (value < DOUBLE_MIN_NORMAL) {
-            value *= TWO_POW_52;
-            h = 2*TWO_POW_N26;
-        } else {
-            h = 2.0;
-        }
-
-        int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32);
-        int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT);
-        int xIndex = ((valueBitsHi<<12)>>>(32-SQRT_LO_BITS));
-
-        double result = MyTSqrt.sqrtXSqrtHiTab[valueExponentIndex] * MyTSqrt.sqrtXSqrtLoTab[xIndex];
-        double slope = MyTSqrt.sqrtSlopeHiTab[valueExponentIndex] * MyTSqrt.sqrtSlopeLoTab[xIndex];
-        value *= 0.25;
-
-        result += (value - result * result) * slope;
-        result += (value - result * result) * slope;
-        return h*(result + (value - result * result) * slope);
-    }
-
-    /**
-     * Quick sqrt, with with a max relative error of about 3.41e-2
-     * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse
-     * accuracy outside this range.
-     *
-     * @param value A double value.
-     * @return Value square root.
-     */
-    public static double sqrtQuick(double value) {
-        if (USE_JDK_MATH) {
-            return Math.sqrt(value);
-        }
-        final long bits = Double.doubleToRawLongBits(value);
-        /*
-         * Constant determined empirically, using a random-based metaheuristic.
-         * Should be possible to find a better one.
-         */
-        return Double.longBitsToDouble((bits+4606859074900000000L)>>>1);
-    }
-
-    /**
-     * Quick inverse of square root, with a max relative error of about 3.44e-2
-     * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse accuracy
-     * outside this range.
-     *
-     * This implementation uses zero step of Newton's method.
-     * Here are the max relative errors on [Double.MIN_NORMAL,Double.MAX_VALUE]
-     * depending on number of steps, if you want to copy-paste this code
-     * and use your own number:
-     * n=0: about 3.44e-2
-     * n=1: about 1.75e-3
-     * n=2: about 4.6e-6
-     * n=3: about 3.17e-11
-     * n=4: about 3.92e-16
-     * n=5: about 3.03e-16
-     *
-     * @param value A double value.
-     * @return Inverse of value square root.
-     */
-    public static double invSqrtQuick(double value) {
-        if (USE_JDK_MATH) {
-            return 1/Math.sqrt(value);
-        }
-        /*
-         * http://en.wikipedia.org/wiki/Fast_inverse_square_root
-         */
-        if (false) {
-            // With one Newton step (much slower than
-            // 1/Math.sqrt(double) if not optimized).
-            final double halfInitial = value * 0.5;
-            long bits = Double.doubleToRawLongBits(value);
-            // If n=0, 6910474759270000000L might be better (3.38e-2 max relative error).
-            bits = 0x5FE6EB50C7B537A9L - (bits>>1);
-            value = Double.longBitsToDouble(bits);
-            value = value * (1.5 - halfInitial * value * value); // Newton step, can repeat.
-            return value;
-        } else {
-            return Double.longBitsToDouble(0x5FE6EB50C7B537A9L - (Double.doubleToRawLongBits(value)>>1));
-        }
-    }
-
-    /**
-     * @param value A double value.
-     * @return Value cubic root.
-     */
-    public static double cbrt(double value) {
-        if (USE_JDK_MATH) {
-            return Math.cbrt(value);
-        }
-        double h;
-        if (value < 0.0) {
-            if (value == Double.NEGATIVE_INFINITY) {
-                return Double.NEGATIVE_INFINITY;
-            }
-            value = -value;
-            // Making sure value is normal.
-            if (value < DOUBLE_MIN_NORMAL) {
-                value *= (TWO_POW_52*TWO_POW_26);
-                // h = <result_sign> * <result_multiplicator_to_avoid_overflow> / <cbrt(value_multiplicator_to_avoid_subnormal)>
-                h = -2*TWO_POW_N26;
-            } else {
-                h = -2.0;
-            }
-        } else {
-            if (!(value < Double.POSITIVE_INFINITY)) { // value is +Infinity, or value is NaN
-                return value;
-            }
-            // Making sure value is normal.
-            if (value < DOUBLE_MIN_NORMAL) {
-                if (value == 0.0) {
-                    // cbrt(0.0) = 0.0, cbrt(-0.0) = -0.0
-                    return value;
-                }
-                value *= (TWO_POW_52*TWO_POW_26);
-                h = 2*TWO_POW_N26;
-            } else {
-                h = 2.0;
-            }
-        }
-
-        // Normal value is (2^<value exponent> * <a value in [1,2[>).
-        // First member cubic root is computed, and multiplied with an approximation
-        // of the cubic root of the second member, to end up with a good guess of
-        // the result before using Newton's (or Archimedes's) method.
-        // To compute the cubic root approximation, we use the formula "cbrt(value) = cbrt(x) * cbrt(value/x)",
-        // choosing x as close to value as possible but inferior to it, so that cbrt(value/x) is close to 1
-        // (we could iterate on this method, using value/x as new value for each iteration,
-        // but finishing with Newton's method is faster).
-
-        // Shift and cast into an int, which overall is faster than working with a long.
-        int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32);
-        int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT);
-        // Getting the first CBRT_LO_BITS bits of the mantissa.
-        int xIndex = ((valueBitsHi<<12)>>>(32-CBRT_LO_BITS));
-        double result = MyTCbrt.cbrtXCbrtHiTab[valueExponentIndex] * MyTCbrt.cbrtXCbrtLoTab[xIndex];
-        double slope = MyTCbrt.cbrtSlopeHiTab[valueExponentIndex] * MyTCbrt.cbrtSlopeLoTab[xIndex];
-
-        // Lowering values to avoid overflows when using Newton's method
-        // (we will then just have to return twice the result).
-        // result^3 = value
-        // (result/2)^3 = value/8
-        value *= 0.125;
-        // No need to divide result here, as division is factorized in result computation tables.
-        // result *= 0.5;
-
-        // Newton's method, looking for y = x^(1/p):
-        // y(n) = y(n-1) + (x-y(n-1)^p) * slope(y(n-1))
-        // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^(1/p-1))
-        // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^((1-p)/p))
-        // with x(n-1)=y(n-1)^p, i.e.:
-        // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(y(n-1)^(1-p))
-        //
-        // For p=3:
-        // y(n) = y(n-1) + (x-y(n-1)^3) * (1/(3*y(n-1)^2))
-
-        // To save time, we don't recompute the slope between Newton's method steps,
-        // as initial slope is good enough for a few iterations.
-        //
-        // NB: slope = 1/(3*trueResult*trueResult)
-        //     As we have result = trueResult/2 (to avoid overflows), we have:
-        //     slope = 4/(3*result*result)
-        //           = (4/3)*resultInv*resultInv
-        //     with newResultInv = 1/newResult
-        //                       = 1/(oldResult+resultDelta)
-        //                       = (oldResultInv)*1/(1+resultDelta/oldResult)
-        //                       = (oldResultInv)*1/(1+resultDelta*oldResultInv)
-        //                      ~= (oldResultInv)*(1-resultDelta*oldResultInv)
-        //     ===> Successive slopes could be computed without division, if needed,
-        //          by computing resultInv (instead of slope right away) and retrieving
-        //          slopes from it.
-
-        result += (value - result * result * result) * slope;
-        result += (value - result * result * result) * slope;
-        return h*(result + (value - result * result * result) * slope);
-    }
-
-    /**
-     * @return sqrt(x^2+y^2) without intermediate overflow or underflow.
-     */
-    public static double hypot(double x, double y) {
-        if (USE_JDK_MATH) {
-            return Math.hypot(x,y);
-        }
-        x = Math.abs(x);
-        y = Math.abs(y);
-        // Ensuring x <= y.
-        if (y < x) {
-            double a = x;
-            x = y;
-            y = a;
-        } else if (!(y >= x)) { // Testing if we have some NaN.
-            return hypot_NaN(x, y);
-        }
-
-        if (y-x == y) {
-            // x too small to subtract from y.
-            return y;
-        } else {
-            double factor;
-            if (y > HYPOT_MAX_MAG) {
-                // y is too large: scaling down.
-                x *= (1/HYPOT_FACTOR);
-                y *= (1/HYPOT_FACTOR);
-                factor = HYPOT_FACTOR;
-            } else if (x < (1/HYPOT_MAX_MAG)) {
-                // x is too small: scaling up.
-                x *= HYPOT_FACTOR;
-                y *= HYPOT_FACTOR;
-                factor = (1/HYPOT_FACTOR);
-            } else {
-                factor = 1.0;
-            }
-            return factor * sqrt(x*x+y*y);
-        }
-    }
-
-    /**
-     * @return sqrt(x^2+y^2+z^2) without intermediate overflow or underflow.
-     */
-    public static double hypot(double x, double y, double z) {
-        if (USE_JDK_MATH) {
-            // No simple JDK equivalent.
-        }
-        x = Math.abs(x);
-        y = Math.abs(y);
-        z = Math.abs(z);
-        /*
-         * Considering that z magnitude is the most likely to be the smaller,
-         * hence ensuring z <= y <= x, and not x <= y <= z, for less swaps.
-         */
-        // Ensuring z <= y.
-        if (z > y) {
-            // y < z: swapping y and z
-            double a = z;
-            z = y;
-            y = a;
-        } else if (!(z <= y)) { // Testing if y or z is NaN.
-            return hypot_NaN(x, y, z);
-        }
-        // Ensuring y <= x.
-        if (z > x) {
-            // x < z <= y: moving x
-            double oldZ = z;
-            z = x;
-            double oldY = y;
-            y = oldZ;
-            x = oldY;
-        } else if (y > x) {
-            // z <= x < y: swapping x and y
-            double a = y;
-            y = x;
-            x = a;
-        } else if (x != x) { // Testing if x is NaN.
-            return hypot_NaN(x, y, z);
-        }
-
-        if (x-y == x) {
-            // y, hence z, too small to subtract from x.
-            return x;
-        } else if (y-z == y) {
-            // z too small to subtract from y, hence x.
-            double factor;
-            if (x > HYPOT_MAX_MAG) {
-                // x is too large: scaling down.
-                x *= (1/HYPOT_FACTOR);
-                y *= (1/HYPOT_FACTOR);
-                factor = HYPOT_FACTOR;
-            } else if (y < (1/HYPOT_MAX_MAG)) {
-                // y is too small: scaling up.
-                x *= HYPOT_FACTOR;
-                y *= HYPOT_FACTOR;
-                factor = (1/HYPOT_FACTOR);
-            } else {
-                factor = 1.0;
-            }
-            return factor * sqrt(x*x+y*y);
-        } else {
-            double factor;
-            if (x > HYPOT_MAX_MAG) {
-                // x is too large: scaling down.
-                x *= (1/HYPOT_FACTOR);
-                y *= (1/HYPOT_FACTOR);
-                z *= (1/HYPOT_FACTOR);
-                factor = HYPOT_FACTOR;
-            } else if (z < (1/HYPOT_MAX_MAG)) {
-                // z is too small: scaling up.
-                x *= HYPOT_FACTOR;
-                y *= HYPOT_FACTOR;
-                z *= HYPOT_FACTOR;
-                factor = (1/HYPOT_FACTOR);
-            } else {
-                factor = 1.0;
-            }
-            // Adding smaller magnitudes together first.
-            return factor * sqrt(x*x+(y*y+z*z));
-        }
-    }
-
-    /*
-     * close values
-     */
-
-    /**
-     * @param value A float value.
-     * @return Floor of value.
-     */
-    public static float floor(float value) {
-        final int exponent = getExponent(value);
-        if (exponent < 0) {
-            // abs(value) < 1.
-            if (value < 0.0f) {
-                return -1.0f;
-            } else {
-                // 0.0f, or -0.0f if value is -0.0f
-                return 0.0f * value;
-            }
-        } else if (exponent < 23) {
-            // A bit faster than using casts.
-            final int bits = Float.floatToRawIntBits(value);
-            final int anteCommaBits = bits & (0xFF800000>>exponent);
-            if ((value < 0.0f) && (anteCommaBits != bits)) {
-                return Float.intBitsToFloat(anteCommaBits) - 1.0f;
-            } else {
-                return Float.intBitsToFloat(anteCommaBits);
-            }
-        } else {
-            // +-Infinity, NaN, or a mathematical integer.
-            return value;
-        }
-    }
-
-    /**
-     * @param value A double value.
-     * @return Floor of value.
-     */
-    public static double floor(double value) {
-        if (USE_JDK_MATH) {
-            return Math.floor(value);
-        }
-        if (ANTI_SLOW_CASTS) {
-            double valueAbs = Math.abs(value);
-            if (valueAbs <= (double)Integer.MAX_VALUE) {
-                if (value > 0.0) {
-                    return (double)(int)value;
-                } else if (value < 0.0) {
-                    double anteCommaDigits = (double)(int)value;
-                    if (value != anteCommaDigits) {
-                        return anteCommaDigits - 1.0;
-                    } else {
-                        return anteCommaDigits;
-                    }
-                } else { // value is +-0.0 (not NaN due to test against Integer.MAX_VALUE)
-                    return value;
-                }
-            } else if (valueAbs < TWO_POW_52) {
-                // We split the value in two:
-                // high part, which is a mathematical integer,
-                // and the rest, for which we can get rid of the
-                // post comma digits by casting into an int.
-                double highPart = ((int)(value * TWO_POW_N26)) * TWO_POW_26;
-                if (value > 0.0) {
-                    return highPart + (double)((int)(value - highPart));
-                } else {
-                    double anteCommaDigits = highPart + (double)((int)(value - highPart));
-                    if (value != anteCommaDigits) {
-                        return anteCommaDigits - 1.0;
-                    } else {
-                        return anteCommaDigits;
-                    }
-                }
-            } else { // abs(value) >= 2^52, or value is NaN
-                return value;
-            }
-        } else {
-            final int exponent = getExponent(value);
-            if (exponent < 0) {
-                // abs(value) < 1.
-                if (value < 0.0) {
-                    return -1.0;
-                } else {
-                    // 0.0, or -0.0 if value is -0.0
-                    return 0.0 * value;
-                }
-            } else if (exponent < 52) {
-                // A bit faster than working on bits.
-                final long matIntPart = (long)value;
-                final double matIntToValue = value-(double)matIntPart;
-                if (matIntToValue >= 0.0) {
-                    return (double)matIntPart;
-                } else {
-                    return (double)(matIntPart - 1);
-                }
-            } else {
-                // +-Infinity, NaN, or a mathematical integer.
-                return value;
-            }
-        }
-    }
-
-    /**
-     * @param value A float value.
-     * @return Ceiling of value.
-     */
-    public static float ceil(float value) {
-        return -floor(-value);
-    }
-
-    /**
-     * @param value A double value.
-     * @return Ceiling of value.
-     */
-    public static double ceil(double value) {
-        if (USE_JDK_MATH) {
-            return Math.ceil(value);
-        }
-        return -floor(-value);
-    }
-
-    /**
-     * Might have different semantics than Math.round(float),
-     * see bugs 6430675 and 8010430.
-     *
-     * @param value A double value.
-     * @return Value rounded to nearest int, choosing superior int in case two
-     *         are equally close (i.e. rounding-up).
-     */
-    public static int round(float value) {
-        // Algorithm by Dmitry Nadezhin
-        // (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html).
-        final int bits = Float.floatToRawIntBits(value);
-        final int biasedExp = ((bits>>23)&0xFF);
-        // Shift to get rid of bits past comma except first one: will need to
-        // 1-shift to the right to end up with correct magnitude.
-        final int shift = (23 - 1 + MAX_FLOAT_EXPONENT) - biasedExp;
-        if ((shift & -32) == 0) {
-            // shift in [0,31], so unbiased exp in [-9,22].
-            int extendedMantissa = (0x00800000 | (bits & 0x007FFFFF));
-            if (bits < 0) {
-                extendedMantissa = -extendedMantissa;
-            }
-            // If value is positive and first bit past comma is 0, rounding
-            // to lower integer, else to upper one, which is what "+1" and
-            // then ">>1" do.
-            return ((extendedMantissa >> shift) + 1) >> 1;
-        } else {
-            // +-Infinity, NaN, or a mathematical integer.
-            if (false && ANTI_SLOW_CASTS) { // not worth it
-                if (Math.abs(value) >= -(float)Integer.MIN_VALUE) {
-                    // +-Infinity or a mathematical integer (mostly) out of int range.
-                    return (value < 0.0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
-                }
-                // NaN or a mathematical integer (mostly) in int range.
-            }
-            return (int)value;
-        }
-    }
-
-    /**
-     * Might have different semantics than Math.round(double),
-     * see bugs 6430675 and 8010430.
-     *
-     * @param value A double value.
-     * @return Value rounded to nearest long, choosing superior long in case two
-     *         are equally close (i.e. rounding-up).
-     */
-    public static long round(double value) {
-        final long bits = Double.doubleToRawLongBits(value);
-        final int biasedExp = (((int)(bits>>52))&0x7FF);
-        // Shift to get rid of bits past comma except first one: will need to
-        // 1-shift to the right to end up with correct magnitude.
-        final int shift = (52 - 1 + MAX_DOUBLE_EXPONENT) - biasedExp;
-        if ((shift & -64) == 0) {
-            // shift in [0,63], so unbiased exp in [-12,51].
-            long extendedMantissa = (0x0010000000000000L | (bits & 0x000FFFFFFFFFFFFFL));
-            if (bits < 0) {
-                extendedMantissa = -extendedMantissa;
-            }
-            // If value is positive and first bit past comma is 0, rounding
-            // to lower integer, else to upper one, which is what "+1" and
-            // then ">>1" do.
-            return ((extendedMantissa >> shift) + 1L) >> 1;
-        } else {
-            // +-Infinity, NaN, or a mathematical integer.
-            if (ANTI_SLOW_CASTS) {
-                if (Math.abs(value) >= -(double)Long.MIN_VALUE) {
-                    // +-Infinity or a mathematical integer (mostly) out of long range.
-                    return (value < 0.0) ? Long.MIN_VALUE : Long.MAX_VALUE;
-                }
-                // NaN or a mathematical integer (mostly) in long range.
-            }
-            return (long)value;
-        }
-    }
-
-    /**
-     * @param value A float value.
-     * @return Value rounded to nearest int, choosing even int in case two
-     *         are equally close.
-     */
-    public static int roundEven(float value) {
-        final int sign = signFromBit(value);
-        value = Math.abs(value);
-        if (ANTI_SLOW_CASTS) {
-            if (value < TWO_POW_23_F) {
-                // Getting rid of post-comma bits.
-                value = ((value + TWO_POW_23_F) - TWO_POW_23_F);
-                return sign * (int)value;
-            } else if (value < (float)Integer.MAX_VALUE) { // <= doesn't work because of float precision
-                // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE]
-                return sign * (int)value;
-            }
-        } else {
-            if (value < TWO_POW_23_F) {
-                // Getting rid of post-comma bits.
-                value = ((value + TWO_POW_23_F) - TWO_POW_23_F);
-            }
-        }
-        return (int)(sign * value);
-    }
-
-    /**
-     * @param value A double value.
-     * @return Value rounded to nearest long, choosing even long in case two
-     *         are equally close.
-     */
-    public static long roundEven(double value) {
-        final int sign = (int)signFromBit(value);
-        value = Math.abs(value);
-        if (value < TWO_POW_52) {
-            // Getting rid of post-comma bits.
-            value = ((value + TWO_POW_52) - TWO_POW_52);
-        }
-        if (ANTI_SLOW_CASTS) {
-            if (value <= (double)Integer.MAX_VALUE) {
-                // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE]
-                return sign * (int)value;
-            }
-        }
-        return (long)(sign * value);
-    }
-
-    /**
-     * @param value A float value.
-     * @return The float mathematical integer closest to the specified value,
-     *         choosing even one if two are equally close, or respectively
-     *         NaN, +-Infinity or +-0.0f if the value is any of these.
-     */
-    public static float rint(float value) {
-        final int sign = signFromBit(value);
-        value = Math.abs(value);
-        if (value < TWO_POW_23_F) {
-            // Getting rid of post-comma bits.
-            value = ((TWO_POW_23_F + value ) - TWO_POW_23_F);
-        }
-        // Restoring original sign.
-        return sign * value;
-    }
-
-    /**
-     * @param value A double value.
-     * @return The double mathematical integer closest to the specified value,
-     *         choosing even one if two are equally close, or respectively
-     *         NaN, +-Infinity or +-0.0 if the value is any of these.
-     */
-    public static double rint(double value) {
-        if (USE_JDK_MATH) {
-            return Math.rint(value);
-        }
-        final int sign = (int)signFromBit(value);
-        value = Math.abs(value);
-        if (value < TWO_POW_52) {
-            // Getting rid of post-comma bits.
-            value = ((TWO_POW_52 + value ) - TWO_POW_52);
-        }
-        // Restoring original sign.
-        return sign * value;
-    }
-
-    /*
-     * ranges
-     */
-
-    /**
-     * @param min A float value.
-     * @param max A float value.
-     * @param value A float value.
-     * @return min if value < min, max if value > max, value otherwise.
-     */
-    public static float toRange(float min, float max, float value) {
-        return NumbersUtils.toRange(min, max, value);
-    }
-
-    /**
-     * @param min A double value.
-     * @param max A double value.
-     * @param value A double value.
-     * @return min if value < min, max if value > max, value otherwise.
-     */
-    public static double toRange(double min, double max, double value) {
-        return NumbersUtils.toRange(min, max, value);
-    }
-
-    /*
-     * binary operators (/,%)
-     */
-
-    /**
-     * Returns dividend - divisor * n, where n is the mathematical integer
-     * closest to dividend/divisor.
-     * If dividend/divisor is equally close to surrounding integers,
-     * we choose n to be the integer of smallest magnitude, which makes
-     * this treatment differ from Math.IEEEremainder(double,double),
-     * where n is chosen to be the even integer.
-     * Note that the choice of n is not done considering the double
-     * approximation of dividend/divisor, because it could cause
-     * result to be outside [-|divisor|/2,|divisor|/2] range.
-     * The practical effect is that if multiple results would be possible,
-     * we always choose the result that is the closest to (and has the same
-     * sign as) the dividend.
-     * Ex. :
-     * - for (-3.0,2.0), this method returns -1.0,
-     *   whereas Math.IEEEremainder returns 1.0.
-     * - for (-5.0,2.0), both this method and Math.IEEEremainder return -1.0.
-     *
-     * If the remainder is zero, its sign is the same as the sign of the first argument.
-     * If either argument is NaN, or the first argument is infinite,
-     * or the second argument is positive zero or negative zero,
-     * then the result is NaN.
-     * If the first argument is finite and the second argument is
-     * infinite, then the result is the same as the first argument.
-     *
-     * NB:
-     * - Modulo operator (%) returns a value in ]-|divisor|,|divisor|[,
-     *   which sign is the same as dividend.
-     * - As for modulo operator, the sign of the divisor has no effect on the result.
-     * - On some architecture, % operator has been observed to return NaN
-     *   for some subnormal values of divisor, when dividend exponent is 1023,
-     *   which impacts the correctness of this method.
-     *
-     * @param dividend Dividend.
-     * @param divisor Divisor.
-     * @return Remainder of dividend/divisor, i.e. a value in [-|divisor|/2,|divisor|/2].
-     */
-    public static double remainder(double dividend, double divisor) {
-        if (Double.isInfinite(divisor)) {
-            if (Double.isInfinite(dividend)) {
-                return Double.NaN;
-            } else {
-                return dividend;
-            }
-        }
-        double value = dividend % divisor;
-        if (Math.abs(value+value) > Math.abs(divisor)) {
-            return value + ((value > 0.0) ? -Math.abs(divisor) : Math.abs(divisor));
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return The same angle, in radians, but in [-PI,PI].
-     */
-    public static double normalizeMinusPiPi(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= -Math.PI) && (angle <= Math.PI)) {
-            return angle;
-        }
-        return remainderTwoPi(angle);
-    }
-
-    /**
-     * Not accurate for large values.
-     *
-     * @param angle Angle in radians.
-     * @return The same angle, in radians, but in [-PI,PI].
-     */
-    public static double normalizeMinusPiPiFast(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= -Math.PI) && (angle <= Math.PI)) {
-            return angle;
-        }
-        return remainderTwoPiFast(angle);
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return The same angle, in radians, but in [0,2*PI].
-     */
-    public static double normalizeZeroTwoPi(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= 0.0) && (angle <= 2*Math.PI)) {
-            return angle;
-        }
-        angle = remainderTwoPi(angle);
-        if (angle < 0.0) {
-            // LO then HI is theoretically better (when starting near 0).
-            return (angle + TWOPI_LO) + TWOPI_HI;
-        } else {
-            return angle;
-        }
-    }
-
-    /**
-     * Not accurate for large values.
-     *
-     * @param angle Angle in radians.
-     * @return The same angle, in radians, but in [0,2*PI].
-     */
-    public static double normalizeZeroTwoPiFast(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= 0.0) && (angle <= 2*Math.PI)) {
-            return angle;
-        }
-        angle = remainderTwoPiFast(angle);
-        if (angle < 0.0) {
-            // LO then HI is theoretically better (when starting near 0).
-            return (angle + TWOPI_LO) + TWOPI_HI;
-        } else {
-            return angle;
-        }
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return Angle value modulo PI, in radians, in [-PI/2,PI/2].
-     */
-    public static double normalizeMinusHalfPiHalfPi(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) {
-            return angle;
-        }
-        return remainderPi(angle);
-    }
-
-    /**
-     * Not accurate for large values.
-     *
-     * @param angle Angle in radians.
-     * @return Angle value modulo PI, in radians, in [-PI/2,PI/2].
-     */
-    public static double normalizeMinusHalfPiHalfPiFast(double angle) {
-        // Not modifying values in output range.
-        if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) {
-            return angle;
-        }
-        return remainderPiFast(angle);
-    }
-
-    /*
-     * floating points utils
-     */
-
-    /**
-     * @param value A float value.
-     * @return true if the specified value is NaN or +-Infinity, false otherwise.
-     */
-    public static boolean isNaNOrInfinite(float value) {
-        return NumbersUtils.isNaNOrInfinite(value);
-    }
-
-    /**
-     * @param value A double value.
-     * @return true if the specified value is NaN or +-Infinity, false otherwise.
-     */
-    public static boolean isNaNOrInfinite(double value) {
-        return NumbersUtils.isNaNOrInfinite(value);
-    }
-
-    /**
-     * @param value A float value.
-     * @return Value unbiased exponent.
-     */
-    public static int getExponent(float value) {
-        return ((Float.floatToRawIntBits(value)>>23)&0xFF)-MAX_FLOAT_EXPONENT;
-    }
-
-    /**
-     * @param value A double value.
-     * @return Value unbiased exponent.
-     */
-    public static int getExponent(double value) {
-        return (((int)(Double.doubleToRawLongBits(value)>>52))&0x7FF)-MAX_DOUBLE_EXPONENT;
-    }
-
-    /**
-     * @param value A float value.
-     * @return -1.0f if the specified value is < 0, 1.0f if it is > 0,
-     *         and the value itself if it is NaN or +-0.0f.
-     */
-    public static float signum(float value) {
-        if (USE_JDK_MATH) {
-            return Math.signum(value);
-        }
-        if ((value == 0.0f) || (value != value)) {
-            return value;
-        }
-        return (float)signFromBit(value);
-    }
-
-    /**
-     * @param value A double value.
-     * @return -1.0 if the specified value is < 0, 1.0 if it is > 0,
-     *         and the value itself if it is NaN or +-0.0.
-     */
-    public static double signum(double value) {
-        if (USE_JDK_MATH) {
-            return Math.signum(value);
-        }
-        if ((value == 0.0) || (value != value)) {
-            return value;
-        }
-        if (ANTI_SLOW_CASTS) {
-            return (double)(int)signFromBit(value);
-        } else {
-            return (double)signFromBit(value);
-        }
-    }
-
-    /**
-     * @param value A float value.
-     * @return -1 if sign bit is 1, 1 if sign bit is 0.
-     */
-    public static int signFromBit(float value) {
-        return ((Float.floatToRawIntBits(value)>>30)|1);
-    }
-
-    /**
-     * @param value A double value.
-     * @return -1 if sign bit is 1, 1 if sign bit is 0.
-     */
-    public static long signFromBit(double value) {
-        // Returning a long, to avoid useless cast into int.
-        return ((Double.doubleToRawLongBits(value)>>62)|1);
-    }
-
-    /**
-     * A sign of NaN can be interpreted as positive or negative.
-     *
-     * @param magnitude A float value.
-     * @param sign A float value.
-     * @return A value with the magnitude of the first argument, and the sign
-     *         of the second argument.
-     */
-    public static float copySign(float magnitude, float sign) {
-        return Float.intBitsToFloat(
-                (Float.floatToRawIntBits(sign) & Integer.MIN_VALUE)
-                | (Float.floatToRawIntBits(magnitude) & Integer.MAX_VALUE));
-    }
-
-    /**
-     * A sign of NaN can be interpreted as positive or negative.
-     *
-     * @param magnitude A double value.
-     * @param sign A double value.
-     * @return A value with the magnitude of the first argument, and the sign
-     *         of the second argument.
-     */
-    public static double copySign(double magnitude, double sign) {
-        return Double.longBitsToDouble(
-                (Double.doubleToRawLongBits(sign) & Long.MIN_VALUE)
-                | (Double.doubleToRawLongBits(magnitude) & Long.MAX_VALUE));
-    }
-
-    /**
-     * The ULP (Unit in the Last Place) is the distance to the next value larger
-     * in magnitude.
-     *
-     * @param value A float value.
-     * @return The size of an ulp of the specified value, or Float.MIN_VALUE
-     *         if it is +-0.0f, or +Infinity if it is +-Infinity, or NaN
-     *         if it is NaN.
-     */
-    public static float ulp(float value) {
-        if (USE_JDK_MATH) {
-            return Math.ulp(value);
-        }
-        /*
-         * Look-up table not really worth it in micro-benchmark,
-         * so should be worse with cache-misses.
-         */
-        final int exponent = getExponent(value);
-        if (exponent >= (MIN_FLOAT_NORMAL_EXPONENT+23)) {
-            if (exponent == MAX_FLOAT_EXPONENT+1) {
-                // NaN or +-Infinity
-                return Math.abs(value);
-            }
-            // normal: returning 2^(exponent-23)
-            return Float.intBitsToFloat((exponent+(MAX_FLOAT_EXPONENT-23))<<23);
-        } else {
-            if (exponent == MIN_FLOAT_NORMAL_EXPONENT-1) {
-                // +-0.0f or subnormal
-                return Float.MIN_VALUE;
-            }
-            // subnormal result
-            return Float.intBitsToFloat(1<<(exponent-MIN_FLOAT_NORMAL_EXPONENT));
-        }
-    }
-
-    /**
-     * The ULP (Unit in the Last Place) is the distance to the next value larger
-     * in magnitude.
-     *
-     * @param value A double value.
-     * @return The size of an ulp of the specified value, or Double.MIN_VALUE
-     *         if it is +-0.0, or +Infinity if it is +-Infinity, or NaN
-     *         if it is NaN.
-     */
-    public static double ulp(double value) {
-        if (USE_JDK_MATH) {
-            return Math.ulp(value);
-        }
-        /*
-         * Look-up table not really worth it in micro-benchmark,
-         * so should be worse with cache-misses.
-         */
-        final int exponent = getExponent(value);
-        if (exponent >= (MIN_DOUBLE_NORMAL_EXPONENT+52)) {
-            if (exponent == MAX_DOUBLE_EXPONENT+1) {
-                // NaN or +-Infinity
-                return Math.abs(value);
-            }
-            // normal: returning 2^(exponent-52)
-            return Double.longBitsToDouble((exponent+(MAX_DOUBLE_EXPONENT-52L))<<52);
-        } else {
-            if (exponent == MIN_DOUBLE_NORMAL_EXPONENT-1) {
-                // +-0.0f or subnormal
-                return Double.MIN_VALUE;
-            }
-            // subnormal result
-            return Double.longBitsToDouble(1L<<(exponent-MIN_DOUBLE_NORMAL_EXPONENT));
-        }
-    }
-
-    /**
-     * If both arguments are +-0.0(f), (float)direction is returned.
-     *
-     * If both arguments are +Infinity or -Infinity,
-     * respectively +Infinity or -Infinity is returned.
-     *
-     * @param start A float value.
-     * @param direction A double value.
-     * @return The float adjacent to start towards direction, considering that
-     *         +(-)Float.MIN_VALUE is adjacent to +(-)0.0f, and that
-     *         +(-)Float.MAX_VALUE is adjacent to +(-)Infinity,
-     *         or NaN if any argument is NaN.
-     */
-    public static float nextAfter(float start, double direction) {
-        if (direction < start) {
-            // Going towards -Infinity.
-            if (start == 0.0f) {
-                // +-0.0f
-                return -Float.MIN_VALUE;
-            }
-            final int bits = Float.floatToRawIntBits(start);
-            return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1));
-        } else if (direction > start) {
-            // Going towards +Infinity.
-            // +0.0f to get rid of eventual -0.0f
-            final int bits = Float.floatToRawIntBits(start + 0.0f);
-            return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1));
-        } else if (start == direction) {
-            return (float)direction;
-        } else {
-            // Returning a NaN derived from the input NaN(s).
-            return start + (float)direction;
-        }
-    }
-
-    /**
-     * If both arguments are +-0.0, direction is returned.
-     *
-     * If both arguments are +Infinity or -Infinity,
-     * respectively +Infinity or -Infinity is returned.
-     *
-     * @param start A double value.
-     * @param direction A double value.
-     * @return The double adjacent to start towards direction, considering that
-     *         +(-)Double.MIN_VALUE is adjacent to +(-)0.0, and that
-     *         +(-)Double.MAX_VALUE is adjacent to +(-)Infinity,
-     *         or NaN if any argument is NaN.
-     */
-    public static double nextAfter(double start, double direction) {
-        if (direction < start) {
-            // Going towards -Infinity.
-            if (start == 0.0) {
-                // +-0.0
-                return -Double.MIN_VALUE;
-            }
-            final long bits = Double.doubleToRawLongBits(start);
-            return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1));
-        } else if (direction > start) {
-            // Going towards +Infinity.
-            // +0.0 to get rid of eventual -0.0
-            final long bits = Double.doubleToRawLongBits(start + 0.0f);
-            return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1));
-        } else if (start == direction) {
-            return direction;
-        } else {
-            // Returning a NaN derived from the input NaN(s).
-            return start + direction;
-        }
-    }
-
-    /**
-     * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY).
-     */
-    public static float nextDown(float start) {
-        if (start > Float.NEGATIVE_INFINITY) {
-            if (start == 0.0f) {
-                // +-0.0f
-                return -Float.MIN_VALUE;
-            }
-            final int bits = Float.floatToRawIntBits(start);
-            return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1));
-        } else if (start == Float.NEGATIVE_INFINITY) {
-            return Float.NEGATIVE_INFINITY;
-        } else {
-            // NaN
-            return start;
-        }
-    }
-
-    /**
-     * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY).
-     */
-    public static double nextDown(double start) {
-        if (start > Double.NEGATIVE_INFINITY) {
-            if (start == 0.0) {
-                // +-0.0
-                return -Double.MIN_VALUE;
-            }
-            final long bits = Double.doubleToRawLongBits(start);
-            return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1));
-        } else if (start == Double.NEGATIVE_INFINITY) {
-            return Double.NEGATIVE_INFINITY;
-        } else {
-            // NaN
-            return start;
-        }
-    }
-
-    /**
-     * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY).
-     */
-    public static float nextUp(float start) {
-        if (start < Float.POSITIVE_INFINITY) {
-            // +0.0f to get rid of eventual -0.0f
-            final int bits = Float.floatToRawIntBits(start + 0.0f);
-            return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1));
-        } else if (start == Float.POSITIVE_INFINITY) {
-            return Float.POSITIVE_INFINITY;
-        } else {
-            // NaN
-            return start;
-        }
-    }
-
-    /**
-     * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY).
-     */
-    public static double nextUp(double start) {
-        if (start < Double.POSITIVE_INFINITY) {
-            // +0.0 to get rid of eventual -0.0
-            final long bits = Double.doubleToRawLongBits(start + 0.0);
-            return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1));
-        } else if (start == Double.POSITIVE_INFINITY) {
-            return Double.POSITIVE_INFINITY;
-        } else {
-            // NaN
-            return start;
-        }
-    }
-
-    /**
-     * Precision may be lost if the result is subnormal.
-     *
-     * @param value A float value.
-     * @param scaleFactor An int value.
-     * @return value * 2^scaleFactor, or a value equivalent to the specified
-     *         one if it is NaN, +-Infinity or +-0.0f.
-     */
-    public static float scalb(float value, int scaleFactor) {
-        // Large enough to imply overflow or underflow for
-        // a finite non-zero value.
-        final int MAX_SCALE = 2*MAX_FLOAT_EXPONENT+23+1;
-
-        // Making sure scaling factor is in a reasonable range.
-        scaleFactor = Math.max(Math.min(scaleFactor, MAX_SCALE), -MAX_SCALE);
-
-        return (float)(((double)value) * twoPowNormal(scaleFactor));
-    }
-
-    /**
-     * Precision may be lost if the result is subnormal.
-     *
-     * @param value A double value.
-     * @param scaleFactor An int value.
-     * @return value * 2^scaleFactor, or a value equivalent to the specified
-     *         one if it is NaN, +-Infinity or +-0.0.
-     */
-    public static double scalb(double value, int scaleFactor) {
-        if ((scaleFactor > -MAX_DOUBLE_EXPONENT) && (scaleFactor <= MAX_DOUBLE_EXPONENT)) {
-            // Quick case (as done in apache FastMath).
-            return value * twoPowNormal(scaleFactor);
-        }
-
-        // Large enough to imply overflow or underflow for
-        // a finite non-zero value.
-        final int MAX_SCALE = 2*MAX_DOUBLE_EXPONENT+52+1;
-
-        // Making sure scaling factor is in a reasonable range.
-        final int exponentAdjust;
-        final int scaleIncrement;
-        final double exponentDelta;
-        if (scaleFactor < 0) {
-            scaleFactor = Math.max(scaleFactor, -MAX_SCALE);
-            scaleIncrement = -512;
-            exponentDelta = TWO_POW_N512;
-        } else {
-            scaleFactor = Math.min(scaleFactor, MAX_SCALE);
-            scaleIncrement = 512;
-            exponentDelta = TWO_POW_512;
-        }
-
-        // Calculating (scaleFactor % +-512), 512 = 2^9, using
-        // technique from "Hacker's Delight" section 10-2.
-        final int t = ((scaleFactor >> (9-1)) >>> (32-9));
-        exponentAdjust = ((scaleFactor + t) & (512-1)) - t;
-
-        value *= twoPowNormal(exponentAdjust);
-        scaleFactor -= exponentAdjust;
-
-        while (scaleFactor != 0) {
-            value *= exponentDelta;
-            scaleFactor -= scaleIncrement;
-        }
-
-        return value;
-    }
-
-    /*
-     * Non-redefined Math public values and treatments.
-     */
-
-    public static float abs(float a) {
-        return Math.abs(a);
-    }
-
-    public static double abs(double a) {
-        return Math.abs(a);
-    }
-
-    public static float min(float a, float b) {
-        return Math.min(a,b);
-    }
-
-    public static double min(double a, double b) {
-        return Math.min(a,b);
-    }
-
-    public static float max(float a, float b) {
-        return Math.max(a,b);
-    }
-
-    public static double max(double a, double b) {
-        return Math.max(a,b);
-    }
-
-    public static double IEEEremainder(double f1, double f2) {
-        return Math.IEEEremainder(f1,f2);
-    }
-
-    public static double random() {
-        return Math.random();
-    }
-
-    //--------------------------------------------------------------------------
-    //  PRIVATE METHODS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Non-instantiable.
-     */
-    private FastMath() {
-    }
-
-    /*
-     * Remainders (accurate).
-     */
-
-    /**
-     * @param angle Angle in radians.
-     * @return Remainder of (angle % (2*PI)), in [-PI,PI].
-     */
-    private static double remainderTwoPi(double angle) {
-        if (USE_JDK_MATH) {
-            return jdkRemainderTwoPi(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle <= (4*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) {
-            double fn = (double)(int)(angle*TWOPI_INV+0.5);
-            angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO;
-            // Ensuring range.
-            // HI/LO can help a bit, even though we are always far from 0.
-            if (angle < -Math.PI) {
-                angle = (angle + TWOPI_HI) + TWOPI_LO;
-            } else if (angle > Math.PI) {
-                angle = (angle - TWOPI_HI) - TWOPI_LO;
-            }
-            return negateResult ? -angle : angle;
-        } else if (angle < Double.POSITIVE_INFINITY) {
-            angle = heavyRemainderTwoPi(angle);
-            return negateResult ? -angle : angle;
-        } else { // angle is +Infinity or NaN
-            return Double.NaN;
-        }
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return Remainder of (angle % PI), in [-PI/2,PI/2].
-     */
-    private static double remainderPi(double angle) {
-        if (USE_JDK_MATH) {
-            return jdkRemainderPi(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle <= (2*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) {
-            double fn = (double)(int)(angle*PI_INV+0.5);
-            angle = (angle - fn*PI_HI) - fn*PI_LO;
-            // Ensuring range.
-            // HI/LO can help a bit, even though we are always far from 0.
-            if (angle < -Math.PI/2) {
-                angle = (angle + PI_HI) + PI_LO;
-            } else if (angle > Math.PI/2) {
-                angle = (angle - PI_HI) - PI_LO;
-            }
-            return negateResult ? -angle : angle;
-        } else if (angle < Double.POSITIVE_INFINITY) {
-            angle = heavyRemainderPi(angle);
-            return negateResult ? -angle : angle;
-        } else { // angle is +Infinity or NaN
-            return Double.NaN;
-        }
-    }
-
-    /**
-     * @param angle Angle in radians.
-     * @return Bits of double corresponding to remainder of (angle % (PI/2)),
-     *         in [-PI/4,PI/4], with quadrant encoded in exponent bits.
-     */
-    private static long remainderPiO2(double angle) {
-        if (USE_JDK_MATH) {
-            return jdkRemainderPiO2(angle, false);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        if (angle <= NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2) {
-            int n = (int)(angle*PIO2_INV+0.5);
-            double fn = (double)n;
-            angle = (angle - fn*PIO2_HI) - fn*PIO2_LO;
-            // Ensuring range.
-            // HI/LO can help a bit, even though we are always far from 0.
-            if (angle < -Math.PI/4) {
-                angle = (angle + PIO2_HI) + PIO2_LO;
-                n--;
-            } else if (angle > Math.PI/4) {
-                angle = (angle - PIO2_HI) - PIO2_LO;
-                n++;
-            }
-            if (negateResult) {
-                angle = -angle;
-            }
-            return encodeRemainderAndQuadrant(angle, n&3);
-        } else if (angle < Double.POSITIVE_INFINITY) {
-            return heavyRemainderPiO2(angle, negateResult);
-        } else { // angle is +Infinity or NaN
-            return encodeRemainderAndQuadrant(Double.NaN, 0);
-        }
-    }
-
-    /*
-     * Remainders (fast).
-     */
-
-    /**
-     * Not accurate for large values.
-     *
-     * @param angle Angle in radians.
-     * @return Remainder of (angle % (2*PI)), in [-PI,PI].
-     */
-    private static double remainderTwoPiFast(double angle) {
-        if (USE_JDK_MATH) {
-            return jdkRemainderTwoPi(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        // - We don't bother with values higher than (2*PI*(2^52)),
-        //   since they are spaced by 2*PI or more from each other.
-        // - For large values, we don't use % because it might be very slow,
-        //   and we split computation in two, because cast from double to int
-        //   with large numbers might be very slow also.
-        if (angle <= TWO_POW_26*(2*Math.PI)) {
-            // ok
-        } else if (angle <= TWO_POW_52*(2*Math.PI)) {
-            // Computing remainder of angle modulo TWO_POW_26*(2*PI).
-            double fn = (double)(int)(angle*(TWOPI_INV/TWO_POW_26)+0.5);
-            angle = (angle - fn*(TWOPI_HI*TWO_POW_26)) - fn*(TWOPI_LO*TWO_POW_26);
-            // Here, angle is in [-TWO_POW_26*PI,TWO_POW_26*PI], or so.
-            if (angle < 0.0) {
-                angle = -angle;
-                negateResult = !negateResult;
-            }
-        } else if (angle < Double.POSITIVE_INFINITY) {
-            return 0.0;
-        } else { // angle is +Infinity or NaN
-            return Double.NaN;
-        }
-
-        // Computing remainder of angle modulo 2*PI.
-        double fn = (double)(int)(angle*TWOPI_INV+0.5);
-        angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO;
-
-        // Ensuring range.
-        // HI/LO can help a bit, even though we are always far from 0.
-        if (angle < -Math.PI) {
-            angle = (angle + TWOPI_HI) + TWOPI_LO;
-        } else if (angle > Math.PI) {
-            angle = (angle - TWOPI_HI) - TWOPI_LO;
-        }
-        return negateResult ? -angle : angle;
-    }
-
-    /**
-     * Not accurate for large values.
-     *
-     * @param angle Angle in radians.
-     * @return Remainder of (angle % PI), in [-PI/2,PI/2].
-     */
-    private static double remainderPiFast(double angle) {
-        if (USE_JDK_MATH) {
-            return jdkRemainderPi(angle);
-        }
-        boolean negateResult = false;
-        if (angle < 0.0) {
-            angle = -angle;
-            negateResult = true;
-        }
-        // - We don't bother with values higher than (PI*(2^52)),
-        //   since they are spaced by PI or more from each other.
-        // - For large values, we don't use % because it might be very slow,
-        //   and we split computation in two, because cast from double to int
-        //   with large numbers might be very slow also.
-        if (angle <= TWO_POW_26*Math.PI) {
-            // ok
-        } else if (angle <= TWO_POW_52*Math.PI) {
-            // Computing remainder of angle modulo TWO_POW_26*PI.
-            double fn = (double)(int)(angle*(PI_INV/TWO_POW_26)+0.5);
-            angle = (angle - fn*(PI_HI*TWO_POW_26)) - fn*(PI_LO*TWO_POW_26);
-            // Here, angle is in [-TWO_POW_26*PI/2,TWO_POW_26*PI/2], or so.
-            if (angle < 0.0) {
-                angle = -angle;
-                negateResult = !negateResult;
-            }
-        } else if (angle < Double.POSITIVE_INFINITY) {
-            return 0.0;
-        } else { // angle is +Infinity or NaN
-            return Double.NaN;
-        }
-
-        // Computing remainder of angle modulo PI.
-        double fn = (double)(int)(angle*PI_INV+0.5);
-        angle = (angle - fn*PI_HI) - fn*PI_LO;
-
-        // Ensuring range.
-        // HI/LO can help a bit, even though we are always far from 0.
-        if (angle < -Math.PI/2) {
-            angle = (angle + PI_HI) + PI_LO;
-        } else if (angle > Math.PI/2) {
-            angle = (angle - PI_HI) - PI_LO;
-        }
-        return negateResult ? -angle : angle;
-    }
-}
diff --git a/src/main/java/net/jafama/IntWrapper.java b/src/main/java/net/jafama/IntWrapper.java
deleted file mode 100644
index a8b616a..0000000
--- a/src/main/java/net/jafama/IntWrapper.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2012 Jeff Hain
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.jafama;
-
-public class IntWrapper {
-    public int value;
-    @Override
-    public String toString() {
-        return Integer.toString(this.value);
-    }
-}
diff --git a/src/main/java/net/jafama/LICENSE-2.0.txt b/src/main/java/net/jafama/LICENSE-2.0.txt
deleted file mode 100644
index d645695..0000000
--- a/src/main/java/net/jafama/LICENSE-2.0.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/src/main/java/net/jafama/NumbersUtils.java b/src/main/java/net/jafama/NumbersUtils.java
deleted file mode 100644
index 269d80a..0000000
--- a/src/main/java/net/jafama/NumbersUtils.java
+++ /dev/null
@@ -1,2660 +0,0 @@
-/*
- * Copyright 2012-2015 Jeff Hain
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.jafama;
-
-/**
- * Class containing various basic utility methods to deal with numbers.
- * This class is meant to be light (no big look-up tables or such).
- *
- * Check methods return boolean if success,
- * for it allows to use them in assertions.
- *
- * toString methods use capital letters, unlike JDK's toStrings, for it is more
- * readable (especially, "l" and "1" can easily be confused with one another).
- *
- * Some methods have an int version additionally to the long version,
- * even though long version could be used instead, for performance reasons,
- * either for the methods themselves (if they do computations with ints
- * instead of longs), or to be used in an int use case (like methods
- * checking whether or not a signed int can fit in such number of bits).
- */
-public final class NumbersUtils {
-
-    //--------------------------------------------------------------------------
-    // MEMBERS
-    //--------------------------------------------------------------------------
-
-    /**
-     * Double.MIN_NORMAL since Java 6.
-     */
-    public static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308
-
-    /**
-     * Float.MIN_NORMAL since Java 6.
-     */
-    public static final float FLOAT_MIN_NORMAL = Float.intBitsToFloat(0x00800000); // 1.17549435E-38f
-
-    private static final int MIN_DOUBLE_EXPONENT = -1074;
-    private static final int MAX_DOUBLE_EXPONENT = 1023;
-
-    /**
-     * All possible upper case chars for representing a number as a String.
-     */
-    private final static char[] CHAR_BY_DIGIT;
-    static {
-        final char minDecimal = '0';
-        final char maxDecimal = '9';
-        final int n1 = maxDecimal - minDecimal + 1;
-        final char minLetter = 'A';
-        final char maxLetter = 'Z';
-        final int n2 = maxLetter - minLetter + 1;
-        CHAR_BY_DIGIT = new char[n1+n2];
-        int i=0;
-        for (char c=minDecimal;c<=maxDecimal;c++) {
-            CHAR_BY_DIGIT[i++] = c;
-        }
-        for (char c=minLetter;c<=maxLetter;c++) {
-            CHAR_BY_DIGIT[i++] = c;
-        }
-    }
-
-    /**
-     * For power-of-two radixes only.
-     */
-    private static final int[] DIV_SHIFT_BY_RADIX;
-    static {
-        DIV_SHIFT_BY_RADIX = new int[32+1];
-        int shift=1;
-        for (int radix=2;radix<=32;radix*=2) {
-            DIV_SHIFT_BY_RADIX[radix] = shift++;
-        }
-    }
-
-    private final static int[] MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1];
-    private final static int[] MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1];
-    static {
-        for (int radix=Character.MIN_RADIX;radix<=Character.MAX_RADIX;radix++) {
-            /*
-             * Brutal but works.
-             * -1 for the sign.
-             */
-            MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix] = Integer.toString(Integer.MIN_VALUE, radix).length()-1;
-            MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix] = Long.toString(Long.MIN_VALUE, radix).length()-1;
-        }
-    }
-
-    static final double NO_CSN_MIN_BOUND_INCL = 1e-3;
-    static final double NO_CSN_MAX_BOUND_EXCL = 1e7;
-
-    private static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2
-    private static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI
-    private static final double PI_HI = 2*PIO2_HI;
-    private static final double PI_LO = 2*PIO2_LO;
-    private static final double TWOPI_HI = 4*PIO2_HI;
-    private static final double TWOPI_LO = 4*PIO2_LO;
-
-    //--------------------------------------------------------------------------
-    // PUBLIC METHODS
-    //--------------------------------------------------------------------------
-
-    /**
-     * @return True if the specified values are equal or both NaN, false otherwise.
-     */
-    public static boolean equal(float a, float b) {
-        // Only does one test if a == b.
-        return (a == b) ? true : ((a != a) && (b != b));
-    }
-
-    /**
-     * @return True if the specified values are equal or both NaN, false otherwise.
-     */
-    public static boolean equal(double a, double b) {
-        // Only does one test if a == b.
-        return (a == b) ? true : ((a != a) && (b != b));
-    }
-
-    /**
-     * @return True if the specified value is a mathematical integer,
-     *         false otherwise (which includes NaN and +-Infinity).
-     */
-    public static boolean isMathematicalInteger(float value) {
-        // Doing magnitude test first, for cast
-        // might be very slow for huge values.
-        // It also helps be faster for huge values,
-        // for which the test with cast always fail.
-        value = Math.abs(value);
-        return ((value >= (float)(1<<23)
-                && (value != Float.POSITIVE_INFINITY)))
-                || (value == (float)(int)value);
-    }
-
-    /**
-     * @return True if the specified value is a mathematical integer,
-     *         false otherwise (which includes NaN and +-Infinity).
-     */
-    public static boolean isMathematicalInteger(double value) {
-        // Doing magnitude test first, for cast
-        // might be very slow for huge values.
-        // It also helps be faster for huge values,
-        // for which the test with cast always fail.
-        value = Math.abs(value);
-        return ((value >= (double)(1L<<52))
-                && (value != Double.POSITIVE_INFINITY))
-                || (value == (double)(long)value);
-    }
-
-    /**
-     * @param value A float value.
-     * @return True if the specified value is equidistant from two adjacent
-     *         mathematical integers, false otherwise (which includes NaN
-     *         and +-Infinity).
-     */
-    public static boolean isEquidistant(float value) {
-        if (false) {
-            // Also works, but slower.
-            final int bits = Float.floatToRawIntBits(value);
-            final int exponent = ((bits>>23)&0xFF)-127;
-            final int nbrOfPostCommaBits = 23 - exponent;
-            if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 25)) {
-                // No mantissa bit after comma, or all mantissa bits
-                // (including implicit 1) are at least one bit away from it.
-                //System.out.println("can't be");
-                return false;
-            }
-            final int mantissa = 0x00800000|(bits&0x007FFFFF);
-            final int postCommaMask = ~((-1)<<nbrOfPostCommaBits);
-            // True if in post-comma bits the only 1-bit is the one for 0.5.
-            return ((mantissa & postCommaMask) == (1<<(nbrOfPostCommaBits-1)));
-        }
-        final float valueAbs = Math.abs(value);
-        if (!(valueAbs < (float)(1<<23))) {
-            // NaN or too large to have a chance
-            return false;
-        }
-        final float twice = valueAbs+valueAbs;
-        // Test on twice first, for it's the most likely to fail.
-        return (twice == (float)(int)twice)
-                && (value != (float)(int)value);
-    }
-
-    /**
-     * @param value A double value.
-     * @return True if the specified value is equidistant from two adjacent
-     *         mathematical integers, false otherwise (which includes NaN
-     *         and +-Infinity).
-     */
-    public static boolean isEquidistant(double value) {
-        if (false) {
-            // Also works, but slower.
-            final long bits = Double.doubleToRawLongBits(value);
-            final int exponent = (((int)(bits>>52))&0x7FF)-1023;
-            final int nbrOfPostCommaBits = 52 - exponent;
-            if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 54)) {
-                // No mantissa bit after comma, or all mantissa bits
-                // (including implicit 1) are at least one bit away from it.
-                return false;
-            }
-            final long mantissa = 0x0010000000000000L|(bits&0x000FFFFFFFFFFFFFL);
-            final long postCommaMask = ~((-1L)<<nbrOfPostCommaBits);
-            // True if in post-comma bits the only 1-bit is the one for 0.5.
-            return ((mantissa & postCommaMask) == (1L<<(nbrOfPostCommaBits-1)));
-        }
-        final double valueAbs = Math.abs(value);
-        if (!(valueAbs < (double)(1L<<52))) {
-            return false;
-        }
-        final double twice = valueAbs+valueAbs;
-        // Test on twice first, for it's the most likely to fail.
-        return (twice == (double)(long)twice)
-                && (value != (double)(long)value);
-    }
-
-    /**
-     * @param value A float value.
-     * @return True if the specified value is NaN or +-Infinity, false otherwise.
-     */
-    public static boolean isNaNOrInfinite(float a) {
-        // a-a is not equal to 0.0f (and is NaN) <-> a is NaN or +-Infinity
-        return !(a-a == 0.0f);
-    }
-
-    /**
-     * @param value A double value.
-     * @return True if the specified value is NaN or +-Infinity, false otherwise.
-     */
-    public static boolean isNaNOrInfinite(double a) {
-        // a-a is not equal to 0.0 (and is NaN) <-> a is NaN or +-Infinity
-        return !(a-a == 0.0);
-    }
-
-    /**
-     * @param value A float value.
-     * @return -1 if sign bit is 1, 1 if sign bit is 0.
-     */
-    public static int signFromBit(float value) {
-        return ((Float.floatToRawIntBits(value)>>30)|1);
-    }
-
-    /**
-     * @param value A double value.
-     * @return -1 if sign bit is 1, 1 if sign bit is 0.
-     */
-    public static long signFromBit(double value) {
-        // Returning a long, to avoid useless cast into int.
-        return ((Double.doubleToRawLongBits(value)>>62)|1);
-    }
-
-    /*
-     * min/max ranges
-     */
-
-    /**
-     * @return True if the specified value is in the specified range (inclusive), false otherwise.
-     */
-    public static boolean isInRange(int min, int max, int a) {
-        return (min <= a) && (a <= max);
-    }
-
-    /**
-     * @return True if the specified value is in the specified range (inclusive), false otherwise.
-     */
-    public static boolean isInRange(long min, long max, long a) {
-        return (min <= a) && (a <= max);
-    }
-
-    /**
-     * Returns false if any value is NaN.
-     *
-     * @return True if the specified value is in the specified range (inclusive), false otherwise.
-     */
-    public static boolean isInRange(float min, float max, float a) {
-        return (min <= a) && (a <= max);
-    }
-
-    /**
-     * Returns false if any value is NaN.
-     *
-     * @return True if the specified value is in the specified range (inclusive), false otherwise.
-     */
-    public static boolean isInRange(double min, double max, double a) {
-        return (min <= a) && (a <= max);
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive).
-     */
-    public static boolean checkIsInRange(int min, int max, int a) {
-        if (!isInRange(min, max, a)) {
-            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive).
-     */
-    public static boolean checkIsInRange(long min, long max, long a) {
-        if (!isInRange(min, max, a)) {
-            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive)
-     *         or any parameter is NaN.
-     */
-    public static boolean checkIsInRange(float min, float max, float a) {
-        if (!isInRange(min, max, a)) {
-            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive)
-     *         or any parameter is NaN.
-     */
-    public static boolean checkIsInRange(double min, double max, double a) {
-        if (!isInRange(min, max, a)) {
-            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
-        }
-        return true;
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @param min A value.
-     * @param max A value.
-     * @param a A value.
-     * @return min if a <= min, else max if a >= max, else a.
-     */
-    public static int toRange(int min, int max, int a) {
-        if (a <= min) {
-            return min;
-        } else if (a >= max) {
-            return max;
-        } else {
-            return a;
-        }
-    }
-
-    /**
-     * @param min A value.
-     * @param max A value.
-     * @param a A value.
-     * @return min if a <= min, else max if a >= max, else a.
-     */
-    public static long toRange(long min, long max, long a) {
-        if (a <= min) {
-            return min;
-        } else if (a >= max) {
-            return max;
-        } else {
-            return a;
-        }
-    }
-
-    /**
-     * @param min A value.
-     * @param max A value.
-     * @param a A value.
-     * @return min if a <= min, else max if a >= max, else a.
-     */
-    public static float toRange(float min, float max, float a) {
-        if (a <= min) {
-            return min;
-        } else if (a >= max) {
-            return max;
-        } else {
-            return a;
-        }
-    }
-
-    /**
-     * @param min A value.
-     * @param max A value.
-     * @param a A value.
-     * @return min if a <= min, else max if a >= max, else a.
-     */
-    public static double toRange(double min, double max, double a) {
-        if (a <= min) {
-            return min;
-        } else if (a >= max) {
-            return max;
-        } else {
-            return a;
-        }
-    }
-
-    /*
-     * bitwise ranges
-     */
-
-    /**
-     * @param bitSize A number of bits, in [1,32].
-     * @return True if the specified value fits as a signed integer
-     *         over the specified number of bits, false otherwise.
-     * @throws IllegalArgumentException if the specified number of bits is not in [1,32].
-     */
-    public static boolean isInRangeSigned(int a, int bitSize) {
-        checkBitSizeForSignedInt(bitSize);
-        return (minSignedIntForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedIntForBitSize_noCheck(bitSize));
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,64].
-     * @return True if the specified value fits as a signed integer
-     *         over the specified number of bits, false otherwise.
-     * @throws IllegalArgumentException if the specified number of bits is not in [1,64].
-     */
-    public static boolean isInRangeSigned(long a, int bitSize) {
-        checkBitSizeForSignedLong(bitSize);
-        return (minSignedLongForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedLongForBitSize_noCheck(bitSize));
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,31].
-     * @return True if the specified value fits as an unsigned integer
-     *         over the specified number of bits, false otherwise.
-     * @throws IllegalArgumentException if the specified number of bits is not in [1,31].
-     */
-    public static boolean isInRangeUnsigned(int a, int bitSize) {
-        return isInRange(0, maxUnsignedIntForBitSize(bitSize), a);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,63].
-     * @return True if the specified value fits as an unsigned integer
-     *         over the specified number of bits, false otherwise.
-     * @throws IllegalArgumentException if the specified number of bits is not in [1,63].
-     */
-    public static boolean isInRangeUnsigned(long a, int bitSize) {
-        return isInRange(0, maxUnsignedLongForBitSize(bitSize), a);
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @param bitSize A number of bits, in [1,32].
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value does not fit
-     *         as a signed integer over the specified number of bits.
-     */
-    public static boolean checkIsInRangeSigned(int a, int bitSize) {
-        if (!isInRangeSigned(a, bitSize)) {
-            throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits");
-        }
-        return true;
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,64].
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value does not fit
-     *         as a signed integer over the specified number of bits.
-     */
-    public static boolean checkIsInRangeSigned(long a, int bitSize) {
-        if (!isInRangeSigned(a, bitSize)) {
-            throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits");
-        }
-        return true;
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,31].
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value does not fit
-     *         as an unsigned integer over the specified number of bits.
-     */
-    public static boolean checkIsInRangeUnsigned(int a, int bitSize) {
-        if (!isInRangeUnsigned(a, bitSize)) {
-            throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits");
-        }
-        return true;
-    }
-
-    /**
-     * @param bitSize A number of bits, in [1,63].
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified value does not fit
-     *         as an unsigned integer over the specified number of bits.
-     */
-    public static boolean checkIsInRangeUnsigned(long a, int bitSize) {
-        if (!isInRangeUnsigned(a, bitSize)) {
-            throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits");
-        }
-        return true;
-    }
-
-    /*
-     * masks (int)
-     */
-
-    /**
-     * @param bitSize A number of bits, in [0,32].
-     * @return Mask with the specified number of left bits set with 0,
-     *         and other bits set with 1.
-     */
-    public static int intMaskMSBits0(int bitSize) {
-        checkIsInRange(0, 32, bitSize);
-        // Shifting in two times, for >>> doesn't work for full bit size (<< as well).
-        final int halfish = (bitSize>>1);
-        return ((-1)>>>halfish)>>>(bitSize-halfish);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,32].
-     * @return Mask with the specified number of left bits set with 1,
-     *         and other bits set with 0.
-     */
-    public static int intMaskMSBits1(int bitSize) {
-        return ~intMaskMSBits0(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,32].
-     * @return Mask with the specified number of right bits set with 0,
-     *         and other bits set with 1.
-     */
-    public static int intMaskLSBits0(int bitSize) {
-        return ~intMaskMSBits0(32-bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,32].
-     * @return Mask with the specified number of right bits set with 1,
-     *         and other bits set with 0.
-     */
-    public static int intMaskLSBits1(int bitSize) {
-        return intMaskMSBits0(32-bitSize);
-    }
-
-    /*
-     * masks (long)
-     */
-
-    /**
-     * @param bitSize A number of bits, in [0,64].
-     * @return Mask with the specified number of left bits set with 0,
-     *         and other bits set with 1.
-     */
-    public static long longMaskMSBits0(int bitSize) {
-        checkIsInRange(0, 64, bitSize);
-        // Shifting in two times, for >>> doesn't work for full bit size (<< as well).
-        final int halfish = (bitSize>>1);
-        return ((-1L)>>>halfish)>>>(bitSize-halfish);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,64].
-     * @return Mask with the specified number of left bits set with 1,
-     *         and other bits set with 0.
-     */
-    public static long longMaskMSBits1(int bitSize) {
-        return ~longMaskMSBits0(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,64].
-     * @return Mask with the specified number of right bits set with 0,
-     *         and other bits set with 1.
-     */
-    public static long longMaskLSBits0(int bitSize) {
-        return ~longMaskMSBits0(64-bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits, in [0,64].
-     * @return Mask with the specified number of right bits set with 1,
-     *         and other bits set with 0.
-     */
-    public static long longMaskLSBits1(int bitSize) {
-        return longMaskMSBits0(64-bitSize);
-    }
-
-    /*
-     * signed/unsigned
-     */
-
-    /**
-     * @return Unsigned value corresponding to bits of the specified byte.
-     */
-    public static short byteAsUnsigned(byte value) {
-        return (short)(((short)value) & 0xFF);
-    }
-
-    /**
-     * @return Unsigned value corresponding to bits of the specified short.
-     */
-    public static int shortAsUnsigned(short value) {
-        return ((int)value) & 0xFFFF;
-    }
-
-    /**
-     * @return Unsigned value corresponding to bits of the specified int.
-     */
-    public static long intAsUnsigned(int value) {
-        return ((long)value) & 0xFFFFFFFF;
-    }
-
-    /*
-     * bitwise ranges
-     */
-
-    /**
-     * @return True if a signed int value can be read over the specified number of bits,
-     *         i.e. if it is in [1,32], false otherwise.
-     */
-    public static boolean isValidBitSizeForSignedInt(int bitSize) {
-        return (bitSize > 0) && (bitSize <= 32);
-    }
-
-    /**
-     * @return True if a signed long value can be read over the specified number of bits,
-     *         i.e. if it is in [1,64], false otherwise.
-     */
-    public static boolean isValidBitSizeForSignedLong(int bitSize) {
-        return (bitSize > 0) && (bitSize <= 64);
-    }
-
-    /**
-     * @return True if an unsigned int value can be read over the specified number of bits,
-     *         i.e. if it is in [1,31], false otherwise.
-     */
-    public static boolean isValidBitSizeForUnsignedInt(int bitSize) {
-        return (bitSize > 0) && (bitSize < 32);
-    }
-
-    /**
-     * @return True if an unsigned long value can be read over the specified number of bits,
-     *         i.e. if it is in [1,63], false otherwise.
-     */
-    public static boolean isValidBitSizeForUnsignedLong(int bitSize) {
-        return (bitSize > 0) && (bitSize < 64);
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if a signed int value can't be read over the
-     *         specified number of bits, i.e. if it is not in [1,32].
-     */
-    public static boolean checkBitSizeForSignedInt(int bitSize) {
-        if (!isValidBitSizeForSignedInt(bitSize)) {
-            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,32] for signed int values");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if a signed long value can't be read over the
-     *         specified number of bits, i.e. if it is not in [1,64].
-     */
-    public static boolean checkBitSizeForSignedLong(int bitSize) {
-        if (!isValidBitSizeForSignedLong(bitSize)) {
-            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,64] for signed long values");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if an unsigned int value can't be read over the
-     *         specified number of bits, i.e. if it is not in [1,31].
-     */
-    public static boolean checkBitSizeForUnsignedInt(int bitSize) {
-        if (!isValidBitSizeForUnsignedInt(bitSize)) {
-            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,31] for unsigned int values");
-        }
-        return true;
-    }
-
-    /**
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if an unsigned long value can't be read over the
-     *         specified number of bits, i.e. if it is not in [1,63].
-     */
-    public static boolean checkBitSizeForUnsignedLong(int bitSize) {
-        if (!isValidBitSizeForUnsignedLong(bitSize)) {
-            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,63] for unsigned long values");
-        }
-        return true;
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @param bitSize A number of bits in [1,32].
-     * @return The min signed int value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static int minSignedIntForBitSize(int bitSize) {
-        checkBitSizeForSignedInt(bitSize);
-        return minSignedIntForBitSize_noCheck(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits in [1,64].
-     * @return The min signed long value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static long minSignedLongForBitSize(int bitSize) {
-        checkBitSizeForSignedLong(bitSize);
-        return minSignedLongForBitSize_noCheck(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits in [1,32].
-     * @return The max signed int value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static int maxSignedIntForBitSize(int bitSize) {
-        checkBitSizeForSignedInt(bitSize);
-        return maxSignedIntForBitSize_noCheck(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits in [1,64].
-     * @return The max signed long value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static long maxSignedLongForBitSize(int bitSize) {
-        checkBitSizeForSignedLong(bitSize);
-        return maxSignedLongForBitSize_noCheck(bitSize);
-    }
-
-    /**
-     * @param bitSize A number of bits in [1,31].
-     * @return The max unsigned int value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static int maxUnsignedIntForBitSize(int bitSize) {
-        checkBitSizeForUnsignedInt(bitSize);
-        // i.e. (1<<bitSize)-1
-        return (Integer.MAX_VALUE>>(31-bitSize));
-    }
-
-    /**
-     * @param bitSize A number of bits in [1,63].
-     * @return The max unsigned long value that can be stored over the specified number of bits.
-     * @throws IllegalArgumentException if the specified number of bits is out of range.
-     */
-    public static long maxUnsignedLongForBitSize(int bitSize) {
-        checkBitSizeForUnsignedLong(bitSize);
-        // i.e. (1L<<bitSize)-1
-        return (Long.MAX_VALUE>>(63-bitSize));
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @return The number of bits required to store the specified value as a signed integer,
-     *         i.e. a result in [1,32].
-     */
-    public static int bitSizeForSignedValue(int value) {
-        if (value > 0) {
-            return 33-Integer.numberOfLeadingZeros(value);
-        } else if (value == 0) {
-            return 1;
-        } else {
-            // Works for Integer.MIN_VALUE as well.
-            return 33-Integer.numberOfLeadingZeros(-value-1);
-        }
-    }
-
-    /**
-     * @return The number of bits required to store the specified value as a signed integer,
-     *         i.e. a result in [1,64].
-     */
-    public static int bitSizeForSignedValue(long value) {
-        if (value > 0) {
-            return 65-Long.numberOfLeadingZeros(value);
-        } else if (value == 0) {
-            return 1;
-        } else {
-            // Works for Long.MIN_VALUE as well.
-            return 65-Long.numberOfLeadingZeros(-value-1);
-        }
-    }
-
-    /**
-     * @param value An integer value in [0,Integer.MAX_VALUE].
-     * @return The number of bits required to store the specified value as an unsigned integer,
-     *         i.e. a result in [1,31].
-     * @throws IllegalArgumentException if the specified value is < 0.
-     */
-    public static int bitSizeForUnsignedValue(int value) {
-        if (value > 0) {
-            return 32-Integer.numberOfLeadingZeros(value);
-        } else {
-            if (value == 0) {
-                return 1;
-            } else {
-                throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0");
-            }
-        }
-    }
-
-    /**
-     * @param value An integer value in [0,Long.MAX_VALUE].
-     * @return The number of bits required to store the specified value as an unsigned integer,
-     *         i.e. a result in [1,63].
-     * @throws IllegalArgumentException if the specified value is < 0.
-     */
-    public static int bitSizeForUnsignedValue(long value) {
-        if (value > 0) {
-            return 64-Long.numberOfLeadingZeros(value);
-        } else {
-            if (value == 0) {
-                return 1;
-            } else {
-                throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0");
-            }
-        }
-    }
-
-    /*
-     * integer functions
-     */
-
-    /**
-     * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise.
-     */
-    public static int signum(int a) {
-        return (a < 0) ? -1 : ((a == 0) ? 0 : 1);
-    }
-
-    /**
-     * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise.
-     */
-    public static int signum(long a) {
-        return (a < 0) ? -1 : ((a == 0) ? 0 : 1);
-    }
-
-    /**
-     * @return True if the specified value is even, false otherwise.
-     */
-    public static boolean isEven(int a) {
-        return ((a&1) == 0);
-    }
-
-    /**
-     * @return True if the specified value is even, false otherwise.
-     */
-    public static boolean isEven(long a) {
-        // faster to work on ints
-        return isEven((int)a);
-    }
-
-    /**
-     * @return True if the specified value is odd, false otherwise.
-     */
-    public static boolean isOdd(int a) {
-        return ((a&1) != 0);
-    }
-
-    /**
-     * @return True if the specified value is odd, false otherwise.
-     */
-    public static boolean isOdd(long a) {
-        // faster to work on ints
-        return isOdd((int)a);
-    }
-
-    /**
-     * @return True if the specified values are both even or both odd, false otherwise.
-     */
-    public static boolean haveSameEvenness(int a, int b) {
-        return (((a^b)&1) == 0);
-    }
-
-    /**
-     * @return True if the specified values are both even or both odd, false otherwise.
-     */
-    public static boolean haveSameEvenness(long a, long b) {
-        // faster to work on ints
-        return haveSameEvenness((int)a, (int)b);
-    }
-
-    /**
-     * @return True if the specified values are both >= 0 or both < 0, false otherwise.
-     */
-    public static boolean haveSameSign(int a, int b) {
-        return ((a^b) >= 0);
-    }
-
-    /**
-     * @return True if the specified values are both >= 0 or both < 0, false otherwise.
-     */
-    public static boolean haveSameSign(long a, long b) {
-        return ((a^b) >= 0);
-    }
-
-    /**
-     * @return True if the specified value is a power of two,
-     *         i.e. a value of the form 2^k, with k >= 0.
-     */
-    public static boolean isPowerOfTwo(int a) {
-        if (a <= 0) {
-            return false;
-        }
-        if (false) {
-            // also works
-            return (a & -a) == a;
-        }
-        return (a & (a-1)) == 0;
-    }
-
-    /**
-     * @return True if the specified value is a power of two,
-     *         i.e. a value of the form 2^k, with k >= 0.
-     */
-    public static boolean isPowerOfTwo(long a) {
-        if (a <= 0) {
-            return false;
-        }
-        if (false) {
-            // also works
-            return (a & -a) == a;
-        }
-        return (a & (a-1)) == 0;
-    }
-
-    /**
-     * @return True if the specified value is a signed power of two,
-     *         i.e. a value of the form +-2^k, with k >= 0.
-     */
-    public static boolean isSignedPowerOfTwo(int a) {
-        if (a > 0) {
-            return (a & (a-1)) == 0;
-        } else {
-            if (a == -a) {
-                // a is 0 or Integer.MIN_VALUE
-                return (a != 0);
-            }
-            return ((-a) & (-a-1)) == 0;
-        }
-    }
-
-    /**
-     * @return True if the specified value is a signed power of two,
-     *         i.e. a value of the form +-2^k, with k >= 0.
-     */
-    public static boolean isSignedPowerOfTwo(long a) {
-        if (a > 0) {
-            return (a & (a-1)) == 0;
-        } else {
-            if (a == -a) {
-                // a is 0 or Long.MIN_VALUE
-                return (a != 0);
-            }
-            return ((-a) & (-a-1)) == 0;
-        }
-    }
-
-    /**
-     * @param a A value in [1,Integer.MAX_VALUE].
-     * @return The highest power of two <= a.
-     */
-    public static int floorPowerOfTwo(int a) {
-        if (a <= 0) {
-            throw new IllegalArgumentException("a ["+a+"] must be > 0");
-        }
-        return Integer.highestOneBit(a);
-    }
-
-    /**
-     * @param a A value in [1,Long.MAX_VALUE].
-     * @return The highest power of two <= a.
-     */
-    public static long floorPowerOfTwo(long a) {
-        if (a <= 0) {
-            throw new IllegalArgumentException("a ["+a+"] must be > 0");
-        }
-        // Faster than copying int method
-        // (less computations on long).
-        return 1L << (63 - Long.numberOfLeadingZeros(a));
-    }
-
-    /**
-     * @param a A value in [0,2^30].
-     * @return The lowest power of two >= a.
-     */
-    public static int ceilingPowerOfTwo(int a) {
-        checkIsInRange(0, (1<<30), a);
-        return (a >= 2) ? Integer.highestOneBit((a-1)<<1) : 1;
-    }
-
-    /**
-     * @param a A value in [0,2^62].
-     * @return The lowest power of two >= a.
-     */
-    public static long ceilingPowerOfTwo(long a) {
-        checkIsInRange(0L, (1L<<62), a);
-        // Faster than copying int method
-        // (less computations on long).
-        return 1L << (64 - Long.numberOfLeadingZeros(a - 1));
-    }
-
-    /**
-     * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division).
-     */
-    public static int meanLow(int a, int b) {
-        return (a & b) + ((a ^ b) >> 1);
-    }
-
-    /**
-     * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division).
-     */
-    public static long meanLow(long a, long b) {
-        return (a & b) + ((a ^ b) >> 1);
-    }
-
-    /**
-     * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division).
-     */
-    public static int meanSml(int a, int b) {
-        int result = meanLow(a,b);
-        if (!haveSameEvenness(a, b)) {
-            // inexact
-            if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) {
-                // both < 0, or only one is < 0 and it has the largest magnitude
-                result++;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division).
-     */
-    public static long meanSml(long a, long b) {
-        long result = meanLow(a,b);
-        if (!haveSameEvenness(a, b)) {
-            // inexact
-            if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) {
-                // both < 0, or only one is < 0 and it has the largest magnitude
-                result++;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Useful because a positive int value could not represent half the width
-     * of full int range width, which is mathematically Integer.MAX_VALUE+1.
-     *
-     * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude)
-     *         between the specified bounds.
-     * @throws IllegalArgumentException if min > max.
-     */
-    public static int negHalfWidth(int min, int max) {
-        if (min > max) {
-            throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]");
-        }
-        int mean = meanLow(min, max);
-        return min - mean - ((min^max)&1);
-    }
-
-    /**
-     * Useful because a positive long value could not represent half the width
-     * of full long range width, which is mathematically Long.MAX_VALUE+1.
-     *
-     * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude)
-     *         between the specified bounds.
-     * @throws IllegalArgumentException if min > max.
-     */
-    public static long negHalfWidth(long min, long max) {
-        if (min > max) {
-            throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]");
-        }
-        long mean = meanLow(min, max);
-        return min - mean - ((min^max)&1);
-    }
-
-    /**
-     * This treatment being designed for optimization, the fact that spot
-     * is a signed power of two is not checked.
-     *
-     * @param value A value.
-     * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0).
-     * @return value % spot, i.e. a value in ]-|spot|,|spot|[.
-     */
-    public static int moduloSignedPowerOfTwo(int value, int spot) {
-        if (spot == Integer.MIN_VALUE) {
-            return (value != Integer.MIN_VALUE) ? value : 0;
-        } else {
-            int s = (value>>31);
-            return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s;
-        }
-    }
-
-    /**
-     * This treatment being designed for optimization, the fact that spot
-     * is a signed power of two is not checked.
-     *
-     * @param value A value.
-     * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0).
-     * @return value % spot, i.e. a value in ]-|spot|,|spot|[.
-     */
-    public static long moduloSignedPowerOfTwo(long value, long spot) {
-        if (spot == Long.MIN_VALUE) {
-            return (value != Long.MIN_VALUE) ? value : 0;
-        } else {
-            long s = (value>>63);
-            return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s;
-        }
-    }
-
-    /**
-     * @param value An integer value > 0.
-     * @return The integer part of the logarithm, in base 2, of the specified value,
-     *         i.e. a result in [0,30]
-     * @throws IllegalArgumentException if the specified value is <= 0.
-     */
-    public static int log2(int value) {
-        if (value <= 0) {
-            throw new IllegalArgumentException("value ["+value+"] must be > 0");
-        }
-        return 31-Integer.numberOfLeadingZeros(value);
-    }
-
-    /**
-     * @param value An integer value > 0.
-     * @return The integer part of the logarithm, in base 2, of the specified value,
-     *         i.e. a result in [0,62]
-     * @throws IllegalArgumentException if the specified value is <= 0.
-     */
-    public static int log2(long value) {
-        if (value <= 0) {
-            throw new IllegalArgumentException("value ["+value+"] must be > 0");
-        }
-        return 63-Long.numberOfLeadingZeros(value);
-    }
-
-    /**
-     * Possibly faster than java.lang.Math.abs(int).
-     *
-     * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE.
-     */
-    public static int abs(int a) {
-        return (a^(a>>31))-(a>>31);
-    }
-
-    /**
-     * Possibly faster than java.lang.Math.abs(long).
-     *
-     * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE.
-     */
-    public static long abs(long a) {
-        return (a^(a>>63))-(a>>63);
-    }
-
-    /**
-     * @return The negative of the absolute value (always exact).
-     */
-    public static int absNeg(int a) {
-        return (a>>31)-(a^(a>>31));
-    }
-
-    /**
-     * @return The negative of the absolute value (always exact).
-     */
-    public static long absNeg(long a) {
-        return (a>>63)-(a^(a>>63));
-    }
-
-    /**
-     * If the specified value is in int range, the returned value is identical.
-     *
-     * @return An int hash of the specified value.
-     */
-    public static int intHash(long a) {
-        if (false) {
-            // also works
-            int hash = ((int)(a>>32)) ^ ((int)a);
-            if (a < 0) {
-                hash = -hash-1;
-            }
-            return hash;
-        }
-        int hash = ((int)(a>>32)) + ((int)a);
-        if (a < 0) {
-            hash++;
-        }
-        return hash;
-    }
-
-    /**
-     * Not defining an asByte(long) method, since asByte((int)aLong) works.
-     *
-     * @param a An int value.
-     * @return The specified value as byte.
-     * @throws ArithmeticException if the specified value is not in [Byte.MIN_VALUE,Byte.MAX_VALUE] range.
-     */
-    public static byte asByte(int a) {
-        if (a != (byte)a) {
-            throw new ArithmeticException("overflow: "+a);
-        }
-        return (byte)a;
-    }
-
-    /**
-     * @param a A long value.
-     * @return The specified value as int.
-     * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int asInt(long a) {
-        if (a != (int)a) {
-            throw new ArithmeticException("overflow: "+a);
-        }
-        return (int)a;
-    }
-
-    /**
-     * @param a A long value.
-     * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int toInt(long a) {
-        if (a != (int)a) {
-            return (a < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
-        }
-        return (int)a;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a+b.
-     * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int plusExact(int a, int b) {
-        final int sum = a + b;
-        // HD 2-12 Overflow iff both arguments
-        // have the opposite sign of the result.
-        if (((a ^ sum) & (b ^ sum)) < 0) {
-            throw new ArithmeticException("overflow: "+a+"+"+b);
-        }
-        return sum;
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a+b.
-     * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long plusExact(long a, long b) {
-        final long sum = a + b;
-        // HD 2-12 Overflow iff both arguments
-        // have the opposite sign of the result.
-        if (((a ^ sum) & (b ^ sum)) < 0) {
-            throw new ArithmeticException("overflow: "+a+"+"+b);
-        }
-        return sum;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b.
-     */
-    public static int plusBounded(int a, int b) {
-        return toInt(((long)a) + ((long)b));
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b.
-     */
-    public static long plusBounded(long a, long b) {
-        final long sum = a + b;
-        if (((a ^ sum) & (b ^ sum)) < 0) {
-            return (sum >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
-        }
-        return sum;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a-b.
-     * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int minusExact(int a, int b) {
-        final int diff = a - b;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of "a".
-        if (((a ^ b) & (a ^ diff)) < 0) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return diff;
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a-b.
-     * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long minusExact(long a, long b) {
-        final long diff = a - b;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of "a".
-        if (((a ^ b) & (a ^ diff)) < 0) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return diff;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b.
-     */
-    public static int minusBounded(int a, int b) {
-        return toInt(((long)a) - ((long)b));
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b.
-     */
-    public static long minusBounded(long a, long b) {
-        final long diff = a - b;
-        if (((a ^ b) & (a ^ diff)) < 0) {
-            return (diff >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
-        }
-        return diff;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The mathematical result of a*b.
-     * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
-     */
-    public static int timesExact(int a, int b) {
-        final long prod = a * (long)b;
-        if (prod != (int)prod) {
-            throw new ArithmeticException("overflow: "+a+"*"+b);
-        }
-        return (int)prod;
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The mathematical result of a*b.
-     * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
-     */
-    public static long timesExact(long a, long b) {
-        final long prod = a * b;
-        final long absA = abs(a);
-        final long absB = abs(b);
-        if (((absA|absB)>>>31) != 0) {
-            // Some bits greater than 2^31 that might cause overflow
-            // Check the result using the divide operator
-            // and check for the special case of Long.MIN_VALUE * -1
-            if (((b != 0) && (prod/b != a)) ||
-                    ((a == Long.MIN_VALUE) && (b == -1))) {
-                throw new ArithmeticException("overflow: "+a+"*"+b);
-            }
-        }
-        return prod;
-    }
-
-    /**
-     * @param a An int value.
-     * @param b An int value.
-     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b.
-     */
-    public static int timesBounded(int a, int b) {
-        return (int)(a * (double)b);
-    }
-
-    /**
-     * @param a A long value.
-     * @param b A long value.
-     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b.
-     */
-    public static long timesBounded(long a, long b) {
-        final long prod = a * b;
-        final long absA = abs(a);
-        final long absB = abs(b);
-        if (((absA|absB)>>>31) != 0) {
-            // Some bits greater than 2^31 that might cause overflow
-            // Check the result using the divide operator
-            // and check for the special case of Long.MIN_VALUE * -1
-            if (((b != 0) && (prod/b != a)) ||
-                    ((a == Long.MIN_VALUE) && (b == -1))) {
-                return ((a^b) >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE;
-            }
-        }
-        return prod;
-    }
-
-    /*
-     * powers
-     */
-
-    /**
-     * Returns the exact result, provided it's in double range,
-     * i.e. if power is in [-1074,1023].
-     *
-     * @param power An int power.
-     * @return 2^power as a double, or +-Infinity in case of overflow.
-     */
-    public static double twoPow(int power) {
-        if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal.
-            if (power >= MIN_DOUBLE_EXPONENT) { // Subnormal.
-                return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT)));
-            } else { // Underflow.
-                return 0.0;
-            }
-        } else if (power > MAX_DOUBLE_EXPONENT) { // Overflow.
-            return Double.POSITIVE_INFINITY;
-        } else { // Normal.
-            return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52);
-        }
-    }
-
-    /**
-     * @param power An int power.
-     * @return 2^power as an int.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in int range, i.e. if power is not in [0,30].
-     */
-    public static int twoPowAsIntExact(int power) {
-        if ((power < 0) || (power > 30)) {
-            throw new ArithmeticException("integer overflow");
-        }
-        return 1 << power;
-    }
-
-    /**
-     * @param power An int power.
-     * @return 2^power as an int, or the closest power of two in int range
-     *         in case of overflow, i.e. if power is not in [0,30].
-     */
-    public static int twoPowAsIntBounded(int power) {
-        power = toRange(0, 30, power);
-        return 1 << power;
-    }
-
-    /**
-     * @param power An int power.
-     * @return 2^power as a long.
-     * @throws ArithmeticException if the mathematical result
-     *         is not in long range, i.e. if power is not in [0,62].
-     */
-    public static long twoPowAsLongExact(int power) {
-        if ((power < 0) || (power > 62)) {
-            throw new ArithmeticException("long overflow");
-        }
-        return 1L << power;
-    }
-
-    /**
-     * @param power An int power.
-     * @return 2^power as a long, or the closest power of two in long range
-     *         in case of overflow, i.e. if power is not in [0,62].
-     */
-    public static long twoPowAsLongBounded(int power) {
-        power = toRange(0, 62, power);
-        return 1L << power;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a.
-     */
-    public static int pow2(int a) {
-        return a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a.
-     */
-    public static long pow2(long a) {
-        return a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a.
-     */
-    public static float pow2(float a) {
-        return a*a;
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param a A value.
-     * @return a*a.
-     */
-    public static strictfp float pow2_strict(float a) {
-        return a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a.
-     */
-    public static double pow2(double a) {
-        return a*a;
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param a A value.
-     * @return a*a.
-     */
-    public static strictfp double pow2_strict(double a) {
-        return a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static int pow3(int a) {
-        return a*a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static long pow3(long a) {
-        return a*a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static float pow3(float a) {
-        return a*a*a;
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static strictfp float pow3_strict(float a) {
-        return a*a*a;
-    }
-
-    /**
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static double pow3(double a) {
-        return a*a*a;
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param a A value.
-     * @return a*a*a.
-     */
-    public static strictfp double pow3_strict(double a) {
-        return a*a*a;
-    }
-
-    /*
-     * Accurate +-m*PI/n.
-     */
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad + 2*PI, accurately computed.
-     */
-    public static double plus2PI(double angRad) {
-        if (angRad > -Math.PI) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + TWOPI_LO) + TWOPI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + TWOPI_HI) + TWOPI_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad + 2*PI, accurately computed.
-     */
-    public static strictfp double plus2PI_strict(double angRad) {
-        if (angRad > -Math.PI) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + TWOPI_LO) + TWOPI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + TWOPI_HI) + TWOPI_LO;
-        }
-    }
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad - 2*PI, accurately computed.
-     */
-    public static double minus2PI(double angRad) {
-        if (angRad < Math.PI) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - TWOPI_LO) - TWOPI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - TWOPI_HI) - TWOPI_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad - 2*PI, accurately computed.
-     */
-    public static strictfp double minus2PI_strict(double angRad) {
-        if (angRad < Math.PI) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - TWOPI_LO) - TWOPI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - TWOPI_HI) - TWOPI_LO;
-        }
-    }
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad + PI, accurately computed.
-     */
-    public static double plusPI(double angRad) {
-        if (angRad > -Math.PI/2) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + PI_LO) + PI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + PI_HI) + PI_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad + PI, accurately computed.
-     */
-    public static strictfp double plusPI_strict(double angRad) {
-        if (angRad > -Math.PI/2) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + PI_LO) + PI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + PI_HI) + PI_LO;
-        }
-    }
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad - PI, accurately computed.
-     */
-    public static double minusPI(double angRad) {
-        if (angRad < Math.PI/2) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - PI_LO) - PI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - PI_HI) - PI_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad - PI, accurately computed.
-     */
-    public static strictfp double minusPI_strict(double angRad) {
-        if (angRad < Math.PI/2) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - PI_LO) - PI_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - PI_HI) - PI_LO;
-        }
-    }
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad + PI/2, accurately computed.
-     */
-    public static double plusPIO2(double angRad) {
-        if (angRad > -Math.PI/4) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + PIO2_LO) + PIO2_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + PIO2_HI) + PIO2_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad + PI/2, accurately computed.
-     */
-    public static strictfp double plusPIO2_strict(double angRad) {
-        if (angRad > -Math.PI/4) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad + PIO2_LO) + PIO2_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad + PIO2_HI) + PIO2_LO;
-        }
-    }
-
-    /**
-     * @param angRad An angle, in radians.
-     * @return angRad - PI/2, accurately computed.
-     */
-    public static double minusPIO2(double angRad) {
-        if (angRad < Math.PI/4) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - PIO2_LO) - PIO2_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - PIO2_HI) - PIO2_LO;
-        }
-    }
-
-    /**
-     * Strict version.
-     *
-     * @param angRad An angle, in radians.
-     * @return angRad - PI/2, accurately computed.
-     */
-    public static strictfp double minusPIO2_strict(double angRad) {
-        if (angRad < Math.PI/4) {
-            // LO then HI, for better accuracy (if starting near 0).
-            return (angRad - PIO2_LO) - PIO2_HI;
-        } else {
-            // HI then LO, for better accuracy (if ending near 0).
-            return (angRad - PIO2_HI) - PIO2_LO;
-        }
-    }
-
-    /*
-     * toString (radix)
-     */
-
-    /**
-     * @param radix Radix to be checked.
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified radix is not in [2,36].
-     */
-    public static boolean checkRadix(int radix) {
-        if (!isInRange(Character.MIN_RADIX, Character.MAX_RADIX, radix)) {
-            throw new IllegalArgumentException("radix ["+radix+"] must be in ["+Character.MIN_RADIX+","+Character.MAX_RADIX+"]");
-        }
-        return true;
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return Number of characters (minus sign included)
-     *         to represent the specified value in the specified radix.
-     */
-    public static int computeNbrOfChars(int value, int radix) {
-        if (value < 0) {
-            // 1 for sign
-            return 1 + computeNbrOfDigits_negValue(value, radix);
-        } else {
-            return computeNbrOfDigits_negValue(-value, radix);
-        }
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return Number of characters (minus sign included)
-     *         to represent the specified value in the specified radix.
-     */
-    public static int computeNbrOfChars(long value, int radix) {
-        if (value < 0) {
-            // 1 for sign
-            return 1 + computeNbrOfDigits_negValue(value, radix);
-        } else {
-            return computeNbrOfDigits_negValue(-value, radix);
-        }
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return Number of characters (minus sign included)
-     *         to represent the specified value in the specified radix.
-     */
-    public static int computeNbrOfChars(int value, int radix, int paddingUpTo) {
-        if (value < 0) {
-            // 1 for sign
-            return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix));
-        } else {
-            return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix));
-        }
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return Number of characters (minus sign included)
-     *         to represent the specified value in the specified radix.
-     */
-    public static int computeNbrOfChars(long value, int radix, int paddingUpTo) {
-        if (value < 0) {
-            // 1 for sign
-            return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix));
-        } else {
-            return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix));
-        }
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return Number of digits of the specified value in the specified radix.
-     */
-    public static int computeNbrOfDigits(int value, int radix) {
-        return computeNbrOfDigits_negValue(-abs(value), radix);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return Number of digits of the specified value in the specified radix.
-     */
-    public static int computeNbrOfDigits(long value, int radix) {
-        return computeNbrOfDigits_negValue(-abs(value), radix);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return Number of digits of the specified value in the specified radix,
-     *         including the specified padding.
-     */
-    public static int computeNbrOfDigits(int value, int radix, int paddingUpTo) {
-        return Math.max(paddingUpTo,computeNbrOfDigits(value, radix));
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return Number of digits of the specified value in the specified radix,
-     *         including the specified padding.
-     */
-    public static int computeNbrOfDigits(long value, int radix, int paddingUpTo) {
-        return Math.max(paddingUpTo,computeNbrOfDigits(value, radix));
-    }
-
-    /**
-     * This method just delegates to Integer.toString(int),
-     * but is defined here to complete the API.
-     *
-     * @return String representation of the specified value in base 10.
-     */
-    public static String toString(int value) {
-        return Integer.toString(value);
-    }
-
-    /**
-     * This method just delegates to Long.toString(long),
-     * but is defined here to complete the API.
-     *
-     * @return String representation of the specified value in base 10.
-     */
-    public static String toString(long value) {
-        return Long.toString(value);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return String representation of the specified value in the specified radix.
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    public static String toString(int value, int radix) {
-        return toString(value, radix, 0);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @return String representation of the specified value in the specified radix.
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    public static String toString(long value, int radix) {
-        return toString(value, radix, 0);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return String representation of the specified value in the specified radix.
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    public static String toString(int value, int radix, int paddingUpTo) {
-        // Only one test if radix+paddingUpTo != 10.
-        if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) {
-            // Using JDK's optimized algorithm.
-            return Integer.toString(value);
-        }
-
-        int negValue;
-        final int signSize;
-        final boolean negative = (value < 0);
-        if (negative) {
-            negValue = value;
-            signSize = 1;
-        } else {
-            negValue = -value;
-            signSize = 0;
-        }
-        // Faster if we just use max possible number of characters (33),
-        // but we prefer to take care of garbage's memory footprint.
-        // Checks radix.
-        final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix));
-
-        final char[] chars = new char[nbrOfChars];
-
-        int charPos = nbrOfChars;
-
-        final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0);
-        // Not allowing Integer.MIN_VALUE so it can be negated.
-        if (radixIsPowerOfTwo && (negValue != Integer.MIN_VALUE)) {
-            final int mask = radix-1;
-            final int divShift = DIV_SHIFT_BY_RADIX[radix];
-            while (negValue <= -radix) {
-                chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)];
-                negValue = -((-negValue) >> divShift);
-            }
-        } else {
-            while (negValue <= -radix) {
-                chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))];
-                negValue /= radix;
-            }
-        }
-        chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)];
-
-        while (charPos > signSize) {
-            chars[--charPos] = '0';
-        }
-
-        if (negative) {
-            chars[0] = '-';
-        }
-
-        return new String(chars);
-    }
-
-    /**
-     * @param radix A radix in [2,36].
-     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
-     * @return String representation of the specified value in the specified radix.
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    public static String toString(long value, int radix, int paddingUpTo) {
-        // Only one test if radix+paddingUpTo != 10.
-        if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) {
-            // Using JDK's optimized algorithm.
-            return Long.toString(value);
-        }
-
-        long negValue;
-        final int signSize;
-        final boolean negative = (value < 0);
-        if (negative) {
-            negValue = value;
-            signSize = 1;
-        } else {
-            negValue = -value;
-            signSize = 0;
-        }
-        // Checks radix.
-        final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix));
-
-        final char[] chars = new char[nbrOfChars];
-
-        int charPos = nbrOfChars;
-
-        final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0);
-        // Not allowing Long.MIN_VALUE so it can be negated.
-        if (radixIsPowerOfTwo && (negValue != Long.MIN_VALUE)) {
-            final int mask = radix-1;
-            final int divShift = DIV_SHIFT_BY_RADIX[radix];
-            while (negValue <= -radix) {
-                chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)];
-                negValue = -((-negValue) >> divShift);
-            }
-        } else {
-            while (negValue <= -radix) {
-                chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))];
-                negValue /= radix;
-            }
-        }
-        chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)];
-
-        while (charPos > signSize) {
-            chars[--charPos] = '0';
-        }
-
-        if (negative) {
-            chars[0] = '-';
-        }
-
-        return new String(chars);
-    }
-
-    /*
-     * toString (bits)
-     */
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified bit range does not fit in a byte.
-     */
-    public static boolean checkBitPositionsByte(int firstBitPos, int lastBitPosExcl) {
-        return checkBitPositions(firstBitPos, lastBitPosExcl, 8);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified bit range does not fit in a short.
-     */
-    public static boolean checkBitPositionsShort(int firstBitPos, int lastBitPosExcl) {
-        return checkBitPositions(firstBitPos, lastBitPosExcl, 16);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified bit range does not fit in an int.
-     */
-    public static boolean checkBitPositionsInt(int firstBitPos, int lastBitPosExcl) {
-        return checkBitPositions(firstBitPos, lastBitPosExcl, 32);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @return True if does not throw.
-     * @throws IllegalArgumentException if the specified bit range does not fit in a long.
-     */
-    public static boolean checkBitPositionsLong(int firstBitPos, int lastBitPosExcl) {
-        return checkBitPositions(firstBitPos, lastBitPosExcl, 64);
-    }
-
-    /**
-     * @return String representation of specified bits, in big endian.
-     */
-    public static String toStringBits(byte bits) {
-        final char[] chars = new char[8];
-        int bitIndex = 8;
-        while (--bitIndex >= 0) {
-            chars[7-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-        }
-        return new String(chars);
-    }
-
-    /**
-     * @return String representation of specified bits, in big endian.
-     */
-    public static String toStringBits(short bits) {
-        final char[] chars = new char[16];
-        int bitIndex = 16;
-        while (--bitIndex >= 0) {
-            chars[15-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-        }
-        return new String(chars);
-    }
-
-    /**
-     * @return String representation of specified bits, in big endian.
-     */
-    public static String toStringBits(int bits) {
-        final char[] chars = new char[32];
-        int bitIndex = 32;
-        while (--bitIndex >= 0) {
-            chars[31-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-        }
-        return new String(chars);
-    }
-
-    /**
-     * @return String representation of specified bits, in big endian.
-     */
-    public static String toStringBits(long bits) {
-        final char[] chars = new char[64];
-        int bitIndex = 64;
-        while (--bitIndex >= 0) {
-            chars[63-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-        }
-        return new String(chars);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
-     *                  false for little endian order.
-     * @param padding True if underscores must be added instead of out-of-range bits,
-     *                false to just add characters corresponding to in-range bits.
-     * @return String representation of specified bits.
-     */
-    public static String toStringBits(
-            byte bits,
-            int firstBitPos,
-            int lastBitPosExcl,
-            boolean bigEndian,
-            boolean padding) {
-        checkBitPositionsByte(firstBitPos, lastBitPosExcl);
-        return toStringBits_0_32_bitPosAlreadyChecked(8,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
-     *                  false for little endian order.
-     * @param padding True if underscores must be added instead of out-of-range bits,
-     *                false to just add characters corresponding to in-range bits.
-     * @return String representation of specified bits.
-     */
-    public static String toStringBits(
-            short bits,
-            int firstBitPos,
-            int lastBitPosExcl,
-            boolean bigEndian,
-            boolean padding) {
-        checkBitPositionsShort(firstBitPos, lastBitPosExcl);
-        return toStringBits_0_32_bitPosAlreadyChecked(16,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
-     *                  false for little endian order.
-     * @param padding True if underscores must be added instead of out-of-range bits,
-     *                false to just add characters corresponding to in-range bits.
-     * @return String representation of specified bits.
-     */
-    public static String toStringBits(
-            int bits,
-            int firstBitPos,
-            int lastBitPosExcl,
-            boolean bigEndian,
-            boolean padding) {
-        checkBitPositionsInt(firstBitPos, lastBitPosExcl);
-        return toStringBits_0_32_bitPosAlreadyChecked(32,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
-    }
-
-    /**
-     * @param firstBitPos First bit position (inclusive).
-     * @param lastBitPosExcl Last bit position (exclusive).
-     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
-     *                  false for little endian order.
-     * @param padding True if underscores must be added instead of out-of-range bits,
-     *                false to just add characters corresponding to in-range bits.
-     * @return String representation of specified bits.
-     */
-    public static String toStringBits(
-            long bits,
-            int firstBitPos,
-            int lastBitPosExcl,
-            boolean bigEndian,
-            boolean padding) {
-        checkBitPositionsLong(firstBitPos, lastBitPosExcl);
-        final int bitSize = 64;
-        final int bitSizeM1 = bitSize-1;
-        final int lastBitPos = lastBitPosExcl-1;
-        if (padding) {
-            final int nbrOfChars = bitSize;
-            final char[] chars = new char[nbrOfChars];
-            int bitIndex = bitSizeM1;
-            if (bigEndian) {
-                final int firstBitIndex = bitSizeM1-lastBitPos;
-                final int lastBitIndex = bitSizeM1-firstBitPos;
-                while (bitIndex > lastBitIndex) {
-                    chars[bitSizeM1-bitIndex] = '_';
-                    --bitIndex;
-                }
-                while (bitIndex >= firstBitIndex) {
-                    chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-                while (bitIndex >= 0) {
-                    chars[bitSizeM1-bitIndex] = '_';
-                    --bitIndex;
-                }
-            } else {
-                while (bitIndex > lastBitPos) {
-                    chars[bitIndex] = '_';
-                    --bitIndex;
-                }
-                while (bitIndex >= firstBitPos) {
-                    chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-                while (bitIndex >= 0) {
-                    chars[bitIndex] = '_';
-                    --bitIndex;
-                }
-            }
-            return new String(chars);
-        } else {
-            final int nbrOfChars = (lastBitPosExcl - firstBitPos);
-            final char[] chars = new char[nbrOfChars];
-            if (bigEndian) {
-                final int firstBitIndex = bitSizeM1-lastBitPos;
-                final int lastBitIndex = bitSizeM1-firstBitPos;
-                int bitIndex = lastBitIndex;
-                while (bitIndex >= firstBitIndex) {
-                    chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-            } else {
-                int bitIndex = lastBitPos;
-                while (bitIndex >= firstBitPos) {
-                    chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-            }
-            return new String(chars);
-        }
-    }
-
-    /*
-     * toString (floating points)
-     *
-     * toStringCSN(double) and toStringNoCSN(double)
-     * could be made faster, by using directly internals
-     * of Double.toString(double), but this would require
-     * copy-paste of much tricky code from JDK, and
-     * the overhead of our little rework is relatively
-     * negligible.
-     */
-
-    /**
-     * @param value A double value.
-     * @return String representing the specified value,
-     *         using "computerized scientific notation",
-     *         which Double.toString(double) uses for non-infinite
-     *         values, when |value| < 1e-3 or |value| >= 1e7.
-     */
-    public static String toStringCSN(double value) {
-        // Quick case (also to get rid of +-0.0,
-        // for which Double.toString(double) doesn't use CSN).
-        if (value == 0.0) {
-            if (Double.doubleToRawLongBits(value) < 0) {
-                return "-0.0E0";
-            } else {
-                return "0.0E0";
-            }
-        }
-
-        final double abs = Math.abs(value);
-        if ((abs >= NO_CSN_MIN_BOUND_INCL) && (abs < NO_CSN_MAX_BOUND_EXCL)) {
-            final boolean neg = (value < 0.0);
-
-            final String rawAbs = Double.toString(abs);
-            if (abs >= 1.0) {
-                /*
-                 * 0123456
-                 * 12.3456 ===> 1.23456E1
-                 * 123.0   ===> 1.23E2
-                 */
-                final int dotIndex = rawAbs.indexOf((int)'.');
-                final int powerOfTen = dotIndex-1;
-                final StringBuilder sb = new StringBuilder();
-                if (neg) {
-                    sb.append('-');
-                }
-                // Adding unit-or-above digits, with dot after first one.
-                sb.append(rawAbs.charAt(0));
-                sb.append('.');
-                sb.append(rawAbs,1,dotIndex);
-                if ((value != (int)value) || (abs < 10.0)) {
-                    // Adding below-unit digits (possibly just 0 if abs < 10.0,
-                    // to end up for example with "3.0E0" instead of "3.E0").
-                    sb.append(rawAbs,dotIndex+1,rawAbs.length());
-                }
-                sb.append('E');
-                sb.append(CHAR_BY_DIGIT[powerOfTen]);
-                return sb.toString();
-            } else {
-                /*
-                 * 012345678
-                 * 0.0123456 ===> 1.23456E-2
-                 * 0.01      ===> 1.0E-2
-                 */
-                int nonZeroIndex = 1;
-                while (rawAbs.charAt(++nonZeroIndex) == '0') {
-                }
-                // Negative.
-                final int powerOfTen = 1-nonZeroIndex;
-                final int nbrOfSignificantDigitsPastDot = (rawAbs.length() - (nonZeroIndex+1));
-                final StringBuilder sb = new StringBuilder();
-                if (neg) {
-                    sb.append('-');
-                }
-                sb.append(rawAbs.charAt(nonZeroIndex));
-                sb.append('.');
-                if (nbrOfSignificantDigitsPastDot > 0) {
-                    // If bug 4428022 make rawAbs being something like "0.0010",
-                    // we add the last '0' here after the dot, which is fine.
-                    sb.append(rawAbs,nonZeroIndex+1,rawAbs.length());
-                } else {
-                    sb.append('0');
-                }
-                sb.append("E-");
-                sb.append(CHAR_BY_DIGIT[-powerOfTen]);
-                return sb.toString();
-            }
-        } else {
-            return Double.toString(value);
-        }
-    }
-
-    /**
-     * @param value A double value.
-     * @return String representing the specified value,
-     *         not in "computerized scientific notation",
-     *         which Double.toString(double) uses for non-infinite
-     *         values, when |value| < 1e-3 or |value| >= 1e7.
-     */
-    public static String toStringNoCSN(double value) {
-        // Quick case.
-        // Should also work with long instead of int,
-        // but less obvious (due to roundings...),
-        // and we just want to speed up the more common
-        // case of "small" integer values.
-        final int intValue = (int)value;
-        if (value == intValue) {
-            if (value == 0.0) {
-                if (Double.doubleToRawLongBits(value) < 0) {
-                    return "-0.0";
-                } else {
-                    return "0.0";
-                }
-            } else {
-                return Integer.toString(intValue)+".0";
-            }
-        }
-
-        final String raw = Double.toString(value);
-        final double abs = Math.abs(value);
-        if (abs >= NO_CSN_MAX_BOUND_EXCL) {
-            if (abs == Double.POSITIVE_INFINITY) {
-                return raw;
-            }
-            /*
-             * 0123456789
-             * 1.234567E5 ===> 123456.7
-             * 1.23456E5  ===> 123456.0 (adding 0)
-             * 1.23E5     ===> 123000.0
-             * 1.0E5      ===> 100000.0
-             */
-            // "." close to start, so using indexOf.
-            final int dotIndex = raw.indexOf((int)'.');
-            // "E" close to end, so using lastIndexOf.
-            final int eIndex = raw.lastIndexOf((int)'E');
-            final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1));
-            final int nbrOfSignificantLoDigits = (eIndex - dotIndex - 1);
-            final int nbrOfZerosToAddBeforeDot = (powerOfTen - nbrOfSignificantLoDigits);
-
-            int start;
-            int end;
-
-            final StringBuilder sb = new StringBuilder();
-            sb.append(raw,0,dotIndex);
-            if (nbrOfZerosToAddBeforeDot >= 0) {
-                // Can copy all digits that were between '.' and 'E'.
-                sb.append(raw,dotIndex+1,eIndex);
-                for (int i=0;i<nbrOfZerosToAddBeforeDot;i++) {
-                    sb.append('0');
-                }
-                sb.append(".0");
-            } else {
-                start = dotIndex+1;
-                sb.append(raw,start,end = start+powerOfTen);
-
-                sb.append('.');
-
-                start = end;
-                sb.append(raw,start,end = eIndex);
-            }
-            return sb.toString();
-        } else if (abs < NO_CSN_MIN_BOUND_INCL) {
-            // Not +-0.0 since already handled.
-            /*
-             * 01234567
-             * 1.234E-4 ===> 0.0001234
-             * 1.0E-4   ===> 0.0001
-             */
-            // "." close to start, so using indexOf.
-            final int dotIndex = raw.indexOf((int)'.');
-            // "E" close to end, so using lastIndexOf.
-            final int eIndex = raw.lastIndexOf((int)'E');
-            // Negative.
-            final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1));
-            final int nbrOfZerosToAddAfterDot = (-powerOfTen-1);
-
-            final StringBuilder sb = new StringBuilder();
-            if (value < 0.0) {
-                sb.append("-0.");
-            } else {
-                sb.append("0.");
-            }
-            for (int i=0;i<nbrOfZerosToAddAfterDot;i++) {
-                sb.append('0');
-            }
-            // First raw digit.
-            sb.append(raw,dotIndex-1,dotIndex);
-            if ((eIndex == dotIndex + 2) && (raw.charAt(dotIndex+1) == '0')) {
-                // Char past dot is alone and '0': no need to add it.
-            } else {
-                // Raw digits that were past dot.
-                sb.append(raw,dotIndex+1,eIndex);
-            }
-            return sb.toString();
-        } else {
-            // abs in [0.001,1e7[.
-            if ((abs < 1.0) && (raw.charAt(raw.length()-1) == '0')) {
-                // Workaround for bug 4428022 (Double.toString(0.004) returns
-                // "0.0040", same with 0.001 etc.).
-                return raw.substring(0, raw.length()-1);
-            } else {
-                return raw;
-            }
-        }
-    }
-
-    //--------------------------------------------------------------------------
-    // PRIVATE METHODS
-    //--------------------------------------------------------------------------
-
-    private NumbersUtils() {
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * Had such isInXXX methods, and corresponding checkXXX methods,
-     * but they seem actually slower in practice, so just keeping this
-     * code here in case some day it becomes faster than regular isInXXX.
-     *
-     * Only works for non-empty ranges, i.e. such as min <= max.
-     * This treatment being designed for optimization, min <= max
-     * is not checked.
-     *
-     * @return True if the specified value is in the specified range (inclusive), false otherwise.
-     */
-    private static boolean dontUseMe_isInNonEmptyRange_(int min, int max, int a) {
-        // Using modulo arithmetic.
-        return (Integer.MIN_VALUE+(a-min) <= Integer.MIN_VALUE+(max-min));
-    }
-
-    /*
-     *
-     */
-
-    private static int minSignedIntForBitSize_noCheck(int bitSize) {
-        // i.e. (-1<<(bitSize-1))
-        return (Integer.MIN_VALUE>>(32-bitSize));
-    }
-
-    private static long minSignedLongForBitSize_noCheck(int bitSize) {
-        // i.e. (-1L<<(bitSize-1))
-        return (Long.MIN_VALUE>>(64-bitSize));
-    }
-
-    private static int maxSignedIntForBitSize_noCheck(int bitSize) {
-        // i.e. (1<<(bitSize-1))-1
-        return (Integer.MAX_VALUE>>(32-bitSize));
-    }
-
-    private static long maxSignedLongForBitSize_noCheck(int bitSize) {
-        // i.e. (1L<<(bitSize-1))-1
-        return (Long.MAX_VALUE>>(64-bitSize));
-    }
-
-    /*
-     *
-     */
-
-    /**
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    private static int computeNbrOfDigits_negValue(int negValue, int radix) {
-        checkRadix(radix);
-        final int maxNbrOfDigits = MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix];
-        int p = radix;
-        for (int i=1;i<maxNbrOfDigits;i++) {
-            if (negValue > -p) {
-                return i;
-            }
-            p *= radix;
-        }
-        return maxNbrOfDigits;
-    }
-
-    /**
-     * @throws IllegalArgumentException if the specified radix is out of range.
-     */
-    private static int computeNbrOfDigits_negValue(long negValue, int radix) {
-        checkRadix(radix);
-        final int maxNbrOfDigits = MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix];
-        long p = radix;
-        for (int i=1;i<maxNbrOfDigits;i++) {
-            if (negValue > -p) {
-                return i;
-            }
-            p *= radix;
-        }
-        return maxNbrOfDigits;
-    }
-
-    /*
-     *
-     */
-
-    private static boolean checkBitPositions(int firstBitPos, int lastBitPosExcl, int bitSize) {
-        if ((firstBitPos < 0) || (firstBitPos > lastBitPosExcl) || (lastBitPosExcl > bitSize)) {
-            throw new IllegalArgumentException(
-                    "bit positions (first="+firstBitPos+",lastExcl="+lastBitPosExcl
-                    +") must verify 0 <= first <= lastExcl <= "+bitSize);
-        }
-        return true;
-    }
-
-    /**
-     * Common method for byte, short and int.
-     * Could be a bit faster to have specific methods for byte and short,
-     * but not much, and that would also make more messy (byte-)code.
-     *
-     * @param bitSize Must be in [0,32].
-     */
-    private static String toStringBits_0_32_bitPosAlreadyChecked(
-            int bitSize,
-            int bits,
-            int firstBitPos,
-            int lastBitPosExcl,
-            boolean bigEndian,
-            boolean padding) {
-        final int bitSizeM1 = bitSize-1;
-        final int lastBitPos = lastBitPosExcl-1;
-        if (padding) {
-            final int nbrOfChars = bitSize;
-            final char[] chars = new char[nbrOfChars];
-            int bitIndex = bitSizeM1;
-            if (bigEndian) {
-                final int firstBitIndex = bitSizeM1-lastBitPos;
-                final int lastBitIndex = bitSizeM1-firstBitPos;
-                while (bitIndex > lastBitIndex) {
-                    chars[bitSizeM1-bitIndex] = '_';
-                    --bitIndex;
-                }
-                while (bitIndex >= firstBitIndex) {
-                    chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-                while (bitIndex >= 0) {
-                    chars[bitSizeM1-bitIndex] = '_';
-                    --bitIndex;
-                }
-            } else {
-                while (bitIndex > lastBitPos) {
-                    chars[bitIndex] = '_';
-                    --bitIndex;
-                }
-                while (bitIndex >= firstBitPos) {
-                    chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-                while (bitIndex >= 0) {
-                    chars[bitIndex] = '_';
-                    --bitIndex;
-                }
-            }
-            return new String(chars);
-        } else {
-            final int nbrOfChars = (lastBitPosExcl - firstBitPos);
-            final char[] chars = new char[nbrOfChars];
-            if (bigEndian) {
-                final int firstBitIndex = bitSizeM1-lastBitPos;
-                final int lastBitIndex = bitSizeM1-firstBitPos;
-                int bitIndex = lastBitIndex;
-                while (bitIndex >= firstBitIndex) {
-                    chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-            } else {
-                int bitIndex = lastBitPos;
-                while (bitIndex >= firstBitPos) {
-                    chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1));
-                    --bitIndex;
-                }
-            }
-            return new String(chars);
-        }
-    }
-}
diff --git a/src/main/java/net/jafama/README.txt b/src/main/java/net/jafama/README.txt
deleted file mode 100644
index 287b6e8..0000000
--- a/src/main/java/net/jafama/README.txt
+++ /dev/null
@@ -1,290 +0,0 @@
-################################################################################
-Jafama 2.2, 2015/12/13
-
-Changes since version 2.1:
-
-- Using a more standard layout for files.
-- Renamed AbstractFastMath into CmnFastMath, and factored non floating point
-  methods common to FastMath and StrictFastMath here, as well as E and PI
-  constants.
-  Also factored out corresponding tests in a new test class.
-  As a result, abs(int) and abs(long) now delegate to Math (and not StrictMath,
-  which usually delegates to Math for non floating point methods) when EITHER
-  FastMath or StrictFastMath delegation options are activated.
-- (Strict)FastMath:
-  - Look-up tables are now (by default) lazily initialized, per method type
-    (i.e. calling sin(double) won't initialize look-up tables for exp(double)).
-    As a result, methods of (Strict)FastMath that don't use tables can now
-    safely be called from code that don't want to trigger tables initialization.
-  - Added a static initTables() method, which ensures their initialization to
-    avoid related slow-down later at runtime (one call from either FastMath or
-    StrictFastMath inits tables for both classes).
-  - Reordered "ifs" in nextAfter(float,double) and nextAfter(double,double),
-    for faster worse case (as I should already have done when submitting
-    JDK-8032016).
-  - In powFast(double,int), reworked "ifs" to reduce the amount of code
-    while preserving special cases for small powers. Is now also a tad faster.
-  - Added methods:
-    - From JDK-8023217:
-      - multiplyExact(long,int)
-      - floorDiv(long,int)
-      - floorMod(long,int)
-    - From JDK-5100935:
-      - multiplyFull(int,int)
-      - multiplyHigh(long,long)
-    - That were not added previously, for Math versions being preferable due to
-      eventual JVM intrinsics, but that I finally decided to add for the sake of
-      completeness:
-      - incrementExact(int)
-      - incrementExact(long)
-      - decrementExact(int)
-      - decrementExact(long)
-      - negateExact(int)
-      - negateExact(long)
-    - And finally, since we mirror each xxxExact method with a xxxBounded
-      version:
-      - multiplyBounded(long,int)
-      - incrementBounded(int)
-      - incrementBounded(long)
-      - decrementBounded(int)
-      - decrementBounded(long)
-      - negateBounded(int)
-      - negateBounded(long)
-- NumbersUtils:
-  - Added methods:
-    - From (Strict)FastMath, so that they can be used to depart +0.0(f)
-      from -0.0(f) (which is a quite low-level need) without having to depend
-      on (Strict)FastMath:
-      - signFromBit(float)
-      - signFromBit(double)
-    - Following request from P. Wendykier:
-      - twoPowAsIntExact(int)
-      - twoPowAsIntBounded(int)
-      - twoPowAsLongExact(int)
-      - twoPowAsLongBounded(int)
-
-################################################################################
-Jafama 2.1, 2014/04/30
-
-Changes since version 2.0:
-
-- FastMath:
-  - Added hypot(double,double,double), which computes sqrt(x^2+y^2+z^2) without
-    intermediate overflow or underflow.
-  - Made reduction more accurate for angles of large magnitudes, by using
-    reduction in [-PI/4,PI/4] and quadrant information stored in two useless
-    bits of exponent, rather than reduction in [-PI,PI].
-  - Added use of TWOPI_LO and TWOPI_HI in some normalization methods, to make
-    them more accurate for results near quadrants limits.
-  - Corrected the spec about range of reliable accuracy for sinQuick(double).
-  - Little optimization for large angles reduction for tan(double).
-  - For round(float) and round(double), now using algorithm by Dmitry Nadezhin
-    (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html).
-  - scalb(double) is now faster for non-huge values of scale factor
-    (the same optimization, didn't seem to be worth it for float case).
-
-- Added StrictFastMath, a strict version of FastMath (and not a fast version of
-  StrictMath!), and related versions of FastMath properties:
-  - jafama.strict.usejdk,
-  - jafama.strict.fastlog,
-  - jafama.strict.fastsqrt.
-  Note that now, to avoid look-up tables and their initialization overhead,
-  you must set both jafama.usejdk (which only applies to FastMath) and
-  jafama.strict.usejdk to true.
-
-- NumbersUtils:
-  - Added DOUBLE_MIN_NORMAL and FLOAT_MIN_NORMAL constants (Double.MIN_NORMAL
-    and Float.MIN_NORMAL being available only from Java 6).
-  - Added methods:
-    - absNeg(int) (returns -abs(value), exact even for Integer.MIN_VALUE)
-    - absNeg(long) (returns -abs(value), exact even for Long.MIN_VALUE)
-    - pow2_strict(float)
-    - pow2_strict(double)
-    - pow3_strict(float)
-    - pow3_strict(double)
-    - plus2PI(double)
-    - plus2PI_strict(double)
-    - minus2PI(double)
-    - minus2PI_strict(double)
-    - plusPI(double)
-    - plusPI_strict(double)
-    - minusPI(double)
-    - minusPI_strict(double)
-    - plusPIO2(double)
-    - plusPIO2_strict(double)
-    - minusPIO2(double)
-    - minusPIO2_strict(double)
-    - toStringCSN(double) (to get "1.5E1" or "1.0E-1", instead of "15.0" or
-      "0.1")
-    - toStringNoCSN(double) (to get "123456789.0" or "0.0001", instead of
-      "1.23456789E8" or "1.0E-4")
-  - Optimized floorPowerOfTwo(int) and ceilingPowerOfTwo(int).
-
-################################################################################
-Jafama 2.0, 2013/02/20
-
-Changes since version 1.2:
-- Changed license from GNU LGPL V3 to Apache V2.
-- Is now Java 5 compatible (but some unit tests require Java 6).
-- Renamed package from odk.lang into net.jafama.
-- Renamed properties from odk.fastmath.xxx into jafama.xxx.
-- To match upcoming JDK8's new Math methods, renamed:
-  - toIntSafe into toIntExact (and asInt in NumbersUtils)
-  - plusNoModulo into addBounded (and plusBounded in NumbersUtils)
-  - plusNoModuloSafe into addExact (and plusExact in NumbersUtils)
-  - minusNoModulo into subtractBounded (and minusBounded in NumbersUtils)
-  - minusNoModuloSafe into subtractExact (and minusExact in NumbersUtils)
-  - timesNoModulo into multiplyBounded (and timesBounded in NumbersUtils)
-  - timesNoModuloSafe into multiplyExact (and timesExact in NumbersUtils)
-- Corrected Javadoc (accuracy claims) for some xxxQuick methods.
-- Removed usage of strictfp, which can cause an overhead.
-  As a result, behavior might change depending on architecture,
-  as well as dynamically due to JIT (ex.: pow2(double)).
-- Minor typos, refactorings and doc enhancements.
-- NumbersUtils:
-  - Renamed mask methods, such as leftBit0LongMask into longMaskMSBits0.
-  - Added methods:
-    - isMathematicalInteger(float)
-    - isMathematicalInteger(double)
-    - isEquidistant(float)
-    - isEquidistant(double)
-    - isNaNOrInfinite(float)
-  - Upgraded some implementations.
-- FastMath:
-  - In spec., added warning about possible FastMath slowness and
-    initialization overhead.
-  - FastMath now only depends on Java 5, and therefore, when delegating to
-    Math, only does so for Math methods that exist in Java 5 and have the
-    same semantics (except for accuracy).
-  - Modified some treatments for JVM crash after JIT-optimization to
-    (hopefully) not occur (crashes observed with Java 6u29, with which
-    workarounds were tested).
-  - Removed usage of StrictMath, and related property, which is allowed due
-    to spec. relaxation by strictfp removal, to make things both simpler,
-    and possibly faster.
-    As a result, for FastMath.log(double), we now use Math.log(double) by
-    default instead of our redefined method, which was used before due to
-    StrictMath.log(double) being possibly very slow. This makes log10(double),
-    log1p(double), logQuick(double), pow(double,double) and
-    powQuick(double,double) more accurate.
-  - Changed sinAndCos(double,DoubleWrapper,DoubleWrapper) into
-    sinAndCos(double,DoubleWrapper), and
-    sinhAndCosh(double,DoubleWrapper,DoubleWrapper) into
-    sinhAndCosh(double,DoubleWrapper).
-  - exp(double) is now more accurate (removed special handling for subnormals,
-    which is useless with proper multiplications order).
-    This makes hyperbolic trigonometry functions, expm1(double),
-    pow(double,double) and powQuick(double,double) more accurate.
-  - round(float) and round(double) now no longer follow Math class, which
-    spec. and behavior changed over time, but just round-up properly.
-  - For asinInRange and acosInRange, replaced < and > with <= and >=,
-    for quick return in case input is a bound.
-  - Removed internal usage of look-up table to compute double powers of two,
-    for it doesn't speed things up much, and to avoid possible cache-misses
-    for methods that are now table-free.
-  - Added methods:
-    - coshm1(double)
-    - asinh(double)
-    - acosh(double)
-    - acosh1p(double)
-    - atanh(double)
-    - log10(double)
-    - sqrtQuick(double)
-    - invSqrtQuick(double)
-    - roundEven(float)
-    - roundEven(double)
-    - rint(float)
-    - rint(double)
-    - abs(long)
-    - floorDiv(int,int)
-    - floorDiv(long,long)
-    - floorMod(int,int)
-    - floorMod(long,long)
-    - isNaNOrInfinite(float)
-    - signum(float)
-    - signum(double)
-    - signFromBit(float)
-    - signFromBit(double)
-    - copySign(float,float)
-    - copySign(double,double)
-    - ulp(float)
-    - ulp(double)
-    - nextAfter(float,double)
-    - nextAfter(double,double)
-    - nextDown(float)
-    - nextDown(double)
-    - nextUp(float)
-    - nextUp(double)
-    - scalb(float,int)
-    - scalb(double,int)
-  - Separated accuracy/correctness tests from benches, and
-    improved/simplified both.
-
-################################################################################
-Jafama 1.2, 2011/03/19
-
-Changes since version 1.1:
-- Now using StrictMath to compute constants and look-up tables, to ensure
-  consistency across various architectures.
-- Now using Math.abs(double) directly instead of FastMath.abs(double), since
-  this method is not redefined.
-- Added PI_SUP constant, the closest upper approximation of Pi as double,
-  especially useful to define a span that covers full angular range
-  (2*Math.PI doesn't).
-- Added log2(long), log2(int).
-- Added odk.fastmath.strict, odk.fastmath.usejdk, odk.fastmath.fastlog and
-  odk.fastmath.fastsqrt properties. See FastMath Javadoc for details.
-  NB: As a consequence, by default, a redefined log(double) is now used instead
-      of Math.log(double), for non-redefined treatments now use StrictMath by
-      default, and StrictMath.log(double) seems usually slow.
-- Simplified toString() implementation for IntWrapper and DoubleWrapper classes.
-- Completed Javadoc and updated tests for FastMath.remainder(double,double)
-  method, which does not behave as Math.IEEEremainder(double,double).
-- Moved some basic numbers related treatments, into a new class (NumbersUtils),
-  since they are very low-level and can be used in many places where a
-  dependency to the heavy (look-up tables) FastMath class could be considered
-  inappropriate.
-  These treatments are still available from FastMath class.
-- In benches, made sure dummy variables are used, to avoid treatments to be
-  optimized away (has not been observed, but might have been with some JVMs).
-
-################################################################################
-Jafama 1.1, 2009/12/05
-
-Changes since version 1.0:
-- for asin pow tabs, use of powFast(double,int) instead of pow(double,double),
-- added expQuick(double), logQuick(double), powQuick(double),
-- changed random numbers computation for tests.
-
-################################################################################
-Jafama 1.0, 2009/07/25
-
-- Placed under the GNU Lesser General Public License, version 3.
-
-- Requires Java 1.6 or later.
-
-- src folder contains the code.
-
-- test folder contains some tests (some of which require JUnit).
-
-- The odk.lang package is due to this code being a core part of ODK
-  library (Optimized Development Kit, of which only this code is
-  open source).
-
-- Copy/paste of FastMath class comments:
-
- * Class providing math treatments that:
- * - are meant to be faster than those of java.lang.Math class (depending on
- *   JVM or JVM options, they might be slower),
- * - are still somehow accurate and robust (handling of NaN and such),
- * - do not (or not directly) generate objects at run time (no "new").
- * 
- * Other than optimized treatments, a valuable feature of this class is the
- * presence of angles normalization methods, derived from those used in
- * java.lang.Math (for which, sadly, no API is provided, letting everyone
- * with the terrible responsibility to write their own ones).
- * 
- * Non-redefined methods of java.lang.Math class are also available,
- * for easy replacement.
- 
-################################################################################
diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheByte.java b/src/main/java/sun/java2d/marlin/ArrayCacheByte.java
index 9aab566..6f4eb7e 100644
--- a/src/main/java/sun/java2d/marlin/ArrayCacheByte.java
+++ b/src/main/java/sun/java2d/marlin/ArrayCacheByte.java
@@ -28,11 +28,13 @@
 import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
 import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
 import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
+
 import static sun.java2d.marlin.MarlinConst.DO_STATS;
 import static sun.java2d.marlin.MarlinConst.DO_CHECKS;
 import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE;
+
 import static sun.java2d.marlin.MarlinUtils.logInfo;
 import static sun.java2d.marlin.MarlinUtils.logException;
 
@@ -48,9 +50,6 @@
  * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
  * files are generated with the following command lines:
  */
-// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java
-// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < ArrayCacheB\yte.java > ArrayCacheFloat.java
-// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < ArrayCacheB\yte.java > ArrayCacheDouble.java
 
 final class ArrayCacheByte {
 
diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java b/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java
index 10db7f7..5857ebf 100644
--- a/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java
+++ b/src/main/java/sun/java2d/marlin/ArrayCacheDouble.java
@@ -28,11 +28,13 @@
 import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
 import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
 import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
+
 import static sun.java2d.marlin.MarlinConst.DO_STATS;
 import static sun.java2d.marlin.MarlinConst.DO_CHECKS;
 import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE;
+
 import static sun.java2d.marlin.MarlinUtils.logInfo;
 import static sun.java2d.marlin.MarlinUtils.logException;
 
@@ -48,9 +50,6 @@
  * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
  * files are generated with the following command lines:
  */
-// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java
-// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < ArrayCacheB\yte.java > ArrayCacheFloat.java
-// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < ArrayCacheB\yte.java > ArrayCacheDouble.java
 
 final class ArrayCacheDouble {
 
diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java b/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java
deleted file mode 100644
index 9fda05d..0000000
--- a/src/main/java/sun/java2d/marlin/ArrayCacheFloat.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
-import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
-import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
-import static sun.java2d.marlin.MarlinConst.DO_STATS;
-import static sun.java2d.marlin.MarlinConst.DO_CHECKS;
-import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY;
-import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY;
-import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE;
-import static sun.java2d.marlin.MarlinUtils.logInfo;
-import static sun.java2d.marlin.MarlinUtils.logException;
-
-import java.lang.ref.WeakReference;
-import java.util.Arrays;
-
-import sun.java2d.marlin.ArrayCacheConst.BucketStats;
-import sun.java2d.marlin.ArrayCacheConst.CacheStats;
-
-/*
- * Note that the ArrayCache[BYTE/INT/FLOAT/DOUBLE] files are nearly identical except
- * for a few type and name differences. Typically, the [BYTE]ArrayCache.java file
- * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
- * files are generated with the following command lines:
- */
-// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java
-// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < ArrayCacheB\yte.java > ArrayCacheFloat.java
-// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < ArrayCacheB\yte.java > ArrayCacheDouble.java
-
-final class ArrayCacheFloat {
-
-    /* members */
-    private final int bucketCapacity;
-    private WeakReference<Bucket[]> refBuckets = null;
-    final CacheStats stats;
-
-    ArrayCacheFloat(final int bucketCapacity) {
-        this.bucketCapacity = bucketCapacity;
-        this.stats = (DO_STATS) ?
-            new CacheStats("ArrayCacheFloat(Dirty)") : null;
-    }
-
-    Bucket getCacheBucket(final int length) {
-        final int bucket = ArrayCacheConst.getBucket(length);
-        return getBuckets()[bucket];
-    }
-
-    private Bucket[] getBuckets() {
-        // resolve reference:
-        Bucket[] buckets = (refBuckets != null) ? refBuckets.get() : null;
-
-        // create a new buckets ?
-        if (buckets == null) {
-            buckets = new Bucket[BUCKETS];
-
-            for (int i = 0; i < BUCKETS; i++) {
-                buckets[i] = new Bucket(ARRAY_SIZES[i], bucketCapacity,
-                        (DO_STATS) ? stats.bucketStats[i] : null);
-            }
-
-            // update weak reference:
-            refBuckets = new WeakReference<Bucket[]>(buckets);
-        }
-        return buckets;
-    }
-
-    Reference createRef(final int initialSize) {
-        return new Reference(this, initialSize);
-    }
-
-    static final class Reference {
-
-        // initial array reference (direct access)
-        final float[] initial;
-        private final ArrayCacheFloat cache;
-
-        Reference(final ArrayCacheFloat cache, final int initialSize) {
-            this.cache = cache;
-            this.initial = createArray(initialSize);
-            if (DO_STATS) {
-                cache.stats.totalInitial += initialSize;
-            }
-        }
-
-        float[] getArray(final int length) {
-            if (length <= MAX_ARRAY_SIZE) {
-                return cache.getCacheBucket(length).getArray();
-            }
-            if (DO_STATS) {
-                cache.stats.oversize++;
-            }
-            if (DO_LOG_OVERSIZE) {
-                logInfo("ArrayCacheFloat(Dirty): "
-                        + "getArray[oversize]: length=\t" + length);
-            }
-            return createArray(length);
-        }
-
-        float[] widenArray(final float[] array, final int usedSize,
-                          final int needSize)
-        {
-            final int length = array.length;
-            if (DO_CHECKS && length >= needSize) {
-                return array;
-            }
-            if (DO_STATS) {
-                cache.stats.resize++;
-            }
-
-            // maybe change bucket:
-            // ensure getNewSize() > newSize:
-            final float[] res = getArray(ArrayCacheConst.getNewSize(usedSize, needSize));
-
-            // use wrapper to ensure proper copy:
-            System.arraycopy(array, 0, res, 0, usedSize); // copy only used elements
-
-            // maybe return current array:
-            putArray(array, 0, usedSize); // ensure array is cleared
-
-            if (DO_LOG_WIDEN_ARRAY) {
-                logInfo("ArrayCacheFloat(Dirty): "
-                        + "widenArray[" + res.length
-                        + "]: usedSize=\t" + usedSize + "\tlength=\t" + length
-                        + "\tneeded length=\t" + needSize);
-            }
-            return res;
-        }
-
-        boolean doCleanRef(final float[] array) {
-            return DO_CLEAN_DIRTY || (array != initial);
-        }
-
-        float[] putArray(final float[] array)
-        {
-            // dirty array helper:
-            return putArray(array, 0, array.length);
-        }
-
-        float[] putArray(final float[] array, final int fromIndex,
-                        final int toIndex)
-        {
-            if (array.length <= MAX_ARRAY_SIZE) {
-                if (DO_CLEAN_DIRTY && (toIndex != 0)) {
-                    // clean-up array of dirty part[fromIndex; toIndex[
-                    fill(array, fromIndex, toIndex, 0.0f);
-                }
-                // ensure to never store initial arrays in cache:
-                if (array != initial) {
-                    cache.getCacheBucket(array.length).putArray(array);
-                }
-            }
-            return initial;
-        }
-    }
-
-    static final class Bucket {
-
-        private int tail = 0;
-        private final int arraySize;
-        private final float[][] arrays;
-        private final BucketStats stats;
-
-        Bucket(final int arraySize,
-               final int capacity, final BucketStats stats)
-        {
-            this.arraySize = arraySize;
-            this.stats = stats;
-            this.arrays = new float[capacity][];
-        }
-
-        float[] getArray() {
-            if (DO_STATS) {
-                stats.getOp++;
-            }
-            // use cache:
-            if (tail != 0) {
-                final float[] array = arrays[--tail];
-                arrays[tail] = null;
-                return array;
-            }
-            if (DO_STATS) {
-                stats.createOp++;
-            }
-            return createArray(arraySize);
-        }
-
-        void putArray(final float[] array)
-        {
-            if (DO_CHECKS && (array.length != arraySize)) {
-                logInfo("ArrayCacheFloat(Dirty): "
-                        + "bad length = " + array.length);
-                return;
-            }
-            if (DO_STATS) {
-                stats.returnOp++;
-            }
-            // fill cache:
-            if (arrays.length > tail) {
-                arrays[tail++] = array;
-
-                if (DO_STATS) {
-                    stats.updateMaxSize(tail);
-                }
-            } else if (DO_CHECKS) {
-                logInfo("ArrayCacheFloat(Dirty): "
-                        + "array capacity exceeded !");
-            }
-        }
-    }
-
-    static float[] createArray(final int length) {
-        return new float[length];
-    }
-
-    static void fill(final float[] array, final int fromIndex,
-                     final int toIndex, final float value)
-    {
-        // clear array data:
-        Arrays.fill(array, fromIndex, toIndex, value);
-        if (DO_CHECKS) {
-            check(array, fromIndex, toIndex, value);
-        }
-    }
-
-    static void check(final float[] array, final int fromIndex,
-                      final int toIndex, final float value)
-    {
-        if (DO_CHECKS) {
-            // check zero on full array:
-            for (int i = 0; i < array.length; i++) {
-                if (array[i] != value) {
-                    logException("Invalid value at: " + i + " = " + array[i]
-                            + " from: " + fromIndex + " to: " + toIndex + "\n"
-                            + Arrays.toString(array), new Throwable());
-
-                    // ensure array is correctly filled:
-                    Arrays.fill(array, value);
-
-                    return;
-                }
-            }
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheInt.java b/src/main/java/sun/java2d/marlin/ArrayCacheInt.java
index 299f5c6..a4dc2d2 100644
--- a/src/main/java/sun/java2d/marlin/ArrayCacheInt.java
+++ b/src/main/java/sun/java2d/marlin/ArrayCacheInt.java
@@ -28,11 +28,13 @@
 import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
 import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
 import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
+
 import static sun.java2d.marlin.MarlinConst.DO_STATS;
 import static sun.java2d.marlin.MarlinConst.DO_CHECKS;
 import static sun.java2d.marlin.MarlinConst.DO_CLEAN_DIRTY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE;
+
 import static sun.java2d.marlin.MarlinUtils.logInfo;
 import static sun.java2d.marlin.MarlinUtils.logException;
 
@@ -48,9 +50,6 @@
  * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
  * files are generated with the following command lines:
  */
-// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java
-// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < ArrayCacheB\yte.java > ArrayCacheFloat.java
-// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < ArrayCacheB\yte.java > ArrayCacheDouble.java
 
 final class ArrayCacheInt {
 
diff --git a/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java b/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java
index f41c858..00d0bda 100644
--- a/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java
+++ b/src/main/java/sun/java2d/marlin/ArrayCacheIntClean.java
@@ -28,10 +28,12 @@
 import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
 import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
 import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
+
 import static sun.java2d.marlin.MarlinConst.DO_STATS;
 import static sun.java2d.marlin.MarlinConst.DO_CHECKS;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_WIDEN_ARRAY;
 import static sun.java2d.marlin.MarlinConst.DO_LOG_OVERSIZE;
+
 import static sun.java2d.marlin.MarlinUtils.logInfo;
 import static sun.java2d.marlin.MarlinUtils.logException;
 
@@ -47,9 +49,6 @@
  * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
  * files are generated with the following command lines:
  */
-// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < ArrayCacheB\yte.java > ArrayCacheInt.java
-// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < ArrayCacheB\yte.java > ArrayCacheFloat.java
-// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < ArrayCacheB\yte.java > ArrayCacheDouble.java
 
 final class ArrayCacheIntClean {
 
diff --git a/src/main/java/sun/java2d/marlin/CollinearSimplifier.java b/src/main/java/sun/java2d/marlin/CollinearSimplifier.java
index 4d162f9..75bc8aa 100644
--- a/src/main/java/sun/java2d/marlin/CollinearSimplifier.java
+++ b/src/main/java/sun/java2d/marlin/CollinearSimplifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021, 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
@@ -25,28 +25,27 @@
 
 package sun.java2d.marlin;
 
-import sun.awt.geom.PathConsumer2D;
 
-final class CollinearSimplifier implements PathConsumer2D {
+final class CollinearSimplifier implements DPathConsumer2D {
 
     private static final int STATE_PREV_LINE = 0;
     private static final int STATE_PREV_POINT = 1;
     private static final int STATE_EMPTY = 2;
 
     // slope precision threshold
-    private static final float EPS = 1e-3f; // aaime proposed 1e-3f
+    private static final double EPS = 1e-3d; // aaime proposed 1e-3d
 
     // members:
-    private PathConsumer2D delegate;
+    private DPathConsumer2D delegate;
     private int state;
-    private float px1, py1;
-    private float pdx, pdy;
-    private float px2, py2;
+    private double px1, py1;
+    private double pdx, pdy;
+    private double px2, py2;
 
     CollinearSimplifier() {
     }
 
-    public CollinearSimplifier init(final PathConsumer2D delegate) {
+    public CollinearSimplifier init(final DPathConsumer2D delegate) {
         if (this.delegate != delegate) {
             this.delegate = delegate;
         }
@@ -75,8 +74,8 @@ public long getNativeConsumer() {
     }
 
     @Override
-    public void quadTo(final float x1, final float y1,
-                       final float xe, final float ye)
+    public void quadTo(final double x1, final double y1,
+                       final double xe, final double ye)
     {
         emitStashedLine();
         delegate.quadTo(x1, y1, xe, ye);
@@ -87,9 +86,9 @@ public void quadTo(final float x1, final float y1,
     }
 
     @Override
-    public void curveTo(final float x1, final float y1,
-                        final float x2, final float y2,
-                        final float xe, final float ye)
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double xe, final double ye)
     {
         emitStashedLine();
         delegate.curveTo(x1, y1, x2, y2, xe, ye);
@@ -100,7 +99,7 @@ public void curveTo(final float x1, final float y1,
     }
 
     @Override
-    public void moveTo(final float xe, final float ye) {
+    public void moveTo(final double xe, final double ye) {
         emitStashedLine();
         delegate.moveTo(xe, ye);
         state = STATE_PREV_POINT;
@@ -109,19 +108,19 @@ public void moveTo(final float xe, final float ye) {
     }
 
     @Override
-    public void lineTo(final float xe, final float ye) {
+    public void lineTo(final double xe, final double ye) {
         // most probable case first:
         if (state == STATE_PREV_LINE) {
             // test for collinearity
-            final float dx = (xe - px2);
-            final float dy = (ye - py2);
+            final double dx = (xe - px2);
+            final double dy = (ye - py2);
 
             // perf: avoid slope computation (fdiv) replaced by 3 fmul
-            if ((dy == 0.0f && pdy == 0.0f && (pdx * dx) >= 0.0f)
+            if ((dy == 0.0d && pdy == 0.0d && (pdx * dx) >= 0.0d)
 // uncertainty on slope:
 //                || (Math.abs(pdx * dy - pdy * dx) < EPS * Math.abs(pdy * dy))) {
 // try 0
-                || ((pdy * dy) != 0.0f && (pdx * dy - pdy * dx) == 0.0f)) {
+                || ((pdy * dy) != 0.0d && (pdx * dy - pdy * dx) == 0.0d)) {
                 // same horizontal orientation or same slope:
                 // TODO: store cumulated error on slope ?
                 // merge segments
diff --git a/src/main/java/sun/java2d/marlin/Curve.java b/src/main/java/sun/java2d/marlin/Curve.java
index c904f3e..2ce0cd4 100644
--- a/src/main/java/sun/java2d/marlin/Curve.java
+++ b/src/main/java/sun/java2d/marlin/Curve.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -27,13 +27,13 @@
 
 final class Curve {
 
-    float ax, ay, bx, by, cx, cy, dx, dy;
-    float dax, day, dbx, dby;
+    double ax, ay, bx, by, cx, cy, dx, dy;
+    double dax, day, dbx, dby;
 
     Curve() {
     }
 
-    void set(final float[] points, final int type) {
+    void set(final double[] points, final int type) {
         // if instead of switch (perf + most probable cases first)
         if (type == 8) {
             set(points[0], points[1],
@@ -50,15 +50,15 @@ void set(final float[] points, final int type) {
         }
     }
 
-    void set(final float x1, final float y1,
-             final float x2, final float y2,
-             final float x3, final float y3,
-             final float x4, final float y4)
+    void set(final double x1, final double y1,
+             final double x2, final double y2,
+             final double x3, final double y3,
+             final double x4, final double y4)
     {
-        final float dx32 = 3.0f * (x3 - x2);
-        final float dy32 = 3.0f * (y3 - y2);
-        final float dx21 = 3.0f * (x2 - x1);
-        final float dy21 = 3.0f * (y2 - y1);
+        final double dx32 = 3.0d * (x3 - x2);
+        final double dy32 = 3.0d * (y3 - y2);
+        final double dx21 = 3.0d * (x2 - x1);
+        final double dy21 = 3.0d * (y2 - y1);
         ax = (x4 - x1) - dx32;  // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2)
         ay = (y4 - y1) - dy32;
         bx = (dx32 - dx21);     // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1
@@ -67,95 +67,95 @@ void set(final float x1, final float y1,
         cy = dy21;
         dx = x1;                // D = P0
         dy = y1;
-        dax = 3.0f * ax;
-        day = 3.0f * ay;
-        dbx = 2.0f * bx;
-        dby = 2.0f * by;
+        dax = 3.0d * ax;
+        day = 3.0d * ay;
+        dbx = 2.0d * bx;
+        dby = 2.0d * by;
     }
 
-    void set(final float x1, final float y1,
-             final float x2, final float y2,
-             final float x3, final float y3)
+    void set(final double x1, final double y1,
+             final double x2, final double y2,
+             final double x3, final double y3)
     {
-        final float dx21 = (x2 - x1);
-        final float dy21 = (y2 - y1);
-        ax = 0.0f;              // A = 0
-        ay = 0.0f;
+        final double dx21 = (x2 - x1);
+        final double dy21 = (y2 - y1);
+        ax = 0.0d;              // A = 0
+        ay = 0.0d;
         bx = (x3 - x2) - dx21;  // B = P3 - P0 - 2 P2
         by = (y3 - y2) - dy21;
-        cx = 2.0f * dx21;       // C = 2 (P2 - P1)
-        cy = 2.0f * dy21;
+        cx = 2.0d * dx21;       // C = 2 (P2 - P1)
+        cy = 2.0d * dy21;
         dx = x1;                // D = P1
         dy = y1;
-        dax = 0.0f;
-        day = 0.0f;
-        dbx = 2.0f * bx;
-        dby = 2.0f * by;
+        dax = 0.0d;
+        day = 0.0d;
+        dbx = 2.0d * bx;
+        dby = 2.0d * by;
     }
 
-    void set(final float x1, final float y1,
-             final float x2, final float y2)
+    void set(final double x1, final double y1,
+             final double x2, final double y2)
     {
-        final float dx21 = (x2 - x1);
-        final float dy21 = (y2 - y1);
-        ax = 0.0f;              // A = 0
-        ay = 0.0f;
-        bx = 0.0f;              // B = 0
-        by = 0.0f;
+        final double dx21 = (x2 - x1);
+        final double dy21 = (y2 - y1);
+        ax = 0.0d;              // A = 0
+        ay = 0.0d;
+        bx = 0.0d;              // B = 0
+        by = 0.0d;
         cx = dx21;              // C = (P2 - P1)
         cy = dy21;
         dx = x1;                // D = P1
         dy = y1;
-        dax = 0.0f;
-        day = 0.0f;
-        dbx = 0.0f;
-        dby = 0.0f;
+        dax = 0.0d;
+        day = 0.0d;
+        dbx = 0.0d;
+        dby = 0.0d;
     }
 
-    int dxRoots(final float[] roots, final int off) {
+    int dxRoots(final double[] roots, final int off) {
         return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
     }
 
-    int dyRoots(final float[] roots, final int off) {
+    int dyRoots(final double[] roots, final int off) {
         return Helpers.quadraticRoots(day, dby, cy, roots, off);
     }
 
-    int infPoints(final float[] pts, final int off) {
+    int infPoints(final double[] pts, final int off) {
         // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
         // Fortunately, this turns out to be quadratic, so there are at
         // most 2 inflection points.
-        final float a = dax * dby - dbx * day;
-        final float b = 2.0f * (cy * dax - day * cx);
-        final float c = cy * dbx - cx * dby;
+        final double a = dax * dby - dbx * day;
+        final double b = 2.0d * (cy * dax - day * cx);
+        final double c = cy * dbx - cx * dby;
 
         return Helpers.quadraticRoots(a, b, c, pts, off);
     }
 
-    int xPoints(final float[] ts, final int off, final float x)
+    int xPoints(final double[] ts, final int off, final double x)
     {
-        return Helpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0f, 1.0f);
+        return Helpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0d, 1.0d);
     }
 
-    int yPoints(final float[] ts, final int off, final float y)
+    int yPoints(final double[] ts, final int off, final double y)
     {
-        return Helpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0f, 1.0f);
+        return Helpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0d, 1.0d);
     }
 
     // finds points where the first and second derivative are
     // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
     // * is a dot product). Unfortunately, we have to solve a cubic.
-    private int perpendiculardfddf(final float[] pts, final int off) {
+    private int perpendiculardfddf(final double[] pts, final int off) {
         assert pts.length >= off + 4;
 
         // these are the coefficients of some multiple of g(t) (not g(t),
         // because the roots of a polynomial are not changed after multiplication
         // by a constant, and this way we save a few multiplications).
-        final float a = 2.0f * (dax * dax + day * day);
-        final float b = 3.0f * (dax * dbx + day * dby);
-        final float c = 2.0f * (dax * cx  + day * cy) + dbx * dbx + dby * dby;
-        final float d = dbx * cx + dby * cy;
+        final double a = 2.0d * (dax * dax + day * day);
+        final double b = 3.0d * (dax * dbx + day * dby);
+        final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
+        final double d = dbx * cx + dby * cy;
 
-        return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0f, 1.0f);
+        return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
     }
 
     // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@@ -171,21 +171,21 @@ private int perpendiculardfddf(final float[] pts, final int off) {
     // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
     // points, so roc-w can have at least 6 roots. This shouldn't be a
     // problem for what we're trying to do (draw a nice looking curve).
-    int rootsOfROCMinusW(final float[] roots, final int off, final float w2, final float err) {
+    int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
         // no OOB exception, because by now off<=6, and roots.length >= 10
         assert off <= 6 && roots.length >= 10;
 
         int ret = off;
         final int end = off + perpendiculardfddf(roots, off);
-        roots[end] = 1.0f; // always check interval end points
+        roots[end] = 1.0d; // always check interval end points
 
-        float t0 = 0.0f, ft0 = ROCsq(t0) - w2;
+        double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
 
         for (int i = off; i <= end; i++) {
-            float t1 = roots[i], ft1 = ROCsq(t1) - w2;
-            if (ft0 == 0.0f) {
+            double t1 = roots[i], ft1 = ROCsq(t1) - w2;
+            if (ft0 == 0.0d) {
                 roots[ret++] = t0;
-            } else if (ft1 * ft0 < 0.0f) { // have opposite signs
+            } else if (ft1 * ft0 < 0.0d) { // have opposite signs
                 // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
                 // ROC(t) >= 0 for all t.
                 roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
@@ -197,9 +197,9 @@ int rootsOfROCMinusW(final float[] roots, final int off, final float w2, final f
         return ret - off;
     }
 
-    private static float eliminateInf(final float x) {
-        return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
-               (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
+    private static double eliminateInf(final double x) {
+        return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
+               (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
     }
 
     // A slight modification of the false position algorithm on wikipedia.
@@ -209,14 +209,14 @@ private static float eliminateInf(final float x) {
     // expressions make it into the language), depending on how closures
     // and turn out. Same goes for the newton's method
     // algorithm in Helpers.java
-    private float falsePositionROCsqMinusX(final float t0, final float t1,
-                                           final float w2, final float err)
+    private double falsePositionROCsqMinusX(final double t0, final double t1,
+                                            final double w2, final double err)
     {
         final int iterLimit = 100;
         int side = 0;
-        float t = t1, ft = eliminateInf(ROCsq(t) - w2);
-        float s = t0, fs = eliminateInf(ROCsq(s) - w2);
-        float r = s, fr;
+        double t = t1, ft = eliminateInf(ROCsq(t) - w2);
+        double s = t0, fs = eliminateInf(ROCsq(s) - w2);
+        double r = s, fr;
 
         for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
             r = (fs * t - ft * s) / (fs - ft);
@@ -229,7 +229,7 @@ private float falsePositionROCsqMinusX(final float t0, final float t1,
                 } else {
                     side = -1;
                 }
-            } else if (fr * fs > 0.0f) {
+            } else if (fr * fs > 0.0d) {
                 fs = fr; s = r;
                 if (side > 0) {
                     ft /= (1 << side);
@@ -244,21 +244,21 @@ private float falsePositionROCsqMinusX(final float t0, final float t1,
         return r;
     }
 
-    private static boolean sameSign(final float x, final float y) {
+    private static boolean sameSign(final double x, final double y) {
         // another way is to test if x*y > 0. This is bad for small x, y.
-        return (x < 0.0f && y < 0.0f) || (x > 0.0f && y > 0.0f);
+        return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d);
     }
 
     // returns the radius of curvature squared at t of this curve
     // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
-    private float ROCsq(final float t) {
-        final float dx = t * (t * dax + dbx) + cx;
-        final float dy = t * (t * day + dby) + cy;
-        final float ddx = 2.0f * dax * t + dbx;
-        final float ddy = 2.0f * day * t + dby;
-        final float dx2dy2 = dx * dx + dy * dy;
-        final float ddx2ddy2 = ddx * ddx + ddy * ddy;
-        final float ddxdxddydy = ddx * dx + ddy * dy;
+    private double ROCsq(final double t) {
+        final double dx = t * (t * dax + dbx) + cx;
+        final double dy = t * (t * day + dby) + cy;
+        final double ddx = 2.0d * dax * t + dbx;
+        final double ddy = 2.0d * day * t + dby;
+        final double dx2dy2 = dx * dx + dy * dy;
+        final double ddx2ddy2 = ddx * ddx + ddy * ddy;
+        final double ddxdxddydy = ddx * dx + ddy * dy;
         return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
     }
 }
diff --git a/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java b/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java
deleted file mode 100644
index 22b5c9e..0000000
--- a/src/main/java/sun/java2d/marlin/DCollinearSimplifier.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-
-final class DCollinearSimplifier implements DPathConsumer2D {
-
-    private static final int STATE_PREV_LINE = 0;
-    private static final int STATE_PREV_POINT = 1;
-    private static final int STATE_EMPTY = 2;
-
-    // slope precision threshold
-    private static final double EPS = 1e-3d; // aaime proposed 1e-3d
-
-    // members:
-    private DPathConsumer2D delegate;
-    private int state;
-    private double px1, py1;
-    private double pdx, pdy;
-    private double px2, py2;
-
-    DCollinearSimplifier() {
-    }
-
-    public DCollinearSimplifier init(final DPathConsumer2D delegate) {
-        if (this.delegate != delegate) {
-            this.delegate = delegate;
-        }
-        this.state = STATE_EMPTY;
-
-        return this; // fluent API
-    }
-
-    @Override
-    public void pathDone() {
-        emitStashedLine();
-        delegate.pathDone();
-        state = STATE_EMPTY;
-    }
-
-    @Override
-    public void closePath() {
-        emitStashedLine();
-        delegate.closePath();
-        state = STATE_EMPTY;
-    }
-
-    @Override
-    public long getNativeConsumer() {
-        return 0;
-    }
-
-    @Override
-    public void quadTo(final double x1, final double y1,
-                       final double xe, final double ye)
-    {
-        emitStashedLine();
-        delegate.quadTo(x1, y1, xe, ye);
-        // final end point:
-        state = STATE_PREV_POINT;
-        px1 = xe;
-        py1 = ye;
-    }
-
-    @Override
-    public void curveTo(final double x1, final double y1,
-                        final double x2, final double y2,
-                        final double xe, final double ye)
-    {
-        emitStashedLine();
-        delegate.curveTo(x1, y1, x2, y2, xe, ye);
-        // final end point:
-        state = STATE_PREV_POINT;
-        px1 = xe;
-        py1 = ye;
-    }
-
-    @Override
-    public void moveTo(final double xe, final double ye) {
-        emitStashedLine();
-        delegate.moveTo(xe, ye);
-        state = STATE_PREV_POINT;
-        px1 = xe;
-        py1 = ye;
-    }
-
-    @Override
-    public void lineTo(final double xe, final double ye) {
-        // most probable case first:
-        if (state == STATE_PREV_LINE) {
-            // test for collinearity
-            final double dx = (xe - px2);
-            final double dy = (ye - py2);
-
-            // perf: avoid slope computation (fdiv) replaced by 3 fmul
-            if ((dy == 0.0d && pdy == 0.0d && (pdx * dx) >= 0.0d)
-// uncertainty on slope:
-//                || (Math.abs(pdx * dy - pdy * dx) < EPS * Math.abs(pdy * dy))) {
-// try 0
-                || ((pdy * dy) != 0.0d && (pdx * dy - pdy * dx) == 0.0d)) {
-                // same horizontal orientation or same slope:
-                // TODO: store cumulated error on slope ?
-                // merge segments
-                px2 = xe;
-                py2 = ye;
-            } else {
-                // emit previous segment
-                delegate.lineTo(px2, py2);
-                px1 = px2;
-                py1 = py2;
-                pdx = dx;
-                pdy = dy;
-                px2 = xe;
-                py2 = ye;
-            }
-        } else if (state == STATE_PREV_POINT) {
-            state = STATE_PREV_LINE;
-            pdx = (xe - px1);
-            pdy = (ye - py1);
-            px2 = xe;
-            py2 = ye;
-        } else if (state == STATE_EMPTY) {
-            delegate.lineTo(xe, ye);
-            state = STATE_PREV_POINT;
-            px1 = xe;
-            py1 = ye;
-        }
-    }
-
-    private void emitStashedLine() {
-        if (state == STATE_PREV_LINE) {
-            delegate.lineTo(px2, py2);
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DCurve.java b/src/main/java/sun/java2d/marlin/DCurve.java
deleted file mode 100644
index 7e06031..0000000
--- a/src/main/java/sun/java2d/marlin/DCurve.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-final class DCurve {
-
-    double ax, ay, bx, by, cx, cy, dx, dy;
-    double dax, day, dbx, dby;
-
-    DCurve() {
-    }
-
-    void set(final double[] points, final int type) {
-        // if instead of switch (perf + most probable cases first)
-        if (type == 8) {
-            set(points[0], points[1],
-                points[2], points[3],
-                points[4], points[5],
-                points[6], points[7]);
-        } else if (type == 4) {
-            set(points[0], points[1],
-                points[2], points[3]);
-        } else {
-            set(points[0], points[1],
-                points[2], points[3],
-                points[4], points[5]);
-        }
-    }
-
-    void set(final double x1, final double y1,
-             final double x2, final double y2,
-             final double x3, final double y3,
-             final double x4, final double y4)
-    {
-        final double dx32 = 3.0d * (x3 - x2);
-        final double dy32 = 3.0d * (y3 - y2);
-        final double dx21 = 3.0d * (x2 - x1);
-        final double dy21 = 3.0d * (y2 - y1);
-        ax = (x4 - x1) - dx32;  // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2)
-        ay = (y4 - y1) - dy32;
-        bx = (dx32 - dx21);     // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1
-        by = (dy32 - dy21);
-        cx = dx21;              // C = 3 (P1 - P0)
-        cy = dy21;
-        dx = x1;                // D = P0
-        dy = y1;
-        dax = 3.0d * ax;
-        day = 3.0d * ay;
-        dbx = 2.0d * bx;
-        dby = 2.0d * by;
-    }
-
-    void set(final double x1, final double y1,
-             final double x2, final double y2,
-             final double x3, final double y3)
-    {
-        final double dx21 = (x2 - x1);
-        final double dy21 = (y2 - y1);
-        ax = 0.0d;              // A = 0
-        ay = 0.0d;
-        bx = (x3 - x2) - dx21;  // B = P3 - P0 - 2 P2
-        by = (y3 - y2) - dy21;
-        cx = 2.0d * dx21;       // C = 2 (P2 - P1)
-        cy = 2.0d * dy21;
-        dx = x1;                // D = P1
-        dy = y1;
-        dax = 0.0d;
-        day = 0.0d;
-        dbx = 2.0d * bx;
-        dby = 2.0d * by;
-    }
-
-    void set(final double x1, final double y1,
-             final double x2, final double y2)
-    {
-        final double dx21 = (x2 - x1);
-        final double dy21 = (y2 - y1);
-        ax = 0.0d;              // A = 0
-        ay = 0.0d;
-        bx = 0.0d;              // B = 0
-        by = 0.0d;
-        cx = dx21;              // C = (P2 - P1)
-        cy = dy21;
-        dx = x1;                // D = P1
-        dy = y1;
-        dax = 0.0d;
-        day = 0.0d;
-        dbx = 0.0d;
-        dby = 0.0d;
-    }
-
-    int dxRoots(final double[] roots, final int off) {
-        return DHelpers.quadraticRoots(dax, dbx, cx, roots, off);
-    }
-
-    int dyRoots(final double[] roots, final int off) {
-        return DHelpers.quadraticRoots(day, dby, cy, roots, off);
-    }
-
-    int infPoints(final double[] pts, final int off) {
-        // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
-        // Fortunately, this turns out to be quadratic, so there are at
-        // most 2 inflection points.
-        final double a = dax * dby - dbx * day;
-        final double b = 2.0d * (cy * dax - day * cx);
-        final double c = cy * dbx - cx * dby;
-
-        return DHelpers.quadraticRoots(a, b, c, pts, off);
-    }
-
-    int xPoints(final double[] ts, final int off, final double x)
-    {
-        return DHelpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0d, 1.0d);
-    }
-
-    int yPoints(final double[] ts, final int off, final double y)
-    {
-        return DHelpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0d, 1.0d);
-    }
-
-    // finds points where the first and second derivative are
-    // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
-    // * is a dot product). Unfortunately, we have to solve a cubic.
-    private int perpendiculardfddf(final double[] pts, final int off) {
-        assert pts.length >= off + 4;
-
-        // these are the coefficients of some multiple of g(t) (not g(t),
-        // because the roots of a polynomial are not changed after multiplication
-        // by a constant, and this way we save a few multiplications).
-        final double a = 2.0d * (dax * dax + day * day);
-        final double b = 3.0d * (dax * dbx + day * dby);
-        final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
-        final double d = dbx * cx + dby * cy;
-
-        return DHelpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
-    }
-
-    // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
-    // a variant of the false position algorithm to find the roots. False
-    // position requires that 2 initial values x0,x1 be given, and that the
-    // function must have opposite signs at those values. To find such
-    // values, we need the local extrema of the ROC function, for which we
-    // need the roots of its derivative; however, it's harder to find the
-    // roots of the derivative in this case than it is to find the roots
-    // of the original function. So, we find all points where this curve's
-    // first and second derivative are perpendicular, and we pretend these
-    // are our local extrema. There are at most 3 of these, so we will check
-    // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
-    // points, so roc-w can have at least 6 roots. This shouldn't be a
-    // problem for what we're trying to do (draw a nice looking curve).
-    int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
-        // no OOB exception, because by now off<=6, and roots.length >= 10
-        assert off <= 6 && roots.length >= 10;
-
-        int ret = off;
-        final int end = off + perpendiculardfddf(roots, off);
-        roots[end] = 1.0d; // always check interval end points
-
-        double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
-
-        for (int i = off; i <= end; i++) {
-            double t1 = roots[i], ft1 = ROCsq(t1) - w2;
-            if (ft0 == 0.0d) {
-                roots[ret++] = t0;
-            } else if (ft1 * ft0 < 0.0d) { // have opposite signs
-                // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
-                // ROC(t) >= 0 for all t.
-                roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
-            }
-            t0 = t1;
-            ft0 = ft1;
-        }
-
-        return ret - off;
-    }
-
-    private static double eliminateInf(final double x) {
-        return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
-               (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
-    }
-
-    // A slight modification of the false position algorithm on wikipedia.
-    // This only works for the ROCsq-x functions. It might be nice to have
-    // the function as an argument, but that would be awkward in java6.
-    // TODO: It is something to consider for java8 (or whenever lambda
-    // expressions make it into the language), depending on how closures
-    // and turn out. Same goes for the newton's method
-    // algorithm in DHelpers.java
-    private double falsePositionROCsqMinusX(final double t0, final double t1,
-                                            final double w2, final double err)
-    {
-        final int iterLimit = 100;
-        int side = 0;
-        double t = t1, ft = eliminateInf(ROCsq(t) - w2);
-        double s = t0, fs = eliminateInf(ROCsq(s) - w2);
-        double r = s, fr;
-
-        for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
-            r = (fs * t - ft * s) / (fs - ft);
-            fr = ROCsq(r) - w2;
-            if (sameSign(fr, ft)) {
-                ft = fr; t = r;
-                if (side < 0) {
-                    fs /= (1 << (-side));
-                    side--;
-                } else {
-                    side = -1;
-                }
-            } else if (fr * fs > 0.0d) {
-                fs = fr; s = r;
-                if (side > 0) {
-                    ft /= (1 << side);
-                    side++;
-                } else {
-                    side = 1;
-                }
-            } else {
-                break;
-            }
-        }
-        return r;
-    }
-
-    private static boolean sameSign(final double x, final double y) {
-        // another way is to test if x*y > 0. This is bad for small x, y.
-        return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d);
-    }
-
-    // returns the radius of curvature squared at t of this curve
-    // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
-    private double ROCsq(final double t) {
-        final double dx = t * (t * dax + dbx) + cx;
-        final double dy = t * (t * day + dby) + cy;
-        final double ddx = 2.0d * dax * t + dbx;
-        final double ddy = 2.0d * day * t + dby;
-        final double dx2dy2 = dx * dx + dy * dy;
-        final double ddx2ddy2 = ddx * ddx + ddy * ddy;
-        final double ddxdxddydy = ddx * dx + ddy * dy;
-        return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DDasher.java b/src/main/java/sun/java2d/marlin/DDasher.java
deleted file mode 100644
index e994c37..0000000
--- a/src/main/java/sun/java2d/marlin/DDasher.java
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.util.Arrays;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
-
-/**
- * The <code>DDasher</code> class takes a series of linear commands
- * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
- * <code>end</code>) and breaks them into smaller segments according to a
- * dash pattern array and a starting dash phase.
- *
- * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
- * short dash, whereas Pisces does not draw anything.  The PostScript
- * semantics are unclear.
- *
- */
-final class DDasher implements DPathConsumer2D, MarlinConst {
-
-    /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
-    static final int REC_LIMIT = 16;
-    static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial
-    static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
-
-    static final double EPS = 1e-6d;
-
-    // More than 24 bits of mantissa means we can no longer accurately
-    // measure the number of times cycled through the dash array so we
-    // punt and override the phase to just be 0 past that point.
-    static final double MAX_CYCLES = 16000000.0d;
-
-    private DPathConsumer2D out;
-    private double[] dash;
-    private int dashLen;
-    private double startPhase;
-    private boolean startDashOn;
-    private int startIdx;
-
-    private boolean starting;
-    private boolean needsMoveTo;
-
-    private int idx;
-    private boolean dashOn;
-    private double phase;
-
-    // The starting point of the path
-    private double sx0, sy0;
-    // the current point
-    private double cx0, cy0;
-
-    // temporary storage for the current curve
-    private final double[] curCurvepts;
-
-    // per-thread renderer context
-    final DRendererContext rdrCtx;
-
-    // flag to recycle dash array copy
-    boolean recycleDashes;
-
-    // We don't emit the first dash right away. If we did, caps would be
-    // drawn on it, but we need joins to be drawn if there's a closePath()
-    // So, we store the path elements that make up the first dash in the
-    // buffer below.
-    private double[] firstSegmentsBuffer; // dynamic array
-    private int firstSegidx;
-
-    // dashes ref (dirty)
-    final ArrayCacheDouble.Reference dashes_ref;
-    // firstSegmentsBuffer ref (dirty)
-    final ArrayCacheDouble.Reference firstSegmentsBuffer_ref;
-
-    // Bounds of the drawing region, at pixel precision.
-    private double[] clipRect;
-
-    // the outcode of the current point
-    private int cOutCode = 0;
-
-    private boolean subdivide = DO_CLIP_SUBDIVIDER;
-
-    private final LengthIterator li = new LengthIterator();
-
-    private final CurveClipSplitter curveSplitter;
-
-    private double cycleLen;
-    private boolean outside;
-    private double totalSkipLen;
-
-    /**
-     * Constructs a <code>DDasher</code>.
-     * @param rdrCtx per-thread renderer context
-     */
-    DDasher(final DRendererContext rdrCtx) {
-        this.rdrCtx = rdrCtx;
-
-        dashes_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
-
-        firstSegmentsBuffer_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
-        firstSegmentsBuffer     = firstSegmentsBuffer_ref.initial;
-
-        // we need curCurvepts to be able to contain 2 curves because when
-        // dashing curves, we need to subdivide it
-        curCurvepts = new double[8 * 2];
-
-        this.curveSplitter = rdrCtx.curveClipSplitter;
-    }
-
-    /**
-     * Initialize the <code>DDasher</code>.
-     *
-     * @param out an output <code>DPathConsumer2D</code>.
-     * @param dash an array of <code>double</code>s containing the dash pattern
-     * @param dashLen length of the given dash array
-     * @param phase a <code>double</code> containing the dash phase
-     * @param recycleDashes true to indicate to recycle the given dash array
-     * @return this instance
-     */
-    DDasher init(final DPathConsumer2D out, final double[] dash, final int dashLen,
-                double phase, final boolean recycleDashes)
-    {
-        if (this.out != out) {
-            this.out = out;
-        }
-
-        // Normalize so 0 <= phase < dash[0]
-        int sidx = 0;
-        dashOn = true;
-
-        // note: BasicStroke constructor checks dash elements and sum > 0
-        double sum = 0.0d;
-        for (int i = 0; i < dashLen; i++) {
-            sum += dash[i];
-        }
-        this.cycleLen = sum;
-
-        double cycles = phase / sum;
-        if (phase < 0.0d) {
-            if (-cycles >= MAX_CYCLES) {
-                phase = 0.0d;
-            } else {
-                int fullcycles = FloatMath.floor_int(-cycles);
-                if ((fullcycles & dashLen & 1) != 0) {
-                    dashOn = !dashOn;
-                }
-                phase += fullcycles * sum;
-                while (phase < 0.0d) {
-                    if (--sidx < 0) {
-                        sidx = dashLen - 1;
-                    }
-                    phase += dash[sidx];
-                    dashOn = !dashOn;
-                }
-            }
-        } else if (phase > 0.0d) {
-            if (cycles >= MAX_CYCLES) {
-                phase = 0.0d;
-            } else {
-                int fullcycles = FloatMath.floor_int(cycles);
-                if ((fullcycles & dashLen & 1) != 0) {
-                    dashOn = !dashOn;
-                }
-                phase -= fullcycles * sum;
-                double d;
-                while (phase >= (d = dash[sidx])) {
-                    phase -= d;
-                    sidx = (sidx + 1) % dashLen;
-                    dashOn = !dashOn;
-                }
-            }
-        }
-
-        this.dash = dash;
-        this.dashLen = dashLen;
-        this.phase = phase;
-        this.startPhase = phase;
-        this.startDashOn = dashOn;
-        this.startIdx = sidx;
-        this.starting = true;
-        this.needsMoveTo = false;
-        this.firstSegidx = 0;
-
-        this.recycleDashes = recycleDashes;
-
-        if (rdrCtx.doClip) {
-            this.clipRect = rdrCtx.clipRect;
-        } else {
-            this.clipRect = null;
-            this.cOutCode = 0;
-        }
-        return this; // fluent API
-    }
-
-    /**
-     * Disposes this dasher:
-     * clean up before reusing this instance
-     */
-    void dispose() {
-        if (DO_CLEAN_DIRTY) {
-            // Force zero-fill dirty arrays:
-            Arrays.fill(curCurvepts, 0.0d);
-        }
-        // Return arrays:
-        if (recycleDashes) {
-            if (dashes_ref.doCleanRef(dash)) {
-                dash = dashes_ref.putArray(dash);
-            }
-        }
-        if (firstSegmentsBuffer_ref.doCleanRef(firstSegmentsBuffer)) {
-            firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
-        }
-    }
-
-    double[] copyDashArray(final float[] dashes) {
-        final int len = dashes.length;
-        final double[] newDashes;
-        if (len <= MarlinConst.INITIAL_ARRAY) {
-            newDashes = dashes_ref.initial;
-        } else {
-            if (DO_STATS) {
-                rdrCtx.stats.stat_array_dasher_dasher.add(len);
-            }
-            newDashes = dashes_ref.getArray(len);
-        }
-        for (int i = 0; i < len; i++) { newDashes[i] = dashes[i]; }
-        return newDashes;
-    }
-
-    @Override
-    public void moveTo(final double x0, final double y0) {
-        if (firstSegidx != 0) {
-            out.moveTo(sx0, sy0);
-            emitFirstSegments();
-        }
-        this.needsMoveTo = true;
-        this.idx = startIdx;
-        this.dashOn = this.startDashOn;
-        this.phase = this.startPhase;
-        this.cx0 = x0;
-        this.cy0 = y0;
-
-        // update starting point:
-        this.sx0 = x0;
-        this.sy0 = y0;
-        this.starting = true;
-
-        if (clipRect != null) {
-            final int outcode = DHelpers.outcode(x0, y0, clipRect);
-            this.cOutCode = outcode;
-            this.outside = false;
-            this.totalSkipLen = 0.0d;
-        }
-    }
-
-    private void emitSeg(double[] buf, int off, int type) {
-        switch (type) {
-        case 4:
-            out.lineTo(buf[off], buf[off + 1]);
-            return;
-        case 8:
-            out.curveTo(buf[off    ], buf[off + 1],
-                        buf[off + 2], buf[off + 3],
-                        buf[off + 4], buf[off + 5]);
-            return;
-        case 6:
-            out.quadTo(buf[off    ], buf[off + 1],
-                       buf[off + 2], buf[off + 3]);
-            return;
-        default:
-        }
-    }
-
-    private void emitFirstSegments() {
-        final double[] fSegBuf = firstSegmentsBuffer;
-
-        for (int i = 0, len = firstSegidx; i < len; ) {
-            int type = (int)fSegBuf[i];
-            emitSeg(fSegBuf, i + 1, type);
-            i += (type - 1);
-        }
-        firstSegidx = 0;
-    }
-
-    // precondition: pts must be in relative coordinates (relative to x0,y0)
-    private void goTo(final double[] pts, final int off, final int type,
-                      final boolean on)
-    {
-        final int index = off + type;
-        final double x = pts[index - 4];
-        final double y = pts[index - 3];
-
-        if (on) {
-            if (starting) {
-                goTo_starting(pts, off, type);
-            } else {
-                if (needsMoveTo) {
-                    needsMoveTo = false;
-                    out.moveTo(cx0, cy0);
-                }
-                emitSeg(pts, off, type);
-            }
-        } else {
-            if (starting) {
-                // low probability test (hotspot)
-                starting = false;
-            }
-            needsMoveTo = true;
-        }
-        this.cx0 = x;
-        this.cy0 = y;
-    }
-
-    private void goTo_starting(final double[] pts, final int off, final int type) {
-        int len = type - 1; // - 2 + 1
-        int segIdx = firstSegidx;
-        double[] buf = firstSegmentsBuffer;
-
-        if (segIdx + len  > buf.length) {
-            if (DO_STATS) {
-                rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
-                    .add(segIdx + len);
-            }
-            firstSegmentsBuffer = buf
-                = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
-                                                     segIdx + len);
-        }
-        buf[segIdx++] = type;
-        len--;
-        // small arraycopy (2, 4 or 6) but with offset:
-        System.arraycopy(pts, off, buf, segIdx, len);
-        firstSegidx = segIdx + len;
-    }
-
-    @Override
-    public void lineTo(final double x1, final double y1) {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1);
-
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
-                                                              orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode1;
-                    skipLineTo(x1, y1);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode1;
-
-            if (this.outside) {
-                this.outside = false;
-                // Adjust current index, phase & dash:
-                skipLen();
-            }
-        }
-        _lineTo(x1, y1);
-    }
-
-    private void _lineTo(final double x1, final double y1) {
-        final double dx = x1 - cx0;
-        final double dy = y1 - cy0;
-
-        double len = dx * dx + dy * dy;
-        if (len == 0.0d) {
-            return;
-        }
-        len = Math.sqrt(len);
-
-        // The scaling factors needed to get the dx and dy of the
-        // transformed dash segments.
-        final double cx = dx / len;
-        final double cy = dy / len;
-
-        final double[] _curCurvepts = curCurvepts;
-        final double[] _dash = dash;
-        final int _dashLen = this.dashLen;
-
-        int _idx = idx;
-        boolean _dashOn = dashOn;
-        double _phase = phase;
-
-        double leftInThisDashSegment, rem;
-
-        while (true) {
-            leftInThisDashSegment = _dash[_idx] - _phase;
-            rem = len - leftInThisDashSegment;
-
-            if (rem <= EPS) {
-                _curCurvepts[0] = x1;
-                _curCurvepts[1] = y1;
-
-                goTo(_curCurvepts, 0, 4, _dashOn);
-
-                // Advance phase within current dash segment
-                _phase += len;
-
-                // compare values using epsilon:
-                if (Math.abs(rem) <= EPS) {
-                    _phase = 0.0d;
-                    _idx = (_idx + 1) % _dashLen;
-                    _dashOn = !_dashOn;
-                }
-                break;
-            }
-
-            _curCurvepts[0] = cx0 + leftInThisDashSegment * cx;
-            _curCurvepts[1] = cy0 + leftInThisDashSegment * cy;
-
-            goTo(_curCurvepts, 0, 4, _dashOn);
-
-            len = rem;
-            // Advance to next dash segment
-            _idx = (_idx + 1) % _dashLen;
-            _dashOn = !_dashOn;
-            _phase = 0.0d;
-        }
-        // Save local state:
-        idx = _idx;
-        dashOn = _dashOn;
-        phase = _phase;
-    }
-
-    private void skipLineTo(final double x1, final double y1) {
-        final double dx = x1 - cx0;
-        final double dy = y1 - cy0;
-
-        double len = dx * dx + dy * dy;
-        if (len != 0.0d) {
-            len = Math.sqrt(len);
-        }
-
-        // Accumulate skipped length:
-        this.outside = true;
-        this.totalSkipLen += len;
-
-        // Fix initial move:
-        this.needsMoveTo = true;
-        this.starting = false;
-
-        this.cx0 = x1;
-        this.cy0 = y1;
-    }
-
-    public void skipLen() {
-        double len = this.totalSkipLen;
-        this.totalSkipLen = 0.0d;
-
-        final double[] _dash = dash;
-        final int _dashLen = this.dashLen;
-
-        int _idx = idx;
-        boolean _dashOn = dashOn;
-        double _phase = phase;
-
-        // -2 to ensure having 2 iterations of the post-loop
-        // to compensate the remaining phase
-        final long fullcycles = (long)Math.floor(len / cycleLen) - 2L;
-
-        if (fullcycles > 0L) {
-            len -= cycleLen * fullcycles;
-
-            final long iterations = fullcycles * _dashLen;
-            _idx = (int) (iterations + _idx) % _dashLen;
-            _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
-        }
-
-        double leftInThisDashSegment, rem;
-
-        while (true) {
-            leftInThisDashSegment = _dash[_idx] - _phase;
-            rem = len - leftInThisDashSegment;
-
-            if (rem <= EPS) {
-                // Advance phase within current dash segment
-                _phase += len;
-
-                // compare values using epsilon:
-                if (Math.abs(rem) <= EPS) {
-                    _phase = 0.0d;
-                    _idx = (_idx + 1) % _dashLen;
-                    _dashOn = !_dashOn;
-                }
-                break;
-            }
-
-            len = rem;
-            // Advance to next dash segment
-            _idx = (_idx + 1) % _dashLen;
-            _dashOn = !_dashOn;
-            _phase = 0.0d;
-        }
-        // Save local state:
-        idx = _idx;
-        dashOn = _dashOn;
-        phase = _phase;
-    }
-
-    // preconditions: curCurvepts must be an array of length at least 2 * type,
-    // that contains the curve we want to dash in the first type elements
-    private void somethingTo(final int type) {
-        final double[] _curCurvepts = curCurvepts;
-        if (DHelpers.isPointCurve(_curCurvepts, type)) {
-            return;
-        }
-        final LengthIterator _li = li;
-        final double[] _dash = dash;
-        final int _dashLen = this.dashLen;
-
-        _li.initializeIterationOnCurve(_curCurvepts, type);
-
-        int _idx = idx;
-        boolean _dashOn = dashOn;
-        double _phase = phase;
-
-        // initially the current curve is at curCurvepts[0...type]
-        int curCurveoff = 0;
-        double prevT = 0.0d;
-        double t;
-        double leftInThisDashSegment = _dash[_idx] - _phase;
-
-        while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
-            if (t != 0.0d) {
-                DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
-                                    _curCurvepts, curCurveoff,
-                                    _curCurvepts, 0, type);
-                prevT = t;
-                goTo(_curCurvepts, 2, type, _dashOn);
-                curCurveoff = type;
-            }
-            // Advance to next dash segment
-            _idx = (_idx + 1) % _dashLen;
-            _dashOn = !_dashOn;
-            _phase = 0.0d;
-            leftInThisDashSegment = _dash[_idx];
-        }
-
-        goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
-
-        _phase += _li.lastSegLen();
-
-        // compare values using epsilon:
-        if (_phase + EPS >= _dash[_idx]) {
-            _phase = 0.0d;
-            _idx = (_idx + 1) % _dashLen;
-            _dashOn = !_dashOn;
-        }
-        // Save local state:
-        idx = _idx;
-        dashOn = _dashOn;
-        phase = _phase;
-
-        // reset LengthIterator:
-        _li.reset();
-    }
-
-    private void skipSomethingTo(final int type) {
-        final double[] _curCurvepts = curCurvepts;
-        if (DHelpers.isPointCurve(_curCurvepts, type)) {
-            return;
-        }
-        final LengthIterator _li = li;
-
-        _li.initializeIterationOnCurve(_curCurvepts, type);
-
-        // In contrary to somethingTo(),
-        // just estimate properly the curve length:
-        final double len = _li.totalLength();
-
-        // Accumulate skipped length:
-        this.outside = true;
-        this.totalSkipLen += len;
-
-        // Fix initial move:
-        this.needsMoveTo = true;
-        this.starting = false;
-    }
-
-    // Objects of this class are used to iterate through curves. They return
-    // t values where the left side of the curve has a specified length.
-    // It does this by subdividing the input curve until a certain error
-    // condition has been met. A recursive subdivision procedure would
-    // return as many as 1<<limit curves, but this is an iterator and we
-    // don't need all the curves all at once, so what we carry out a
-    // lazy inorder traversal of the recursion tree (meaning we only move
-    // through the tree when we need the next subdivided curve). This saves
-    // us a lot of memory because at any one time we only need to store
-    // limit+1 curves - one for each level of the tree + 1.
-    // NOTE: the way we do things here is not enough to traverse a general
-    // tree; however, the trees we are interested in have the property that
-    // every non leaf node has exactly 2 children
-    static final class LengthIterator {
-        // Holds the curves at various levels of the recursion. The root
-        // (i.e. the original curve) is at recCurveStack[0] (but then it
-        // gets subdivided, the left half is put at 1, so most of the time
-        // only the right half of the original curve is at 0)
-        private final double[][] recCurveStack; // dirty
-        // sidesRight[i] indicates whether the node at level i+1 in the path from
-        // the root to the current leaf is a left or right child of its parent.
-        private final boolean[] sidesRight; // dirty
-        private int curveType;
-        // lastT and nextT delimit the current leaf.
-        private double nextT;
-        private double lenAtNextT;
-        private double lastT;
-        private double lenAtLastT;
-        private double lenAtLastSplit;
-        private double lastSegLen;
-        // the current level in the recursion tree. 0 is the root. limit
-        // is the deepest possible leaf.
-        private int recLevel;
-        private boolean done;
-
-        // the lengths of the lines of the control polygon. Only its first
-        // curveType/2 - 1 elements are valid. This is an optimization. See
-        // next() for more detail.
-        private final double[] curLeafCtrlPolyLengths = new double[3];
-
-        LengthIterator() {
-            this.recCurveStack = new double[REC_LIMIT + 1][8];
-            this.sidesRight = new boolean[REC_LIMIT];
-            // if any methods are called without first initializing this object
-            // on a curve, we want it to fail ASAP.
-            this.nextT = Double.MAX_VALUE;
-            this.lenAtNextT = Double.MAX_VALUE;
-            this.lenAtLastSplit = Double.MIN_VALUE;
-            this.recLevel = Integer.MIN_VALUE;
-            this.lastSegLen = Double.MAX_VALUE;
-            this.done = true;
-        }
-
-        /**
-         * Reset this LengthIterator.
-         */
-        void reset() {
-            // keep data dirty
-            // as it appears not useful to reset data:
-            if (DO_CLEAN_DIRTY) {
-                final int recLimit = recCurveStack.length - 1;
-                for (int i = recLimit; i >= 0; i--) {
-                    Arrays.fill(recCurveStack[i], 0.0d);
-                }
-                Arrays.fill(sidesRight, false);
-                Arrays.fill(curLeafCtrlPolyLengths, 0.0d);
-                Arrays.fill(nextRoots, 0.0d);
-                Arrays.fill(flatLeafCoefCache, 0.0d);
-                flatLeafCoefCache[2] = -1.0d;
-            }
-        }
-
-        void initializeIterationOnCurve(final double[] pts, final int type) {
-            // optimize arraycopy (8 values faster than 6 = type):
-            System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
-            this.curveType = type;
-            this.recLevel = 0;
-            this.lastT = 0.0d;
-            this.lenAtLastT = 0.0d;
-            this.nextT = 0.0d;
-            this.lenAtNextT = 0.0d;
-            // initializes nextT and lenAtNextT properly
-            goLeft();
-
-            this.lenAtLastSplit = 0.0d;
-            if (recLevel > 0) {
-                this.sidesRight[0] = false;
-                this.done = false;
-            } else {
-                // the root of the tree is a leaf so we're done.
-                this.sidesRight[0] = true;
-                this.done = true;
-            }
-            this.lastSegLen = 0.0d;
-        }
-
-        // 0 == false, 1 == true, -1 == invalid cached value.
-        private int cachedHaveLowAcceleration = -1;
-
-        private boolean haveLowAcceleration(final double err) {
-            if (cachedHaveLowAcceleration == -1) {
-                final double len1 = curLeafCtrlPolyLengths[0];
-                final double len2 = curLeafCtrlPolyLengths[1];
-                // the test below is equivalent to !within(len1/len2, 1, err).
-                // It is using a multiplication instead of a division, so it
-                // should be a bit faster.
-                if (!DHelpers.within(len1, len2, err * len2)) {
-                    cachedHaveLowAcceleration = 0;
-                    return false;
-                }
-                if (curveType == 8) {
-                    final double len3 = curLeafCtrlPolyLengths[2];
-                    // if len1 is close to 2 and 2 is close to 3, that probably
-                    // means 1 is close to 3 so the second part of this test might
-                    // not be needed, but it doesn't hurt to include it.
-                    final double errLen3 = err * len3;
-                    if (!(DHelpers.within(len2, len3, errLen3) &&
-                          DHelpers.within(len1, len3, errLen3))) {
-                        cachedHaveLowAcceleration = 0;
-                        return false;
-                    }
-                }
-                cachedHaveLowAcceleration = 1;
-                return true;
-            }
-
-            return (cachedHaveLowAcceleration == 1);
-        }
-
-        // we want to avoid allocations/gc so we keep this array so we
-        // can put roots in it,
-        private final double[] nextRoots = new double[4];
-
-        // caches the coefficients of the current leaf in its flattened
-        // form (see inside next() for what that means). The cache is
-        // invalid when it's third element is negative, since in any
-        // valid flattened curve, this would be >= 0.
-        private final double[] flatLeafCoefCache = new double[]{0.0d, 0.0d, -1.0d, 0.0d};
-
-        // returns the t value where the remaining curve should be split in
-        // order for the left subdivided curve to have length len. If len
-        // is >= than the length of the uniterated curve, it returns 1.
-        double next(final double len) {
-            final double targetLength = lenAtLastSplit + len;
-            while (lenAtNextT < targetLength) {
-                if (done) {
-                    lastSegLen = lenAtNextT - lenAtLastSplit;
-                    return 1.0d;
-                }
-                goToNextLeaf();
-            }
-            lenAtLastSplit = targetLength;
-            final double leaflen = lenAtNextT - lenAtLastT;
-            double t = (targetLength - lenAtLastT) / leaflen;
-
-            // cubicRootsInAB is a fairly expensive call, so we just don't do it
-            // if the acceleration in this section of the curve is small enough.
-            if (!haveLowAcceleration(0.05d)) {
-                // We flatten the current leaf along the x axis, so that we're
-                // left with a, b, c which define a 1D Bezier curve. We then
-                // solve this to get the parameter of the original leaf that
-                // gives us the desired length.
-                final double[] _flatLeafCoefCache = flatLeafCoefCache;
-
-                if (_flatLeafCoefCache[2] < 0.0d) {
-                    double x =     curLeafCtrlPolyLengths[0],
-                           y = x + curLeafCtrlPolyLengths[1];
-                    if (curveType == 8) {
-                        double z = y + curLeafCtrlPolyLengths[2];
-                        _flatLeafCoefCache[0] = 3.0d * (x - y) + z;
-                        _flatLeafCoefCache[1] = 3.0d * (y - 2.0d * x);
-                        _flatLeafCoefCache[2] = 3.0d * x;
-                        _flatLeafCoefCache[3] = -z;
-                    } else if (curveType == 6) {
-                        _flatLeafCoefCache[0] = 0.0d;
-                        _flatLeafCoefCache[1] = y - 2.0d * x;
-                        _flatLeafCoefCache[2] = 2.0d * x;
-                        _flatLeafCoefCache[3] = -y;
-                    }
-                }
-                double a = _flatLeafCoefCache[0];
-                double b = _flatLeafCoefCache[1];
-                double c = _flatLeafCoefCache[2];
-                double d = t * _flatLeafCoefCache[3];
-
-                // we use cubicRootsInAB here, because we want only roots in 0, 1,
-                // and our quadratic root finder doesn't filter, so it's just a
-                // matter of convenience.
-                final int n = DHelpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d);
-                if (n == 1 && !Double.isNaN(nextRoots[0])) {
-                    t = nextRoots[0];
-                }
-            }
-            // t is relative to the current leaf, so we must make it a valid parameter
-            // of the original curve.
-            t = t * (nextT - lastT) + lastT;
-            if (t >= 1.0d) {
-                t = 1.0d;
-                done = true;
-            }
-            // even if done = true, if we're here, that means targetLength
-            // is equal to, or very, very close to the total length of the
-            // curve, so lastSegLen won't be too high. In cases where len
-            // overshoots the curve, this method will exit in the while
-            // loop, and lastSegLen will still be set to the right value.
-            lastSegLen = len;
-            return t;
-        }
-
-        double totalLength() {
-            while (!done) {
-                goToNextLeaf();
-            }
-            // reset LengthIterator:
-            reset();
-
-            return lenAtNextT;
-        }
-
-        double lastSegLen() {
-            return lastSegLen;
-        }
-
-        // go to the next leaf (in an inorder traversal) in the recursion tree
-        // preconditions: must be on a leaf, and that leaf must not be the root.
-        private void goToNextLeaf() {
-            // We must go to the first ancestor node that has an unvisited
-            // right child.
-            final boolean[] _sides = sidesRight;
-            int _recLevel = recLevel;
-            _recLevel--;
-
-            while(_sides[_recLevel]) {
-                if (_recLevel == 0) {
-                    recLevel = 0;
-                    done = true;
-                    return;
-                }
-                _recLevel--;
-            }
-
-            _sides[_recLevel] = true;
-            // optimize arraycopy (8 values faster than 6 = type):
-            System.arraycopy(recCurveStack[_recLevel++], 0,
-                             recCurveStack[_recLevel], 0, 8);
-            recLevel = _recLevel;
-            goLeft();
-        }
-
-        // go to the leftmost node from the current node. Return its length.
-        private void goLeft() {
-            final double len = onLeaf();
-            if (len >= 0.0d) {
-                lastT = nextT;
-                lenAtLastT = lenAtNextT;
-                nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC;
-                lenAtNextT += len;
-                // invalidate caches
-                flatLeafCoefCache[2] = -1.0d;
-                cachedHaveLowAcceleration = -1;
-            } else {
-                DHelpers.subdivide(recCurveStack[recLevel],
-                                   recCurveStack[recLevel + 1],
-                                   recCurveStack[recLevel], curveType);
-
-                sidesRight[recLevel] = false;
-                recLevel++;
-                goLeft();
-            }
-        }
-
-        // this is a bit of a hack. It returns -1 if we're not on a leaf, and
-        // the length of the leaf if we are on a leaf.
-        private double onLeaf() {
-            final double[] curve = recCurveStack[recLevel];
-            final int _curveType = curveType;
-            double polyLen = 0.0d;
-
-            double x0 = curve[0], y0 = curve[1];
-            for (int i = 2; i < _curveType; i += 2) {
-                final double x1 = curve[i], y1 = curve[i + 1];
-                final double len = DHelpers.linelen(x0, y0, x1, y1);
-                polyLen += len;
-                curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
-                x0 = x1;
-                y0 = y1;
-            }
-
-            final double lineLen = DHelpers.linelen(curve[0], curve[1], x0, y0);
-
-            if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) {
-                return (polyLen + lineLen) / 2.0d;
-            }
-            return -1.0d;
-        }
-    }
-
-    @Override
-    public void curveTo(final double x1, final double y1,
-                        final double x2, final double y2,
-                        final double x3, final double y3)
-    {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
-            final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3,
-                                                               orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode3;
-                    skipCurveTo(x1, y1, x2, y2, x3, y3);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode3;
-
-            if (this.outside) {
-                this.outside = false;
-                // Adjust current index, phase & dash:
-                skipLen();
-            }
-        }
-        _curveTo(x1, y1, x2, y2, x3, y3);
-    }
-
-    private void _curveTo(final double x1, final double y1,
-                          final double x2, final double y2,
-                          final double x3, final double y3)
-    {
-        final double[] _curCurvepts = curCurvepts;
-
-        // monotonize curve:
-        final CurveBasicMonotonizer monotonizer
-            = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
-
-        final int nSplits = monotonizer.nbSplits;
-        final double[] mid = monotonizer.middle;
-
-        // Implicitely rdrCtx.isFirstSegment = true
-
-        for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
-            // optimize arraycopy (8 values faster than 6 = type):
-            System.arraycopy(mid, off, _curCurvepts, 0, 8);
-
-            somethingTo(8);
-
-            // set flag rdrCtx.isFirstSegment = false for other parts:
-            rdrCtx.isFirstSegment = false; // TODO: handle conflict with clipper
-        }
-        // reset trigger to process further joins (normal operations)
-        rdrCtx.isFirstSegment = true;
-    }
-
-    private void skipCurveTo(final double x1, final double y1,
-                             final double x2, final double y2,
-                             final double x3, final double y3)
-    {
-        final double[] _curCurvepts = curCurvepts;
-        _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
-        _curCurvepts[2] = x1;  _curCurvepts[3] = y1;
-        _curCurvepts[4] = x2;  _curCurvepts[5] = y2;
-        _curCurvepts[6] = x3;  _curCurvepts[7] = y3;
-
-        skipSomethingTo(8);
-
-        this.cx0 = x3;
-        this.cy0 = y3;
-    }
-
-    @Override
-    public void quadTo(final double x1, final double y1,
-                       final double x2, final double y2)
-    {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => call lineTo() with subdivided curves:
-                        boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
-                                                              x2, y2, orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode2;
-                    skipQuadTo(x1, y1, x2, y2);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode2;
-
-            if (this.outside) {
-                this.outside = false;
-                // Adjust current index, phase & dash:
-                skipLen();
-            }
-        }
-        _quadTo(x1, y1, x2, y2);
-    }
-
-    private void _quadTo(final double x1, final double y1,
-                         final double x2, final double y2)
-    {
-        final double[] _curCurvepts = curCurvepts;
-
-        // monotonize quad:
-        final CurveBasicMonotonizer monotonizer
-            = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
-
-        final int nSplits = monotonizer.nbSplits;
-        final double[] mid = monotonizer.middle;
-
-        // Implicitely rdrCtx.isFirstSegment = true
-
-        for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
-            // optimize arraycopy (8 values faster than 6 = type):
-            System.arraycopy(mid, off, _curCurvepts, 0, 8);
-
-            somethingTo(6);
-
-            // set flag rdrCtx.isFirstSegment = false for other parts:
-            rdrCtx.isFirstSegment = false; // TODO: handle conflict with clipper
-        }
-        // reset trigger to process further joins (normal operations)
-        rdrCtx.isFirstSegment = true;
-    }
-
-    private void skipQuadTo(final double x1, final double y1,
-                            final double x2, final double y2)
-    {
-        final double[] _curCurvepts = curCurvepts;
-        _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
-        _curCurvepts[2] = x1;  _curCurvepts[3] = y1;
-        _curCurvepts[4] = x2;  _curCurvepts[5] = y2;
-
-        skipSomethingTo(6);
-
-        this.cx0 = x2;
-        this.cy0 = y2;
-    }
-
-    @Override
-    public void closePath() {
-        if (cx0 != sx0 || cy0 != sy0) {
-            lineTo(sx0, sy0);
-        }
-        if (firstSegidx != 0) {
-            if (!dashOn || needsMoveTo) {
-                out.moveTo(sx0, sy0);
-            }
-            emitFirstSegments();
-        }
-        moveTo(sx0, sy0);
-    }
-
-    @Override
-    public void pathDone() {
-        if (firstSegidx != 0) {
-            out.moveTo(sx0, sy0);
-            emitFirstSegments();
-        }
-        out.pathDone();
-
-        // Dispose this instance:
-        dispose();
-    }
-
-    @Override
-    public long getNativeConsumer() {
-        throw new InternalError("DDasher does not use a native consumer");
-    }
-}
-
diff --git a/src/main/java/sun/java2d/marlin/DHelpers.java b/src/main/java/sun/java2d/marlin/DHelpers.java
deleted file mode 100644
index 9a01cfb..0000000
--- a/src/main/java/sun/java2d/marlin/DHelpers.java
+++ /dev/null
@@ -1,1020 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.util.Arrays;
-import net.jafama.FastMath;
-import sun.java2d.marlin.stats.Histogram;
-import sun.java2d.marlin.stats.StatLong;
-
-final class DHelpers implements MarlinConst {
-
-    private static final double EPS = 1e-9d;
-
-    private DHelpers() {
-        throw new Error("This is a non instantiable class");
-    }
-
-    static boolean within(final double x, final double y) {
-        return within(x, y, EPS);
-    }
-
-    static boolean within(final double x, final double y, final double err) {
-        return withinD(y - x, err);
-    }
-
-    static boolean withinD(final double d, final double err) {
-        return (d <= err && d >= -err);
-    }
-
-    static boolean withinD(final double dx, final double dy, final double err)
-    {
-        assert err > 0 : "";
-        // compare taxicab distance. ERR will always be small, so using
-        // true distance won't give much benefit
-        return (withinD(dx, err) && // we want to avoid calling Math.abs
-                withinD(dy, err));  // this is just as good.
-    }
-
-    static boolean isPointCurve(final double[] curve, final int type) {
-        return isPointCurve(curve, type, EPS);
-    }
-
-    static boolean isPointCurve(final double[] curve, final int type, final double err) {
-        for (int i = 2; i < type; i++) {
-            if (!within(curve[i], curve[i - 2], err)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    static double evalCubic(final double a, final double b,
-                            final double c, final double d,
-                            final double t)
-    {
-        return t * (t * (t * a + b) + c) + d;
-    }
-
-    static double evalQuad(final double a, final double b,
-                           final double c, final double t)
-    {
-        return t * (t * a + b) + c;
-    }
-
-    static int quadraticRoots(final double a, final double b, final double c,
-                              final double[] zeroes, final int off)
-    {
-        int ret = off;
-        if (a != 0.0d) {
-            double d = b * b - 4.0d * a * c;
-            if (d > 0.0d) {
-                d = Math.sqrt(d);
-                // For accuracy, calculate one root using:
-                //     (-b +/- d) / 2a
-                // and the other using:
-                //     2c / (-b +/- d)
-                // Choose the sign of the +/- so that b+d gets larger in magnitude
-                if (b < 0.0d) {
-                    d = -d;
-                }
-                final double q = (b + d) / -2.0d;
-                // We already tested a for being 0 above
-                zeroes[ret++] = q / a;
-                if (q != 0.0d) {
-                    zeroes[ret++] = c / q;
-                }
-            } else if (d == 0.0d) {
-                zeroes[ret++] = -b / (2.0d * a);
-            }
-        } else if (b != 0.0d) {
-            zeroes[ret++] = -c / b;
-        }
-        return ret - off;
-    }
-
-    // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
-    static int cubicRootsInAB(final double d, double a, double b, double c,
-                              final double[] pts, final int off,
-                              final double A, final double B)
-    {
-        if (d == 0.0d) {
-            final int num = quadraticRoots(a, b, c, pts, off);
-            return filterOutNotInAB(pts, off, num, A, B) - off;
-        }
-        // From Graphics Gems:
-        // https://github.com/erich666/GraphicsGems/blob/master/gems/Roots3And4.c
-        // (also from awt.geom.CubicCurve2D. But here we don't need as
-        // much accuracy and we don't want to create arrays so we use
-        // our own customized version).
-
-        // normal form: x^3 + ax^2 + bx + c = 0
-        a /= d;
-        b /= d;
-        c /= d;
-
-        //  substitute x = y - A/3 to eliminate quadratic term:
-        //     x^3 +Px + Q = 0
-        //
-        // Since we actually need P/3 and Q/2 for all of the
-        // calculations that follow, we will calculate
-        // p = P/3
-        // q = Q/2
-        // instead and use those values for simplicity of the code.
-        final double sq_A = a * a;
-        final double p = (1.0d / 3.0d) * ((-1.0d / 3.0d) * sq_A + b);
-        final double sub = (1.0d / 3.0d) * a;
-        final double q = (1.0d / 2.0d) * ((2.0d / 27.0d) * a * sq_A - sub * b + c);
-
-        // use Cardano's formula
-        final double cb_p = p * p * p;
-        final double D = q * q + cb_p;
-
-        int num;
-
-        if (within(D, 0.0d)) {
-            if (within(q, 0.0d)) {
-                /* one triple solution */
-                pts[off    ] = (- sub);
-                num = 1;
-            } else {
-                /* one single and one double solution */
-                final double u = FastMath.cbrt(-q);
-                pts[off    ] = (2.0d * u - sub);
-                pts[off + 1] = (- u - sub);
-                num = 2;
-            }
-        } else if (D < 0.0d) {
-            // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
-            final double phi = (1.0d / 3.0d) * FastMath.acos(-q / Math.sqrt(-cb_p));
-            final double t = 2.0d * Math.sqrt(-p);
-
-            pts[off    ] = ( t * FastMath.cos(phi) - sub);
-            pts[off + 1] = (-t * FastMath.cos(phi + (Math.PI / 3.0d)) - sub);
-            pts[off + 2] = (-t * FastMath.cos(phi - (Math.PI / 3.0d)) - sub);
-            num = 3;
-        } else {
-            final double sqrt_D = Math.sqrt(D);
-            final double u =   FastMath.cbrt(sqrt_D - q);
-            final double v = - FastMath.cbrt(sqrt_D + q);
-
-            pts[off    ] = (u + v - sub);
-            num = 1;
-        }
-        return filterOutNotInAB(pts, off, num, A, B) - off;
-    }
-
-    // returns the index 1 past the last valid element remaining after filtering
-    static int filterOutNotInAB(final double[] nums, final int off, final int len,
-                                final double a, final double b)
-    {
-        int ret = off;
-        for (int i = off, end = off + len; i < end; i++) {
-            if (nums[i] >= a && nums[i] < b) {
-                nums[ret++] = nums[i];
-            }
-        }
-        return ret;
-    }
-
-    static double fastLineLen(final double x0, final double y0,
-                              final double x1, final double y1)
-    {
-        final double dx = x1 - x0;
-        final double dy = y1 - y0;
-
-        // use manhattan norm:
-        return Math.abs(dx) + Math.abs(dy);
-    }
-
-    static double linelen(final double x0, final double y0,
-                          final double x1, final double y1)
-    {
-        final double dx = x1 - x0;
-        final double dy = y1 - y0;
-        return Math.sqrt(dx * dx + dy * dy);
-    }
-
-    static double fastQuadLen(final double x0, final double y0,
-                              final double x1, final double y1,
-                              final double x2, final double y2)
-    {
-        final double dx1 = x1 - x0;
-        final double dx2 = x2 - x1;
-        final double dy1 = y1 - y0;
-        final double dy2 = y2 - y1;
-
-        // use manhattan norm:
-        return Math.abs(dx1) + Math.abs(dx2)
-             + Math.abs(dy1) + Math.abs(dy2);
-    }
-
-    static double quadlen(final double x0, final double y0,
-                          final double x1, final double y1,
-                          final double x2, final double y2)
-    {
-        return (linelen(x0, y0, x1, y1)
-                + linelen(x1, y1, x2, y2)
-                + linelen(x0, y0, x2, y2)) / 2.0d;
-    }
-
-    static double fastCurvelen(final double x0, final double y0,
-                               final double x1, final double y1,
-                               final double x2, final double y2,
-                               final double x3, final double y3)
-    {
-        final double dx1 = x1 - x0;
-        final double dx2 = x2 - x1;
-        final double dx3 = x3 - x2;
-        final double dy1 = y1 - y0;
-        final double dy2 = y2 - y1;
-        final double dy3 = y3 - y2;
-
-        // use manhattan norm:
-        return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3)
-             + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3);
-    }
-
-    static double curvelen(final double x0, final double y0,
-                           final double x1, final double y1,
-                           final double x2, final double y2,
-                           final double x3, final double y3)
-    {
-        return (linelen(x0, y0, x1, y1)
-              + linelen(x1, y1, x2, y2)
-              + linelen(x2, y2, x3, y3)
-              + linelen(x0, y0, x3, y3)) / 2.0d;
-    }
-
-    // finds values of t where the curve in pts should be subdivided in order
-    // to get good offset curves a distance of w away from the middle curve.
-    // Stores the points in ts, and returns how many of them there were.
-    static int findSubdivPoints(final DCurve c, final double[] pts,
-                                final double[] ts, final int type,
-                                final double w2)
-    {
-        final double x12 = pts[2] - pts[0];
-        final double y12 = pts[3] - pts[1];
-        // if the curve is already parallel to either axis we gain nothing
-        // from rotating it.
-        if ((y12 != 0.0d) && (x12 != 0.0d)) {
-            // we rotate it so that the first vector in the control polygon is
-            // parallel to the x-axis. This will ensure that rotated quarter
-            // circles won't be subdivided.
-            final double hypot = Math.sqrt(x12 * x12 + y12 * y12);
-            final double cos = x12 / hypot;
-            final double sin = y12 / hypot;
-            final double x1 = cos * pts[0] + sin * pts[1];
-            final double y1 = cos * pts[1] - sin * pts[0];
-            final double x2 = cos * pts[2] + sin * pts[3];
-            final double y2 = cos * pts[3] - sin * pts[2];
-            final double x3 = cos * pts[4] + sin * pts[5];
-            final double y3 = cos * pts[5] - sin * pts[4];
-
-            switch(type) {
-            case 8:
-                final double x4 = cos * pts[6] + sin * pts[7];
-                final double y4 = cos * pts[7] - sin * pts[6];
-                c.set(x1, y1, x2, y2, x3, y3, x4, y4);
-                break;
-            case 6:
-                c.set(x1, y1, x2, y2, x3, y3);
-                break;
-            default:
-            }
-        } else {
-            c.set(pts, type);
-        }
-
-        int ret = 0;
-        // we subdivide at values of t such that the remaining rotated
-        // curves are monotonic in x and y.
-        ret += c.dxRoots(ts, ret);
-        ret += c.dyRoots(ts, ret);
-
-        // subdivide at inflection points.
-        if (type == 8) {
-            // quadratic curves can't have inflection points
-            ret += c.infPoints(ts, ret);
-        }
-
-        // now we must subdivide at points where one of the offset curves will have
-        // a cusp. This happens at ts where the radius of curvature is equal to w.
-        ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
-
-        ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
-        isort(ts, ret);
-        return ret;
-    }
-
-    // finds values of t where the curve in pts should be subdivided in order
-    // to get intersections with the given clip rectangle.
-    // Stores the points in ts, and returns how many of them there were.
-    static int findClipPoints(final DCurve curve, final double[] pts,
-                              final double[] ts, final int type,
-                              final int outCodeOR,
-                              final double[] clipRect)
-    {
-        curve.set(pts, type);
-
-        // clip rectangle (ymin, ymax, xmin, xmax)
-        int ret = 0;
-
-        if ((outCodeOR & OUTCODE_LEFT) != 0) {
-            ret += curve.xPoints(ts, ret, clipRect[2]);
-        }
-        if ((outCodeOR & OUTCODE_RIGHT) != 0) {
-            ret += curve.xPoints(ts, ret, clipRect[3]);
-        }
-        if ((outCodeOR & OUTCODE_TOP) != 0) {
-            ret += curve.yPoints(ts, ret, clipRect[0]);
-        }
-        if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
-            ret += curve.yPoints(ts, ret, clipRect[1]);
-        }
-        isort(ts, ret);
-        return ret;
-    }
-
-    static void subdivide(final double[] src,
-                          final double[] left, final double[] right,
-                          final int type)
-    {
-        switch(type) {
-        case 8:
-            subdivideCubic(src, left, right);
-            return;
-        case 6:
-            subdivideQuad(src, left, right);
-            return;
-        default:
-            throw new InternalError("Unsupported curve type");
-        }
-    }
-
-    static void isort(final double[] a, final int len) {
-        for (int i = 1, j; i < len; i++) {
-            final double ai = a[i];
-            j = i - 1;
-            for (; j >= 0 && a[j] > ai; j--) {
-                a[j + 1] = a[j];
-            }
-            a[j + 1] = ai;
-        }
-    }
-
-    // Most of these are copied from classes in java.awt.geom because we need
-    // both single and double precision variants of these functions, and Line2D,
-    // CubicCurve2D, QuadCurve2D don't provide them.
-    /**
-     * Subdivides the cubic curve specified by the coordinates
-     * stored in the <code>src</code> array at indices <code>srcoff</code>
-     * through (<code>srcoff</code>&nbsp;+&nbsp;7) and stores the
-     * resulting two subdivided curves into the two result arrays at the
-     * corresponding indices.
-     * Either or both of the <code>left</code> and <code>right</code>
-     * arrays may be <code>null</code> or a reference to the same array
-     * as the <code>src</code> array.
-     * Note that the last point in the first subdivided curve is the
-     * same as the first point in the second subdivided curve. Thus,
-     * it is possible to pass the same array for <code>left</code>
-     * and <code>right</code> and to use offsets, such as <code>rightoff</code>
-     * equals (<code>leftoff</code> + 6), in order
-     * to avoid allocating extra storage for this common point.
-     * @param src the array holding the coordinates for the source curve
-     * @param left the array for storing the coordinates for the first
-     * half of the subdivided curve
-     * @param right the array for storing the coordinates for the second
-     * half of the subdivided curve
-     * @since 1.7
-     */
-    static void subdivideCubic(final double[] src,
-                               final double[] left,
-                               final double[] right)
-    {
-        double  x1 = src[0];
-        double  y1 = src[1];
-        double cx1 = src[2];
-        double cy1 = src[3];
-        double cx2 = src[4];
-        double cy2 = src[5];
-        double  x2 = src[6];
-        double  y2 = src[7];
-
-        left[0]  = x1;
-        left[1]  = y1;
-
-        right[6] = x2;
-        right[7] = y2;
-
-        x1 = (x1 + cx1) / 2.0d;
-        y1 = (y1 + cy1) / 2.0d;
-        x2 = (x2 + cx2) / 2.0d;
-        y2 = (y2 + cy2) / 2.0d;
-
-        double cx = (cx1 + cx2) / 2.0d;
-        double cy = (cy1 + cy2) / 2.0d;
-
-        cx1 = (x1 + cx) / 2.0d;
-        cy1 = (y1 + cy) / 2.0d;
-        cx2 = (x2 + cx) / 2.0d;
-        cy2 = (y2 + cy) / 2.0d;
-        cx  = (cx1 + cx2) / 2.0d;
-        cy  = (cy1 + cy2) / 2.0d;
-
-        left[2] = x1;
-        left[3] = y1;
-        left[4] = cx1;
-        left[5] = cy1;
-        left[6] = cx;
-        left[7] = cy;
-
-        right[0] = cx;
-        right[1] = cy;
-        right[2] = cx2;
-        right[3] = cy2;
-        right[4] = x2;
-        right[5] = y2;
-    }
-
-    static void subdivideCubicAt(final double t,
-                                 final double[] src, final int offS,
-                                 final double[] pts, final int offL, final int offR)
-    {
-        double  x1 = src[offS    ];
-        double  y1 = src[offS + 1];
-        double cx1 = src[offS + 2];
-        double cy1 = src[offS + 3];
-        double cx2 = src[offS + 4];
-        double cy2 = src[offS + 5];
-        double  x2 = src[offS + 6];
-        double  y2 = src[offS + 7];
-
-        pts[offL    ] = x1;
-        pts[offL + 1] = y1;
-
-        pts[offR + 6] = x2;
-        pts[offR + 7] = y2;
-
-        x1 =  x1 + t * (cx1 - x1);
-        y1 =  y1 + t * (cy1 - y1);
-        x2 = cx2 + t * (x2 - cx2);
-        y2 = cy2 + t * (y2 - cy2);
-
-        double cx = cx1 + t * (cx2 - cx1);
-        double cy = cy1 + t * (cy2 - cy1);
-
-        cx1 =  x1 + t * (cx - x1);
-        cy1 =  y1 + t * (cy - y1);
-        cx2 =  cx + t * (x2 - cx);
-        cy2 =  cy + t * (y2 - cy);
-        cx  = cx1 + t * (cx2 - cx1);
-        cy  = cy1 + t * (cy2 - cy1);
-
-        pts[offL + 2] = x1;
-        pts[offL + 3] = y1;
-        pts[offL + 4] = cx1;
-        pts[offL + 5] = cy1;
-        pts[offL + 6] = cx;
-        pts[offL + 7] = cy;
-
-        pts[offR    ] = cx;
-        pts[offR + 1] = cy;
-        pts[offR + 2] = cx2;
-        pts[offR + 3] = cy2;
-        pts[offR + 4] = x2;
-        pts[offR + 5] = y2;
-    }
-
-    static void subdivideQuad(final double[] src,
-                              final double[] left,
-                              final double[] right)
-    {
-        double x1 = src[0];
-        double y1 = src[1];
-        double cx = src[2];
-        double cy = src[3];
-        double x2 = src[4];
-        double y2 = src[5];
-
-        left[0]  = x1;
-        left[1]  = y1;
-
-        right[4] = x2;
-        right[5] = y2;
-
-        x1 = (x1 + cx) / 2.0d;
-        y1 = (y1 + cy) / 2.0d;
-        x2 = (x2 + cx) / 2.0d;
-        y2 = (y2 + cy) / 2.0d;
-        cx = (x1 + x2) / 2.0d;
-        cy = (y1 + y2) / 2.0d;
-
-        left[2] = x1;
-        left[3] = y1;
-        left[4] = cx;
-        left[5] = cy;
-
-        right[0] = cx;
-        right[1] = cy;
-        right[2] = x2;
-        right[3] = y2;
-    }
-
-    static void subdivideQuadAt(final double t,
-                                final double[] src, final int offS,
-                                final double[] pts, final int offL, final int offR)
-    {
-        double x1 = src[offS    ];
-        double y1 = src[offS + 1];
-        double cx = src[offS + 2];
-        double cy = src[offS + 3];
-        double x2 = src[offS + 4];
-        double y2 = src[offS + 5];
-
-        pts[offL    ] = x1;
-        pts[offL + 1] = y1;
-
-        pts[offR + 4] = x2;
-        pts[offR + 5] = y2;
-
-        x1 = x1 + t * (cx - x1);
-        y1 = y1 + t * (cy - y1);
-        x2 = cx + t * (x2 - cx);
-        y2 = cy + t * (y2 - cy);
-        cx = x1 + t * (x2 - x1);
-        cy = y1 + t * (y2 - y1);
-
-        pts[offL + 2] = x1;
-        pts[offL + 3] = y1;
-        pts[offL + 4] = cx;
-        pts[offL + 5] = cy;
-
-        pts[offR    ] = cx;
-        pts[offR + 1] = cy;
-        pts[offR + 2] = x2;
-        pts[offR + 3] = y2;
-    }
-
-    static void subdivideLineAt(final double t,
-                                final double[] src, final int offS,
-                                final double[] pts, final int offL, final int offR)
-    {
-        double x1 = src[offS    ];
-        double y1 = src[offS + 1];
-        double x2 = src[offS + 2];
-        double y2 = src[offS + 3];
-
-        pts[offL    ] = x1;
-        pts[offL + 1] = y1;
-
-        pts[offR + 2] = x2;
-        pts[offR + 3] = y2;
-
-        x1 = x1 + t * (x2 - x1);
-        y1 = y1 + t * (y2 - y1);
-
-        pts[offL + 2] = x1;
-        pts[offL + 3] = y1;
-
-        pts[offR    ] = x1;
-        pts[offR + 1] = y1;
-    }
-
-    static void subdivideAt(final double t,
-                            final double[] src, final int offS,
-                            final double[] pts, final int offL, final int type)
-    {
-        // if instead of switch (perf + most probable cases first)
-        if (type == 8) {
-            subdivideCubicAt(t, src, offS, pts, offL, offL + type);
-        } else if (type == 4) {
-            subdivideLineAt(t, src, offS, pts, offL, offL + type);
-        } else {
-            subdivideQuadAt(t, src, offS, pts, offL, offL + type);
-        }
-    }
-
-    // From sun.java2d.loops.GeneralRenderer:
-
-    static int outcode(final double x, final double y,
-                       final double[] clipRect)
-    {
-        int code;
-        if (y < clipRect[0]) {
-            code = OUTCODE_TOP;
-        } else if (y >= clipRect[1]) {
-            code = OUTCODE_BOTTOM;
-        } else {
-            code = 0;
-        }
-        if (x < clipRect[2]) {
-            code |= OUTCODE_LEFT;
-        } else if (x >= clipRect[3]) {
-            code |= OUTCODE_RIGHT;
-        }
-        return code;
-    }
-
-    // a stack of polynomial curves where each curve shares endpoints with
-    // adjacent ones.
-    static final class PolyStack {
-        private static final byte TYPE_LINETO  = (byte) 0;
-        private static final byte TYPE_QUADTO  = (byte) 1;
-        private static final byte TYPE_CUBICTO = (byte) 2;
-
-        // curves capacity = edges count (8192) = edges x 2 (coords)
-        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
-        // types capacity = edges count (4096)
-        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
-        double[] curves;
-        int end;
-        byte[] curveTypes;
-        int numCurves;
-
-        // curves ref (dirty)
-        final ArrayCacheDouble.Reference curves_ref;
-        // curveTypes ref (dirty)
-        final ArrayCacheByte.Reference curveTypes_ref;
-
-        // used marks (stats only)
-        int curveTypesUseMark;
-        int curvesUseMark;
-
-        private final StatLong stat_polystack_types;
-        private final StatLong stat_polystack_curves;
-        private final Histogram hist_polystack_curves;
-        private final StatLong stat_array_polystack_curves;
-        private final StatLong stat_array_polystack_curveTypes;
-
-        PolyStack(final DRendererContext rdrCtx) {
-            this(rdrCtx, null, null, null, null, null);
-        }
-
-        PolyStack(final DRendererContext rdrCtx,
-                  final StatLong stat_polystack_types,
-                  final StatLong stat_polystack_curves,
-                  final Histogram hist_polystack_curves,
-                  final StatLong stat_array_polystack_curves,
-                  final StatLong stat_array_polystack_curveTypes)
-        {
-            curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
-            curves     = curves_ref.initial;
-
-            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
-            curveTypes     = curveTypes_ref.initial;
-            numCurves = 0;
-            end = 0;
-
-            if (DO_STATS) {
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-            this.stat_polystack_types = stat_polystack_types;
-            this.stat_polystack_curves = stat_polystack_curves;
-            this.hist_polystack_curves = hist_polystack_curves;
-            this.stat_array_polystack_curves = stat_array_polystack_curves;
-            this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;
-        }
-
-        /**
-         * Disposes this PolyStack:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            end       = 0;
-            numCurves = 0;
-
-            if (DO_STATS) {
-                stat_polystack_types.add(curveTypesUseMark);
-                stat_polystack_curves.add(curvesUseMark);
-                hist_polystack_curves.add(curvesUseMark);
-
-                // reset marks
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-
-            // Return arrays:
-            // curves and curveTypes are kept dirty
-            if (curves_ref.doCleanRef(curves)) {
-                curves = curves_ref.putArray(curves);
-            }
-            if (curveTypes_ref.doCleanRef(curveTypes)) {
-                curveTypes = curveTypes_ref.putArray(curveTypes);
-            }
-        }
-
-        private void ensureSpace(final int n) {
-            // use substraction to avoid integer overflow:
-            if (curves.length - end < n) {
-                if (DO_STATS) {
-                    stat_array_polystack_curves.add(end + n);
-                }
-                curves = curves_ref.widenArray(curves, end, end + n);
-            }
-            if (curveTypes.length <= numCurves) {
-                if (DO_STATS) {
-                    stat_array_polystack_curveTypes.add(numCurves + 1);
-                }
-                curveTypes = curveTypes_ref.widenArray(curveTypes,
-                                                       numCurves,
-                                                       numCurves + 1);
-            }
-        }
-
-        void pushCubic(double x0, double y0,
-                       double x1, double y1,
-                       double x2, double y2)
-        {
-            ensureSpace(6);
-            curveTypes[numCurves++] = TYPE_CUBICTO;
-            // we reverse the coordinate order to make popping easier
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x2;    _curves[e++] = y2;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushQuad(double x0, double y0,
-                      double x1, double y1)
-        {
-            ensureSpace(4);
-            curveTypes[numCurves++] = TYPE_QUADTO;
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushLine(double x, double y) {
-            ensureSpace(2);
-            curveTypes[numCurves++] = TYPE_LINETO;
-            curves[end++] = x;    curves[end++] = y;
-        }
-
-        void pullAll(final DPathConsumer2D io) {
-            final int nc = numCurves;
-            if (nc == 0) {
-                return;
-            }
-            if (DO_STATS) {
-                // update used marks:
-                if (numCurves > curveTypesUseMark) {
-                    curveTypesUseMark = numCurves;
-                }
-                if (end > curvesUseMark) {
-                    curvesUseMark = end;
-                }
-            }
-            final byte[]  _curveTypes = curveTypes;
-            final double[] _curves = curves;
-            int e = 0;
-
-            for (int i = 0; i < nc; i++) {
-                switch(_curveTypes[i]) {
-                case TYPE_LINETO:
-                    io.lineTo(_curves[e], _curves[e+1]);
-                    e += 2;
-                    continue;
-                case TYPE_CUBICTO:
-                    io.curveTo(_curves[e],   _curves[e+1],
-                               _curves[e+2], _curves[e+3],
-                               _curves[e+4], _curves[e+5]);
-                    e += 6;
-                    continue;
-                case TYPE_QUADTO:
-                    io.quadTo(_curves[e],   _curves[e+1],
-                              _curves[e+2], _curves[e+3]);
-                    e += 4;
-                    continue;
-                default:
-                }
-            }
-            numCurves = 0;
-            end = 0;
-        }
-
-        void popAll(final DPathConsumer2D io) {
-            int nc = numCurves;
-            if (nc == 0) {
-                return;
-            }
-            if (DO_STATS) {
-                // update used marks:
-                if (numCurves > curveTypesUseMark) {
-                    curveTypesUseMark = numCurves;
-                }
-                if (end > curvesUseMark) {
-                    curvesUseMark = end;
-                }
-            }
-            final byte[]  _curveTypes = curveTypes;
-            final double[] _curves = curves;
-            int e  = end;
-
-            while (nc != 0) {
-                switch(_curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    e -= 2;
-                    io.lineTo(_curves[e], _curves[e+1]);
-                    continue;
-                case TYPE_CUBICTO:
-                    e -= 6;
-                    io.curveTo(_curves[e],   _curves[e+1],
-                               _curves[e+2], _curves[e+3],
-                               _curves[e+4], _curves[e+5]);
-                    continue;
-                case TYPE_QUADTO:
-                    e -= 4;
-                    io.quadTo(_curves[e],   _curves[e+1],
-                              _curves[e+2], _curves[e+3]);
-                    continue;
-                default:
-                }
-            }
-            numCurves = 0;
-            end = 0;
-        }
-
-        @Override
-        public String toString() {
-            String ret = "";
-            int nc = numCurves;
-            int last = end;
-            int len;
-            while (nc != 0) {
-                switch(curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    len = 2;
-                    ret += "line: ";
-                    break;
-                case TYPE_QUADTO:
-                    len = 4;
-                    ret += "quad: ";
-                    break;
-                case TYPE_CUBICTO:
-                    len = 6;
-                    ret += "cubic: ";
-                    break;
-                default:
-                    len = 0;
-                }
-                last -= len;
-                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
-                                       + "\n";
-            }
-            return ret;
-        }
-    }
-
-    // a stack of integer indices
-    static final class IndexStack {
-
-        // integer capacity = edges count / 4 ~ 1024
-        private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2;
-
-        private int end;
-        private int[] indices;
-
-        // indices ref (dirty)
-        private final ArrayCacheInt.Reference indices_ref;
-
-        // used marks (stats only)
-        private int indicesUseMark;
-
-        private final StatLong stat_idxstack_indices;
-        private final Histogram hist_idxstack_indices;
-        private final StatLong stat_array_idxstack_indices;
-
-        IndexStack(final DRendererContext rdrCtx) {
-            this(rdrCtx, null, null, null);
-        }
-
-        IndexStack(final DRendererContext rdrCtx,
-                   final StatLong stat_idxstack_indices,
-                   final Histogram hist_idxstack_indices,
-                   final StatLong stat_array_idxstack_indices)
-        {
-            indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K
-            indices     = indices_ref.initial;
-            end = 0;
-
-            if (DO_STATS) {
-                indicesUseMark = 0;
-            }
-            this.stat_idxstack_indices = stat_idxstack_indices;
-            this.hist_idxstack_indices = hist_idxstack_indices;
-            this.stat_array_idxstack_indices = stat_array_idxstack_indices;
-        }
-
-        /**
-         * Disposes this PolyStack:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            end = 0;
-
-            if (DO_STATS) {
-                stat_idxstack_indices.add(indicesUseMark);
-                hist_idxstack_indices.add(indicesUseMark);
-
-                // reset marks
-                indicesUseMark = 0;
-            }
-
-            // Return arrays:
-            // indices is kept dirty
-            if (indices_ref.doCleanRef(indices)) {
-                indices = indices_ref.putArray(indices);
-            }
-        }
-
-        boolean isEmpty() {
-            return (end == 0);
-        }
-
-        void reset() {
-            end = 0;
-        }
-
-        void push(final int v) {
-            // remove redundant values (reverse order):
-            int[] _values = indices;
-            final int nc = end;
-            if (nc != 0) {
-                if (_values[nc - 1] == v) {
-                    // remove both duplicated values:
-                    end--;
-                    return;
-                }
-            }
-            if (_values.length <= nc) {
-                if (DO_STATS) {
-                    stat_array_idxstack_indices.add(nc + 1);
-                }
-                indices = _values = indices_ref.widenArray(_values, nc, nc + 1);
-            }
-            _values[end++] = v;
-
-            if (DO_STATS) {
-                // update used marks:
-                if (end > indicesUseMark) {
-                    indicesUseMark = end;
-                }
-            }
-        }
-
-        void pullAll(final double[] points, final DPathConsumer2D io, 
-                     final boolean moveFirst)
-        {
-            final int nc = end;
-            if (nc == 0) {
-                return;
-            }
-            final int[] _values = indices;
-            
-            int i = 0;
-            
-            if (moveFirst) {
-                int j = _values[i] << 1;
-                io.moveTo(points[j], points[j + 1]);
-                i++;
-            }
-
-            for (int j; i < nc; i++) {
-                j = _values[i] << 1;
-                io.lineTo(points[j], points[j + 1]);
-            }
-            end = 0;
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java b/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java
index 51dd530..72ffecf 100644
--- a/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java
+++ b/src/main/java/sun/java2d/marlin/DMarlinRenderingEngine.java
@@ -65,7 +65,7 @@ public final class DMarlinRenderingEngine extends RenderingEngine
     private enum NormMode {
         ON_WITH_AA {
             @Override
-            PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
+            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
                                                     final PathIterator src)
             {
                 // NormalizingPathIterator NearestPixelCenter:
@@ -74,7 +74,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
         },
         ON_NO_AA{
             @Override
-            PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
+            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
                                                     final PathIterator src)
             {
                 // NearestPixel NormalizingPathIterator:
@@ -83,7 +83,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
         },
         OFF{
             @Override
-            PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
+            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
                                                     final PathIterator src)
             {
                 // return original path iterator if normalization is disabled:
@@ -91,7 +91,7 @@ PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
             }
         };
 
-        abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
+        abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx,
                                                          PathIterator src);
     }
 
@@ -129,7 +129,7 @@ public Shape createStrokedShape(Shape src,
                                     float[] dashes,
                                     float dashphase)
     {
-        final DRendererContext rdrCtx = getRendererContext();
+        final RendererContext rdrCtx = getRendererContext();
         try {
             // initialize a large copyable Path2D to avoid a lot of array growing:
             final Path2D.Double p2d = rdrCtx.getPath2D();
@@ -151,7 +151,7 @@ public Shape createStrokedShape(Shape src,
             return new Path2D.Double(p2d);
 
         } finally {
-            // recycle the DRendererContext instance
+            // recycle the RendererContext instance
             returnRendererContext(rdrCtx);
         }
     }
@@ -249,7 +249,7 @@ public void strokeTo(Shape src,
                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
                 : NormMode.OFF;
 
-        final DRendererContext rdrCtx = getRendererContext();
+        final RendererContext rdrCtx = getRendererContext();
         try {
             if ((clip != null) &&
                     (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) {
@@ -284,12 +284,12 @@ public void strokeTo(Shape src,
             strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias,
                      rdrCtx.p2dAdapter.init(consumer));
         } finally {
-            // recycle the DRendererContext instance
+            // recycle the RendererContext instance
             returnRendererContext(rdrCtx);
         }
     }
 
-    void strokeTo(final DRendererContext rdrCtx,
+    void strokeTo(final RendererContext rdrCtx,
                   Shape src,
                   AffineTransform at,
                   BasicStroke bs,
@@ -390,7 +390,7 @@ private double userSpaceLineWidth(AffineTransform at, double lw) {
         return (lw / widthScale);
     }
 
-    void strokeTo(final DRendererContext rdrCtx,
+    void strokeTo(final RendererContext rdrCtx,
                   Shape src,
                   AffineTransform at,
                   double width,
@@ -494,7 +494,7 @@ void strokeTo(final DRendererContext rdrCtx,
             at = null;
         }
 
-        final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+        final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 
         if (DO_TRACE_PATH) {
             // trace Stroker:
@@ -724,7 +724,7 @@ static final class NearestPixelQuarter
         }
     }
 
-    private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi,
+    private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
                                DPathConsumer2D pc2d)
     {
         if (USE_PATH_SIMPLIFIER) {
@@ -910,17 +910,17 @@ public AATileGenerator getAATileGenerator(Shape s,
                                               int[] bbox)
     {
         MarlinTileGenerator ptg = null;
-        DRenderer r = null;
+        Renderer r = null;
 
-        final DRendererContext rdrCtx = getRendererContext();
+        final RendererContext rdrCtx = getRendererContext();
         try {
             if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
                 // Define the initial clip bounds:
                 final double[] clipRect = rdrCtx.clipRect;
 
                 // Adjust the clipping rectangle with the renderer offsets
-                final double rdrOffX = DRenderer.RDR_OFFSET_X;
-                final double rdrOffY = DRenderer.RDR_OFFSET_Y;
+                final double rdrOffX = Renderer.RDR_OFFSET_X;
+                final double rdrOffY = Renderer.RDR_OFFSET_Y;
 
                 // add a small rounding error:
                 final double margin = 1e-3d;
@@ -1033,9 +1033,9 @@ public AATileGenerator getAATileGenerator(double x, double y,
         }
 
         MarlinTileGenerator ptg = null;
-        DRenderer r = null;
+        Renderer r = null;
 
-        final DRendererContext rdrCtx = getRendererContext();
+        final RendererContext rdrCtx = getRendererContext();
         try {
             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
                                      clip.getWidth(), clip.getHeight(),
@@ -1104,15 +1104,15 @@ public float getMinimumAAPenSize() {
         }
     }
 
-    // --- DRendererContext handling ---
-    // use ThreadLocal or ConcurrentLinkedQueue to get one DRendererContext
+    // --- RendererContext handling ---
+    // use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext
     private static final boolean USE_THREAD_LOCAL;
 
     // reference type stored in either TL or CLQ
     static final int REF_TYPE;
 
-    // Per-thread DRendererContext
-    private static final ReentrantContextProvider<DRendererContext> RDR_CTX_PROVIDER;
+    // Per-thread RendererContext
+    private static final ReentrantContextProvider<RendererContext> RDR_CTX_PROVIDER;
 
     // Static initializer to use TL or CLQ mode
     static {
@@ -1137,19 +1137,19 @@ public float getMinimumAAPenSize() {
         }
 
         if (USE_THREAD_LOCAL) {
-            RDR_CTX_PROVIDER = new ReentrantContextProviderTL<DRendererContext>(REF_TYPE)
+            RDR_CTX_PROVIDER = new ReentrantContextProviderTL<RendererContext>(REF_TYPE)
                 {
                     @Override
-                    protected DRendererContext newContext() {
-                        return DRendererContext.createContext();
+                    protected RendererContext newContext() {
+                        return RendererContext.createContext();
                     }
                 };
         } else {
-            RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ<DRendererContext>(REF_TYPE)
+            RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ<RendererContext>(REF_TYPE)
                 {
                     @Override
-                    protected DRendererContext newContext() {
-                        return DRendererContext.createContext();
+                    protected RendererContext newContext() {
+                        return RendererContext.createContext();
                     }
                 };
         }
@@ -1273,26 +1273,26 @@ private static void logSettings(final String reClass) {
 
         logInfo("Renderer settings:");
         logInfo("SORT         = " + MergeSort.SORT_TYPE);
-        logInfo("CUB_DEC_BND  = " + DRenderer.CUB_DEC_BND);
-        logInfo("CUB_INC_BND  = " + DRenderer.CUB_INC_BND);
-        logInfo("QUAD_DEC_BND = " + DRenderer.QUAD_DEC_BND);
+        logInfo("CUB_DEC_BND  = " + Renderer.CUB_DEC_BND);
+        logInfo("CUB_INC_BND  = " + Renderer.CUB_INC_BND);
+        logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);
 
         logInfo("INITIAL_EDGES_CAPACITY               = "
                 + MarlinConst.INITIAL_EDGES_CAPACITY);
         logInfo("INITIAL_CROSSING_COUNT               = "
-                + DRenderer.INITIAL_CROSSING_COUNT);
+                + Renderer.INITIAL_CROSSING_COUNT);
 
         logInfo("=========================================================="
                 + "=====================");
     }
 
     /**
-     * Get the DRendererContext instance dedicated to the current thread
-     * @return DRendererContext instance
+     * Get the RendererContext instance dedicated to the current thread
+     * @return RendererContext instance
      */
     @SuppressWarnings({"unchecked"})
-    static DRendererContext getRendererContext() {
-        final DRendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();
+    static RendererContext getRendererContext() {
+        final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();
         if (DO_MONITORS) {
             rdrCtx.stats.mon_pre_getAATileGenerator.start();
         }
@@ -1300,10 +1300,10 @@ static DRendererContext getRendererContext() {
     }
 
     /**
-     * Reset and return the given DRendererContext instance for reuse
-     * @param rdrCtx DRendererContext instance
+     * Reset and return the given RendererContext instance for reuse
+     * @param rdrCtx RendererContext instance
      */
-    static void returnRendererContext(final DRendererContext rdrCtx) {
+    static void returnRendererContext(final RendererContext rdrCtx) {
         rdrCtx.dispose();
 
         if (DO_MONITORS) {
diff --git a/src/main/java/sun/java2d/marlin/DPathSimplifier.java b/src/main/java/sun/java2d/marlin/DPathSimplifier.java
deleted file mode 100644
index a33ae6c..0000000
--- a/src/main/java/sun/java2d/marlin/DPathSimplifier.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-final class DPathSimplifier implements DPathConsumer2D {
-
-    // distance threshold in pixels (device)
-    private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance();
-    // squared tolerance in pixels
-    private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD;
-
-    // members:
-    private DPathConsumer2D delegate;
-    // current reference point
-    private double cx, cy;
-    // flag indicating if the given point was skipped
-    private boolean skipped;
-    // last skipped point
-    private double sx, sy;
-
-    DPathSimplifier() {
-    }
-
-    DPathSimplifier init(final DPathConsumer2D delegate) {
-        if (this.delegate != delegate) {
-            this.delegate = delegate;
-        }
-        skipped = false;
-        return this; // fluent API
-    }
-
-    private void finishPath() {
-        if (skipped) {
-            _lineTo(sx, sy);
-        }
-    }
-
-    @Override
-    public void pathDone() {
-        finishPath();
-        delegate.pathDone();
-    }
-
-    @Override
-    public void closePath() {
-        finishPath();
-        delegate.closePath();
-    }
-
-    @Override
-    public void moveTo(final double xe, final double ye) {
-        finishPath();
-        delegate.moveTo(xe, ye);
-        cx = xe;
-        cy = ye;
-    }
-
-    @Override
-    public void lineTo(final double xe, final double ye) {
-        // Test if segment is too small:
-        double dx = (xe - cx);
-        double dy = (ye - cy);
-
-        if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-            skipped = true;
-            sx = xe;
-            sy = ye;
-            return;
-        }
-        _lineTo(xe, ye);
-    }
-
-    private void _lineTo(final double xe, final double ye) {
-        delegate.lineTo(xe, ye);
-        cx = xe;
-        cy = ye;
-        skipped = false;
-    }
-
-    @Override
-    public void quadTo(final double x1, final double y1,
-                       final double xe, final double ye)
-    {
-        // Test if curve is too small:
-        double dx = (xe - cx);
-        double dy = (ye - cy);
-
-        if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-            // check control points P1:
-            dx = (x1 - cx);
-            dy = (y1 - cy);
-
-            if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-                skipped = true;
-                sx = xe;
-                sy = ye;
-                return;
-            }
-        }
-        delegate.quadTo(x1, y1, xe, ye);
-        cx = xe;
-        cy = ye;
-        skipped = false;
-    }
-
-    @Override
-    public void curveTo(final double x1, final double y1,
-                        final double x2, final double y2,
-                        final double xe, final double ye)
-    {
-        // Test if curve is too small:
-        double dx = (xe - cx);
-        double dy = (ye - cy);
-
-        if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-            // check control points P1:
-            dx = (x1 - cx);
-            dy = (y1 - cy);
-
-            if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-                // check control points P2:
-                dx = (x2 - cx);
-                dy = (y2 - cy);
-
-                if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
-                    skipped = true;
-                    sx = xe;
-                    sy = ye;
-                    return;
-                }
-            }
-        }
-        delegate.curveTo(x1, y1, x2, y2, xe, ye);
-        cx = xe;
-        cy = ye;
-        skipped = false;
-    }
-
-    @Override
-    public long getNativeConsumer() {
-        return 0;
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DRenderer.java b/src/main/java/sun/java2d/marlin/DRenderer.java
deleted file mode 100644
index 3e48172..0000000
--- a/src/main/java/sun/java2d/marlin/DRenderer.java
+++ /dev/null
@@ -1,1578 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import static sun.java2d.marlin.OffHeapArray.SIZE_INT;
-import sun.misc.Unsafe;
-
-final class DRenderer implements DPathConsumer2D, MarlinRenderer {
-
-    static final boolean DISABLE_RENDER = false;
-
-    static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags();
-    static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics();
-
-    private static final int ALL_BUT_LSB = 0xFFFFFFFE;
-    private static final int ERR_STEP_MAX = 0x7FFFFFFF; // = 2^31 - 1
-
-    private static final double POWER_2_TO_32 = 0x1.0p32d;
-
-    // use double to make tosubpix methods faster (no int to double conversion)
-    static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X;
-    static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y;
-    static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
-    static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
-
-    static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X;
-    static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y;
-
-    // number of subpixels corresponding to a tile line
-    private static final int SUBPIXEL_TILE
-        = TILE_H << SUBPIXEL_LG_POSITIONS_Y;
-
-    // 2176 pixels (height) x 8 subpixels = 68K
-    static final int INITIAL_BUCKET_ARRAY
-        = INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y;
-
-    // crossing capacity = edges count / 4 ~ 1024
-    static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;
-
-    // common to all types of input path segments.
-    // OFFSET as bytes
-    // only integer values:
-    public static final long OFF_CURX_OR  = 0;
-    public static final long OFF_ERROR    = OFF_CURX_OR  + SIZE_INT;
-    public static final long OFF_BUMP_X   = OFF_ERROR    + SIZE_INT;
-    public static final long OFF_BUMP_ERR = OFF_BUMP_X   + SIZE_INT;
-    public static final long OFF_NEXT     = OFF_BUMP_ERR + SIZE_INT;
-    public static final long OFF_YMAX     = OFF_NEXT     + SIZE_INT;
-
-    // size of one edge in bytes
-    public static final int SIZEOF_EDGE_BYTES = (int)(OFF_YMAX + SIZE_INT);
-
-    // curve break into lines
-    // cubic error in subpixels to decrement step
-    private static final double CUB_DEC_ERR_SUBPIX
-        = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel
-    // cubic error in subpixels to increment step
-    private static final double CUB_INC_ERR_SUBPIX
-        = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel
-    // scale factor for Y-axis contribution to quad / cubic errors:
-    public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;
-
-    // TestNonAARasterization (JDK-8170879): cubics
-    // bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
-// 2018
-    // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max =  9), 4042 warnings (avg = 0,06)
-
-    // cubic bind length to decrement step
-    public static final double CUB_DEC_BND
-        = 8.0d * CUB_DEC_ERR_SUBPIX;
-    // cubic bind length to increment step
-    public static final double CUB_INC_BND
-        = 8.0d * CUB_INC_ERR_SUBPIX;
-
-    // cubic countlg
-    public static final int CUB_COUNT_LG = 2;
-    // cubic count = 2^countlg
-    private static final int CUB_COUNT = 1 << CUB_COUNT_LG;
-    // cubic count^2 = 4^countlg
-    private static final int CUB_COUNT_2 = 1 << (2 * CUB_COUNT_LG);
-    // cubic count^3 = 8^countlg
-    private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);
-    // cubic dt = 1 / count
-    private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT;
-    // cubic dt^2 = 1 / count^2 = 1 / 4^countlg
-    private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2;
-    // cubic dt^3 = 1 / count^3 = 1 / 8^countlg
-    private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3;
-
-    // quad break into lines
-    // quadratic error in subpixels
-    private static final double QUAD_DEC_ERR_SUBPIX
-        = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel
-
-    // TestNonAARasterization (JDK-8170879): quads
-    // bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
-// 2018
-    // 0.50px  = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10)
-
-    // quadratic bind length to decrement step
-    public static final double QUAD_DEC_BND
-        = 8.0d * QUAD_DEC_ERR_SUBPIX;
-
-//////////////////////////////////////////////////////////////////////////////
-//  SCAN LINE
-//////////////////////////////////////////////////////////////////////////////
-    // crossings ie subpixel edge x coordinates
-    private int[] crossings;
-    // auxiliary storage for crossings (merge sort)
-    private int[] aux_crossings;
-
-    // indices into the segment pointer lists. They indicate the "active"
-    // sublist in the segment lists (the portion of the list that contains
-    // all the segments that cross the next scan line).
-    private int edgeCount;
-    private int[] edgePtrs;
-    // auxiliary storage for edge pointers (merge sort)
-    private int[] aux_edgePtrs;
-
-    // max used for both edgePtrs and crossings (stats only)
-    private int activeEdgeMaxUsed;
-
-    // crossings ref (dirty)
-    private final ArrayCacheInt.Reference crossings_ref;
-    // edgePtrs ref (dirty)
-    private final ArrayCacheInt.Reference edgePtrs_ref;
-    // merge sort initial arrays (large enough to satisfy most usages) (1024)
-    // aux_crossings ref (dirty)
-    private final ArrayCacheInt.Reference aux_crossings_ref;
-    // aux_edgePtrs ref (dirty)
-    private final ArrayCacheInt.Reference aux_edgePtrs_ref;
-
-//////////////////////////////////////////////////////////////////////////////
-//  EDGE LIST
-//////////////////////////////////////////////////////////////////////////////
-    private int edgeMinY = Integer.MAX_VALUE;
-    private int edgeMaxY = Integer.MIN_VALUE;
-    private double edgeMinX = Double.POSITIVE_INFINITY;
-    private double edgeMaxX = Double.NEGATIVE_INFINITY;
-
-    // edges [ints] stored in off-heap memory
-    private final OffHeapArray edges;
-
-    private int[] edgeBuckets;
-    private int[] edgeBucketCounts; // 2*newedges + (1 if pruning needed)
-    // used range for edgeBuckets / edgeBucketCounts
-    private int buckets_minY;
-    private int buckets_maxY;
-
-    // edgeBuckets ref (clean)
-    private final ArrayCacheIntClean.Reference edgeBuckets_ref;
-    // edgeBucketCounts ref (clean)
-    private final ArrayCacheIntClean.Reference edgeBucketCounts_ref;
-
-    // Flattens using adaptive forward differencing. This only carries out
-    // one iteration of the AFD loop. All it does is update AFD variables (i.e.
-    // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
-    private void quadBreakIntoLinesAndAdd(double x0, double y0,
-                                          final DCurve c,
-                                          final double x2, final double y2)
-    {
-        int count = 1; // dt = 1 / count
-
-        // maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
-        double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;
-
-        final double _DEC_BND = QUAD_DEC_BND;
-
-        while (maxDD >= _DEC_BND) {
-            // divide step by half:
-            maxDD /= 4.0d; // error divided by 2^2 = 4
-
-            count <<= 1;
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_quadBreak_dec.add(count);
-            }
-        }
-
-        final int nL = count; // line count
-
-        if (count > 1) {
-            final double icount = 1.0d / count; // dt
-            final double icount2 = icount * icount; // dt^2
-
-            final double ddx = c.dbx * icount2;
-            final double ddy = c.dby * icount2;
-            double dx = c.bx * icount2 + c.cx * icount;
-            double dy = c.by * icount2 + c.cy * icount;
-
-            // we use x0, y0 to walk the line
-            for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {
-                x1 += dx;
-                y1 += dy;
-
-                addLine(x0, y0, x1, y1);
-                x0 = x1;
-                y0 = y1;
-            }
-        }
-        addLine(x0, y0, x2, y2);
-
-        if (DO_STATS) {
-            rdrCtx.stats.stat_rdr_quadBreak.add(nL);
-        }
-    }
-
-    // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
-    // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
-    // numerical errors, and our callers already have the exact values.
-    // Another alternative would be to pass all the control points, and call
-    // c.set here, but then too many numbers are passed around.
-    private void curveBreakIntoLinesAndAdd(double x0, double y0,
-                                           final DCurve c,
-                                           final double x3, final double y3)
-    {
-        int count            = CUB_COUNT;
-        final double icount  = CUB_INV_COUNT;   // dt
-        final double icount2 = CUB_INV_COUNT_2; // dt^2
-        final double icount3 = CUB_INV_COUNT_3; // dt^3
-
-        // the dx and dy refer to forward differencing variables, not the last
-        // coefficients of the "points" polynomial
-        double dddx, dddy, ddx, ddy, dx, dy;
-        dddx = 2.0d * c.dax * icount3;
-        dddy = 2.0d * c.day * icount3;
-        ddx = dddx + c.dbx * icount2;
-        ddy = dddy + c.dby * icount2;
-        dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
-        dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;
-
-        int nL = 0; // line count
-
-        final double _DEC_BND = CUB_DEC_BND;
-        final double _INC_BND = CUB_INC_BND;
-        final double _SCALE_DY = SCALE_DY;
-
-        // we use x0, y0 to walk the line
-        for (double x1 = x0, y1 = y0; count > 0; ) {
-            // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges
-
-            // double step:
-            // can only do this on even "count" values, because we must divide count by 2
-            while ((count % 2 == 0)
-                    && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) {
-                dx = 2.0d * dx + ddx;
-                dy = 2.0d * dy + ddy;
-                ddx = 4.0d * (ddx + dddx);
-                ddy = 4.0d * (ddy + dddy);
-                dddx *= 8.0d;
-                dddy *= 8.0d;
-
-                count >>= 1;
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
-                }
-            }
-
-            // divide step by half:
-            while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) {
-                dddx /= 8.0d;
-                dddy /= 8.0d;
-                ddx = ddx / 4.0d - dddx;
-                ddy = ddy / 4.0d - dddy;
-                dx = (dx - ddx) / 2.0d;
-                dy = (dy - ddy) / 2.0d;
-
-                count <<= 1;
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_rdr_curveBreak_dec.add(count);
-                }
-            }
-            if (--count == 0) {
-                break;
-            }
-
-            x1 += dx;
-            y1 += dy;
-            dx += ddx;
-            dy += ddy;
-            ddx += dddx;
-            ddy += dddy;
-
-            addLine(x0, y0, x1, y1);
-            x0 = x1;
-            y0 = y1;
-        }
-        addLine(x0, y0, x3, y3);
-
-        if (DO_STATS) {
-            rdrCtx.stats.stat_rdr_curveBreak.add(nL + 1);
-        }
-    }
-
-    private void addLine(double x1, double y1, double x2, double y2) {
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_addLine.start();
-        }
-        if (DO_STATS) {
-            rdrCtx.stats.stat_rdr_addLine.add(1);
-        }
-        int or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
-        if (y2 < y1) {
-            or = 0;
-            double tmp = y2;
-            y2 = y1;
-            y1 = tmp;
-            tmp = x2;
-            x2 = x1;
-            x1 = tmp;
-        }
-
-        // convert subpixel coordinates [double] into pixel positions [int]
-
-        // The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)
-        // Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply
-        // ceil(y1) or ceil(y2)
-        // upper integer (inclusive)
-        final int firstCrossing = FloatMath.max(FloatMath.ceil_int(y1), boundsMinY);
-
-        // note: use boundsMaxY (last Y exclusive) to compute correct coverage
-        // upper integer (exclusive)
-        final int lastCrossing  = FloatMath.min(FloatMath.ceil_int(y2), boundsMaxY);
-
-        /* skip horizontal lines in pixel space and clip edges
-           out of y range [boundsMinY; boundsMaxY] */
-        if (firstCrossing >= lastCrossing) {
-            if (DO_MONITORS) {
-                rdrCtx.stats.mon_rdr_addLine.stop();
-            }
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_addLine_skip.add(1);
-            }
-            return;
-        }
-
-        // edge min/max X/Y are in subpixel space (half-open interval):
-        // note: Use integer crossings to ensure consistent range within
-        // edgeBuckets / edgeBucketCounts arrays in case of NaN values (int = 0)
-        if (firstCrossing < edgeMinY) {
-            edgeMinY = firstCrossing;
-        }
-        if (lastCrossing > edgeMaxY) {
-            edgeMaxY = lastCrossing;
-        }
-
-        final double slope = (x1 - x2) / (y1 - y2);
-
-        if (slope >= 0.0d) { // <==> x1 < x2
-            if (x1 < edgeMinX) {
-                edgeMinX = x1;
-            }
-            if (x2 > edgeMaxX) {
-                edgeMaxX = x2;
-            }
-        } else {
-            if (x2 < edgeMinX) {
-                edgeMinX = x2;
-            }
-            if (x1 > edgeMaxX) {
-                edgeMaxX = x1;
-            }
-        }
-
-        // local variables for performance:
-        final int _SIZEOF_EDGE_BYTES = SIZEOF_EDGE_BYTES;
-
-        final OffHeapArray _edges = edges;
-
-        // get free pointer (ie length in bytes)
-        final int edgePtr = _edges.used;
-
-        // use substraction to avoid integer overflow:
-        if (_edges.length - edgePtr < _SIZEOF_EDGE_BYTES) {
-            // suppose _edges.length > _SIZEOF_EDGE_BYTES
-            // so doubling size is enough to add needed bytes
-            // note: throw IOOB if neededSize > 2Gb:
-            final long edgeNewSize = ArrayCacheConst.getNewLargeSize(
-                                        _edges.length,
-                                        edgePtr + _SIZEOF_EDGE_BYTES);
-
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_edges_resizes.add(edgeNewSize);
-            }
-            _edges.resize(edgeNewSize);
-        }
-
-
-        final Unsafe _unsafe = OffHeapArray.UNSAFE;
-        final long SIZE_INT = 4L;
-        long addr   = _edges.address + edgePtr;
-
-        // The x value must be bumped up to its position at the next HPC we will evaluate.
-        // "firstcrossing" is the (sub)pixel number where the next crossing occurs
-        // thus, the actual coordinate of the next HPC is "firstcrossing + 0.5"
-        // so the Y distance we cover is "firstcrossing + 0.5 - trueY".
-        // Note that since y1 (and y2) are already biased by -0.5 in tosubpixy(), we have
-        // y1 = trueY - 0.5
-        // trueY = y1 + 0.5
-        // firstcrossing + 0.5 - trueY = firstcrossing + 0.5 - (y1 + 0.5)
-        //                             = firstcrossing - y1
-        // The x coordinate at that HPC is then:
-        // x1_intercept = x1 + (firstcrossing - y1) * slope
-        // The next VPC is then given by:
-        // VPC index = ceil(x1_intercept - 0.5), or alternately
-        // VPC index = floor(x1_intercept - 0.5 + 1 - epsilon)
-        // epsilon is hard to pin down in floating point, but easy in fixed point, so if
-        // we convert to fixed point then these operations get easier:
-        // long x1_fixed = x1_intercept * 2^32;  (fixed point 32.32 format)
-        // curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1)
-        //                 = fixed_floor(x1_fixed + 2^31 - 1)
-        //                 = fixed_floor(x1_fixed + 0x7FFFFFFF)
-        // and error       = fixed_fract(x1_fixed + 0x7FFFFFFF)
-        final double x1_intercept = x1 + (firstCrossing - y1) * slope;
-
-        // inlined scalb(x1_intercept, 32):
-        final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))
-                                     + 0x7FFFFFFFL;
-        // curx:
-        // last bit corresponds to the orientation
-        _unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or);
-        addr += SIZE_INT;
-        _unsafe.putInt(addr,  ((int)  x1_fixed_biased) >>> 1);
-        addr += SIZE_INT;
-
-        // inlined scalb(slope, 32):
-        final long slope_fixed = (long) (POWER_2_TO_32 * slope);
-
-        // last bit set to 0 to keep orientation:
-        _unsafe.putInt(addr, (((int) (slope_fixed >> 31L)) & ALL_BUT_LSB));
-        addr += SIZE_INT;
-        _unsafe.putInt(addr,  ((int)  slope_fixed) >>> 1);
-        addr += SIZE_INT;
-
-        final int[] _edgeBuckets      = edgeBuckets;
-        final int[] _edgeBucketCounts = edgeBucketCounts;
-
-        final int _boundsMinY = boundsMinY;
-
-        // each bucket is a linked list. this method adds ptr to the
-        // start of the "bucket"th linked list.
-        final int bucketIdx = firstCrossing - _boundsMinY;
-
-        // pointer from bucket
-        _unsafe.putInt(addr, _edgeBuckets[bucketIdx]);
-        addr += SIZE_INT;
-        // y max (exclusive)
-        _unsafe.putInt(addr,  lastCrossing);
-
-        // Update buckets:
-        // directly the edge struct "pointer"
-        _edgeBuckets[bucketIdx]       = edgePtr;
-        _edgeBucketCounts[bucketIdx] += 2; // 1 << 1
-        // last bit means edge end
-        _edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1;
-
-        // update free pointer (ie length in bytes)
-        _edges.used += _SIZEOF_EDGE_BYTES;
-
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_addLine.stop();
-        }
-    }
-
-// END EDGE LIST
-//////////////////////////////////////////////////////////////////////////////
-
-    // Cache to store RLE-encoded coverage mask of the current primitive
-    final MarlinCache cache;
-
-    // Bounds of the drawing region, at subpixel precision.
-    private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
-
-    // Current winding rule
-    private int windingRule;
-
-    // Current drawing position, i.e., final point of last segment
-    private double x0, y0;
-
-    // Position of most recent 'moveTo' command
-    private double sx0, sy0;
-
-    // per-thread renderer context
-    final DRendererContext rdrCtx;
-    // dirty curve
-    private final DCurve curve;
-
-    // clean alpha array (zero filled)
-    private int[] alphaLine;
-
-    // alphaLine ref (clean)
-    private final ArrayCacheIntClean.Reference alphaLine_ref;
-
-    private boolean enableBlkFlags = false;
-    private boolean prevUseBlkFlags = false;
-
-    /* block flags (0|1) */
-    private int[] blkFlags;
-
-    // blkFlags ref (clean)
-    private final ArrayCacheIntClean.Reference blkFlags_ref;
-
-    DRenderer(final DRendererContext rdrCtx) {
-        this.rdrCtx = rdrCtx;
-        this.curve = rdrCtx.curve;
-        this.cache = rdrCtx.cache;
-
-        this.edges = rdrCtx.newOffHeapArray(INITIAL_EDGES_CAPACITY); // 96K
-
-        edgeBuckets_ref      = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K
-        edgeBucketCounts_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K
-
-        edgeBuckets      = edgeBuckets_ref.initial;
-        edgeBucketCounts = edgeBucketCounts_ref.initial;
-
-        // 4096 pixels large
-        alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 16K
-        alphaLine     = alphaLine_ref.initial;
-
-        crossings_ref     = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
-        aux_crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
-        edgePtrs_ref      = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
-        aux_edgePtrs_ref  = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
-
-        crossings     = crossings_ref.initial;
-        aux_crossings = aux_crossings_ref.initial;
-        edgePtrs      = edgePtrs_ref.initial;
-        aux_edgePtrs  = aux_edgePtrs_ref.initial;
-
-        blkFlags_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line
-        blkFlags     = blkFlags_ref.initial;
-    }
-
-    DRenderer init(final int pix_boundsX, final int pix_boundsY,
-                  final int pix_boundsWidth, final int pix_boundsHeight,
-                  final int windingRule)
-    {
-        this.windingRule = windingRule;
-
-        // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
-        this.boundsMinX =  pix_boundsX << SUBPIXEL_LG_POSITIONS_X;
-        this.boundsMaxX =
-            (pix_boundsX + pix_boundsWidth) << SUBPIXEL_LG_POSITIONS_X;
-        this.boundsMinY =  pix_boundsY << SUBPIXEL_LG_POSITIONS_Y;
-        this.boundsMaxY =
-            (pix_boundsY + pix_boundsHeight) << SUBPIXEL_LG_POSITIONS_Y;
-
-        if (DO_LOG_BOUNDS) {
-            MarlinUtils.logInfo("boundsXY = [" + boundsMinX + " ... "
-                                + boundsMaxX + "[ [" + boundsMinY + " ... "
-                                + boundsMaxY + "[");
-        }
-
-        // see addLine: ceil(boundsMaxY) => boundsMaxY + 1
-        // +1 for edgeBucketCounts
-        final int edgeBucketsLength = (boundsMaxY - boundsMinY) + 1;
-
-        if (edgeBucketsLength > INITIAL_BUCKET_ARRAY) {
-            if (DO_STATS) {
-                rdrCtx.stats.stat_array_renderer_edgeBuckets
-                    .add(edgeBucketsLength);
-                rdrCtx.stats.stat_array_renderer_edgeBucketCounts
-                    .add(edgeBucketsLength);
-            }
-            edgeBuckets = edgeBuckets_ref.getArray(edgeBucketsLength);
-            edgeBucketCounts = edgeBucketCounts_ref.getArray(edgeBucketsLength);
-        }
-
-        edgeMinY = Integer.MAX_VALUE;
-        edgeMaxY = Integer.MIN_VALUE;
-        edgeMinX = Double.POSITIVE_INFINITY;
-        edgeMaxX = Double.NEGATIVE_INFINITY;
-
-        // reset used mark:
-        edgeCount = 0;
-        activeEdgeMaxUsed = 0;
-        edges.used = 0;
-
-        return this; // fluent API
-    }
-
-    /**
-     * Disposes this renderer and recycle it clean up before reusing this instance
-     */
-    void dispose() {
-        if (DO_STATS) {
-            rdrCtx.stats.stat_rdr_activeEdges.add(activeEdgeMaxUsed);
-            rdrCtx.stats.stat_rdr_edges.add(edges.used);
-            rdrCtx.stats.stat_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);
-            rdrCtx.stats.hist_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);
-            rdrCtx.stats.totalOffHeap += edges.length;
-        }
-        // Return arrays:
-        if (crossings_ref.doCleanRef(crossings)) {
-            crossings = crossings_ref.putArray(crossings);
-        }
-        if (aux_crossings_ref.doCleanRef(aux_crossings)) {
-            aux_crossings = aux_crossings_ref.putArray(aux_crossings);
-        }
-
-        if (edgePtrs_ref.doCleanRef(edgePtrs)) {
-            edgePtrs = edgePtrs_ref.putArray(edgePtrs);
-        }
-        if (aux_edgePtrs_ref.doCleanRef(aux_edgePtrs)) {
-            aux_edgePtrs = aux_edgePtrs_ref.putArray(aux_edgePtrs);
-        }
-
-        if (alphaLine_ref.doSetRef(alphaLine)) {
-            alphaLine = alphaLine_ref.putArrayClean(alphaLine); // already zero filled
-        }
-        if (blkFlags_ref.doSetRef(blkFlags)) {
-            blkFlags  = blkFlags_ref.putArrayClean(blkFlags); // already zero filled
-        }
-
-        if (edgeMinY != Integer.MAX_VALUE) {
-            // if context is maked as DIRTY:
-            if (rdrCtx.dirty) {
-                // may happen if an exception if thrown in the pipeline processing:
-                // clear completely buckets arrays:
-                buckets_minY = 0;
-                buckets_maxY = boundsMaxY - boundsMinY;
-            }
-            // clear only used part
-            edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, buckets_minY,
-                                                                buckets_maxY);
-            edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts,
-                                                             buckets_minY,
-                                                             buckets_maxY + 1);
-        } else {
-            // unused arrays
-            if (edgeBuckets_ref.doSetRef(edgeBuckets)) {
-                edgeBuckets = edgeBuckets_ref.putArrayClean(edgeBuckets);
-            }
-            if (edgeBucketCounts_ref.doSetRef(edgeBucketCounts)) {
-                edgeBucketCounts = edgeBucketCounts_ref.putArrayClean(edgeBucketCounts);
-            }
-        }
-
-        // At last: resize back off-heap edges to initial size
-        if (edges.length != INITIAL_EDGES_CAPACITY) {
-            // note: may throw OOME:
-            edges.resize(INITIAL_EDGES_CAPACITY);
-        }
-        if (DO_CLEAN_DIRTY) {
-            // Force zero-fill dirty arrays:
-            edges.fill(BYTE_0);
-        }
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_endRendering.stop();
-        }
-        // recycle the RendererContext instance
-        DMarlinRenderingEngine.returnRendererContext(rdrCtx);
-    }
-
-    private static double tosubpixx(final double pix_x) {
-        return SUBPIXEL_SCALE_X * pix_x;
-    }
-
-    private static double tosubpixy(final double pix_y) {
-        // shift y by -0.5 for fast ceil(y - 0.5):
-        return SUBPIXEL_SCALE_Y * pix_y - 0.5d;
-    }
-
-    @Override
-    public void moveTo(final double pix_x0, final double pix_y0) {
-        closePath();
-        final double sx = tosubpixx(pix_x0);
-        final double sy = tosubpixy(pix_y0);
-        this.sx0 = sx;
-        this.sy0 = sy;
-        this.x0 = sx;
-        this.y0 = sy;
-    }
-
-    @Override
-    public void lineTo(final double pix_x1, final double pix_y1) {
-        final double x1 = tosubpixx(pix_x1);
-        final double y1 = tosubpixy(pix_y1);
-        addLine(x0, y0, x1, y1);
-        x0 = x1;
-        y0 = y1;
-    }
-
-    @Override
-    public void curveTo(final double pix_x1, final double pix_y1,
-                        final double pix_x2, final double pix_y2,
-                        final double pix_x3, final double pix_y3)
-    {
-        final double xe = tosubpixx(pix_x3);
-        final double ye = tosubpixy(pix_y3);
-        curve.set(x0, y0,
-                tosubpixx(pix_x1), tosubpixy(pix_y1),
-                tosubpixx(pix_x2), tosubpixy(pix_y2),
-                xe, ye);
-        curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
-        x0 = xe;
-        y0 = ye;
-    }
-
-    @Override
-    public void quadTo(final double pix_x1, final double pix_y1,
-                       final double pix_x2, final double pix_y2)
-    {
-        final double xe = tosubpixx(pix_x2);
-        final double ye = tosubpixy(pix_y2);
-        curve.set(x0, y0,
-                tosubpixx(pix_x1), tosubpixy(pix_y1),
-                xe, ye);
-        quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
-        x0 = xe;
-        y0 = ye;
-    }
-
-    @Override
-    public void closePath() {
-        if (x0 != sx0 || y0 != sy0) {
-            addLine(x0, y0, sx0, sy0);
-            x0 = sx0;
-            y0 = sy0;
-        }
-    }
-
-    @Override
-    public void pathDone() {
-        closePath();
-    }
-
-    @Override
-    public long getNativeConsumer() {
-        throw new InternalError("Renderer does not use a native consumer.");
-    }
-
-    private void _endRendering(final int ymin, final int ymax) {
-        if (DISABLE_RENDER) {
-            return;
-        }
-
-        // Get X bounds as true pixel boundaries to compute correct pixel coverage:
-        final int bboxx0 = bbox_spminX;
-        final int bboxx1 = bbox_spmaxX;
-
-        final boolean windingRuleEvenOdd = (windingRule == WIND_EVEN_ODD);
-
-        // Useful when processing tile line by tile line
-        final int[] _alpha = alphaLine;
-
-        // local vars (performance):
-        final MarlinCache _cache = cache;
-        final OffHeapArray _edges = edges;
-        final int[] _edgeBuckets = edgeBuckets;
-        final int[] _edgeBucketCounts = edgeBucketCounts;
-
-        int[] _crossings = this.crossings;
-        int[] _edgePtrs  = this.edgePtrs;
-
-        // merge sort auxiliary storage:
-        int[] _aux_crossings = this.aux_crossings;
-        int[] _aux_edgePtrs  = this.aux_edgePtrs;
-
-        // copy constants:
-        final long _OFF_ERROR    = OFF_ERROR;
-        final long _OFF_BUMP_X   = OFF_BUMP_X;
-        final long _OFF_BUMP_ERR = OFF_BUMP_ERR;
-
-        final long _OFF_NEXT     = OFF_NEXT;
-        final long _OFF_YMAX     = OFF_YMAX;
-
-        final int _ALL_BUT_LSB   = ALL_BUT_LSB;
-        final int _ERR_STEP_MAX  = ERR_STEP_MAX;
-
-        // unsafe I/O:
-        final Unsafe _unsafe = OffHeapArray.UNSAFE;
-        final long    addr0  = _edges.address;
-        long addr;
-        final int _SUBPIXEL_LG_POSITIONS_X = SUBPIXEL_LG_POSITIONS_X;
-        final int _SUBPIXEL_LG_POSITIONS_Y = SUBPIXEL_LG_POSITIONS_Y;
-        final int _SUBPIXEL_MASK_X = SUBPIXEL_MASK_X;
-        final int _SUBPIXEL_MASK_Y = SUBPIXEL_MASK_Y;
-        final int _SUBPIXEL_POSITIONS_X = SUBPIXEL_POSITIONS_X;
-
-        final int _MIN_VALUE = Integer.MIN_VALUE;
-        final int _MAX_VALUE = Integer.MAX_VALUE;
-
-        // Now we iterate through the scanlines. We must tell emitRow the coord
-        // of the first non-transparent pixel, so we must keep accumulators for
-        // the first and last pixels of the section of the current pixel row
-        // that we will emit.
-        // We also need to accumulate pix_bbox, but the iterator does it
-        // for us. We will just get the values from it once this loop is done
-        int minX = _MAX_VALUE;
-        int maxX = _MIN_VALUE;
-
-        int y = ymin;
-        int bucket = y - boundsMinY;
-
-        int numCrossings = this.edgeCount;
-        int edgePtrsLen = _edgePtrs.length;
-        int crossingsLen = _crossings.length;
-        int _arrayMaxUsed = activeEdgeMaxUsed;
-        int ptrLen = 0, newCount, ptrEnd;
-
-        int bucketcount, i, j, ecur;
-        int cross, lastCross;
-        int x0, x1, tmp, sum, prev, curx, curxo, crorientation, err;
-        int pix_x, pix_xmaxm1, pix_xmax;
-
-        int low, high, mid, prevNumCrossings;
-        boolean useBinarySearch;
-
-        final int[] _blkFlags = blkFlags;
-        final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
-        final int _BLK_SIZE = BLOCK_SIZE;
-
-        final boolean _enableBlkFlagsHeuristics = ENABLE_BLOCK_FLAGS_HEURISTICS && this.enableBlkFlags;
-
-        // Use block flags if large pixel span and few crossings:
-        // ie mean(distance between crossings) is high
-        boolean useBlkFlags = this.prevUseBlkFlags;
-
-        final int stroking = rdrCtx.stroking;
-
-        int lastY = -1; // last emited row
-
-        final DPQSSorterContext sorter = rdrCtx.sorterCtx;
-        boolean skipISort, useDPQS;
-
-        // Iteration on scanlines
-        for (; y < ymax; y++, bucket++) {
-            // --- from former ScanLineIterator.next()
-            bucketcount = _edgeBucketCounts[bucket];
-
-            // marker on previously sorted edges:
-            prevNumCrossings = numCrossings;
-
-            // bucketCount indicates new edge / edge end:
-            if (bucketcount != 0) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_rdr_activeEdges_updates.add(prevNumCrossings);
-                }
-
-                // last bit set to 1 means that edges ends
-                if ((bucketcount & 0x1) != 0) {
-                    // eviction in active edge list
-                    // cache edges[] address + offset
-                    addr = addr0 + _OFF_YMAX;
-
-                    for (i = 0, newCount = 0; i < prevNumCrossings; i++) {
-                        // get the pointer to the edge
-                        ecur = _edgePtrs[i];
-                        // random access so use unsafe:
-                        if (_unsafe.getInt(addr + ecur) > y) {
-                            _edgePtrs[newCount++] = ecur;
-                        }
-                    }
-                    // update marker on sorted edges minus removed edges:
-                    prevNumCrossings = numCrossings = newCount;
-                }
-
-                ptrLen = bucketcount >> 1; // number of new edge
-
-                if (ptrLen != 0) {
-                    if (DO_STATS) {
-                        rdrCtx.stats.stat_rdr_activeEdges_adds.add(ptrLen);
-                        if (ptrLen > 10) {
-                            rdrCtx.stats.stat_rdr_activeEdges_adds_high.add(ptrLen);
-                        }
-                    }
-                    ptrEnd = numCrossings + ptrLen;
-
-                    if (edgePtrsLen < ptrEnd) {
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd);
-                        }
-                        this.edgePtrs = _edgePtrs
-                            = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // bad mark ? TODO: fix edge ptr mark
-                                                      ptrEnd);
-
-                        edgePtrsLen = _edgePtrs.length;
-                        // Get larger auxiliary storage:
-                        aux_edgePtrs_ref.putArray(_aux_edgePtrs);
-
-                        // use ArrayCache.getNewSize() to use the same growing
-                        // factor than widenArray():
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_array_renderer_aux_edgePtrs.add(ptrEnd);
-                        }
-                        this.aux_edgePtrs = _aux_edgePtrs
-                            = aux_edgePtrs_ref.getArray(
-                                ArrayCacheConst.getNewSize(numCrossings, ptrEnd)
-                            );
-                    }
-
-                    // cache edges[] address + offset
-                    addr = addr0 + _OFF_NEXT;
-
-                    // add new edges to active edge list:
-                    for (ecur = _edgeBuckets[bucket];
-                         numCrossings < ptrEnd; numCrossings++)
-                    {
-                        // store the pointer to the edge
-                        _edgePtrs[numCrossings] = ecur;
-                        // random access so use unsafe:
-                        ecur = _unsafe.getInt(addr + ecur);
-                    }
-
-                    if (crossingsLen < numCrossings) {
-                        // Get larger array:
-                        crossings_ref.putArray(_crossings);
-
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_array_renderer_crossings
-                                .add(numCrossings);
-                        }
-                        this.crossings = _crossings
-                            = crossings_ref.getArray(numCrossings);
-
-                        // Get larger auxiliary storage:
-                        aux_crossings_ref.putArray(_aux_crossings);
-
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_array_renderer_aux_crossings
-                                .add(numCrossings);
-                        }
-                        this.aux_crossings = _aux_crossings
-                            = aux_crossings_ref.getArray(numCrossings);
-
-                        crossingsLen = _crossings.length;
-                    }
-                    if (DO_STATS) {
-                        // update max used mark
-                        if (numCrossings > _arrayMaxUsed) {
-                            _arrayMaxUsed = numCrossings;
-                        }
-                    }
-                } // ptrLen != 0
-            } // bucketCount != 0
-
-
-            if (numCrossings != 0) {
-                /*
-                 * thresholds to switch to optimized merge sort
-                 * for newly added edges + final merge pass.
-                 */
-                if (((numCrossings <= 40) || ((ptrLen <= 10) && (numCrossings <= MergeSort.DISABLE_ISORT_THRESHOLD)))) {
-                    if (DO_STATS) {
-                        rdrCtx.stats.hist_rdr_crossings.add(numCrossings);
-                        rdrCtx.stats.hist_rdr_crossings_adds.add(ptrLen);
-                    }
-
-                    /*
-                     * threshold to use binary insertion sort instead of
-                     * straight insertion sort (to reduce minimize comparisons).
-                     */
-                    useBinarySearch = (numCrossings >= 20);
-
-                    // if small enough:
-                    lastCross = _MIN_VALUE;
-
-                    for (i = 0; i < numCrossings; i++) {
-                        // get the pointer to the edge
-                        ecur = _edgePtrs[i];
-
-                        /* convert subpixel coordinates into pixel
-                            positions for coming scanline */
-                        /* note: it is faster to always update edges even
-                           if it is removed from AEL for coming or last scanline */
-
-                        // random access so use unsafe:
-                        addr = addr0 + ecur; // ecur + OFF_F_CURX
-
-                        // get current crossing:
-                        curx = _unsafe.getInt(addr);
-
-                        // update crossing with orientation at last bit:
-                        cross = curx;
-
-                        // Increment x using DDA (fixed point):
-                        curx += _unsafe.getInt(addr + _OFF_BUMP_X);
-
-                        // Increment error:
-                        err  =  _unsafe.getInt(addr + _OFF_ERROR)
-                              + _unsafe.getInt(addr + _OFF_BUMP_ERR);
-
-                        // Manual carry handling:
-                        // keep sign and carry bit only and ignore last bit (preserve orientation):
-                        _unsafe.putInt(addr,               curx - ((err >> 30) & _ALL_BUT_LSB));
-                        _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
-
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);
-                        }
-
-                        // insertion sort of crossings:
-                        if (cross < lastCross) {
-                            if (DO_STATS) {
-                                rdrCtx.stats.stat_rdr_crossings_sorts.add(i);
-                            }
-
-                            /* use binary search for newly added edges
-                               in crossings if arrays are large enough */
-                            if (useBinarySearch && (i >= prevNumCrossings)) {
-                                if (DO_STATS) {
-                                    rdrCtx.stats.stat_rdr_crossings_bsearch.add(i);
-                                }
-                                low = 0;
-                                high = i - 1;
-
-                                do {
-                                    // note: use signed shift (not >>>) for performance
-                                    // as indices are small enough to exceed Integer.MAX_VALUE
-                                    mid = (low + high) >> 1;
-
-                                    if (_crossings[mid] < cross) {
-                                        low = mid + 1;
-                                    } else {
-                                        high = mid - 1;
-                                    }
-                                } while (low <= high);
-
-                                for (j = i - 1; j >= low; j--) {
-                                    _crossings[j + 1] = _crossings[j];
-                                    _edgePtrs [j + 1] = _edgePtrs[j];
-                                }
-                                _crossings[low] = cross;
-                                _edgePtrs [low] = ecur;
-
-                            } else {
-                                j = i - 1;
-                                _crossings[i] = _crossings[j];
-                                _edgePtrs[i]  = _edgePtrs[j];
-
-                                while ((--j >= 0) && (_crossings[j] > cross)) {
-                                    _crossings[j + 1] = _crossings[j];
-                                    _edgePtrs [j + 1] = _edgePtrs[j];
-                                }
-                                _crossings[j + 1] = cross;
-                                _edgePtrs [j + 1] = ecur;
-                            }
-
-                        } else {
-                            _crossings[i] = lastCross = cross;
-                        }
-                    }
-                } else {
-                    if (DO_STATS) {
-                        rdrCtx.stats.stat_rdr_crossings_msorts.add(numCrossings);
-                        rdrCtx.stats.hist_rdr_crossings_ratio
-                            .add((1000 * ptrLen) / numCrossings);
-                        rdrCtx.stats.hist_rdr_crossings_msorts.add(numCrossings);
-                        rdrCtx.stats.hist_rdr_crossings_msorts_adds.add(ptrLen);
-                    }
-
-                    // Copy sorted data in auxiliary arrays
-                    // and perform insertion sort on almost sorted data
-                    // (ie i < prevNumCrossings):
-
-                    skipISort = (prevNumCrossings >= MergeSort.DISABLE_ISORT_THRESHOLD);
-                    useDPQS   = MergeSort.USE_DPQS && (skipISort || (ptrLen >= MergeSort.DPQS_THRESHOLD));
-
-                    if (DO_STATS && useDPQS) {
-                        rdrCtx.stats.stat_rdr_crossings_dpqs.add((skipISort) ? numCrossings : ptrLen);
-                    }
-
-                    lastCross = _MIN_VALUE;
-
-                    for (i = 0; i < numCrossings; i++) {
-                        // get the pointer to the edge
-                        ecur = _edgePtrs[i];
-
-                        /* convert subpixel coordinates into pixel
-                            positions for coming scanline */
-                        /* note: it is faster to always update edges even
-                           if it is removed from AEL for coming or last scanline */
-
-                        // random access so use unsafe:
-                        addr = addr0 + ecur; // ecur + OFF_F_CURX
-
-                        // get current crossing:
-                        curx = _unsafe.getInt(addr);
-
-                        // update crossing with orientation at last bit:
-                        cross = curx;
-
-                        // Increment x using DDA (fixed point):
-                        curx += _unsafe.getInt(addr + _OFF_BUMP_X);
-
-                        // Increment error:
-                        err  =  _unsafe.getInt(addr + _OFF_ERROR)
-                              + _unsafe.getInt(addr + _OFF_BUMP_ERR);
-
-                        // Manual carry handling:
-                        // keep sign and carry bit only and ignore last bit (preserve orientation):
-                        _unsafe.putInt(addr,               curx - ((err >> 30) & _ALL_BUT_LSB));
-                        _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
-
-                        if (DO_STATS) {
-                            rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);
-                        }
-
-                        if (skipISort) {
-                            if (useDPQS) {
-                                // simply store crossing as edgePtrs is in-place:
-                                // will be sorted efficiently by DPQS later:
-                                _crossings[i]     = cross;
-                            } else {
-                                // store crossing/edgePtrs in auxiliary arrays:
-                                // will be sorted efficiently by MergeSort later:
-                                _aux_crossings[i] = cross;
-                                _aux_edgePtrs [i] = ecur;
-                            }
-                        } else if (i >= prevNumCrossings) {
-                            if (useDPQS) {
-                                // store crossing/edgePtrs in auxiliary arrays:
-                                // will be sorted efficiently by DPQS later:
-                                _aux_crossings[i] = cross;
-                                _aux_edgePtrs [i] = ecur;
-                            } else {
-                                // simply store crossing as edgePtrs is in-place:
-                                // will be sorted efficiently by MergeSort later:
-                                _crossings[i]      = cross;
-                            }
-                        } else {
-                            if (cross < lastCross) {
-                                if (DO_STATS) {
-                                    rdrCtx.stats.stat_rdr_crossings_sorts.add(i);
-                                }
-                                // (straight) insertion sort of crossings:
-                                j = i - 1;
-                                _aux_crossings[i] = _aux_crossings[j];
-                                _aux_edgePtrs [i]  = _aux_edgePtrs[j];
-
-                                while ((--j >= 0) && (_aux_crossings[j] > cross)) {
-                                    _aux_crossings[j + 1] = _aux_crossings[j];
-                                    _aux_edgePtrs [j + 1] = _aux_edgePtrs[j];
-                                }
-                                _aux_crossings[j + 1] = cross;
-                                _aux_edgePtrs [j + 1] = ecur;
-                            } else {
-                                // auxiliary storage:
-                                _aux_crossings[i] = lastCross = cross;
-                                _aux_edgePtrs [i] = ecur;
-                            }
-                        }
-                    }
-
-                    // use Mergesort using auxiliary arrays (sort only right part)
-                    MergeSort.mergeSortNoCopy(_crossings,     _edgePtrs,
-                                              _aux_crossings, _aux_edgePtrs,
-                                              numCrossings, prevNumCrossings,
-                                              skipISort, sorter, useDPQS
-                                            );
-                }
-
-                // reset ptrLen
-                ptrLen = 0;
-                // --- from former ScanLineIterator.next()
-
-
-                /* note: bboxx0 and bboxx1 must be pixel boundaries
-                   to have correct coverage computation */
-
-                // right shift on crossings to get the x-coordinate:
-                curxo = _crossings[0];
-                x0    = curxo >> 1;
-                if (x0 < minX) {
-                    minX = x0; // subpixel coordinate
-                }
-
-                x1 = _crossings[numCrossings - 1] >> 1;
-                if (x1 > maxX) {
-                    maxX = x1; // subpixel coordinate
-                }
-
-
-                // compute pixel coverages
-                prev = curx = x0;
-                // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
-                // last bit contains orientation (0 or 1)
-                crorientation = ((curxo & 0x1) << 1) - 1;
-
-                if (windingRuleEvenOdd) {
-                    sum = crorientation;
-
-                    // Even Odd winding rule: take care of mask ie sum(orientations)
-                    for (i = 1; i < numCrossings; i++) {
-                        curxo = _crossings[i];
-                        curx  =  curxo >> 1;
-                        // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
-                        // last bit contains orientation (0 or 1)
-                        crorientation = ((curxo & 0x1) << 1) - 1;
-
-                        if ((sum & 0x1) != 0) {
-                            // TODO: perform line clipping on left-right sides
-                            // to avoid such bound checks:
-                            x0 = (prev > bboxx0) ? prev : bboxx0;
-
-                            if (curx < bboxx1) {
-                                x1 = curx;
-                            } else {
-                                x1 = bboxx1;
-                                // skip right side (fast exit loop):
-                                i = numCrossings;
-                            }
-
-                            if (x0 < x1) {
-                                x0 -= bboxx0; // turn x0, x1 from coords to indices
-                                x1 -= bboxx0; // in the alpha array.
-
-                                pix_x      =  x0      >> _SUBPIXEL_LG_POSITIONS_X;
-                                pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
-
-                                if (pix_x == pix_xmaxm1) {
-                                    // Start and end in same pixel
-                                    tmp = (x1 - x0); // number of subpixels
-                                    _alpha[pix_x    ] += tmp;
-                                    _alpha[pix_x + 1] -= tmp;
-
-                                    if (useBlkFlags) {
-                                        // flag used blocks:
-                                        // note: block processing handles extra pixel:
-                                        _blkFlags[pix_x    >> _BLK_SIZE_LG] = 1;
-                                    }
-                                } else {
-                                    tmp = (x0 & _SUBPIXEL_MASK_X);
-                                    _alpha[pix_x    ]
-                                        += (_SUBPIXEL_POSITIONS_X - tmp);
-                                    _alpha[pix_x + 1]
-                                        += tmp;
-
-                                    pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
-
-                                    tmp = (x1 & _SUBPIXEL_MASK_X);
-                                    _alpha[pix_xmax    ]
-                                        -= (_SUBPIXEL_POSITIONS_X - tmp);
-                                    _alpha[pix_xmax + 1]
-                                        -= tmp;
-
-                                    if (useBlkFlags) {
-                                        // flag used blocks:
-                                        // note: block processing handles extra pixel:
-                                        _blkFlags[pix_x    >> _BLK_SIZE_LG] = 1;
-                                        _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
-                                    }
-                                }
-                            }
-                        }
-
-                        sum += crorientation;
-                        prev = curx;
-                    }
-                } else {
-                    // Non-zero winding rule: optimize that case (default)
-                    // and avoid processing intermediate crossings
-                    for (i = 1, sum = 0;; i++) {
-                        sum += crorientation;
-
-                        if (sum != 0) {
-                            // prev = min(curx)
-                            if (prev > curx) {
-                                prev = curx;
-                            }
-                        } else {
-                            // TODO: perform line clipping on left-right sides
-                            // to avoid such bound checks:
-                            x0 = (prev > bboxx0) ? prev : bboxx0;
-
-                            if (curx < bboxx1) {
-                                x1 = curx;
-                            } else {
-                                x1 = bboxx1;
-                                // skip right side (fast exit loop):
-                                i = numCrossings;
-                            }
-
-                            if (x0 < x1) {
-                                x0 -= bboxx0; // turn x0, x1 from coords to indices
-                                x1 -= bboxx0; // in the alpha array.
-
-                                pix_x      =  x0      >> _SUBPIXEL_LG_POSITIONS_X;
-                                pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
-
-                                if (pix_x == pix_xmaxm1) {
-                                    // Start and end in same pixel
-                                    tmp = (x1 - x0); // number of subpixels
-                                    _alpha[pix_x    ] += tmp;
-                                    _alpha[pix_x + 1] -= tmp;
-
-                                    if (useBlkFlags) {
-                                        // flag used blocks:
-                                        // note: block processing handles extra pixel:
-                                        _blkFlags[pix_x    >> _BLK_SIZE_LG] = 1;
-                                    }
-                                } else {
-                                    tmp = (x0 & _SUBPIXEL_MASK_X);
-                                    _alpha[pix_x    ]
-                                        += (_SUBPIXEL_POSITIONS_X - tmp);
-                                    _alpha[pix_x + 1]
-                                        += tmp;
-
-                                    pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
-
-                                    tmp = (x1 & _SUBPIXEL_MASK_X);
-                                    _alpha[pix_xmax    ]
-                                        -= (_SUBPIXEL_POSITIONS_X - tmp);
-                                    _alpha[pix_xmax + 1]
-                                        -= tmp;
-
-                                    if (useBlkFlags) {
-                                        // flag used blocks:
-                                        // note: block processing handles extra pixel:
-                                        _blkFlags[pix_x    >> _BLK_SIZE_LG] = 1;
-                                        _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
-                                    }
-                                }
-                            }
-                            prev = _MAX_VALUE;
-                        }
-
-                        if (i == numCrossings) {
-                            break;
-                        }
-
-                        curxo = _crossings[i];
-                        curx  =  curxo >> 1;
-                        // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
-                        // last bit contains orientation (0 or 1)
-                        crorientation = ((curxo & 0x1) << 1) - 1;
-                    }
-                }
-            } // numCrossings > 0
-
-            // even if this last row had no crossings, alpha will be zeroed
-            // from the last emitRow call. But this doesn't matter because
-            // maxX < minX, so no row will be emitted to the MarlinCache.
-            if ((y & _SUBPIXEL_MASK_Y) == _SUBPIXEL_MASK_Y) {
-                lastY = y >> _SUBPIXEL_LG_POSITIONS_Y;
-
-                // convert subpixel to pixel coordinate within boundaries:
-                minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
-                maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
-
-                if (maxX >= minX) {
-                    // note: alpha array will be zeroed by copyAARow()
-                    // +1 because alpha [pix_minX; pix_maxX[
-                    // fix range [x0; x1[
-                    // note: if x1=bboxx1, then alpha is written up to bboxx1+1
-                    // inclusive: alpha[bboxx1] ignored, alpha[bboxx1+1] == 0
-                    // (normally so never cleared below)
-                    copyAARow(_alpha, lastY, minX, maxX + 1, useBlkFlags);
-
-                    // speculative for next pixel row (scanline coherence):
-                    if (_enableBlkFlagsHeuristics) {
-                        // Use block flags if large pixel span and few crossings:
-                        // ie mean(distance between crossings) is larger than
-                        // 1 block size;
-
-                        // fast check width:
-                        maxX -= minX;
-
-                        // if stroking: numCrossings /= 2
-                        // => shift numCrossings by 1
-                        // condition = (width / (numCrossings - 1)) > blockSize
-                        useBlkFlags = (maxX > _BLK_SIZE) && (maxX >
-                            (((numCrossings >> stroking) - 1) << _BLK_SIZE_LG));
-
-                        if (DO_STATS) {
-                            tmp = FloatMath.max(1,
-                                    ((numCrossings >> stroking) - 1));
-                            rdrCtx.stats.hist_tile_generator_encoding_dist
-                                .add(maxX / tmp);
-                        }
-                    }
-                } else {
-                    _cache.clearAARow(lastY);
-                }
-                minX = _MAX_VALUE;
-                maxX = _MIN_VALUE;
-            }
-        } // scan line iterator
-
-        // Emit final row
-        y--;
-        y >>= _SUBPIXEL_LG_POSITIONS_Y;
-
-        // convert subpixel to pixel coordinate within boundaries:
-        minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
-        maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
-
-        if (maxX >= minX) {
-            // note: alpha array will be zeroed by copyAARow()
-            // +1 because alpha [pix_minX; pix_maxX[
-            // fix range [x0; x1[
-            // note: if x1=bboxx1, then alpha is written up to bboxx1+1
-            // inclusive: alpha[bboxx1] ignored then cleared and
-            // alpha[bboxx1+1] == 0 (normally so never cleared after)
-            copyAARow(_alpha, y, minX, maxX + 1, useBlkFlags);
-        } else if (y != lastY) {
-            _cache.clearAARow(y);
-        }
-
-        // update member:
-        edgeCount = numCrossings;
-        prevUseBlkFlags = useBlkFlags;
-
-        if (DO_STATS) {
-            // update max used mark
-            activeEdgeMaxUsed = _arrayMaxUsed;
-        }
-    }
-
-    boolean endRendering() {
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_endRendering.start();
-        }
-        if (edgeMinY == Integer.MAX_VALUE) {
-            return false; // undefined edges bounds
-        }
-
-        // bounds as half-open intervals
-        final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX);
-        final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX);
-
-        // edge Min/Max Y are already rounded to subpixels within bounds:
-        final int spminY = edgeMinY;
-        final int spmaxY = edgeMaxY;
-
-        buckets_minY = spminY - boundsMinY;
-        buckets_maxY = spmaxY - boundsMinY;
-
-        if (DO_LOG_BOUNDS) {
-            MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX
-                                + "[ [" + edgeMinY + " ... " + edgeMaxY + "[");
-            MarlinUtils.logInfo("spXY    = [" + spminX + " ... " + spmaxX
-                                + "[ [" + spminY + " ... " + spmaxY + "[");
-        }
-
-        // test clipping for shapes out of bounds
-        if ((spminX >= spmaxX) || (spminY >= spmaxY)) {
-            return false;
-        }
-
-        // half open intervals
-        // inclusive:
-        final int pminX =  spminX                    >> SUBPIXEL_LG_POSITIONS_X;
-        // exclusive:
-        final int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
-        // inclusive:
-        final int pminY =  spminY                    >> SUBPIXEL_LG_POSITIONS_Y;
-        // exclusive:
-        final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
-
-        // store BBox to answer ptg.getBBox():
-        this.cache.init(pminX, pminY, pmaxX, pmaxY);
-
-        // Heuristics for using block flags:
-        if (ENABLE_BLOCK_FLAGS) {
-            enableBlkFlags = this.cache.useRLE;
-            prevUseBlkFlags = enableBlkFlags && !ENABLE_BLOCK_FLAGS_HEURISTICS;
-
-            if (enableBlkFlags) {
-                // ensure blockFlags array is large enough:
-                // note: +2 to ensure enough space left at end
-                final int blkLen = ((pmaxX - pminX) >> BLOCK_SIZE_LG) + 2;
-                if (blkLen > INITIAL_ARRAY) {
-                    blkFlags = blkFlags_ref.getArray(blkLen);
-                }
-            }
-        }
-
-        // memorize the rendering bounding box:
-        /* note: bbox_spminX and bbox_spmaxX must be pixel boundaries
-           to have correct coverage computation */
-        // inclusive:
-        bbox_spminX = pminX << SUBPIXEL_LG_POSITIONS_X;
-        // exclusive:
-        bbox_spmaxX = pmaxX << SUBPIXEL_LG_POSITIONS_X;
-        // inclusive:
-        bbox_spminY = spminY;
-        // exclusive:
-        bbox_spmaxY = spmaxY;
-
-        if (DO_LOG_BOUNDS) {
-            MarlinUtils.logInfo("pXY       = [" + pminX + " ... " + pmaxX
-                                + "[ [" + pminY + " ... " + pmaxY + "[");
-            MarlinUtils.logInfo("bbox_spXY = [" + bbox_spminX + " ... "
-                                + bbox_spmaxX + "[ [" + bbox_spminY + " ... "
-                                + bbox_spmaxY + "[");
-        }
-
-        // Prepare alpha line:
-        // add 2 to better deal with the last pixel in a pixel row.
-        final int width = (pmaxX - pminX) + 2;
-
-        // Useful when processing tile line by tile line
-        if (width > INITIAL_AA_ARRAY) {
-            if (DO_STATS) {
-                rdrCtx.stats.stat_array_renderer_alphaline.add(width);
-            }
-            alphaLine = alphaLine_ref.getArray(width);
-        }
-
-        // process first tile line:
-        endRendering(pminY);
-
-        return true;
-    }
-
-    private int bbox_spminX, bbox_spmaxX, bbox_spminY, bbox_spmaxY;
-
-    void endRendering(final int pminY) {
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_endRendering_Y.start();
-        }
-
-        final int spminY       = pminY << SUBPIXEL_LG_POSITIONS_Y;
-        final int fixed_spminY = FloatMath.max(bbox_spminY, spminY);
-
-        // avoid rendering for last call to nextTile()
-        if (fixed_spminY < bbox_spmaxY) {
-            // process a complete tile line ie scanlines for 32 rows
-            final int spmaxY = FloatMath.min(bbox_spmaxY, spminY + SUBPIXEL_TILE);
-
-            // process tile line [0 - 32]
-            cache.resetTileLine(pminY);
-
-            // Process only one tile line:
-            _endRendering(fixed_spminY, spmaxY);
-        }
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_endRendering_Y.stop();
-        }
-    }
-
-    void copyAARow(final int[] alphaRow,
-                   final int pix_y, final int pix_from, final int pix_to,
-                   final boolean useBlockFlags)
-    {
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_copyAARow.start();
-        }
-        if (useBlockFlags) {
-            if (DO_STATS) {
-                rdrCtx.stats.hist_tile_generator_encoding.add(1);
-            }
-            cache.copyAARowRLE_WithBlockFlags(blkFlags, alphaRow, pix_y, pix_from, pix_to);
-        } else {
-            if (DO_STATS) {
-                rdrCtx.stats.hist_tile_generator_encoding.add(0);
-            }
-            cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to);
-        }
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_rdr_copyAARow.stop();
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DRendererContext.java b/src/main/java/sun/java2d/marlin/DRendererContext.java
deleted file mode 100644
index 4719f95..0000000
--- a/src/main/java/sun/java2d/marlin/DRendererContext.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.awt.geom.Path2D;
-import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicInteger;
-import sun.java2d.ReentrantContext;
-import sun.java2d.marlin.ArrayCacheConst.CacheStats;
-import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
-
-/**
- * This class is a renderer context dedicated to a single thread
- */
-final class DRendererContext extends ReentrantContext implements IRendererContext {
-
-    // RendererContext creation counter
-    private static final AtomicInteger CTX_COUNT = new AtomicInteger(1);
-
-    /**
-     * Create a new renderer context
-     *
-     * @return new RendererContext instance
-     */
-    static DRendererContext createContext() {
-        return new DRendererContext("ctx"
-                       + Integer.toString(CTX_COUNT.getAndIncrement()));
-    }
-
-    // Smallest object used as Cleaner's parent reference
-    private final Object cleanerObj;
-    // dirty flag indicating an exception occured during pipeline in pathTo()
-    boolean dirty = false;
-    // shared data
-    final double[] double6 = new double[6];
-    // shared curve (dirty) (Renderer / Stroker)
-    final DCurve curve = new DCurve();
-    // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
-    final NormalizingPathIterator nPCPathIterator;
-    // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
-    final NormalizingPathIterator nPQPathIterator;
-    // MarlinRenderingEngine.TransformingPathConsumer2D
-    final DTransformingPathConsumer2D transformerPC2D; // TODO: clear if dirty ?
-    // recycled Path2D instance (weak)
-    private WeakReference<Path2D.Double> refPath2D = null;
-    final DRenderer renderer;
-    final DStroker stroker;
-    // Simplifies out collinear lines
-    final DCollinearSimplifier simplifier = new DCollinearSimplifier();
-    // Simplifies path
-    final DPathSimplifier pathSimplifier = new DPathSimplifier();
-    final DDasher dasher;
-    final MarlinTileGenerator ptg;
-    final MarlinCache cache;
-    // flag indicating the shape is stroked (1) or filled (0)
-    int stroking = 0;
-    // flag indicating to clip the shape
-    boolean doClip = false;
-    // flag indicating if the path is closed or not (in advance) to handle properly caps
-    boolean closedPath = false;
-    // clip rectangle (ymin, ymax, xmin, xmax):
-    final double[] clipRect = new double[4];
-    // clip inverse scale (mean) to adjust length checks
-    double clipInvScale = 0.0d;
-    // CurveBasicMonotonizer instance
-    final CurveBasicMonotonizer monotonizer;
-    // flag indicating to force the stroker to process joins
-    boolean isFirstSegment = true;
-    // CurveClipSplitter instance
-    final CurveClipSplitter curveClipSplitter;
-    // DPQS Sorter context
-    final DPQSSorterContext sorterCtx;
-
-    // Array caches:
-    /* clean int[] cache (zero-filled) = 5 refs */
-    private final ArrayCacheIntClean cleanIntCache = new ArrayCacheIntClean(5);
-    /* dirty int[] cache = 5 refs */
-    private final ArrayCacheInt dirtyIntCache = new ArrayCacheInt(5);
-    /* dirty double[] cache = 4 refs (2 polystack) */
-    private final ArrayCacheDouble dirtyDoubleCache = new ArrayCacheDouble(4);
-    /* dirty byte[] cache = 2 ref (2 polystack) */
-    private final ArrayCacheByte dirtyByteCache = new ArrayCacheByte(2);
-
-    // RendererContext statistics
-    final RendererStats stats;
-
-    final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter();
-
-    /**
-     * Constructor
-     *
-     * @param name context name (debugging)
-     */
-    DRendererContext(final String name) {
-        if (LOG_CREATE_CONTEXT) {
-            MarlinUtils.logInfo("new RendererContext = " + name);
-        }
-        this.cleanerObj = new Object();
-
-        // create first stats (needed by newOffHeapArray):
-        if (DO_STATS || DO_MONITORS) {
-            stats = RendererStats.createInstance(cleanerObj, name);
-            // push cache stats:
-            stats.cacheStats = new CacheStats[] { cleanIntCache.stats,
-                dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats
-            };
-        } else {
-            stats = null;
-        }
-
-        // NormalizingPathIterator instances:
-        nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6);
-        nPQPathIterator  = new NormalizingPathIterator.NearestPixelQuarter(double6);
-
-        // curve monotonizer & clip subdivider (before transformerPC2D init)
-        monotonizer = new CurveBasicMonotonizer(this);
-        curveClipSplitter = new CurveClipSplitter(this);
-
-        // MarlinRenderingEngine.TransformingPathConsumer2D
-        transformerPC2D = new DTransformingPathConsumer2D(this);
-
-        // Renderer:
-        cache = new MarlinCache(this);
-        renderer = new DRenderer(this); // needs MarlinCache from rdrCtx.cache
-        ptg = new MarlinTileGenerator(stats, renderer, cache);
-
-        stroker = new DStroker(this);
-        dasher = new DDasher(this);
-
-        sorterCtx = (MergeSort.USE_DPQS) ? new DPQSSorterContext() : null;
-    }
-
-    /**
-     * Disposes this renderer context:
-     * clean up before reusing this context
-     */
-    void dispose() {
-        if (DO_STATS) {
-            if (stats.totalOffHeap > stats.totalOffHeapMax) {
-                stats.totalOffHeapMax = stats.totalOffHeap;
-            }
-            stats.totalOffHeap = 0L;
-        }
-        stroking   = 0;
-        doClip     = false;
-        closedPath = false;
-        clipInvScale = 0.0d;
-        isFirstSegment = true;
-
-        // if context is maked as DIRTY:
-        if (dirty) {
-            // may happen if an exception if thrown in the pipeline processing:
-            // force cleanup of all possible pipelined blocks (except Renderer):
-
-            // NormalizingPathIterator instances:
-            this.nPCPathIterator.dispose();
-            this.nPQPathIterator.dispose();
-            // Dasher:
-            this.dasher.dispose();
-            // Stroker:
-            this.stroker.dispose();
-
-            // mark context as CLEAN:
-            dirty = false;
-        }
-    }
-
-    Path2D.Double getPath2D() {
-        // resolve reference:
-        Path2D.Double p2d = (refPath2D != null) ? refPath2D.get() : null;
-
-        // create a new Path2D ?
-        if (p2d == null) {
-            p2d = new Path2D.Double(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
-
-            // update weak reference:
-            refPath2D = new WeakReference<Path2D.Double>(p2d);
-        }
-        // reset the path anyway:
-        p2d.reset();
-        return p2d;
-    }
-
-    @Override
-    public RendererStats stats() {
-        return stats;
-    }
-
-    @Override
-    public OffHeapArray newOffHeapArray(final long initialSize) {
-        if (DO_STATS) {
-            stats.totalOffHeapInitial += initialSize;
-        }
-        return new OffHeapArray(cleanerObj, initialSize);
-    }
-
-    @Override
-    public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) {
-        return cleanIntCache.createRef(initialSize);
-    }
-
-    ArrayCacheInt.Reference newDirtyIntArrayRef(final int initialSize) {
-        return dirtyIntCache.createRef(initialSize);
-    }
-
-    ArrayCacheDouble.Reference newDirtyDoubleArrayRef(final int initialSize) {
-        return dirtyDoubleCache.createRef(initialSize);
-    }
-
-    ArrayCacheByte.Reference newDirtyByteArrayRef(final int initialSize) {
-        return dirtyByteCache.createRef(initialSize);
-    }
-
-    static final class PathConsumer2DAdapter implements DPathConsumer2D {
-        private sun.awt.geom.PathConsumer2D out;
-
-        PathConsumer2DAdapter() {}
-
-        PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) {
-            if (this.out != out) {
-                this.out = out;
-            }
-            return this;
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            out.moveTo((float)x0, (float)y0);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            out.lineTo((float)x1, (float)y1);
-        }
-
-        @Override
-        public void closePath() {
-            out.closePath();
-        }
-
-        @Override
-        public void pathDone() {
-            out.pathDone();
-        }
-
-        @Override
-        public void curveTo(double x1, double y1,
-                            double x2, double y2,
-                            double x3, double y3)
-        {
-            out.curveTo((float)x1, (float)y1,
-                    (float)x2, (float)y2,
-                    (float)x3, (float)y3);
-        }
-
-        @Override
-        public void quadTo(double x1, double y1, double x2, double y2) {
-            out.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            throw new InternalError("Not using a native peer");
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DStroker.java b/src/main/java/sun/java2d/marlin/DStroker.java
deleted file mode 100644
index 00a3880..0000000
--- a/src/main/java/sun/java2d/marlin/DStroker.java
+++ /dev/null
@@ -1,1388 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.util.Arrays;
-import sun.java2d.marlin.DHelpers.PolyStack;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
-import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
-import sun.java2d.marlin.debug.MarlinDebugThreadLocal;
-
-// TODO: some of the arithmetic here is too verbose and prone to hard to
-// debug typos. We should consider making a small Point/Vector class that
-// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
-final class DStroker implements DPathConsumer2D, MarlinConst {
-
-    private static final int MOVE_TO = 0;
-    private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
-    private static final int CLOSE = 2;
-
-    // round join threshold = 1 subpixel
-    private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS);
-    private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
-
-    // kappa = (4/3) * (SQRT(2) - 1)
-    private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
-
-    // SQRT(2)
-    private static final double SQRT_2 = Math.sqrt(2.0d);
-
-    private DPathConsumer2D out;
-
-    private int capStyle;
-    private int joinStyle;
-
-    private double lineWidth2;
-    private double invHalfLineWidth2Sq;
-
-    private final double[] offset0 = new double[2];
-    private final double[] offset1 = new double[2];
-    private final double[] offset2 = new double[2];
-    private final double[] miter = new double[2];
-    private double miterLimitSq;
-
-    private int prev;
-
-    // The starting point of the path, and the slope there.
-    private double sx0, sy0, sdx, sdy;
-    // the current point and the slope there.
-    private double cx0, cy0, cdx, cdy; // c stands for current
-    // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
-    // first and last points on the left parallel path. Since this path is
-    // parallel, it's slope at any point is parallel to the slope of the
-    // original path (thought they may have different directions), so these
-    // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
-    // would be error prone and hard to read, so we keep these anyway.
-    private double smx, smy, cmx, cmy;
-
-    private final PolyStack reverse;
-
-    private final double[] lp = new double[8];
-    private final double[] rp = new double[8];
-
-    // per-thread renderer context
-    final DRendererContext rdrCtx;
-
-    // dirty curve
-    final DCurve curve;
-
-    // Bounds of the drawing region, at pixel precision.
-    private double[] clipRect;
-
-    // the outcode of the current point
-    private int cOutCode = 0;
-
-    // the outcode of the starting point
-    private int sOutCode = 0;
-
-    // flag indicating if the path is opened (clipped)
-    private boolean opened = false;
-    // flag indicating if the starting point's cap is done
-    private boolean capStart = false;
-    // flag indicating to monotonize curves
-    private boolean monotonize;
-
-    private boolean subdivide = false;
-    private final CurveClipSplitter curveSplitter;
-
-    /**
-     * Constructs a <code>DStroker</code>.
-     * @param rdrCtx per-thread renderer context
-     */
-    DStroker(final DRendererContext rdrCtx) {
-        this.rdrCtx = rdrCtx;
-
-        this.reverse = (rdrCtx.stats != null) ?
-            new PolyStack(rdrCtx,
-                    rdrCtx.stats.stat_str_polystack_types,
-                    rdrCtx.stats.stat_str_polystack_curves,
-                    rdrCtx.stats.hist_str_polystack_curves,
-                    rdrCtx.stats.stat_array_str_polystack_curves,
-                    rdrCtx.stats.stat_array_str_polystack_types)
-            : new PolyStack(rdrCtx);
-
-        this.curve = rdrCtx.curve;
-        this.curveSplitter = rdrCtx.curveClipSplitter;
-    }
-
-    /**
-     * Inits the <code>DStroker</code>.
-     *
-     * @param pc2d an output <code>DPathConsumer2D</code>.
-     * @param lineWidth the desired line width in pixels
-     * @param capStyle the desired end cap style, one of
-     * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
-     * <code>CAP_SQUARE</code>.
-     * @param joinStyle the desired line join style, one of
-     * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
-     * <code>JOIN_BEVEL</code>.
-     * @param miterLimit the desired miter limit
-     * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
-     * @return this instance
-     */
-    DStroker init(final DPathConsumer2D pc2d,
-                  final double lineWidth,
-                  final int capStyle,
-                  final int joinStyle,
-                  final double miterLimit,
-                  final boolean subdivideCurves)
-    {
-        if (this.out != pc2d) {
-            this.out = pc2d;
-        }
-
-        this.lineWidth2 = lineWidth / 2.0d;
-        this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
-        this.monotonize = subdivideCurves;
-
-        this.capStyle = capStyle;
-        this.joinStyle = joinStyle;
-
-        final double limit = miterLimit * lineWidth2;
-        this.miterLimitSq = limit * limit;
-
-        this.prev = CLOSE;
-
-        rdrCtx.stroking = 1;
-
-        if (rdrCtx.doClip) {
-            // Adjust the clipping rectangle with the stroker margin (miter limit, width)
-            double margin = lineWidth2;
-
-            if (capStyle == CAP_SQUARE) {
-                margin *= SQRT_2;
-            }
-            if ((joinStyle == JOIN_MITER) && (margin < limit)) {
-                margin = limit;
-            }
-
-            // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
-            // adjust clip rectangle (ymin, ymax, xmin, xmax):
-            final double[] _clipRect = rdrCtx.clipRect;
-            _clipRect[0] -= margin;
-            _clipRect[1] += margin;
-            _clipRect[2] -= margin;
-            _clipRect[3] += margin;
-            this.clipRect = _clipRect;
-
-            if (MarlinConst.DO_LOG_CLIP) {
-                MarlinUtils.logInfo("clipRect (stroker): "
-                                    + Arrays.toString(rdrCtx.clipRect));
-            }
-
-            // initialize curve splitter here for stroker & dasher:
-            if (DO_CLIP_SUBDIVIDER) {
-                subdivide = subdivideCurves;
-                // adjust padded clip rectangle:
-                curveSplitter.init();
-            } else {
-                subdivide = false;
-            }
-        } else {
-            this.clipRect = null;
-            this.cOutCode = 0;
-            this.sOutCode = 0;
-        }
-        return this; // fluent API
-    }
-
-    void disableClipping() {
-        this.clipRect = null;
-        this.cOutCode = 0;
-        this.sOutCode = 0;
-    }
-
-    /**
-     * Disposes this stroker:
-     * clean up before reusing this instance
-     */
-    void dispose() {
-        reverse.dispose();
-
-        opened   = false;
-        capStart = false;
-
-        if (DO_CLEAN_DIRTY) {
-            // Force zero-fill dirty arrays:
-            Arrays.fill(offset0, 0.0d);
-            Arrays.fill(offset1, 0.0d);
-            Arrays.fill(offset2, 0.0d);
-            Arrays.fill(miter, 0.0d);
-            Arrays.fill(lp, 0.0d);
-            Arrays.fill(rp, 0.0d);
-        }
-    }
-
-    private static void computeOffset(final double lx, final double ly,
-                                      final double w, final double[] m)
-    {
-        double len = lx*lx + ly*ly;
-        if (len == 0.0d) {
-            m[0] = 0.0d;
-            m[1] = 0.0d;
-        } else {
-            len = Math.sqrt(len);
-            m[0] =  (ly * w) / len;
-            m[1] = -(lx * w) / len;
-        }
-    }
-
-    // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
-    // clockwise (if dx1,dy1 needs to be rotated clockwise to close
-    // the smallest angle between it and dx2,dy2).
-    // This is equivalent to detecting whether a point q is on the right side
-    // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
-    // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
-    // clockwise order.
-    // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
-    private static boolean isCW(final double dx1, final double dy1,
-                                final double dx2, final double dy2)
-    {
-        return dx1 * dy2 <= dy1 * dx2;
-    }
-
-    private void mayDrawRoundJoin(double cx, double cy,
-                                  double omx, double omy,
-                                  double mx, double my,
-                                  boolean rev)
-    {
-        if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) {
-            return;
-        }
-
-        final double domx = omx - mx;
-        final double domy = omy - my;
-        final double lenSq = domx*domx + domy*domy;
-
-        if (lenSq < ROUND_JOIN_THRESHOLD) {
-            return;
-        }
-
-        if (rev) {
-            omx = -omx;
-            omy = -omy;
-            mx  = -mx;
-            my  = -my;
-        }
-        drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
-    }
-
-    private void drawRoundJoin(double cx, double cy,
-                               double omx, double omy,
-                               double mx, double my,
-                               boolean rev)
-    {
-        // The sign of the dot product of mx,my and omx,omy is equal to the
-        // the sign of the cosine of ext
-        // (ext is the angle between omx,omy and mx,my).
-        final double cosext = omx * mx + omy * my;
-        // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
-        // need 1 curve to approximate the circle section that joins omx,omy
-        // and mx,my.
-        if (cosext >= 0.0d) {
-            drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
-        } else {
-            // we need to split the arc into 2 arcs spanning the same angle.
-            // The point we want will be one of the 2 intersections of the
-            // perpendicular bisector of the chord (omx,omy)->(mx,my) and the
-            // circle. We could find this by scaling the vector
-            // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
-            // on the circle), but that can have numerical problems when the angle
-            // between omx,omy and mx,my is close to 180 degrees. So we compute a
-            // normal of (omx,omy)-(mx,my). This will be the direction of the
-            // perpendicular bisector. To get one of the intersections, we just scale
-            // this vector that its length is lineWidth2 (this works because the
-            // perpendicular bisector goes through the origin). This scaling doesn't
-            // have numerical problems because we know that lineWidth2 divided by
-            // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
-            // we know the angle of the arc is > 90 degrees).
-            double nx = my - omy, ny = omx - mx;
-            double nlen = Math.sqrt(nx*nx + ny*ny);
-            double scale = lineWidth2/nlen;
-            double mmx = nx * scale, mmy = ny * scale;
-
-            // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
-            // computed the wrong intersection so we get the other one.
-            // The test above is equivalent to if (rev).
-            if (rev) {
-                mmx = -mmx;
-                mmy = -mmy;
-            }
-            drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
-            drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
-        }
-    }
-
-    // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
-    private void drawBezApproxForArc(final double cx, final double cy,
-                                     final double omx, final double omy,
-                                     final double mx, final double my,
-                                     boolean rev)
-    {
-        final double cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq;
-
-        // check round off errors producing cos(ext) > 1 and a NaN below
-        // cos(ext) == 1 implies colinear segments and an empty join anyway
-        if (cosext2 >= 0.5d) {
-            // just return to avoid generating a flat curve:
-            return;
-        }
-
-        // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
-        // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
-        // define the bezier curve we're computing.
-        // It is computed using the constraints that P1-P0 and P3-P2 are parallel
-        // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
-        double cv = ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) /
-                            (1.0d + Math.sqrt(cosext2 + 0.5d)));
-        // if clockwise, we need to negate cv.
-        if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
-            cv = -cv;
-        }
-        final double x1 = cx + omx;
-        final double y1 = cy + omy;
-        final double x2 = x1 - cv * omy;
-        final double y2 = y1 + cv * omx;
-
-        final double x4 = cx + mx;
-        final double y4 = cy + my;
-        final double x3 = x4 + cv * my;
-        final double y3 = y4 - cv * mx;
-
-        emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
-    }
-
-    private void drawRoundCap(double cx, double cy, double mx, double my) {
-        final double Cmx = C * mx;
-        final double Cmy = C * my;
-        emitCurveTo(cx + mx - Cmy, cy + my + Cmx,
-                    cx - my + Cmx, cy + mx + Cmy,
-                    cx - my,       cy + mx);
-        emitCurveTo(cx - my - Cmx, cy + mx - Cmy,
-                    cx - mx - Cmy, cy - my + Cmx,
-                    cx - mx,       cy - my);
-    }
-
-    // Return the intersection point of the lines (x0, y0) -> (x1, y1)
-    // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
-    private static void computeMiter(final double x0, final double y0,
-                                     final double x1, final double y1,
-                                     final double x0p, final double y0p,
-                                     final double x1p, final double y1p,
-                                     final double[] m)
-    {
-        double x10 = x1 - x0;
-        double y10 = y1 - y0;
-        double x10p = x1p - x0p;
-        double y10p = y1p - y0p;
-
-        // if this is 0, the lines are parallel. If they go in the
-        // same direction, there is no intersection so m[off] and
-        // m[off+1] will contain infinity, so no miter will be drawn.
-        // If they go in the same direction that means that the start of the
-        // current segment and the end of the previous segment have the same
-        // tangent, in which case this method won't even be involved in
-        // miter drawing because it won't be called by drawMiter (because
-        // (mx == omx && my == omy) will be true, and drawMiter will return
-        // immediately).
-        double den = x10*y10p - x10p*y10;
-        double t = x10p*(y0-y0p) - y10p*(x0-x0p);
-        t /= den;
-        m[0] = x0 + t*x10;
-        m[1] = y0 + t*y10;
-    }
-
-    // Return the intersection point of the lines (x0, y0) -> (x1, y1)
-    // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
-    private static void safeComputeMiter(final double x0, final double y0,
-                                         final double x1, final double y1,
-                                         final double x0p, final double y0p,
-                                         final double x1p, final double y1p,
-                                         final double[] m)
-    {
-        double x10 = x1 - x0;
-        double y10 = y1 - y0;
-        double x10p = x1p - x0p;
-        double y10p = y1p - y0p;
-
-        // if this is 0, the lines are parallel. If they go in the
-        // same direction, there is no intersection so m[off] and
-        // m[off+1] will contain infinity, so no miter will be drawn.
-        // If they go in the same direction that means that the start of the
-        // current segment and the end of the previous segment have the same
-        // tangent, in which case this method won't even be involved in
-        // miter drawing because it won't be called by drawMiter (because
-        // (mx == omx && my == omy) will be true, and drawMiter will return
-        // immediately).
-        double den = x10*y10p - x10p*y10;
-        if (den == 0.0d) {
-            m[2] = (x0 + x0p) / 2.0d;
-            m[3] = (y0 + y0p) / 2.0d;
-        } else {
-            double t = x10p*(y0-y0p) - y10p*(x0-x0p);
-            t /= den;
-            m[2] = x0 + t*x10;
-            m[3] = y0 + t*y10;
-        }
-    }
-
-    private void drawMiter(final double pdx, final double pdy,
-                           final double x0, final double y0,
-                           final double dx, final double dy,
-                           double omx, double omy,
-                           double mx, double my,
-                           boolean rev)
-    {
-        if ((mx == omx && my == omy) ||
-            (pdx == 0.0d && pdy == 0.0d) ||
-            (dx == 0.0d && dy == 0.0d))
-        {
-            return;
-        }
-
-        if (rev) {
-            omx = -omx;
-            omy = -omy;
-            mx  = -mx;
-            my  = -my;
-        }
-
-        computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
-                     (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
-
-        final double miterX = miter[0];
-        final double miterY = miter[1];
-        double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
-
-        // If the lines are parallel, lenSq will be either NaN or +inf
-        // (actually, I'm not sure if the latter is possible. The important
-        // thing is that -inf is not possible, because lenSq is a square).
-        // For both of those values, the comparison below will fail and
-        // no miter will be drawn, which is correct.
-        if (lenSq < miterLimitSq) {
-            emitLineTo(miterX, miterY, rev);
-        }
-    }
-
-    @Override
-    public void moveTo(final double x0, final double y0) {
-        _moveTo(x0, y0, cOutCode);
-        // update starting point:
-        this.sx0 = x0;
-        this.sy0 = y0;
-        this.sdx = 1.0d;
-        this.sdy = 0.0d;
-        this.opened   = false;
-        this.capStart = false;
-
-        if (clipRect != null) {
-            final int outcode = DHelpers.outcode(x0, y0, clipRect);
-            this.cOutCode = outcode;
-            this.sOutCode = outcode;
-        }
-    }
-
-    private void _moveTo(final double x0, final double y0,
-                        final int outcode)
-    {
-        if (prev == MOVE_TO) {
-            this.cx0 = x0;
-            this.cy0 = y0;
-        } else {
-            if (prev == DRAWING_OP_TO) {
-                finish(outcode);
-            }
-            this.prev = MOVE_TO;
-            this.cx0 = x0;
-            this.cy0 = y0;
-            this.cdx = 1.0d;
-            this.cdy = 0.0d;
-        }
-    }
-
-    @Override
-    public void lineTo(final double x1, final double y1) {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
-                                                              orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode1;
-                    _moveTo(x1, y1, outcode0);
-                    opened = true;
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode1;
-        }
-
-        double dx = x1 - cx0;
-        double dy = y1 - cy0;
-        if (dx == 0.0d && dy == 0.0d) {
-            dx = 1.0d;
-        }
-        computeOffset(dx, dy, lineWidth2, offset0);
-        final double mx = offset0[0];
-        final double my = offset0[1];
-
-        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
-
-        emitLineTo(cx0 + mx, cy0 + my);
-        emitLineTo( x1 + mx,  y1 + my);
-
-        emitLineToRev(cx0 - mx, cy0 - my);
-        emitLineToRev( x1 - mx,  y1 - my);
-
-        this.prev = DRAWING_OP_TO;
-        this.cx0 = x1;
-        this.cy0 = y1;
-        this.cdx = dx;
-        this.cdy = dy;
-        this.cmx = mx;
-        this.cmy = my;
-    }
-
-    @Override
-    public void closePath() {
-        // distinguish empty path at all vs opened path ?
-        if (prev != DRAWING_OP_TO && !opened) {
-            if (prev == CLOSE) {
-                return;
-            }
-            emitMoveTo(cx0, cy0 - lineWidth2);
-
-            this.sdx = 1.0d;
-            this.sdy = 0.0d;
-            this.cdx = 1.0d;
-            this.cdy = 0.0d;
-
-            this.smx = 0.0d;
-            this.smy = -lineWidth2;
-            this.cmx = 0.0d;
-            this.cmy = -lineWidth2;
-
-            finish(cOutCode);
-            return;
-        }
-
-        // basic acceptance criteria
-        if ((sOutCode & cOutCode) == 0) {
-            if (cx0 != sx0 || cy0 != sy0) {
-                // may subdivide line:
-                lineTo(sx0, sy0);
-            }
-
-            // ignore starting point outside:
-            if (sOutCode == 0) {
-                drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, sOutCode);
-
-                emitLineTo(sx0 + smx, sy0 + smy);
-
-                if (opened) {
-                    emitLineTo(sx0 - smx, sy0 - smy);
-                } else {
-                    emitMoveTo(sx0 - smx, sy0 - smy);
-                }
-            }
-        }
-        // Ignore caps like finish(false)
-        emitReverse();
-
-        this.prev = CLOSE;
-        this.cx0 = sx0;
-        this.cy0 = sy0;
-        this.cOutCode = sOutCode;
-
-        if (opened) {
-            // do not emit close
-            opened = false;
-        } else {
-            emitClose();
-        }
-    }
-
-    private void emitReverse() {
-        reverse.popAll(out);
-    }
-
-    @Override
-    public void pathDone() {
-        if (prev == DRAWING_OP_TO) {
-            finish(cOutCode);
-        }
-
-        out.pathDone();
-
-        // this shouldn't matter since this object won't be used
-        // after the call to this method.
-        this.prev = CLOSE;
-
-        // Dispose this instance:
-        dispose();
-    }
-
-    private void finish(final int outcode) {
-        // Problem: impossible to guess if the path will be closed in advance
-        //          i.e. if caps must be drawn or not ?
-        // Solution: use the ClosedPathDetector before Stroker to determine
-        // if the path is a closed path or not
-        if (rdrCtx.closedPath) {
-            emitReverse();
-        } else {
-            if (outcode == 0) {
-                // current point = end's cap:
-                if (capStyle == CAP_ROUND) {
-                    drawRoundCap(cx0, cy0, cmx, cmy);
-                } else if (capStyle == CAP_SQUARE) {
-                    emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
-                    emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
-                }
-            }
-            emitReverse();
-
-            if (!capStart) {
-                capStart = true;
-
-                if (sOutCode == 0) {
-                    // starting point = initial cap:
-                    if (capStyle == CAP_ROUND) {
-                        drawRoundCap(sx0, sy0, -smx, -smy);
-                    } else if (capStyle == CAP_SQUARE) {
-                        emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
-                        emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
-                    }
-                }
-            }
-        }
-        emitClose();
-    }
-
-    private void emitMoveTo(final double x0, final double y0) {
-        out.moveTo(x0, y0);
-    }
-
-    private void emitLineTo(final double x1, final double y1) {
-        out.lineTo(x1, y1);
-    }
-
-    private void emitLineToRev(final double x1, final double y1) {
-        reverse.pushLine(x1, y1);
-    }
-
-    private void emitLineTo(final double x1, final double y1,
-                            final boolean rev)
-    {
-        if (rev) {
-            emitLineToRev(x1, y1);
-        } else {
-            emitLineTo(x1, y1);
-        }
-    }
-
-    private void emitQuadTo(final double x1, final double y1,
-                            final double x2, final double y2)
-    {
-        out.quadTo(x1, y1, x2, y2);
-    }
-
-    private void emitQuadToRev(final double x0, final double y0,
-                               final double x1, final double y1)
-    {
-        reverse.pushQuad(x0, y0, x1, y1);
-    }
-
-    private void emitCurveTo(final double x1, final double y1,
-                             final double x2, final double y2,
-                             final double x3, final double y3)
-    {
-        out.curveTo(x1, y1, x2, y2, x3, y3);
-    }
-
-    private void emitCurveToRev(final double x0, final double y0,
-                                final double x1, final double y1,
-                                final double x2, final double y2)
-    {
-        reverse.pushCubic(x0, y0, x1, y1, x2, y2);
-    }
-
-    private void emitCurveTo(final double x0, final double y0,
-                             final double x1, final double y1,
-                             final double x2, final double y2,
-                             final double x3, final double y3, final boolean rev)
-    {
-        if (rev) {
-            reverse.pushCubic(x0, y0, x1, y1, x2, y2);
-        } else {
-            out.curveTo(x1, y1, x2, y2, x3, y3);
-        }
-    }
-
-    private void emitClose() {
-        out.closePath();
-    }
-
-    private void drawJoin(double pdx, double pdy,
-                          double x0, double y0,
-                          double dx, double dy,
-                          double omx, double omy,
-                          double mx, double my,
-                          final int outcode)
-    {
-        if (prev != DRAWING_OP_TO) {
-            emitMoveTo(x0 + mx, y0 + my);
-            if (!opened) {
-                this.sdx = dx;
-                this.sdy = dy;
-                this.smx = mx;
-                this.smy = my;
-            }
-        } else if (rdrCtx.isFirstSegment) {
-            // Precision on isCW is causing instabilities with Dasher !
-            final boolean cw = isCW(pdx, pdy, dx, dy);
-            if (outcode == 0) {
-                if (joinStyle == JOIN_MITER) {
-                    drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
-                } else if (joinStyle == JOIN_ROUND) {
-                    mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw);
-                }
-            }
-            emitLineTo(x0, y0, !cw);
-        }
-        if (!rdrCtx.isFirstSegment) {
-            // reset trigger to process further joins (normal operations)
-            rdrCtx.isFirstSegment = true;
-        }
-
-        prev = DRAWING_OP_TO;
-    }
-
-    private int getLineOffsets(final double x1, final double y1,
-                               final double x2, final double y2,
-                               final double[] left, final double[] right)
-    {
-        computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
-        final double mx = offset0[0];
-        final double my = offset0[1];
-        left[0] = x1 + mx;
-        left[1] = y1 + my;
-        left[2] = x2 + mx;
-        left[3] = y2 + my;
-
-        right[0] = x1 - mx;
-        right[1] = y1 - my;
-        right[2] = x2 - mx;
-        right[3] = y2 - my;
-
-        return 4;
-    }
-
-    private int computeOffsetCubic(final double[] pts, final int off,
-                                   final double[] leftOff,
-                                   final double[] rightOff)
-    {
-        // if p1=p2 or p3=p4 it means that the derivative at the endpoint
-        // vanishes, which creates problems with computeOffset. Usually
-        // this happens when this stroker object is trying to widen
-        // a curve with a cusp. What happens is that curveTo splits
-        // the input curve at the cusp, and passes it to this function.
-        // because of inaccuracies in the splitting, we consider points
-        // equal if they're very close to each other.
-        final double x1 = pts[off    ]; final double y1 = pts[off + 1];
-        final double x2 = pts[off + 2]; final double y2 = pts[off + 3];
-        final double x3 = pts[off + 4]; final double y3 = pts[off + 5];
-        final double x4 = pts[off + 6]; final double y4 = pts[off + 7];
-
-        double dx1 = x2 - x1; double dy1 = y2 - y1;
-        double dx4 = x4 - x3; double dy4 = y4 - y3;
-
-        // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
-        // in which case ignore if p1 == p2
-        final boolean p1eqp2 = DHelpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2));
-        final boolean p3eqp4 = DHelpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4));
-
-        if (p1eqp2 && p3eqp4) {
-            return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
-        } else if (p1eqp2) {
-            dx1 = x3 - x1;
-            dy1 = y3 - y1;
-        } else if (p3eqp4) {
-            dx4 = x4 - x2;
-            dy4 = y4 - y2;
-        }
-
-        // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
-        double dotsq = (dx1 * dx4 + dy1 * dy4);
-        dotsq *= dotsq;
-        final double l1sq = dx1 * dx1 + dy1 * dy1;
-        final double l4sq = dx4 * dx4 + dy4 * dy4;
-
-        if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
-            return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
-        }
-
-//      What we're trying to do in this function is to approximate an ideal
-//      offset curve (call it I) of the input curve B using a bezier curve Bp.
-//      The constraints I use to get the equations are:
-//
-//      1. The computed curve Bp should go through I(0) and I(1). These are
-//      x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
-//      4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
-//
-//      2. Bp should have slope equal in absolute value to I at the endpoints. So,
-//      (by the way, the operator || in the comments below means "aligned with".
-//      It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
-//      vectors I'(0) and Bp'(0) are aligned, which is the same as saying
-//      that the tangent lines of I and Bp at 0 are parallel. Mathematically
-//      this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
-//      nonzero constant.)
-//      I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and
-//      I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1).
-//      We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same
-//      is true for any bezier curve; therefore, we get the equations
-//          (1) p2p = c1 * (p2-p1) + p1p
-//          (2) p3p = c2 * (p4-p3) + p4p
-//      We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number
-//      of unknowns from 4 to 2 (i.e. just c1 and c2).
-//      To eliminate these 2 unknowns we use the following constraint:
-//
-//      3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note
-//      that I(0.5) is *the only* reason for computing dxm,dym. This gives us
-//          (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
-//          (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
-//      We can substitute (1) and (2) from above into (4) and we get:
-//          (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
-//      which is equivalent to
-//          (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
-//
-//      The right side of this is a 2D vector, and we know I(0.5), which gives us
-//      Bp(0.5), which gives us the value of the right side.
-//      The left side is just a matrix vector multiplication in disguise. It is
-//
-//      [x2-x1, x4-x3][c1]
-//      [y2-y1, y4-y3][c2]
-//      which, is equal to
-//      [dx1, dx4][c1]
-//      [dy1, dy4][c2]
-//      At this point we are left with a simple linear system and we solve it by
-//      getting the inverse of the matrix above. Then we use [c1,c2] to compute
-//      p2p and p3p.
-
-        final double xm = (x1 + x4 + 3.0d * (x2 + x3)) / 8.0d;
-        final double ym = (y1 + y4 + 3.0d * (y2 + y3)) / 8.0d;
-        // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
-        // c*B'(0.5) for some constant c.
-        final double dxm = x3 + x4 - (x1 + x2);
-        final double dym = y3 + y4 - (y1 + y2);
-
-        // this computes the offsets at t=0, 0.5, 1, using the property that
-        // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
-        // the (dx/dt, dy/dt) vectors at the endpoints.
-        computeOffset(dx1, dy1, lineWidth2, offset0);
-        computeOffset(dxm, dym, lineWidth2, offset1);
-        computeOffset(dx4, dy4, lineWidth2, offset2);
-
-        // left side:
-        double x1p = x1 + offset0[0]; // start
-        double y1p = y1 + offset0[1]; // point
-        double xi  = xm + offset1[0]; // interpolation
-        double yi  = ym + offset1[1]; // point
-        double x4p = x4 + offset2[0]; // end
-        double y4p = y4 + offset2[1]; // point
-
-if (false) {
-        final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get();
-        // never release (reset):
-        dbgCtx.addPoint(xi, yi);
-}
-
-        final double invdet43 = 4.0d / (3.0d * (dx1 * dy4 - dy1 * dx4));
-
-        double two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p);
-        double two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p);
-
-        double c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
-        double c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
-
-        double x2p, y2p, x3p, y3p;
-
-        if (c1 * c2 > 0.0) {
-//            System.out.println("Buggy solver (left): c1 = " + c1 + " c2 = " + c2);
-
-            // use lower quality approximation but good enough
-            // to ensure cuve being in its convex hull
-            x2p = x2 + offset1[0]; // 2nd
-            y2p = y2 + offset1[1]; // point
-            x3p = x3 + offset1[0]; // 3nd
-            y3p = y3 + offset1[1]; // point
-
-            safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x2p, y2p, x2p-dxm, y2p-dym, leftOff);
-            x2p = leftOff[2]; y2p = leftOff[3];
-
-            safeComputeMiter(x4p, y4p, x4p+dx4, y4p+dy4, x3p, y3p, x3p-dxm, y3p-dym, leftOff);
-            x3p = leftOff[2]; y3p = leftOff[3];
-        } else {
-            x2p = x1p + c1 * dx1; y2p = y1p + c1 * dy1;
-            x3p = x4p + c2 * dx4; y3p = y4p + c2 * dy4;
-        }
-
-        leftOff[0] = x1p; leftOff[1] = y1p;
-        leftOff[2] = x2p; leftOff[3] = y2p;
-        leftOff[4] = x3p; leftOff[5] = y3p;
-        leftOff[6] = x4p; leftOff[7] = y4p;
-
-        // Right side:
-        x1p = x1 - offset0[0]; // start
-        y1p = y1 - offset0[1]; // point
-        xi =  xm  - offset1[0]; // interpolation
-        yi =  ym  - offset1[1]; // point
-        x4p = x4 - offset2[0]; // end
-        y4p = y4 - offset2[1]; // point
-
-if (false) {
-        final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get();
-        // never release (reset):
-        dbgCtx.addPoint(xi, yi);
-}
-
-        two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p);
-        two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p);
-
-        c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
-        c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
-
-        if (c1 * c2 > 0.0) {
-//            System.out.println("Buggy solver (right): c1 = " + c1 + " c2 = " + c2);
-
-            // use lower quality approximation but good enough
-            // to ensure cuve being in its convex hull
-            x2p = x2 - offset1[0]; // 2nd
-            y2p = y2 - offset1[1]; // point
-            x3p = x3 - offset1[0]; // 3nd
-            y3p = y3 - offset1[1]; // point
-
-            safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x2p, y2p, x2p-dxm, y2p-dym, rightOff);
-            x2p = rightOff[2]; y2p = rightOff[3];
-
-            safeComputeMiter(x4p, y4p, x4p+dx4, y4p+dy4, x3p, y3p, x3p-dxm, y3p-dym, rightOff);
-            x3p = rightOff[2]; y3p = rightOff[3];
-        } else {
-            x2p = x1p + c1 * dx1; y2p = y1p + c1 * dy1;
-            x3p = x4p + c2 * dx4; y3p = y4p + c2 * dy4;
-        }
-
-        rightOff[0] = x1p; rightOff[1] = y1p;
-        rightOff[2] = x2p; rightOff[3] = y2p;
-        rightOff[4] = x3p; rightOff[5] = y3p;
-        rightOff[6] = x4p; rightOff[7] = y4p;
-
-        return 8;
-    }
-
-    // compute offset curves using bezier spline through t=0.5 (i.e.
-    // ComputedCurve(0.5) == IdealParallelCurve(0.5))
-    // return the kind of curve in the right and left arrays.
-    private int computeOffsetQuad(final double[] pts, final int off,
-                                  final double[] leftOff,
-                                  final double[] rightOff)
-    {
-        return computeOffsetQuad(pts, off, leftOff, rightOff, true);
-    }
-
-    private int computeOffsetQuad(final double[] pts, final int off,
-                                  final double[] leftOff,
-                                  final double[] rightOff,
-                                  final boolean checkCtrlPoints)
-    {
-        final double x1 = pts[off    ]; final double y1 = pts[off + 1];
-        final double x2 = pts[off + 2]; final double y2 = pts[off + 3];
-        final double x3 = pts[off + 4]; final double y3 = pts[off + 5];
-
-        final double dx12 = x2 - x1; final double dy12 = y2 - y1;
-        final double dx23 = x3 - x2; final double dy23 = y3 - y2;
-
-        if (checkCtrlPoints) {
-            // if p1=p2 or p2=p3 it means that the derivative at the endpoint
-            // vanishes, which creates problems with computeOffset. Usually
-            // this happens when this stroker object is trying to widen
-            // a curve with a cusp. What happens is that curveTo splits
-            // the input curve at the cusp, and passes it to this function.
-            // because of inaccuracies in the splitting, we consider points
-            // equal if they're very close to each other.
-
-            // if p1 == p2 or p2 == p3: draw line from p1->p3
-            final boolean p1eqp2 = DHelpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2));
-            final boolean p2eqp3 = DHelpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3));
-
-            if (p1eqp2 || p2eqp3) {
-                return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
-            }
-
-            // if p2-p1 and p3-p2 are parallel, that must mean this curve is a line
-            double dotsq = (dx12 * dx23 + dy12 * dy23);
-            dotsq *= dotsq;
-            final double l1sq = dx12 * dx12 + dy12 * dy12;
-            final double l3sq = dx23 * dx23 + dy23 * dy23;
-
-            if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
-                return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
-            }
-        }
-
-        // this computes the offsets at t=0, 0.5, 1, using the property that
-        // for any bezier curve the vectors p2-p1 and p3-p2 are parallel to
-        // the (dx/dt, dy/dt) vectors at the endpoints.
-        computeOffset(dx12, dy12, lineWidth2, offset0);
-        computeOffset(dx23, dy23, lineWidth2, offset1);
-
-        double x1p = x1 + offset0[0]; // start
-        double y1p = y1 + offset0[1]; // point
-        double x3p = x3 + offset1[0]; // end
-        double y3p = y3 + offset1[1]; // point
-
-        safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, leftOff);
-        leftOff[0] = x1p; leftOff[1] = y1p;
-        leftOff[4] = x3p; leftOff[5] = y3p;
-
-        x1p = x1 - offset0[0]; // start
-        y1p = y1 - offset0[1]; // point
-        x3p = x3 - offset1[0]; // end
-        y3p = y3 - offset1[1]; // point
-
-        safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, rightOff);
-        rightOff[0] = x1p; rightOff[1] = y1p;
-        rightOff[4] = x3p; rightOff[5] = y3p;
-
-        return 6;
-    }
-
-    @Override
-    public void curveTo(final double x1, final double y1,
-                        final double x2, final double y2,
-                        final double x3, final double y3)
-    {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
-            final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
-                                                               x2, y2, x3, y3,
-                                                               orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode3;
-                    _moveTo(x3, y3, outcode0);
-                    opened = true;
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode3;
-        }
-        _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
-    }
-
-    private void _curveTo(final double x1, final double y1,
-                          final double x2, final double y2,
-                          final double x3, final double y3,
-                          final int outcode0)
-    {
-        // need these so we can update the state at the end of this method
-        double dxs = x1 - cx0;
-        double dys = y1 - cy0;
-        double dxf = x3 - x2;
-        double dyf = y3 - y2;
-
-        if ((dxs == 0.0d) && (dys == 0.0d)) {
-            dxs = x2 - cx0;
-            dys = y2 - cy0;
-            if ((dxs == 0.0d) && (dys == 0.0d)) {
-                dxs = x3 - cx0;
-                dys = y3 - cy0;
-            }
-        }
-        if ((dxf == 0.0d) && (dyf == 0.0d)) {
-            dxf = x3 - x1;
-            dyf = y3 - y1;
-            if ((dxf == 0.0d) && (dyf == 0.0d)) {
-                dxf = x3 - cx0;
-                dyf = y3 - cy0;
-            }
-        }
-        if ((dxs == 0.0d) && (dys == 0.0d)) {
-            // this happens if the "curve" is just a point
-            // fix outcode0 for lineTo() call:
-            if (clipRect != null) {
-                this.cOutCode = outcode0;
-            }
-            lineTo(cx0, cy0);
-            return;
-        }
-
-        // if these vectors are too small, normalize them, to avoid future
-        // precision problems.
-        if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
-            final double len = Math.sqrt(dxs * dxs + dys * dys);
-            dxs /= len;
-            dys /= len;
-        }
-        if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
-            final double len = Math.sqrt(dxf * dxf + dyf * dyf);
-            dxf /= len;
-            dyf /= len;
-        }
-
-        computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
-
-        int nSplits = 0;
-        final double[] mid;
-        final double[] l = lp;
-
-        if (monotonize) {
-            // monotonize curve:
-            final CurveBasicMonotonizer monotonizer
-                = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
-
-            nSplits = monotonizer.nbSplits;
-            mid = monotonizer.middle;
-        } else {
-            // use left instead:
-            mid = l;
-            mid[0] = cx0; mid[1] = cy0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-            mid[6] = x3;  mid[7] = y3;
-        }
-        final double[] r = rp;
-
-        int kind = 0;
-        for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
-            kind = computeOffsetCubic(mid, off, l, r);
-
-            emitLineTo(l[0], l[1]);
-
-            switch(kind) {
-            case 8:
-                emitCurveTo(l[2], l[3], l[4], l[5], l[6], l[7]);
-                emitCurveToRev(r[0], r[1], r[2], r[3], r[4], r[5]);
-                break;
-            case 4:
-                emitLineTo(l[2], l[3]);
-                emitLineToRev(r[0], r[1]);
-                break;
-            default:
-            }
-            emitLineToRev(r[kind - 2], r[kind - 1]);
-        }
-
-        this.prev = DRAWING_OP_TO;
-        this.cx0 = x3;
-        this.cy0 = y3;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-    }
-
-    @Override
-    public void quadTo(final double x1, final double y1,
-                       final double x2, final double y2)
-    {
-        final int outcode0 = this.cOutCode;
-
-        if (clipRect != null) {
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => call lineTo() with subdivided curves:
-                        boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
-                                                              x2, y2, orCode, this);
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode2;
-                    _moveTo(x2, y2, outcode0);
-                    opened = true;
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode2;
-        }
-        _quadTo(x1, y1, x2, y2, outcode0);
-    }
-
-    private void _quadTo(final double x1, final double y1,
-                         final double x2, final double y2,
-                         final int outcode0)
-    {
-        // need these so we can update the state at the end of this method
-        double dxs = x1 - cx0;
-        double dys = y1 - cy0;
-        double dxf = x2 - x1;
-        double dyf = y2 - y1;
-
-        if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) {
-            dxs = dxf = x2 - cx0;
-            dys = dyf = y2 - cy0;
-        }
-        if ((dxs == 0.0d) && (dys == 0.0d)) {
-            // this happens if the "curve" is just a point
-            // fix outcode0 for lineTo() call:
-            if (clipRect != null) {
-                this.cOutCode = outcode0;
-            }
-            lineTo(cx0, cy0);
-            return;
-        }
-        // if these vectors are too small, normalize them, to avoid future
-        // precision problems.
-        if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
-            final double len = Math.sqrt(dxs * dxs + dys * dys);
-            dxs /= len;
-            dys /= len;
-        }
-        if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
-            final double len = Math.sqrt(dxf * dxf + dyf * dyf);
-            dxf /= len;
-            dyf /= len;
-        }
-        computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
-
-        int nSplits = 0;
-        final double[] mid;
-        final double[] l = lp;
-
-        if (monotonize) {
-            // monotonize quad:
-            final CurveBasicMonotonizer monotonizer
-                = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
-
-            nSplits = monotonizer.nbSplits;
-            mid = monotonizer.middle;
-        } else {
-            // use left instead:
-            mid = l;
-            mid[0] = cx0; mid[1] = cy0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-        }
-        final double[] r = rp;
-
-        int kind = 0;
-        for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
-            kind = computeOffsetQuad(mid, off, l, r);
-
-            emitLineTo(l[0], l[1]);
-
-            switch(kind) {
-            case 6:
-                emitQuadTo(l[2], l[3], l[4], l[5]);
-                emitQuadToRev(r[0], r[1], r[2], r[3]);
-                break;
-            case 4:
-                emitLineTo(l[2], l[3]);
-                emitLineToRev(r[0], r[1]);
-                break;
-            default:
-            }
-            emitLineToRev(r[kind - 2], r[kind - 1]);
-        }
-
-        this.prev = DRAWING_OP_TO;
-        this.cx0 = x2;
-        this.cy0 = y2;
-        this.cdx = dxf;
-        this.cdy = dyf;
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-    }
-
-    @Override public long getNativeConsumer() {
-        throw new InternalError("Stroker doesn't use a native consumer");
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java b/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java
deleted file mode 100644
index d4d9b7e..0000000
--- a/src/main/java/sun/java2d/marlin/DTransformingPathConsumer2D.java
+++ /dev/null
@@ -1,1389 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Path2D;
-import java.util.Arrays;
-import sun.java2d.marlin.DHelpers.IndexStack;
-import sun.java2d.marlin.DHelpers.PolyStack;
-
-final class DTransformingPathConsumer2D {
-
-    // smaller uncertainty in double variant
-    static final double CLIP_RECT_PADDING = 0.25d;
-
-    private final DRendererContext rdrCtx;
-
-    // recycled ClosedPathDetector instance from detectClosedPath()
-    private final ClosedPathDetector   cpDetector;
-
-    // recycled PathClipFilter instance from pathClipper()
-    private final PathClipFilter       pathClipper;
-
-    // recycled DPathConsumer2D instance from wrapPath2D()
-    private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
-
-    // recycled DPathConsumer2D instances from deltaTransformConsumer()
-    private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
-    private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
-
-    // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
-    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
-    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
-
-    // recycled PathTracer instances from tracer...() methods
-    private final PathTracer tracerInput      = new PathTracer("[Input]");
-    private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
-    private final PathTracer tracerFiller     = new PathTracer("Filler");
-    private final PathTracer tracerStroker    = new PathTracer("Stroker");
-    private final PathTracer tracerDasher     = new PathTracer("Dasher");
-
-    DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
-        // used by RendererContext
-        this.rdrCtx = rdrCtx;
-        this.cpDetector = new ClosedPathDetector(rdrCtx);
-        this.pathClipper = new PathClipFilter(rdrCtx);
-    }
-
-    DPathConsumer2D wrapPath2D(Path2D.Double p2d) {
-        return wp_Path2DWrapper.init(p2d);
-    }
-
-    DPathConsumer2D traceInput(DPathConsumer2D out) {
-        return tracerInput.init(out);
-    }
-
-    DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
-        return tracerCPDetector.init(out);
-    }
-
-    DPathConsumer2D traceFiller(DPathConsumer2D out) {
-        return tracerFiller.init(out);
-    }
-
-    DPathConsumer2D traceStroker(DPathConsumer2D out) {
-        return tracerStroker.init(out);
-    }
-
-    DPathConsumer2D traceDasher(DPathConsumer2D out) {
-        return tracerDasher.init(out);
-    }
-
-    DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
-        return cpDetector.init(out);
-    }
-
-    DPathConsumer2D pathClipper(DPathConsumer2D out) {
-        return pathClipper.init(out);
-    }
-
-    DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
-                                          AffineTransform at)
-    {
-        if (at == null) {
-            return out;
-        }
-        final double mxx = at.getScaleX();
-        final double mxy = at.getShearX();
-        final double myx = at.getShearY();
-        final double myy = at.getScaleY();
-
-        if (mxy == 0.0d && myx == 0.0d) {
-            if (mxx == 1.0d && myy == 1.0d) {
-                return out;
-            } else {
-                // Scale only
-                if (rdrCtx.doClip) {
-                    // adjust clip rectangle (ymin, ymax, xmin, xmax):
-                    rdrCtx.clipInvScale = adjustClipScale(rdrCtx.clipRect,
-                        mxx, myy);
-                }
-                return dt_DeltaScaleFilter.init(out, mxx, myy);
-            }
-        } else {
-            if (rdrCtx.doClip) {
-                // adjust clip rectangle (ymin, ymax, xmin, xmax):
-                rdrCtx.clipInvScale = adjustClipInverseDelta(rdrCtx.clipRect,
-                    mxx, mxy, myx, myy);
-            }
-            return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
-        }
-    }
-
-    private static double adjustClipScale(final double[] clipRect,
-                                          final double mxx, final double myy)
-    {
-        // Adjust the clipping rectangle (iv_DeltaScaleFilter):
-        final double scaleY = 1.0d / myy;
-        clipRect[0] *= scaleY;
-        clipRect[1] *= scaleY;
-
-        if (clipRect[1] < clipRect[0]) {
-            double tmp = clipRect[0];
-            clipRect[0] = clipRect[1];
-            clipRect[1] = tmp;
-        }
-
-        final double scaleX = 1.0d / mxx;
-        clipRect[2] *= scaleX;
-        clipRect[3] *= scaleX;
-
-        if (clipRect[3] < clipRect[2]) {
-            double tmp = clipRect[2];
-            clipRect[2] = clipRect[3];
-            clipRect[3] = tmp;
-        }
-
-        if (MarlinConst.DO_LOG_CLIP) {
-                MarlinUtils.logInfo("clipRect (ClipScale): "
-                                    + Arrays.toString(clipRect));
-        }
-        return 0.5d * (Math.abs(scaleX) + Math.abs(scaleY));
-    }
-
-    private static double adjustClipInverseDelta(final double[] clipRect,
-                                                 final double mxx, final double mxy,
-                                                 final double myx, final double myy)
-    {
-        // Adjust the clipping rectangle (iv_DeltaTransformFilter):
-        final double det = mxx * myy - mxy * myx;
-        final double imxx =  myy / det;
-        final double imxy = -mxy / det;
-        final double imyx = -myx / det;
-        final double imyy =  mxx / det;
-
-        double xmin, xmax, ymin, ymax;
-        double x, y;
-        // xmin, ymin:
-        x = clipRect[2] * imxx + clipRect[0] * imxy;
-        y = clipRect[2] * imyx + clipRect[0] * imyy;
-
-        xmin = xmax = x;
-        ymin = ymax = y;
-
-        // xmax, ymin:
-        x = clipRect[3] * imxx + clipRect[0] * imxy;
-        y = clipRect[3] * imyx + clipRect[0] * imyy;
-
-        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
-        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
-
-        // xmin, ymax:
-        x = clipRect[2] * imxx + clipRect[1] * imxy;
-        y = clipRect[2] * imyx + clipRect[1] * imyy;
-
-        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
-        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
-
-        // xmax, ymax:
-        x = clipRect[3] * imxx + clipRect[1] * imxy;
-        y = clipRect[3] * imyx + clipRect[1] * imyy;
-
-        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
-        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
-
-        clipRect[0] = ymin;
-        clipRect[1] = ymax;
-        clipRect[2] = xmin;
-        clipRect[3] = xmax;
-
-        if (MarlinConst.DO_LOG_CLIP) {
-                MarlinUtils.logInfo("clipRect (ClipInverseDelta): "
-                                    + Arrays.toString(clipRect));
-        }
-
-        final double scaleX = Math.sqrt(imxx * imxx + imxy * imxy);
-        final double scaleY = Math.sqrt(imyx * imyx + imyy * imyy);
-
-        return 0.5d * (scaleX + scaleY);
-    }
-
-    DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
-                                                 AffineTransform at)
-    {
-        if (at == null) {
-            return out;
-        }
-        double mxx = at.getScaleX();
-        double mxy = at.getShearX();
-        double myx = at.getShearY();
-        double myy = at.getScaleY();
-
-        if (mxy == 0.0d && myx == 0.0d) {
-            if (mxx == 1.0d && myy == 1.0d) {
-                return out;
-            } else {
-                return iv_DeltaScaleFilter.init(out, 1.0d / mxx, 1.0d / myy);
-            }
-        } else {
-            final double det = mxx * myy - mxy * myx;
-            return iv_DeltaTransformFilter.init(out,
-                                                myy / det,
-                                               -mxy / det,
-                                               -myx / det,
-                                                mxx / det);
-        }
-    }
-
-    static final class DeltaScaleFilter implements DPathConsumer2D {
-        private DPathConsumer2D out;
-        private double sx, sy;
-
-        DeltaScaleFilter() {}
-
-        DeltaScaleFilter init(DPathConsumer2D out,
-                              double mxx, double myy)
-        {
-            if (this.out != out) {
-                this.out = out;
-            }
-            sx = mxx;
-            sy = myy;
-            return this; // fluent API
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            out.moveTo(x0 * sx, y0 * sy);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            out.lineTo(x1 * sx, y1 * sy);
-        }
-
-        @Override
-        public void quadTo(double x1, double y1,
-                           double x2, double y2)
-        {
-            out.quadTo(x1 * sx, y1 * sy,
-                       x2 * sx, y2 * sy);
-        }
-
-        @Override
-        public void curveTo(double x1, double y1,
-                            double x2, double y2,
-                            double x3, double y3)
-        {
-            out.curveTo(x1 * sx, y1 * sy,
-                        x2 * sx, y2 * sy,
-                        x3 * sx, y3 * sy);
-        }
-
-        @Override
-        public void closePath() {
-            out.closePath();
-        }
-
-        @Override
-        public void pathDone() {
-            out.pathDone();
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            return 0;
-        }
-    }
-
-    static final class DeltaTransformFilter implements DPathConsumer2D {
-        private DPathConsumer2D out;
-        private double mxx, mxy, myx, myy;
-
-        DeltaTransformFilter() {}
-
-        DeltaTransformFilter init(DPathConsumer2D out,
-                                  double mxx, double mxy,
-                                  double myx, double myy)
-        {
-            if (this.out != out) {
-                this.out = out;
-            }
-            this.mxx = mxx;
-            this.mxy = mxy;
-            this.myx = myx;
-            this.myy = myy;
-            return this; // fluent API
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            out.moveTo(x0 * mxx + y0 * mxy,
-                       x0 * myx + y0 * myy);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            out.lineTo(x1 * mxx + y1 * mxy,
-                       x1 * myx + y1 * myy);
-        }
-
-        @Override
-        public void quadTo(double x1, double y1,
-                           double x2, double y2)
-        {
-            out.quadTo(x1 * mxx + y1 * mxy,
-                       x1 * myx + y1 * myy,
-                       x2 * mxx + y2 * mxy,
-                       x2 * myx + y2 * myy);
-        }
-
-        @Override
-        public void curveTo(double x1, double y1,
-                            double x2, double y2,
-                            double x3, double y3)
-        {
-            out.curveTo(x1 * mxx + y1 * mxy,
-                        x1 * myx + y1 * myy,
-                        x2 * mxx + y2 * mxy,
-                        x2 * myx + y2 * myy,
-                        x3 * mxx + y3 * mxy,
-                        x3 * myx + y3 * myy);
-        }
-
-        @Override
-        public void closePath() {
-            out.closePath();
-        }
-
-        @Override
-        public void pathDone() {
-            out.pathDone();
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            return 0;
-        }
-    }
-
-    static final class Path2DWrapper implements DPathConsumer2D {
-        private Path2D.Double p2d;
-
-        Path2DWrapper() {}
-
-        Path2DWrapper init(Path2D.Double p2d) {
-            if (this.p2d != p2d) {
-                this.p2d = p2d;
-            }
-            return this;
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            p2d.moveTo(x0, y0);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            p2d.lineTo(x1, y1);
-        }
-
-        @Override
-        public void closePath() {
-            p2d.closePath();
-        }
-
-        @Override
-        public void pathDone() {}
-
-        @Override
-        public void curveTo(double x1, double y1,
-                            double x2, double y2,
-                            double x3, double y3)
-        {
-            p2d.curveTo(x1, y1, x2, y2, x3, y3);
-        }
-
-        @Override
-        public void quadTo(double x1, double y1, double x2, double y2) {
-            p2d.quadTo(x1, y1, x2, y2);
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            throw new InternalError("Not using a native peer");
-        }
-    }
-
-    static final class ClosedPathDetector implements DPathConsumer2D {
-
-        private final DRendererContext rdrCtx;
-        private final PolyStack stack;
-
-        private DPathConsumer2D out;
-
-        ClosedPathDetector(final DRendererContext rdrCtx) {
-            this.rdrCtx = rdrCtx;
-            this.stack = (rdrCtx.stats != null) ?
-                new PolyStack(rdrCtx,
-                        rdrCtx.stats.stat_cpd_polystack_types,
-                        rdrCtx.stats.stat_cpd_polystack_curves,
-                        rdrCtx.stats.hist_cpd_polystack_curves,
-                        rdrCtx.stats.stat_array_cpd_polystack_curves,
-                        rdrCtx.stats.stat_array_cpd_polystack_types)
-                : new PolyStack(rdrCtx);
-        }
-
-        ClosedPathDetector init(DPathConsumer2D out) {
-            if (this.out != out) {
-                this.out = out;
-            }
-            return this; // fluent API
-        }
-
-        /**
-         * Disposes this instance:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            stack.dispose();
-        }
-
-        @Override
-        public void pathDone() {
-            // previous path is not closed:
-            finish(false);
-            out.pathDone();
-
-            // TODO: fix possible leak if exception happened
-            // Dispose this instance:
-            dispose();
-        }
-
-        @Override
-        public void closePath() {
-            // path is closed
-            finish(true);
-            out.closePath();
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            // previous path is not closed:
-            finish(false);
-            out.moveTo(x0, y0);
-        }
-
-        private void finish(final boolean closed) {
-            rdrCtx.closedPath = closed;
-            stack.pullAll(out);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            stack.pushLine(x1, y1);
-        }
-
-        @Override
-        public void curveTo(double x3, double y3,
-                            double x2, double y2,
-                            double x1, double y1)
-        {
-            stack.pushCubic(x1, y1, x2, y2, x3, y3);
-        }
-
-        @Override
-        public void quadTo(double x2, double y2, double x1, double y1) {
-            stack.pushQuad(x1, y1, x2, y2);
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            throw new InternalError("Not using a native peer");
-        }
-    }
-
-    static final class PathClipFilter implements DPathConsumer2D {
-
-        private static final boolean TRACE = false;
-
-        private static final int MOVE_TO = 0;
-        private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
-        private static final int CLOSE = 2;
-
-        private DPathConsumer2D out;
-
-        private int prev;
-
-        // Bounds of the drawing region, at pixel precision.
-        private final double[] clipRect;
-
-        private final double[] corners = new double[8];
-        private boolean init_corners = false;
-
-        private final IndexStack stack;
-
-        // the current outcode of the current sub path
-        private int cOutCode = 0;
-
-        // the outcode of the starting point
-        private int sOutCode = 0;
-        
-        // the cumulated (and) outcode of the complete path
-        private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
-
-        private boolean outside = false;
-
-        // The starting point of the path
-        private double sx0, sy0;
-
-        // The current point (TODO stupid repeated info)
-        private double cx0, cy0;
-
-        // The current point OUTSIDE
-        private double cox0, coy0;
-
-        private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
-        private final CurveClipSplitter curveSplitter;
-
-        PathClipFilter(final DRendererContext rdrCtx) {
-            this.clipRect = rdrCtx.clipRect;
-            this.curveSplitter = rdrCtx.curveClipSplitter;
-
-            this.stack = (rdrCtx.stats != null) ?
-                new IndexStack(rdrCtx,
-                        rdrCtx.stats.stat_pcf_idxstack_indices,
-                        rdrCtx.stats.hist_pcf_idxstack_indices,
-                        rdrCtx.stats.stat_array_pcf_idxstack_indices)
-                : new IndexStack(rdrCtx);
-        }
-
-        PathClipFilter init(final DPathConsumer2D out) {
-            if (this.out != out) {
-                this.out = out;
-            }
-
-            if (MarlinConst.DO_CLIP_SUBDIVIDER) {
-                // adjust padded clip rectangle:
-                curveSplitter.init();
-            }
-
-            this.init_corners = true;
-            this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
-            this.prev = CLOSE;
-
-            return this; // fluent API
-        }
-
-        /**
-         * Disposes this instance:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            stack.dispose();
-        }
-
-        private void finishPath() {
-            if (outside) {
-                // criteria: inside or totally outside ?
-                if (gOutCode == 0) {
-                    finish();
-                } else {
-                    this.outside = false;
-                    stack.reset();
-                }
-            }
-        }
-
-        private void finish() {
-            this.outside = false;
-
-            if (!stack.isEmpty()) {
-                if (init_corners) {
-                    init_corners = false;
-
-                    final double[] _corners = corners;
-                    final double[] _clipRect = clipRect;
-                    // Top Left (0):
-                    _corners[0] = _clipRect[2];
-                    _corners[1] = _clipRect[0];
-                    // Bottom Left (1):
-                    _corners[2] = _clipRect[2];
-                    _corners[3] = _clipRect[1];
-                    // Top right (2):
-                    _corners[4] = _clipRect[3];
-                    _corners[5] = _clipRect[0];
-                    // Bottom Right (3):
-                    _corners[6] = _clipRect[3];
-                    _corners[7] = _clipRect[1];
-                }
-                stack.pullAll(corners, out, (prev == MOVE_TO));
-                prev = DRAWING_OP_TO;
-            }
-            // go to the last outside point:
-            this.cx0 = cox0;
-            this.cy0 = coy0;
-        }
-
-        @Override
-        public void pathDone() {
-            if (TRACE) {
-                System.out.println("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev);
-            }
-            _closePath();
-
-            out.pathDone();
-
-            // this shouldn't matter since this object won't be used
-            // after the call to this method.
-            this.prev = CLOSE;
-
-            // TODO: fix possible leak if exception happened
-            // Dispose this instance:
-            dispose();
-        }
-
-        @Override
-        public void closePath() {
-            if (TRACE) {
-                System.out.println("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev);
-            }
-            _closePath();
-
-            out.closePath();
-
-            // if outside, moveTo is needed
-            if (sOutCode != 0) {
-                this.prev = MOVE_TO;
-            } else {
-                this.prev = CLOSE;
-            }
-
-            // back to starting point:
-            this.cOutCode = sOutCode;
-            this.cx0 = sx0;
-            this.cy0 = sy0;
-        }
-        
-        private void _closePath() {
-            // preserve outside flag for the lineTo call below
-            final boolean prevOutside = outside;
-            finishPath();
-
-            if (prev == DRAWING_OP_TO) {
-                // Should clip
-                final int orCode = (cOutCode | sOutCode);
-                if (orCode != 0) {
-                    if (cx0 != sx0 || cy0 != sy0) {
-                        // restore outside flag before lineTo:
-                        this.outside = prevOutside;
-                        // may subdivide line:
-                        lineTo(sx0, sy0);
-                    }
-                }
-            }
-            finishPath();
-        }
-
-        @Override
-        public void moveTo(final double x0, final double y0) {
-            if (TRACE) {
-                System.out.println("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev);
-            }
-            _closePath();
-
-            this.prev = MOVE_TO;
-
-            // update starting point:
-            final int outcode = DHelpers.outcode(x0, y0, clipRect);
-            this.cOutCode = outcode;
-            this.sOutCode = outcode;
-            this.cx0 = x0;
-            this.cy0 = y0;
-
-            this.sx0 = x0;
-            this.sy0 = y0;
-        }
-
-        @Override
-        public void lineTo(final double xe, final double ye) {
-            final int outcode0 = this.cOutCode;
-            final int outcode1 = DHelpers.outcode(xe, ye, clipRect);
-
-            if (TRACE) {
-                if (subdivide) {
-                    System.out.println("----------------------");
-                }
-                if (outside) {
-                    System.out.println("LineTo co (" + cox0 + ", " + coy0 + ")");
-                }
-                System.out.println("LineTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
-            }
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1);
-            if (orCode != 0) {
-                final int sideCode = (outcode0 & outcode1);
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        boolean ret;
-                        // subdivide curve => callback with subdivided parts:
-                        if (outside) {
-                            ret = curveSplitter.splitLine(cox0, coy0, xe, ye,
-                                                          orCode, this);
-                        } else {
-                            ret = curveSplitter.splitLine(cx0, cy0, xe, ye,
-                                                          orCode, this);
-                        }
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode1;
-                    this.gOutCode &= sideCode;
-                    // keep last point coordinate before entering the clip again:
-                    this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
-
-                    if (TRACE) {
-                        System.out.println("skipped: (" + cox0 + ", " + coy0 + ")");
-                    }
-
-                    clip(sideCode, outcode0, outcode1);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode1;
-            this.gOutCode = 0;
-
-            if (outside) {
-                finish();
-
-                // emit last point outside before entering again...
-                if (outcode0 != 0) {
-                    if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
-                    }
-                    if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
-                    } else {
-                        out.lineTo(cox0, coy0);
-                    }
-                    prev = DRAWING_OP_TO;
-                }
-            }
-            // clipping disabled:
-            if (prev == MOVE_TO) {
-                out.moveTo(cx0, cy0);
-            }
-            prev = DRAWING_OP_TO;
-
-            out.lineTo(xe, ye);
-            this.cx0 = xe;
-            this.cy0 = ye;
-
-            if (TRACE && subdivide) {
-                System.out.println("----------------------");
-            }
-        }
-
-        private void clip(final int sideCode,
-                          final int outcode0,
-                          final int outcode1)
-        {
-            // corner or cross-boundary on left or right side:
-            if ((outcode0 != outcode1)
-                    && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0))
-            {
-                // combine outcodes:
-                final int mergeCode = (outcode0 | outcode1);
-                final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B;
-                final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R;
-                final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2;
-
-                // add corners to outside stack:
-                switch (tbCode) {
-                    case MarlinConst.OUTCODE_TOP:
-                        stack.push(off); // top
-                        return;
-                    case MarlinConst.OUTCODE_BOTTOM:
-                        stack.push(off + 1); // bottom
-                        return;
-                    default:
-                        // both TOP / BOTTOM:
-                        if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
-                            // top to bottom
-                            stack.push(off); // top
-                            stack.push(off + 1); // bottom
-                        } else {
-                            // bottom to top
-                            stack.push(off + 1); // bottom
-                            stack.push(off); // top
-                        }
-                }
-            }
-        }
-
-        @Override
-        public void curveTo(final double x1, final double y1,
-                            final double x2, final double y2,
-                            final double xe, final double ye)
-        {
-            final int outcode0 = this.cOutCode;
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
-            final int outcode3 = DHelpers.outcode(xe, ye, clipRect);
-
-            if (TRACE) {
-                if (subdivide) {
-                    System.out.println("----------------------");
-                }
-                if (outside) {
-                    System.out.println("CurveTo co (" + cox0 + ", " + coy0 + ")");
-                }
-                System.out.println("CurveTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside);
-            }
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret;
-                        if (outside) {
-                            ret = curveSplitter.splitCurve(cox0, coy0, x1, y1,
-                                                           x2, y2, xe, ye,
-                                                           orCode, this);
-                        } else {
-                            ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
-                                                           x2, y2, xe, ye,
-                                                           orCode, this);
-                        }
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode3;
-                    this.gOutCode &= sideCode;
-                    // keep last point coordinate before entering the clip again:
-                    this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
-
-                    if (TRACE) {
-                        System.out.println("skipped: (" + cox0 + ", " + coy0 + ")");
-                    }
-
-                    clip(sideCode, outcode0, outcode3);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode3;
-            this.gOutCode = 0;
-
-            if (outside) {
-                finish();
-
-                // emit last point outside before entering again...
-                if (outcode0 != 0) {
-                    if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
-                    }
-                    if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
-                    } else {
-                        out.lineTo(cox0, coy0);
-                    }
-                    prev = DRAWING_OP_TO;
-                }
-            }
-            // clipping disabled:
-            if (prev == MOVE_TO) {
-                out.moveTo(cx0, cy0);
-            }
-            prev = DRAWING_OP_TO;
-
-            out.curveTo(x1, y1, x2, y2, xe, ye);
-            this.cx0 = xe;
-            this.cy0 = ye;
-
-            if (TRACE && subdivide) {
-                System.out.println("----------------------");
-            }
-        }
-
-        @Override
-        public void quadTo(final double x1, final double y1,
-                           final double xe, final double ye)
-        {
-            final int outcode0 = this.cOutCode;
-            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-            final int outcode2 = DHelpers.outcode(xe, ye, clipRect);
-
-            if (TRACE) {
-                if (subdivide) {
-                    System.out.println("----------------------");
-                }
-                if (outside) {
-                    System.out.println("QuadTo co (" + cox0 + ", " + coy0 + ")");
-                }
-                System.out.println("QuadTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
-            }
-
-            // Should clip
-            final int orCode = (outcode0 | outcode1 | outcode2);
-            if (orCode != 0) {
-                final int sideCode = outcode0 & outcode1 & outcode2;
-
-                // basic rejection criteria:
-                if (sideCode == 0) {
-                    // overlap clip:
-                    if (subdivide) {
-                        // avoid reentrance
-                        subdivide = false;
-                        // subdivide curve => callback with subdivided parts:
-                        boolean ret;
-                        if (outside) {
-                            ret = curveSplitter.splitQuad(cox0, coy0, x1, y1,
-                                                          xe, ye, orCode, this);
-                        } else {
-                            ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
-                                                          xe, ye, orCode, this);
-                        }
-                        // reentrance is done:
-                        subdivide = true;
-                        if (ret) {
-                            return;
-                        }
-                    }
-                    // already subdivided so render it
-                } else {
-                    this.cOutCode = outcode2;
-                    this.gOutCode &= sideCode;
-                    // keep last point coordinate before entering the clip again:
-                    this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
-
-                    clip(sideCode, outcode0, outcode2);
-                    return;
-                }
-            }
-
-            this.cOutCode = outcode2;
-            this.gOutCode = 0;
-
-            if (outside) {
-                finish();
-
-                // emit last point outside before entering again...
-                if (outcode0 != 0) {
-                    if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
-                    }
-                    if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
-                    } else {
-                        out.lineTo(cox0, coy0);
-                    }
-                    prev = DRAWING_OP_TO;
-                }
-            }
-            // clipping disabled:
-            if (prev == MOVE_TO) {
-                out.moveTo(cx0, cy0);
-            }
-            prev = DRAWING_OP_TO;
-
-            out.quadTo(x1, y1, xe, ye);
-            this.cx0 = xe;
-            this.cy0 = ye;
-
-            if (TRACE && subdivide) {
-                System.out.println("----------------------");
-            }
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            throw new InternalError("Not using a native peer");
-        }
-    }
-
-    static final class CurveClipSplitter {
-
-        static final double LEN_TH = MarlinProperties.getSubdividerMinLength();
-        static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d);
-
-        private static final boolean TRACE = false;
-
-        private static final int MAX_N_CURVES = 3 * 4;
-
-        private final DRendererContext rdrCtx;
-
-        // scaled length threshold:
-        private double minLength;
-
-        // clip rectangle (ymin, ymax, xmin, xmax):
-        final double[] clipRect;
-
-        // clip rectangle (ymin, ymax, xmin, xmax) including padding:
-        final double[] clipRectPad = new double[4];
-        private boolean init_clipRectPad = false;
-
-        // This is where the curve to be processed is put. We give it
-        // enough room to store all curves.
-        final double[] middle = new double[MAX_N_CURVES * 8 + 2];
-        // t values at subdivision points
-        private final double[] subdivTs = new double[MAX_N_CURVES];
-
-        // dirty curve
-        private final DCurve curve;
-
-        CurveClipSplitter(final DRendererContext rdrCtx) {
-            this.rdrCtx = rdrCtx;
-            this.clipRect = rdrCtx.clipRect;
-            this.curve = rdrCtx.curve;
-        }
-
-        void init() {
-            this.init_clipRectPad = true;
-
-            if (DO_CHECK_LENGTH) {
-                this.minLength = (this.rdrCtx.clipInvScale == 0.0d) ? LEN_TH
-                                    : (LEN_TH * this.rdrCtx.clipInvScale);
-
-                if (MarlinConst.DO_LOG_CLIP) {
-                    MarlinUtils.logInfo("CurveClipSplitter.minLength = "
-                                            + minLength);
-                }
-            }
-        }
-
-        private void initPaddedClip() {
-            // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
-            // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
-            // add a rounding error (curve subdivision ~ 0.1px):
-            final double[] _clipRect = clipRect;
-            final double[] _clipRectPad = clipRectPad;
-
-            _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
-            _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
-            _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
-            _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
-
-            if (TRACE) {
-                MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
-                                        + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
-            }
-        }
-
-        boolean splitLine(final double x0, final double y0,
-                          final double x1, final double y1,
-                          final int outCodeOR,
-                          final DPathConsumer2D out)
-        {
-            if (TRACE) {
-                MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
-            }
-
-            if (DO_CHECK_LENGTH && DHelpers.fastLineLen(x0, y0, x1, y1) <= minLength) {
-                return false;
-            }
-
-            final double[] mid = middle;
-            mid[0] = x0;  mid[1] = y0;
-            mid[2] = x1;  mid[3] = y1;
-
-            return subdivideAtIntersections(4, outCodeOR, out);
-        }
-
-        boolean splitQuad(final double x0, final double y0,
-                          final double x1, final double y1,
-                          final double x2, final double y2,
-                          final int outCodeOR,
-                          final DPathConsumer2D out)
-        {
-            if (TRACE) {
-                MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
-            }
-
-            if (DO_CHECK_LENGTH && DHelpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= minLength) {
-                return false;
-            }
-
-            final double[] mid = middle;
-            mid[0] = x0;  mid[1] = y0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-
-            return subdivideAtIntersections(6, outCodeOR, out);
-        }
-
-        boolean splitCurve(final double x0, final double y0,
-                           final double x1, final double y1,
-                           final double x2, final double y2,
-                           final double x3, final double y3,
-                           final int outCodeOR,
-                           final DPathConsumer2D out)
-        {
-            if (TRACE) {
-                MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
-            }
-
-            if (DO_CHECK_LENGTH && DHelpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= minLength) {
-                return false;
-            }
-
-            final double[] mid = middle;
-            mid[0] = x0;  mid[1] = y0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-            mid[6] = x3;  mid[7] = y3;
-
-            return subdivideAtIntersections(8, outCodeOR, out);
-        }
-
-        private boolean subdivideAtIntersections(final int type, final int outCodeOR,
-                                                 final DPathConsumer2D out)
-        {
-            final double[] mid = middle;
-            final double[] subTs = subdivTs;
-
-            if (init_clipRectPad) {
-                init_clipRectPad = false;
-                initPaddedClip();
-            }
-
-            final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
-                                                        outCodeOR, clipRectPad);
-
-            if (TRACE) {
-                MarlinUtils.logInfo("nSplits: " + nSplits);
-                MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
-            }
-            if (nSplits == 0) {
-                // only curve support shortcut
-                return false;
-            }
-            double prevT = 0.0d;
-
-            for (int i = 0, off = 0; i < nSplits; i++, off += type) {
-                final double t = subTs[i];
-
-                DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
-                                     mid, off, mid, off, type);
-                prevT = t;
-            }
-
-            for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
-                if (TRACE) {
-                    MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
-                }
-                emitCurrent(type, mid, off, out);
-            }
-            return true;
-        }
-
-        static void emitCurrent(final int type, final double[] pts,
-                                final int off, final DPathConsumer2D out)
-        {
-            // if instead of switch (perf + most probable cases first)
-            if (type == 8) {
-                out.curveTo(pts[off + 2], pts[off + 3],
-                            pts[off + 4], pts[off + 5],
-                            pts[off + 6], pts[off + 7]);
-            } else if (type == 4) {
-                out.lineTo(pts[off + 2], pts[off + 3]);
-            } else {
-                out.quadTo(pts[off + 2], pts[off + 3],
-                           pts[off + 4], pts[off + 5]);
-            }
-        }
-    }
-
-    static final class CurveBasicMonotonizer {
-
-        private static final int MAX_N_CURVES = 11;
-
-        // squared half line width (for stroker)
-        private double lw2;
-
-        // number of splitted curves
-        int nbSplits;
-
-        // This is where the curve to be processed is put. We give it
-        // enough room to store all curves.
-        final double[] middle = new double[MAX_N_CURVES * 6 + 2];
-        // t values at subdivision points
-        private final double[] subdivTs = new double[MAX_N_CURVES - 1];
-
-        // dirty curve
-        private final DCurve curve;
-
-        CurveBasicMonotonizer(final DRendererContext rdrCtx) {
-            this.curve = rdrCtx.curve;
-        }
-
-        void init(final double lineWidth) {
-            this.lw2 = (lineWidth * lineWidth) / 4.0d;
-        }
-
-        CurveBasicMonotonizer curve(final double x0, final double y0,
-                                    final double x1, final double y1,
-                                    final double x2, final double y2,
-                                    final double x3, final double y3)
-        {
-            final double[] mid = middle;
-            mid[0] = x0;  mid[1] = y0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-            mid[6] = x3;  mid[7] = y3;
-
-            final double[] subTs = subdivTs;
-            final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 8, lw2);
-
-            double prevT = 0.0d;
-            for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
-                final double t = subTs[i];
-
-                DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT),
-                                          mid, off, mid, off, off + 6);
-                prevT = t;
-            }
-
-            this.nbSplits = nSplits;
-            return this;
-        }
-
-        CurveBasicMonotonizer quad(final double x0, final double y0,
-                                   final double x1, final double y1,
-                                   final double x2, final double y2)
-        {
-            final double[] mid = middle;
-            mid[0] = x0;  mid[1] = y0;
-            mid[2] = x1;  mid[3] = y1;
-            mid[4] = x2;  mid[5] = y2;
-
-            final double[] subTs = subdivTs;
-            final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 6, lw2);
-
-            double prevt = 0.0d;
-            for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
-                final double t = subTs[i];
-                DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt),
-                                         mid, off, mid, off, off + 4);
-                prevt = t;
-            }
-
-            this.nbSplits = nSplits;
-            return this;
-        }
-    }
-
-    static final class PathTracer implements DPathConsumer2D {
-        private final String prefix;
-        private DPathConsumer2D out;
-
-        PathTracer(String name) {
-            this.prefix = name + ": ";
-        }
-
-        PathTracer init(DPathConsumer2D out) {
-            if (this.out != out) {
-                this.out = out;
-            }
-            return this; // fluent API
-        }
-
-        @Override
-        public void moveTo(double x0, double y0) {
-            log("p.moveTo(" + x0 + ", " + y0 + ");");
-            out.moveTo(x0, y0);
-        }
-
-        @Override
-        public void lineTo(double x1, double y1) {
-            log("p.lineTo(" + x1 + ", " + y1 + ");");
-            out.lineTo(x1, y1);
-        }
-
-        @Override
-        public void curveTo(double x1, double y1,
-                            double x2, double y2,
-                            double x3, double y3)
-        {
-            log("p.curveTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2  + ", " + x3 + ", " + y3 + ");");
-            out.curveTo(x1, y1, x2, y2, x3, y3);
-        }
-
-        @Override
-        public void quadTo(double x1, double y1,
-                           double x2, double y2) {
-            log("p.quadTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2  + ");");
-            out.quadTo(x1, y1, x2, y2);
-        }
-
-        @Override
-        public void closePath() {
-            log("p.closePath();");
-            out.closePath();
-        }
-
-        @Override
-        public void pathDone() {
-            log("p.pathDone();");
-            out.pathDone();
-        }
-
-        private void log(final String message) {
-            MarlinUtils.logInfo(prefix + message);
-        }
-
-        @Override
-        public long getNativeConsumer() {
-            throw new InternalError("Not using a native peer");
-        }
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/Dasher.java b/src/main/java/sun/java2d/marlin/Dasher.java
index cfcb208..aa75a29 100644
--- a/src/main/java/sun/java2d/marlin/Dasher.java
+++ b/src/main/java/sun/java2d/marlin/Dasher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -28,7 +28,6 @@
 import java.util.Arrays;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
-import sun.awt.geom.PathConsumer2D;
 
 /**
  * The <code>Dasher</code> class takes a series of linear commands
@@ -41,24 +40,24 @@
  * semantics are unclear.
  *
  */
-final class Dasher implements PathConsumer2D, MarlinConst {
+final class Dasher implements DPathConsumer2D, MarlinConst {
 
     /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
     static final int REC_LIMIT = 16;
-    static final float CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01
-    static final float MIN_T_INC = 1.0f / (1 << REC_LIMIT);
+    static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial
+    static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
 
-    static final float EPS = 1e-6f;
+    static final double EPS = 1e-6d;
 
     // More than 24 bits of mantissa means we can no longer accurately
     // measure the number of times cycled through the dash array so we
     // punt and override the phase to just be 0 past that point.
-    static final float MAX_CYCLES = 16000000.0f;
+    static final double MAX_CYCLES = 16000000.0d;
 
-    private PathConsumer2D out;
-    private float[] dash;
+    private DPathConsumer2D out;
+    private double[] dash;
     private int dashLen;
-    private float startPhase;
+    private double startPhase;
     private boolean startDashOn;
     private int startIdx;
 
@@ -67,15 +66,15 @@ final class Dasher implements PathConsumer2D, MarlinConst {
 
     private int idx;
     private boolean dashOn;
-    private float phase;
+    private double phase;
 
     // The starting point of the path
-    private float sx0, sy0;
+    private double sx0, sy0;
     // the current point
-    private float cx0, cy0;
+    private double cx0, cy0;
 
     // temporary storage for the current curve
-    private final float[] curCurvepts;
+    private final double[] curCurvepts;
 
     // per-thread renderer context
     final RendererContext rdrCtx;
@@ -87,16 +86,16 @@ final class Dasher implements PathConsumer2D, MarlinConst {
     // drawn on it, but we need joins to be drawn if there's a closePath()
     // So, we store the path elements that make up the first dash in the
     // buffer below.
-    private float[] firstSegmentsBuffer; // dynamic array
+    private double[] firstSegmentsBuffer; // dynamic array
     private int firstSegidx;
 
     // dashes ref (dirty)
-    final ArrayCacheFloat.Reference dashes_ref;
+    final ArrayCacheDouble.Reference dashes_ref;
     // firstSegmentsBuffer ref (dirty)
-    final ArrayCacheFloat.Reference firstSegmentsBuffer_ref;
+    final ArrayCacheDouble.Reference firstSegmentsBuffer_ref;
 
     // Bounds of the drawing region, at pixel precision.
-    private float[] clipRect;
+    private double[] clipRect;
 
     // the outcode of the current point
     private int cOutCode = 0;
@@ -107,9 +106,9 @@ final class Dasher implements PathConsumer2D, MarlinConst {
 
     private final CurveClipSplitter curveSplitter;
 
-    private float cycleLen;
+    private double cycleLen;
     private boolean outside;
-    private float totalSkipLen;
+    private double totalSkipLen;
 
     /**
      * Constructs a <code>Dasher</code>.
@@ -118,14 +117,14 @@ final class Dasher implements PathConsumer2D, MarlinConst {
     Dasher(final RendererContext rdrCtx) {
         this.rdrCtx = rdrCtx;
 
-        dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
+        dashes_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
 
-        firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
+        firstSegmentsBuffer_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
         firstSegmentsBuffer     = firstSegmentsBuffer_ref.initial;
 
         // we need curCurvepts to be able to contain 2 curves because when
         // dashing curves, we need to subdivide it
-        curCurvepts = new float[8 * 2];
+        curCurvepts = new double[8 * 2];
 
         this.curveSplitter = rdrCtx.curveClipSplitter;
     }
@@ -133,15 +132,15 @@ final class Dasher implements PathConsumer2D, MarlinConst {
     /**
      * Initialize the <code>Dasher</code>.
      *
-     * @param out an output <code>PathConsumer2D</code>.
-     * @param dash an array of <code>float</code>s containing the dash pattern
+     * @param out an output <code>DPathConsumer2D</code>.
+     * @param dash an array of <code>double</code>s containing the dash pattern
      * @param dashLen length of the given dash array
-     * @param phase a <code>float</code> containing the dash phase
+     * @param phase a <code>double</code> containing the dash phase
      * @param recycleDashes true to indicate to recycle the given dash array
      * @return this instance
      */
-    Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen,
-                float phase, final boolean recycleDashes)
+    Dasher init(final DPathConsumer2D out, final double[] dash, final int dashLen,
+                double phase, final boolean recycleDashes)
     {
         if (this.out != out) {
             this.out = out;
@@ -152,23 +151,23 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen,
         dashOn = true;
 
         // note: BasicStroke constructor checks dash elements and sum > 0
-        float sum = 0.0f;
+        double sum = 0.0d;
         for (int i = 0; i < dashLen; i++) {
             sum += dash[i];
         }
         this.cycleLen = sum;
 
-        float cycles = phase / sum;
-        if (phase < 0.0f) {
+        double cycles = phase / sum;
+        if (phase < 0.0d) {
             if (-cycles >= MAX_CYCLES) {
-                phase = 0.0f;
+                phase = 0.0d;
             } else {
                 int fullcycles = FloatMath.floor_int(-cycles);
                 if ((fullcycles & dashLen & 1) != 0) {
                     dashOn = !dashOn;
                 }
                 phase += fullcycles * sum;
-                while (phase < 0.0f) {
+                while (phase < 0.0d) {
                     if (--sidx < 0) {
                         sidx = dashLen - 1;
                     }
@@ -176,16 +175,16 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen,
                     dashOn = !dashOn;
                 }
             }
-        } else if (phase > 0.0f) {
+        } else if (phase > 0.0d) {
             if (cycles >= MAX_CYCLES) {
-                phase = 0.0f;
+                phase = 0.0d;
             } else {
                 int fullcycles = FloatMath.floor_int(cycles);
                 if ((fullcycles & dashLen & 1) != 0) {
                     dashOn = !dashOn;
                 }
                 phase -= fullcycles * sum;
-                float d;
+                double d;
                 while (phase >= (d = dash[sidx])) {
                     phase -= d;
                     sidx = (sidx + 1) % dashLen;
@@ -222,7 +221,7 @@ Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen,
     void dispose() {
         if (DO_CLEAN_DIRTY) {
             // Force zero-fill dirty arrays:
-            Arrays.fill(curCurvepts, 0.0f);
+            Arrays.fill(curCurvepts, 0.0d);
         }
         // Return arrays:
         if (recycleDashes) {
@@ -235,9 +234,9 @@ void dispose() {
         }
     }
 
-    float[] copyDashArray(final float[] dashes) {
+    double[] copyDashArray(final float[] dashes) {
         final int len = dashes.length;
-        final float[] newDashes;
+        final double[] newDashes;
         if (len <= MarlinConst.INITIAL_ARRAY) {
             newDashes = dashes_ref.initial;
         } else {
@@ -246,12 +245,12 @@ float[] copyDashArray(final float[] dashes) {
             }
             newDashes = dashes_ref.getArray(len);
         }
-        System.arraycopy(dashes, 0, newDashes, 0, len);
+        for (int i = 0; i < len; i++) { newDashes[i] = dashes[i]; }
         return newDashes;
     }
 
     @Override
-    public void moveTo(final float x0, final float y0) {
+    public void moveTo(final double x0, final double y0) {
         if (firstSegidx != 0) {
             out.moveTo(sx0, sy0);
             emitFirstSegments();
@@ -272,11 +271,11 @@ public void moveTo(final float x0, final float y0) {
             final int outcode = Helpers.outcode(x0, y0, clipRect);
             this.cOutCode = outcode;
             this.outside = false;
-            this.totalSkipLen = 0.0f;
+            this.totalSkipLen = 0.0d;
         }
     }
 
-    private void emitSeg(float[] buf, int off, int type) {
+    private void emitSeg(double[] buf, int off, int type) {
         switch (type) {
         case 4:
             out.lineTo(buf[off], buf[off + 1]);
@@ -295,7 +294,7 @@ private void emitSeg(float[] buf, int off, int type) {
     }
 
     private void emitFirstSegments() {
-        final float[] fSegBuf = firstSegmentsBuffer;
+        final double[] fSegBuf = firstSegmentsBuffer;
 
         for (int i = 0, len = firstSegidx; i < len; ) {
             int type = (int)fSegBuf[i];
@@ -306,12 +305,12 @@ private void emitFirstSegments() {
     }
 
     // precondition: pts must be in relative coordinates (relative to x0,y0)
-    private void goTo(final float[] pts, final int off, final int type,
+    private void goTo(final double[] pts, final int off, final int type,
                       final boolean on)
     {
         final int index = off + type;
-        final float x = pts[index - 4];
-        final float y = pts[index - 3];
+        final double x = pts[index - 4];
+        final double y = pts[index - 3];
 
         if (on) {
             if (starting) {
@@ -334,10 +333,10 @@ private void goTo(final float[] pts, final int off, final int type,
         this.cy0 = y;
     }
 
-    private void goTo_starting(final float[] pts, final int off, final int type) {
+    private void goTo_starting(final double[] pts, final int off, final int type) {
         int len = type - 1; // - 2 + 1
         int segIdx = firstSegidx;
-        float[] buf = firstSegmentsBuffer;
+        double[] buf = firstSegmentsBuffer;
 
         if (segIdx + len  > buf.length) {
             if (DO_STATS) {
@@ -356,7 +355,7 @@ private void goTo_starting(final float[] pts, final int off, final int type) {
     }
 
     @Override
-    public void lineTo(final float x1, final float y1) {
+    public void lineTo(final double x1, final double y1) {
         final int outcode0 = this.cOutCode;
 
         if (clipRect != null) {
@@ -402,30 +401,30 @@ public void lineTo(final float x1, final float y1) {
         _lineTo(x1, y1);
     }
 
-    private void _lineTo(final float x1, final float y1) {
-        final float dx = x1 - cx0;
-        final float dy = y1 - cy0;
+    private void _lineTo(final double x1, final double y1) {
+        final double dx = x1 - cx0;
+        final double dy = y1 - cy0;
 
-        float len = dx * dx + dy * dy;
-        if (len == 0.0f) {
+        double len = dx * dx + dy * dy;
+        if (len == 0.0d) {
             return;
         }
-        len = (float) Math.sqrt(len);
+        len = Math.sqrt(len);
 
         // The scaling factors needed to get the dx and dy of the
         // transformed dash segments.
-        final float cx = dx / len;
-        final float cy = dy / len;
+        final double cx = dx / len;
+        final double cy = dy / len;
 
-        final float[] _curCurvepts = curCurvepts;
-        final float[] _dash = dash;
+        final double[] _curCurvepts = curCurvepts;
+        final double[] _dash = dash;
         final int _dashLen = this.dashLen;
 
         int _idx = idx;
         boolean _dashOn = dashOn;
-        float _phase = phase;
+        double _phase = phase;
 
-        float leftInThisDashSegment, rem;
+        double leftInThisDashSegment, rem;
 
         while (true) {
             leftInThisDashSegment = _dash[_idx] - _phase;
@@ -442,7 +441,7 @@ private void _lineTo(final float x1, final float y1) {
 
                 // compare values using epsilon:
                 if (Math.abs(rem) <= EPS) {
-                    _phase = 0.0f;
+                    _phase = 0.0d;
                     _idx = (_idx + 1) % _dashLen;
                     _dashOn = !_dashOn;
                 }
@@ -458,7 +457,7 @@ private void _lineTo(final float x1, final float y1) {
             // Advance to next dash segment
             _idx = (_idx + 1) % _dashLen;
             _dashOn = !_dashOn;
-            _phase = 0.0f;
+            _phase = 0.0d;
         }
         // Save local state:
         idx = _idx;
@@ -466,13 +465,13 @@ private void _lineTo(final float x1, final float y1) {
         phase = _phase;
     }
 
-    private void skipLineTo(final float x1, final float y1) {
-        final float dx = x1 - cx0;
-        final float dy = y1 - cy0;
+    private void skipLineTo(final double x1, final double y1) {
+        final double dx = x1 - cx0;
+        final double dy = y1 - cy0;
 
-        float len = dx * dx + dy * dy;
-        if (len != 0.0f) {
-            len = (float)Math.sqrt(len);
+        double len = dx * dx + dy * dy;
+        if (len != 0.0d) {
+            len = Math.sqrt(len);
         }
 
         // Accumulate skipped length:
@@ -488,15 +487,15 @@ private void skipLineTo(final float x1, final float y1) {
     }
 
     public void skipLen() {
-        float len = this.totalSkipLen;
-        this.totalSkipLen = 0.0f;
+        double len = this.totalSkipLen;
+        this.totalSkipLen = 0.0d;
 
-        final float[] _dash = dash;
+        final double[] _dash = dash;
         final int _dashLen = this.dashLen;
 
         int _idx = idx;
         boolean _dashOn = dashOn;
-        float _phase = phase;
+        double _phase = phase;
 
         // -2 to ensure having 2 iterations of the post-loop
         // to compensate the remaining phase
@@ -510,7 +509,7 @@ public void skipLen() {
             _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
         }
 
-        float leftInThisDashSegment, rem;
+        double leftInThisDashSegment, rem;
 
         while (true) {
             leftInThisDashSegment = _dash[_idx] - _phase;
@@ -522,7 +521,7 @@ public void skipLen() {
 
                 // compare values using epsilon:
                 if (Math.abs(rem) <= EPS) {
-                    _phase = 0.0f;
+                    _phase = 0.0d;
                     _idx = (_idx + 1) % _dashLen;
                     _dashOn = !_dashOn;
                 }
@@ -533,7 +532,7 @@ public void skipLen() {
             // Advance to next dash segment
             _idx = (_idx + 1) % _dashLen;
             _dashOn = !_dashOn;
-            _phase = 0.0f;
+            _phase = 0.0d;
         }
         // Save local state:
         idx = _idx;
@@ -544,29 +543,29 @@ public void skipLen() {
     // preconditions: curCurvepts must be an array of length at least 2 * type,
     // that contains the curve we want to dash in the first type elements
     private void somethingTo(final int type) {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
         if (Helpers.isPointCurve(_curCurvepts, type)) {
             return;
         }
         final LengthIterator _li = li;
-        final float[] _dash = dash;
+        final double[] _dash = dash;
         final int _dashLen = this.dashLen;
 
         _li.initializeIterationOnCurve(_curCurvepts, type);
 
         int _idx = idx;
         boolean _dashOn = dashOn;
-        float _phase = phase;
+        double _phase = phase;
 
         // initially the current curve is at curCurvepts[0...type]
         int curCurveoff = 0;
-        float prevT = 0.0f;
-        float t;
-        float leftInThisDashSegment = _dash[_idx] - _phase;
+        double prevT = 0.0d;
+        double t;
+        double leftInThisDashSegment = _dash[_idx] - _phase;
 
-        while ((t = _li.next(leftInThisDashSegment)) < 1.0f) {
-            if (t != 0.0f) {
-                Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
+        while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
+            if (t != 0.0d) {
+                Helpers.subdivideAt((t - prevT) / (1.0d - prevT),
                                     _curCurvepts, curCurveoff,
                                     _curCurvepts, 0, type);
                 prevT = t;
@@ -576,7 +575,7 @@ private void somethingTo(final int type) {
             // Advance to next dash segment
             _idx = (_idx + 1) % _dashLen;
             _dashOn = !_dashOn;
-            _phase = 0.0f;
+            _phase = 0.0d;
             leftInThisDashSegment = _dash[_idx];
         }
 
@@ -586,7 +585,7 @@ private void somethingTo(final int type) {
 
         // compare values using epsilon:
         if (_phase + EPS >= _dash[_idx]) {
-            _phase = 0.0f;
+            _phase = 0.0d;
             _idx = (_idx + 1) % _dashLen;
             _dashOn = !_dashOn;
         }
@@ -600,7 +599,7 @@ private void somethingTo(final int type) {
     }
 
     private void skipSomethingTo(final int type) {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
         if (Helpers.isPointCurve(_curCurvepts, type)) {
             return;
         }
@@ -610,7 +609,7 @@ private void skipSomethingTo(final int type) {
 
         // In contrary to somethingTo(),
         // just estimate properly the curve length:
-        final float len = _li.totalLength();
+        final double len = _li.totalLength();
 
         // Accumulate skipped length:
         this.outside = true;
@@ -639,18 +638,18 @@ static final class LengthIterator {
         // (i.e. the original curve) is at recCurveStack[0] (but then it
         // gets subdivided, the left half is put at 1, so most of the time
         // only the right half of the original curve is at 0)
-        private final float[][] recCurveStack; // dirty
+        private final double[][] recCurveStack; // dirty
         // sidesRight[i] indicates whether the node at level i+1 in the path from
         // the root to the current leaf is a left or right child of its parent.
         private final boolean[] sidesRight; // dirty
         private int curveType;
         // lastT and nextT delimit the current leaf.
-        private float nextT;
-        private float lenAtNextT;
-        private float lastT;
-        private float lenAtLastT;
-        private float lenAtLastSplit;
-        private float lastSegLen;
+        private double nextT;
+        private double lenAtNextT;
+        private double lastT;
+        private double lenAtLastT;
+        private double lenAtLastSplit;
+        private double lastSegLen;
         // the current level in the recursion tree. 0 is the root. limit
         // is the deepest possible leaf.
         private int recLevel;
@@ -659,18 +658,18 @@ static final class LengthIterator {
         // the lengths of the lines of the control polygon. Only its first
         // curveType/2 - 1 elements are valid. This is an optimization. See
         // next() for more detail.
-        private final float[] curLeafCtrlPolyLengths = new float[3];
+        private final double[] curLeafCtrlPolyLengths = new double[3];
 
         LengthIterator() {
-            this.recCurveStack = new float[REC_LIMIT + 1][8];
+            this.recCurveStack = new double[REC_LIMIT + 1][8];
             this.sidesRight = new boolean[REC_LIMIT];
             // if any methods are called without first initializing this object
             // on a curve, we want it to fail ASAP.
-            this.nextT = Float.MAX_VALUE;
-            this.lenAtNextT = Float.MAX_VALUE;
-            this.lenAtLastSplit = Float.MIN_VALUE;
+            this.nextT = Double.MAX_VALUE;
+            this.lenAtNextT = Double.MAX_VALUE;
+            this.lenAtLastSplit = Double.MIN_VALUE;
             this.recLevel = Integer.MIN_VALUE;
-            this.lastSegLen = Float.MAX_VALUE;
+            this.lastSegLen = Double.MAX_VALUE;
             this.done = true;
         }
 
@@ -683,29 +682,29 @@ void reset() {
             if (DO_CLEAN_DIRTY) {
                 final int recLimit = recCurveStack.length - 1;
                 for (int i = recLimit; i >= 0; i--) {
-                    Arrays.fill(recCurveStack[i], 0.0f);
+                    Arrays.fill(recCurveStack[i], 0.0d);
                 }
                 Arrays.fill(sidesRight, false);
-                Arrays.fill(curLeafCtrlPolyLengths, 0.0f);
-                Arrays.fill(nextRoots, 0.0f);
-                Arrays.fill(flatLeafCoefCache, 0.0f);
-                flatLeafCoefCache[2] = -1.0f;
+                Arrays.fill(curLeafCtrlPolyLengths, 0.0d);
+                Arrays.fill(nextRoots, 0.0d);
+                Arrays.fill(flatLeafCoefCache, 0.0d);
+                flatLeafCoefCache[2] = -1.0d;
             }
         }
 
-        void initializeIterationOnCurve(final float[] pts, final int type) {
+        void initializeIterationOnCurve(final double[] pts, final int type) {
             // optimize arraycopy (8 values faster than 6 = type):
             System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
             this.curveType = type;
             this.recLevel = 0;
-            this.lastT = 0.0f;
-            this.lenAtLastT = 0.0f;
-            this.nextT = 0.0f;
-            this.lenAtNextT = 0.0f;
+            this.lastT = 0.0d;
+            this.lenAtLastT = 0.0d;
+            this.nextT = 0.0d;
+            this.lenAtNextT = 0.0d;
             // initializes nextT and lenAtNextT properly
             goLeft();
 
-            this.lenAtLastSplit = 0.0f;
+            this.lenAtLastSplit = 0.0d;
             if (recLevel > 0) {
                 this.sidesRight[0] = false;
                 this.done = false;
@@ -714,16 +713,16 @@ void initializeIterationOnCurve(final float[] pts, final int type) {
                 this.sidesRight[0] = true;
                 this.done = true;
             }
-            this.lastSegLen = 0.0f;
+            this.lastSegLen = 0.0d;
         }
 
         // 0 == false, 1 == true, -1 == invalid cached value.
         private int cachedHaveLowAcceleration = -1;
 
-        private boolean haveLowAcceleration(final float err) {
+        private boolean haveLowAcceleration(final double err) {
             if (cachedHaveLowAcceleration == -1) {
-                final float len1 = curLeafCtrlPolyLengths[0];
-                final float len2 = curLeafCtrlPolyLengths[1];
+                final double len1 = curLeafCtrlPolyLengths[0];
+                final double len2 = curLeafCtrlPolyLengths[1];
                 // the test below is equivalent to !within(len1/len2, 1, err).
                 // It is using a multiplication instead of a division, so it
                 // should be a bit faster.
@@ -732,11 +731,11 @@ private boolean haveLowAcceleration(final float err) {
                     return false;
                 }
                 if (curveType == 8) {
-                    final float len3 = curLeafCtrlPolyLengths[2];
+                    final double len3 = curLeafCtrlPolyLengths[2];
                     // if len1 is close to 2 and 2 is close to 3, that probably
                     // means 1 is close to 3 so the second part of this test might
                     // not be needed, but it doesn't hurt to include it.
-                    final float errLen3 = err * len3;
+                    final double errLen3 = err * len3;
                     if (!(Helpers.within(len2, len3, errLen3) &&
                           Helpers.within(len1, len3, errLen3))) {
                         cachedHaveLowAcceleration = 0;
@@ -752,73 +751,73 @@ private boolean haveLowAcceleration(final float err) {
 
         // we want to avoid allocations/gc so we keep this array so we
         // can put roots in it,
-        private final float[] nextRoots = new float[4];
+        private final double[] nextRoots = new double[4];
 
         // caches the coefficients of the current leaf in its flattened
         // form (see inside next() for what that means). The cache is
         // invalid when it's third element is negative, since in any
         // valid flattened curve, this would be >= 0.
-        private final float[] flatLeafCoefCache = new float[]{0.0f, 0.0f, -1.0f, 0.0f};
+        private final double[] flatLeafCoefCache = new double[]{0.0d, 0.0d, -1.0d, 0.0d};
 
         // returns the t value where the remaining curve should be split in
         // order for the left subdivided curve to have length len. If len
         // is >= than the length of the uniterated curve, it returns 1.
-        float next(final float len) {
-            final float targetLength = lenAtLastSplit + len;
+        double next(final double len) {
+            final double targetLength = lenAtLastSplit + len;
             while (lenAtNextT < targetLength) {
                 if (done) {
                     lastSegLen = lenAtNextT - lenAtLastSplit;
-                    return 1.0f;
+                    return 1.0d;
                 }
                 goToNextLeaf();
             }
             lenAtLastSplit = targetLength;
-            final float leaflen = lenAtNextT - lenAtLastT;
-            float t = (targetLength - lenAtLastT) / leaflen;
+            final double leaflen = lenAtNextT - lenAtLastT;
+            double t = (targetLength - lenAtLastT) / leaflen;
 
             // cubicRootsInAB is a fairly expensive call, so we just don't do it
             // if the acceleration in this section of the curve is small enough.
-            if (!haveLowAcceleration(0.05f)) {
+            if (!haveLowAcceleration(0.05d)) {
                 // We flatten the current leaf along the x axis, so that we're
                 // left with a, b, c which define a 1D Bezier curve. We then
                 // solve this to get the parameter of the original leaf that
                 // gives us the desired length.
-                final float[] _flatLeafCoefCache = flatLeafCoefCache;
+                final double[] _flatLeafCoefCache = flatLeafCoefCache;
 
-                if (_flatLeafCoefCache[2] < 0.0f) {
-                    float x =     curLeafCtrlPolyLengths[0],
-                          y = x + curLeafCtrlPolyLengths[1];
+                if (_flatLeafCoefCache[2] < 0.0d) {
+                    double x =     curLeafCtrlPolyLengths[0],
+                           y = x + curLeafCtrlPolyLengths[1];
                     if (curveType == 8) {
-                        float z = y + curLeafCtrlPolyLengths[2];
-                        _flatLeafCoefCache[0] = 3.0f * (x - y) + z;
-                        _flatLeafCoefCache[1] = 3.0f * (y - 2.0f * x);
-                        _flatLeafCoefCache[2] = 3.0f * x;
+                        double z = y + curLeafCtrlPolyLengths[2];
+                        _flatLeafCoefCache[0] = 3.0d * (x - y) + z;
+                        _flatLeafCoefCache[1] = 3.0d * (y - 2.0d * x);
+                        _flatLeafCoefCache[2] = 3.0d * x;
                         _flatLeafCoefCache[3] = -z;
                     } else if (curveType == 6) {
-                        _flatLeafCoefCache[0] = 0.0f;
-                        _flatLeafCoefCache[1] = y - 2.0f * x;
-                        _flatLeafCoefCache[2] = 2.0f * x;
+                        _flatLeafCoefCache[0] = 0.0d;
+                        _flatLeafCoefCache[1] = y - 2.0d * x;
+                        _flatLeafCoefCache[2] = 2.0d * x;
                         _flatLeafCoefCache[3] = -y;
                     }
                 }
-                float a = _flatLeafCoefCache[0];
-                float b = _flatLeafCoefCache[1];
-                float c = _flatLeafCoefCache[2];
-                float d = t * _flatLeafCoefCache[3];
+                double a = _flatLeafCoefCache[0];
+                double b = _flatLeafCoefCache[1];
+                double c = _flatLeafCoefCache[2];
+                double d = t * _flatLeafCoefCache[3];
 
                 // we use cubicRootsInAB here, because we want only roots in 0, 1,
                 // and our quadratic root finder doesn't filter, so it's just a
                 // matter of convenience.
-                final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f);
-                if (n == 1 && !Float.isNaN(nextRoots[0])) {
+                final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d);
+                if (n == 1 && !Double.isNaN(nextRoots[0])) {
                     t = nextRoots[0];
                 }
             }
             // t is relative to the current leaf, so we must make it a valid parameter
             // of the original curve.
             t = t * (nextT - lastT) + lastT;
-            if (t >= 1.0f) {
-                t = 1.0f;
+            if (t >= 1.0d) {
+                t = 1.0d;
                 done = true;
             }
             // even if done = true, if we're here, that means targetLength
@@ -830,7 +829,7 @@ float next(final float len) {
             return t;
         }
 
-        float totalLength() {
+        double totalLength() {
             while (!done) {
                 goToNextLeaf();
             }
@@ -840,7 +839,7 @@ float totalLength() {
             return lenAtNextT;
         }
 
-        float lastSegLen() {
+        double lastSegLen() {
             return lastSegLen;
         }
 
@@ -872,19 +871,19 @@ private void goToNextLeaf() {
 
         // go to the leftmost node from the current node. Return its length.
         private void goLeft() {
-            final float len = onLeaf();
-            if (len >= 0.0f) {
+            final double len = onLeaf();
+            if (len >= 0.0d) {
                 lastT = nextT;
                 lenAtLastT = lenAtNextT;
                 nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC;
                 lenAtNextT += len;
                 // invalidate caches
-                flatLeafCoefCache[2] = -1.0f;
+                flatLeafCoefCache[2] = -1.0d;
                 cachedHaveLowAcceleration = -1;
             } else {
                 Helpers.subdivide(recCurveStack[recLevel],
-                                  recCurveStack[recLevel + 1],
-                                  recCurveStack[recLevel], curveType);
+                                   recCurveStack[recLevel + 1],
+                                   recCurveStack[recLevel], curveType);
 
                 sidesRight[recLevel] = false;
                 recLevel++;
@@ -894,34 +893,34 @@ private void goLeft() {
 
         // this is a bit of a hack. It returns -1 if we're not on a leaf, and
         // the length of the leaf if we are on a leaf.
-        private float onLeaf() {
-            final float[] curve = recCurveStack[recLevel];
+        private double onLeaf() {
+            final double[] curve = recCurveStack[recLevel];
             final int _curveType = curveType;
-            float polyLen = 0.0f;
+            double polyLen = 0.0d;
 
-            float x0 = curve[0], y0 = curve[1];
+            double x0 = curve[0], y0 = curve[1];
             for (int i = 2; i < _curveType; i += 2) {
-                final float x1 = curve[i], y1 = curve[i + 1];
-                final float len = Helpers.linelen(x0, y0, x1, y1);
+                final double x1 = curve[i], y1 = curve[i + 1];
+                final double len = Helpers.linelen(x0, y0, x1, y1);
                 polyLen += len;
                 curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
                 x0 = x1;
                 y0 = y1;
             }
 
-            final float lineLen = Helpers.linelen(curve[0], curve[1], x0, y0);
+            final double lineLen = Helpers.linelen(curve[0], curve[1], x0, y0);
 
             if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) {
-                return (polyLen + lineLen) / 2.0f;
+                return (polyLen + lineLen) / 2.0d;
             }
-            return -1.0f;
+            return -1.0d;
         }
     }
 
     @Override
-    public void curveTo(final float x1, final float y1,
-                        final float x2, final float y2,
-                        final float x3, final float y3)
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double x3, final double y3)
     {
         final int outcode0 = this.cOutCode;
 
@@ -969,18 +968,18 @@ public void curveTo(final float x1, final float y1,
         _curveTo(x1, y1, x2, y2, x3, y3);
     }
 
-    private void _curveTo(final float x1, final float y1,
-                          final float x2, final float y2,
-                          final float x3, final float y3)
+    private void _curveTo(final double x1, final double y1,
+                          final double x2, final double y2,
+                          final double x3, final double y3)
     {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
 
         // monotonize curve:
         final CurveBasicMonotonizer monotonizer
             = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
 
         final int nSplits = monotonizer.nbSplits;
-        final float[] mid = monotonizer.middle;
+        final double[] mid = monotonizer.middle;
 
         // Implicitely rdrCtx.isFirstSegment = true
 
@@ -997,11 +996,11 @@ private void _curveTo(final float x1, final float y1,
         rdrCtx.isFirstSegment = true;
     }
 
-    private void skipCurveTo(final float x1, final float y1,
-                             final float x2, final float y2,
-                             final float x3, final float y3)
+    private void skipCurveTo(final double x1, final double y1,
+                             final double x2, final double y2,
+                             final double x3, final double y3)
     {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
         _curCurvepts[2] = x1;  _curCurvepts[3] = y1;
         _curCurvepts[4] = x2;  _curCurvepts[5] = y2;
@@ -1014,8 +1013,8 @@ private void skipCurveTo(final float x1, final float y1,
     }
 
     @Override
-    public void quadTo(final float x1, final float y1,
-                       final float x2, final float y2)
+    public void quadTo(final double x1, final double y1,
+                       final double x2, final double y2)
     {
         final int outcode0 = this.cOutCode;
 
@@ -1062,17 +1061,17 @@ public void quadTo(final float x1, final float y1,
         _quadTo(x1, y1, x2, y2);
     }
 
-    private void _quadTo(final float x1, final float y1,
-                         final float x2, final float y2)
+    private void _quadTo(final double x1, final double y1,
+                         final double x2, final double y2)
     {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
 
         // monotonize quad:
         final CurveBasicMonotonizer monotonizer
             = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
 
         final int nSplits = monotonizer.nbSplits;
-        final float[] mid = monotonizer.middle;
+        final double[] mid = monotonizer.middle;
 
         // Implicitely rdrCtx.isFirstSegment = true
 
@@ -1089,10 +1088,10 @@ private void _quadTo(final float x1, final float y1,
         rdrCtx.isFirstSegment = true;
     }
 
-    private void skipQuadTo(final float x1, final float y1,
-                            final float x2, final float y2)
+    private void skipQuadTo(final double x1, final double y1,
+                            final double x2, final double y2)
     {
-        final float[] _curCurvepts = curCurvepts;
+        final double[] _curCurvepts = curCurvepts;
         _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
         _curCurvepts[2] = x1;  _curCurvepts[3] = y1;
         _curCurvepts[4] = x2;  _curCurvepts[5] = y2;
diff --git a/src/main/java/sun/java2d/marlin/Helpers.java b/src/main/java/sun/java2d/marlin/Helpers.java
index 63788ee..48b5ab0 100644
--- a/src/main/java/sun/java2d/marlin/Helpers.java
+++ b/src/main/java/sun/java2d/marlin/Helpers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -26,32 +26,30 @@
 package sun.java2d.marlin;
 
 import java.util.Arrays;
-import net.jafama.FastMath;
 import sun.java2d.marlin.stats.Histogram;
 import sun.java2d.marlin.stats.StatLong;
-import sun.awt.geom.PathConsumer2D;
 
 final class Helpers implements MarlinConst {
 
-    private static final float EPS = 1e-9f;
+    private static final double EPS = 1e-9d;
 
     private Helpers() {
         throw new Error("This is a non instantiable class");
     }
 
-    static boolean within(final float x, final float y) {
+    static boolean within(final double x, final double y) {
         return within(x, y, EPS);
     }
 
-    static boolean within(final float x, final float y, final float err) {
+    static boolean within(final double x, final double y, final double err) {
         return withinD(y - x, err);
     }
 
-    static boolean withinD(final float d, final float err) {
+    static boolean withinD(final double d, final double err) {
         return (d <= err && d >= -err);
     }
 
-    static boolean withinD(final float dx, final float dy, final float err)
+    static boolean withinD(final double dx, final double dy, final double err)
     {
         assert err > 0 : "";
         // compare taxicab distance. ERR will always be small, so using
@@ -60,11 +58,11 @@ static boolean withinD(final float dx, final float dy, final float err)
                 withinD(dy, err));  // this is just as good.
     }
 
-    static boolean isPointCurve(final float[] curve, final int type) {
+    static boolean isPointCurve(final double[] curve, final int type) {
         return isPointCurve(curve, type, EPS);
     }
 
-    static boolean isPointCurve(final float[] curve, final int type, final float err) {
+    static boolean isPointCurve(final double[] curve, final int type, final double err) {
         for (int i = 2; i < type; i++) {
             if (!within(curve[i], curve[i - 2], err)) {
                 return false;
@@ -73,57 +71,57 @@ static boolean isPointCurve(final float[] curve, final int type, final float err
         return true;
     }
 
-    static float evalCubic(final float a, final float b,
-                           final float c, final float d,
-                           final float t)
+    static double evalCubic(final double a, final double b,
+                            final double c, final double d,
+                            final double t)
     {
         return t * (t * (t * a + b) + c) + d;
     }
 
-    static float evalQuad(final float a, final float b,
-                          final float c, final float t)
+    static double evalQuad(final double a, final double b,
+                           final double c, final double t)
     {
         return t * (t * a + b) + c;
     }
 
-    static int quadraticRoots(final float a, final float b, final float c,
-                              final float[] zeroes, final int off)
+    static int quadraticRoots(final double a, final double b, final double c,
+                              final double[] zeroes, final int off)
     {
         int ret = off;
-        if (a != 0.0f) {
-            float d = b * b - 4.0f * a * c;
-            if (d > 0.0f) {
-                d = (float)Math.sqrt(d);
+        if (a != 0.0d) {
+            double d = b * b - 4.0d * a * c;
+            if (d > 0.0d) {
+                d = Math.sqrt(d);
                 // For accuracy, calculate one root using:
                 //     (-b +/- d) / 2a
                 // and the other using:
                 //     2c / (-b +/- d)
                 // Choose the sign of the +/- so that b+d gets larger in magnitude
-                if (b < 0.0f) {
+                if (b < 0.0d) {
                     d = -d;
                 }
-                final float q = (b + d) / -2.0f;
+                final double q = (b + d) / -2.0d;
                 // We already tested a for being 0 above
                 zeroes[ret++] = q / a;
-                if (q != 0.0f) {
+                if (q != 0.0d) {
                     zeroes[ret++] = c / q;
                 }
-            } else if (d == 0.0f) {
-                zeroes[ret++] = -b / (2.0f * a);
+            } else if (d == 0.0d) {
+                zeroes[ret++] = -b / (2.0d * a);
             }
-        } else if (b != 0.0f) {
+        } else if (b != 0.0d) {
             zeroes[ret++] = -c / b;
         }
         return ret - off;
     }
 
     // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
-    static int cubicRootsInAB(final float d0, float a0, float b0, float c0,
-                              final float[] pts, final int off,
-                              final float A, final float B)
+    static int cubicRootsInAB(final double d, double a, double b, double c,
+                              final double[] pts, final int off,
+                              final double A, final double B)
     {
-        if (d0 == 0.0f) {
-            final int num = quadraticRoots(a0, b0, c0, pts, off);
+        if (d == 0.0d) {
+            final int num = quadraticRoots(a, b, c, pts, off);
             return filterOutNotInAB(pts, off, num, A, B) - off;
         }
         // From Graphics Gems:
@@ -133,11 +131,9 @@ static int cubicRootsInAB(final float d0, float a0, float b0, float c0,
         // our own customized version).
 
         // normal form: x^3 + ax^2 + bx + c = 0
-
-        // 2018.1: Need double precision if d is very small (flat curve) !
-        final double a = ((double)a0) / d0;
-        final double b = ((double)b0) / d0;
-        final double c = ((double)c0) / d0;
+        a /= d;
+        b /= d;
+        c /= d;
 
         //  substitute x = y - A/3 to eliminate quadratic term:
         //     x^3 +Px + Q = 0
@@ -158,41 +154,41 @@ static int cubicRootsInAB(final float d0, float a0, float b0, float c0,
 
         int num;
 
-        if (DHelpers.within(D, 0.0d)) {
-            if (DHelpers.within(q, 0.0d)) {
+        if (within(D, 0.0d)) {
+            if (within(q, 0.0d)) {
                 /* one triple solution */
-                pts[off    ] = (float) (- sub);
+                pts[off    ] = (- sub);
                 num = 1;
             } else {
                 /* one single and one double solution */
-                final double u = FastMath.cbrt(-q);
-                pts[off    ] = (float) (2.0d * u - sub);
-                pts[off + 1] = (float) (- u - sub);
+                final double u = Math.cbrt(-q);
+                pts[off    ] = (2.0d * u - sub);
+                pts[off + 1] = (- u - sub);
                 num = 2;
             }
         } else if (D < 0.0d) {
             // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
-            final double phi = (1.0d / 3.0d) * FastMath.acos(-q / Math.sqrt(-cb_p));
+            final double phi = (1.0d / 3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
             final double t = 2.0d * Math.sqrt(-p);
 
-            pts[off    ] = (float) ( t * FastMath.cos(phi) - sub);
-            pts[off + 1] = (float) (-t * FastMath.cos(phi + (Math.PI / 3.0d)) - sub);
-            pts[off + 2] = (float) (-t * FastMath.cos(phi - (Math.PI / 3.0d)) - sub);
+            pts[off    ] = ( t * Math.cos(phi) - sub);
+            pts[off + 1] = (-t * Math.cos(phi + (Math.PI / 3.0d)) - sub);
+            pts[off + 2] = (-t * Math.cos(phi - (Math.PI / 3.0d)) - sub);
             num = 3;
         } else {
             final double sqrt_D = Math.sqrt(D);
-            final double u =   FastMath.cbrt(sqrt_D - q);
-            final double v = - FastMath.cbrt(sqrt_D + q);
+            final double u =   Math.cbrt(sqrt_D - q);
+            final double v = - Math.cbrt(sqrt_D + q);
 
-            pts[off    ] = (float) (u + v - sub);
+            pts[off    ] = (u + v - sub);
             num = 1;
         }
         return filterOutNotInAB(pts, off, num, A, B) - off;
     }
 
     // returns the index 1 past the last valid element remaining after filtering
-    static int filterOutNotInAB(final float[] nums, final int off, final int len,
-                                final float a, final float b)
+    static int filterOutNotInAB(final double[] nums, final int off, final int len,
+                                final double a, final double b)
     {
         int ret = off;
         for (int i = off, end = off + len; i < end; i++) {
@@ -203,105 +199,104 @@ static int filterOutNotInAB(final float[] nums, final int off, final int len,
         return ret;
     }
 
-    static float fastLineLen(final float x0, final float y0,
-                             final float x1, final float y1)
+    static double fastLineLen(final double x0, final double y0,
+                              final double x1, final double y1)
     {
-        final float dx = x1 - x0;
-        final float dy = y1 - y0;
+        final double dx = x1 - x0;
+        final double dy = y1 - y0;
 
         // use manhattan norm:
         return Math.abs(dx) + Math.abs(dy);
     }
 
-    static float linelen(final float x0, final float y0,
-                         final float x1, final float y1)
+    static double linelen(final double x0, final double y0,
+                          final double x1, final double y1)
     {
-        final float dx = x1 - x0;
-        final float dy = y1 - y0;
-        return (float) Math.sqrt(dx * dx + dy * dy);
+        final double dx = x1 - x0;
+        final double dy = y1 - y0;
+        return Math.sqrt(dx * dx + dy * dy);
     }
 
-    static float fastQuadLen(final float x0, final float y0,
-                             final float x1, final float y1,
-                             final float x2, final float y2)
+    static double fastQuadLen(final double x0, final double y0,
+                              final double x1, final double y1,
+                              final double x2, final double y2)
     {
-        final float dx1 = x1 - x0;
-        final float dx2 = x2 - x1;
-        final float dy1 = y1 - y0;
-        final float dy2 = y2 - y1;
+        final double dx1 = x1 - x0;
+        final double dx2 = x2 - x1;
+        final double dy1 = y1 - y0;
+        final double dy2 = y2 - y1;
 
         // use manhattan norm:
         return Math.abs(dx1) + Math.abs(dx2)
              + Math.abs(dy1) + Math.abs(dy2);
     }
 
-    static float quadlen(final float x0, final float y0,
-                         final float x1, final float y1,
-                         final float x2, final float y2)
+    static double quadlen(final double x0, final double y0,
+                          final double x1, final double y1,
+                          final double x2, final double y2)
     {
         return (linelen(x0, y0, x1, y1)
                 + linelen(x1, y1, x2, y2)
-                + linelen(x0, y0, x2, y2)) / 2.0f;
+                + linelen(x0, y0, x2, y2)) / 2.0d;
     }
 
-
-    static float fastCurvelen(final float x0, final float y0,
-                              final float x1, final float y1,
-                              final float x2, final float y2,
-                              final float x3, final float y3)
+    static double fastCurvelen(final double x0, final double y0,
+                               final double x1, final double y1,
+                               final double x2, final double y2,
+                               final double x3, final double y3)
     {
-        final float dx1 = x1 - x0;
-        final float dx2 = x2 - x1;
-        final float dx3 = x3 - x2;
-        final float dy1 = y1 - y0;
-        final float dy2 = y2 - y1;
-        final float dy3 = y3 - y2;
+        final double dx1 = x1 - x0;
+        final double dx2 = x2 - x1;
+        final double dx3 = x3 - x2;
+        final double dy1 = y1 - y0;
+        final double dy2 = y2 - y1;
+        final double dy3 = y3 - y2;
 
         // use manhattan norm:
         return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3)
              + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3);
     }
 
-    static float curvelen(final float x0, final float y0,
-                          final float x1, final float y1,
-                          final float x2, final float y2,
-                          final float x3, final float y3)
+    static double curvelen(final double x0, final double y0,
+                           final double x1, final double y1,
+                           final double x2, final double y2,
+                           final double x3, final double y3)
     {
         return (linelen(x0, y0, x1, y1)
               + linelen(x1, y1, x2, y2)
               + linelen(x2, y2, x3, y3)
-              + linelen(x0, y0, x3, y3)) / 2.0f;
+              + linelen(x0, y0, x3, y3)) / 2.0d;
     }
 
     // finds values of t where the curve in pts should be subdivided in order
     // to get good offset curves a distance of w away from the middle curve.
     // Stores the points in ts, and returns how many of them there were.
-    static int findSubdivPoints(final Curve c, final float[] pts,
-                                final float[] ts, final int type,
-                                final float w2)
+    static int findSubdivPoints(final Curve c, final double[] pts,
+                                final double[] ts, final int type,
+                                final double w2)
     {
-        final float x12 = pts[2] - pts[0];
-        final float y12 = pts[3] - pts[1];
+        final double x12 = pts[2] - pts[0];
+        final double y12 = pts[3] - pts[1];
         // if the curve is already parallel to either axis we gain nothing
         // from rotating it.
-        if ((y12 != 0.0f) && (x12 != 0.0f)) {
+        if ((y12 != 0.0d) && (x12 != 0.0d)) {
             // we rotate it so that the first vector in the control polygon is
             // parallel to the x-axis. This will ensure that rotated quarter
             // circles won't be subdivided.
-            final float hypot = (float)Math.sqrt(x12 * x12 + y12 * y12);
-            final float cos = x12 / hypot;
-            final float sin = y12 / hypot;
-            final float x1 = cos * pts[0] + sin * pts[1];
-            final float y1 = cos * pts[1] - sin * pts[0];
-            final float x2 = cos * pts[2] + sin * pts[3];
-            final float y2 = cos * pts[3] - sin * pts[2];
-            final float x3 = cos * pts[4] + sin * pts[5];
-            final float y3 = cos * pts[5] - sin * pts[4];
+            final double hypot = Math.sqrt(x12 * x12 + y12 * y12);
+            final double cos = x12 / hypot;
+            final double sin = y12 / hypot;
+            final double x1 = cos * pts[0] + sin * pts[1];
+            final double y1 = cos * pts[1] - sin * pts[0];
+            final double x2 = cos * pts[2] + sin * pts[3];
+            final double y2 = cos * pts[3] - sin * pts[2];
+            final double x3 = cos * pts[4] + sin * pts[5];
+            final double y3 = cos * pts[5] - sin * pts[4];
 
             switch(type) {
             case 8:
-                final float x4 = cos * pts[6] + sin * pts[7];
-                final float y4 = cos * pts[7] - sin * pts[6];
+                final double x4 = cos * pts[6] + sin * pts[7];
+                final double y4 = cos * pts[7] - sin * pts[6];
                 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
                 break;
             case 6:
@@ -327,9 +322,9 @@ static int findSubdivPoints(final Curve c, final float[] pts,
 
         // now we must subdivide at points where one of the offset curves will have
         // a cusp. This happens at ts where the radius of curvature is equal to w.
-        ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001f);
+        ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
 
-        ret = filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
+        ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
         isort(ts, ret);
         return ret;
     }
@@ -337,10 +332,10 @@ static int findSubdivPoints(final Curve c, final float[] pts,
     // finds values of t where the curve in pts should be subdivided in order
     // to get intersections with the given clip rectangle.
     // Stores the points in ts, and returns how many of them there were.
-    static int findClipPoints(final Curve curve, final float[] pts,
-                              final float[] ts, final int type,
+    static int findClipPoints(final Curve curve, final double[] pts,
+                              final double[] ts, final int type,
                               final int outCodeOR,
-                              final float[] clipRect)
+                              final double[] clipRect)
     {
         curve.set(pts, type);
 
@@ -363,8 +358,8 @@ static int findClipPoints(final Curve curve, final float[] pts,
         return ret;
     }
 
-    static void subdivide(final float[] src,
-                          final float[] left, final float[] right,
+    static void subdivide(final double[] src,
+                          final double[] left, final double[] right,
                           final int type)
     {
         switch(type) {
@@ -379,9 +374,9 @@ static void subdivide(final float[] src,
         }
     }
 
-    static void isort(final float[] a, final int len) {
+    static void isort(final double[] a, final int len) {
         for (int i = 1, j; i < len; i++) {
-            final float ai = a[i];
+            final double ai = a[i];
             j = i - 1;
             for (; j >= 0 && a[j] > ai; j--) {
                 a[j + 1] = a[j];
@@ -415,18 +410,18 @@ static void isort(final float[] a, final int len) {
      * half of the subdivided curve
      * @since 1.7
      */
-    static void subdivideCubic(final float[] src,
-                               final float[] left,
-                               final float[] right)
+    static void subdivideCubic(final double[] src,
+                               final double[] left,
+                               final double[] right)
     {
-        float  x1 = src[0];
-        float  y1 = src[1];
-        float cx1 = src[2];
-        float cy1 = src[3];
-        float cx2 = src[4];
-        float cy2 = src[5];
-        float  x2 = src[6];
-        float  y2 = src[7];
+        double  x1 = src[0];
+        double  y1 = src[1];
+        double cx1 = src[2];
+        double cy1 = src[3];
+        double cx2 = src[4];
+        double cy2 = src[5];
+        double  x2 = src[6];
+        double  y2 = src[7];
 
         left[0]  = x1;
         left[1]  = y1;
@@ -434,20 +429,20 @@ static void subdivideCubic(final float[] src,
         right[6] = x2;
         right[7] = y2;
 
-        x1 = (x1 + cx1) / 2.0f;
-        y1 = (y1 + cy1) / 2.0f;
-        x2 = (x2 + cx2) / 2.0f;
-        y2 = (y2 + cy2) / 2.0f;
+        x1 = (x1 + cx1) / 2.0d;
+        y1 = (y1 + cy1) / 2.0d;
+        x2 = (x2 + cx2) / 2.0d;
+        y2 = (y2 + cy2) / 2.0d;
 
-        float cx = (cx1 + cx2) / 2.0f;
-        float cy = (cy1 + cy2) / 2.0f;
+        double cx = (cx1 + cx2) / 2.0d;
+        double cy = (cy1 + cy2) / 2.0d;
 
-        cx1 = (x1 + cx) / 2.0f;
-        cy1 = (y1 + cy) / 2.0f;
-        cx2 = (x2 + cx) / 2.0f;
-        cy2 = (y2 + cy) / 2.0f;
-        cx  = (cx1 + cx2) / 2.0f;
-        cy  = (cy1 + cy2) / 2.0f;
+        cx1 = (x1 + cx) / 2.0d;
+        cy1 = (y1 + cy) / 2.0d;
+        cx2 = (x2 + cx) / 2.0d;
+        cy2 = (y2 + cy) / 2.0d;
+        cx  = (cx1 + cx2) / 2.0d;
+        cy  = (cy1 + cy2) / 2.0d;
 
         left[2] = x1;
         left[3] = y1;
@@ -464,18 +459,18 @@ static void subdivideCubic(final float[] src,
         right[5] = y2;
     }
 
-    static void subdivideCubicAt(final float t,
-                                 final float[] src, final int offS,
-                                 final float[] pts, final int offL, final int offR)
+    static void subdivideCubicAt(final double t,
+                                 final double[] src, final int offS,
+                                 final double[] pts, final int offL, final int offR)
     {
-        float  x1 = src[offS    ];
-        float  y1 = src[offS + 1];
-        float cx1 = src[offS + 2];
-        float cy1 = src[offS + 3];
-        float cx2 = src[offS + 4];
-        float cy2 = src[offS + 5];
-        float  x2 = src[offS + 6];
-        float  y2 = src[offS + 7];
+        double  x1 = src[offS    ];
+        double  y1 = src[offS + 1];
+        double cx1 = src[offS + 2];
+        double cy1 = src[offS + 3];
+        double cx2 = src[offS + 4];
+        double cy2 = src[offS + 5];
+        double  x2 = src[offS + 6];
+        double  y2 = src[offS + 7];
 
         pts[offL    ] = x1;
         pts[offL + 1] = y1;
@@ -488,8 +483,8 @@ static void subdivideCubicAt(final float t,
         x2 = cx2 + t * (x2 - cx2);
         y2 = cy2 + t * (y2 - cy2);
 
-        float cx = cx1 + t * (cx2 - cx1);
-        float cy = cy1 + t * (cy2 - cy1);
+        double cx = cx1 + t * (cx2 - cx1);
+        double cy = cy1 + t * (cy2 - cy1);
 
         cx1 =  x1 + t * (cx - x1);
         cy1 =  y1 + t * (cy - y1);
@@ -513,16 +508,16 @@ static void subdivideCubicAt(final float t,
         pts[offR + 5] = y2;
     }
 
-    static void subdivideQuad(final float[] src,
-                              final float[] left,
-                              final float[] right)
+    static void subdivideQuad(final double[] src,
+                              final double[] left,
+                              final double[] right)
     {
-        float x1 = src[0];
-        float y1 = src[1];
-        float cx = src[2];
-        float cy = src[3];
-        float x2 = src[4];
-        float y2 = src[5];
+        double x1 = src[0];
+        double y1 = src[1];
+        double cx = src[2];
+        double cy = src[3];
+        double x2 = src[4];
+        double y2 = src[5];
 
         left[0]  = x1;
         left[1]  = y1;
@@ -530,12 +525,12 @@ static void subdivideQuad(final float[] src,
         right[4] = x2;
         right[5] = y2;
 
-        x1 = (x1 + cx) / 2.0f;
-        y1 = (y1 + cy) / 2.0f;
-        x2 = (x2 + cx) / 2.0f;
-        y2 = (y2 + cy) / 2.0f;
-        cx = (x1 + x2) / 2.0f;
-        cy = (y1 + y2) / 2.0f;
+        x1 = (x1 + cx) / 2.0d;
+        y1 = (y1 + cy) / 2.0d;
+        x2 = (x2 + cx) / 2.0d;
+        y2 = (y2 + cy) / 2.0d;
+        cx = (x1 + x2) / 2.0d;
+        cy = (y1 + y2) / 2.0d;
 
         left[2] = x1;
         left[3] = y1;
@@ -548,16 +543,16 @@ static void subdivideQuad(final float[] src,
         right[3] = y2;
     }
 
-    static void subdivideQuadAt(final float t,
-                                final float[] src, final int offS,
-                                final float[] pts, final int offL, final int offR)
+    static void subdivideQuadAt(final double t,
+                                final double[] src, final int offS,
+                                final double[] pts, final int offL, final int offR)
     {
-        float x1 = src[offS    ];
-        float y1 = src[offS + 1];
-        float cx = src[offS + 2];
-        float cy = src[offS + 3];
-        float x2 = src[offS + 4];
-        float y2 = src[offS + 5];
+        double x1 = src[offS    ];
+        double y1 = src[offS + 1];
+        double cx = src[offS + 2];
+        double cy = src[offS + 3];
+        double x2 = src[offS + 4];
+        double y2 = src[offS + 5];
 
         pts[offL    ] = x1;
         pts[offL + 1] = y1;
@@ -583,14 +578,14 @@ static void subdivideQuadAt(final float t,
         pts[offR + 3] = y2;
     }
 
-    static void subdivideLineAt(final float t,
-                                final float[] src, final int offS,
-                                final float[] pts, final int offL, final int offR)
+    static void subdivideLineAt(final double t,
+                                final double[] src, final int offS,
+                                final double[] pts, final int offL, final int offR)
     {
-        float x1 = src[offS    ];
-        float y1 = src[offS + 1];
-        float x2 = src[offS + 2];
-        float y2 = src[offS + 3];
+        double x1 = src[offS    ];
+        double y1 = src[offS + 1];
+        double x2 = src[offS + 2];
+        double y2 = src[offS + 3];
 
         pts[offL    ] = x1;
         pts[offL + 1] = y1;
@@ -608,9 +603,9 @@ static void subdivideLineAt(final float t,
         pts[offR + 1] = y1;
     }
 
-    static void subdivideAt(final float t,
-                            final float[] src, final int offS,
-                            final float[] pts, final int offL, final int type)
+    static void subdivideAt(final double t,
+                            final double[] src, final int offS,
+                            final double[] pts, final int offL, final int type)
     {
         // if instead of switch (perf + most probable cases first)
         if (type == 8) {
@@ -624,8 +619,8 @@ static void subdivideAt(final float t,
 
     // From sun.java2d.loops.GeneralRenderer:
 
-    static int outcode(final float x, final float y,
-                       final float[] clipRect)
+    static int outcode(final double x, final double y,
+                       final double[] clipRect)
     {
         int code;
         if (y < clipRect[0]) {
@@ -656,13 +651,13 @@ static final class PolyStack {
         // types capacity = edges count (4096)
         private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
 
-        float[] curves;
+        double[] curves;
         int end;
         byte[] curveTypes;
         int numCurves;
 
         // curves ref (dirty)
-        final ArrayCacheFloat.Reference curves_ref;
+        final ArrayCacheDouble.Reference curves_ref;
         // curveTypes ref (dirty)
         final ArrayCacheByte.Reference curveTypes_ref;
 
@@ -687,7 +682,7 @@ static final class PolyStack {
                   final StatLong stat_array_polystack_curves,
                   final StatLong stat_array_polystack_curveTypes)
         {
-            curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
+            curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
             curves     = curves_ref.initial;
 
             curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
@@ -752,14 +747,14 @@ private void ensureSpace(final int n) {
             }
         }
 
-        void pushCubic(float x0, float y0,
-                       float x1, float y1,
-                       float x2, float y2)
+        void pushCubic(double x0, double y0,
+                       double x1, double y1,
+                       double x2, double y2)
         {
             ensureSpace(6);
             curveTypes[numCurves++] = TYPE_CUBICTO;
             // we reverse the coordinate order to make popping easier
-            final float[] _curves = curves;
+            final double[] _curves = curves;
             int e = end;
             _curves[e++] = x2;    _curves[e++] = y2;
             _curves[e++] = x1;    _curves[e++] = y1;
@@ -767,25 +762,25 @@ void pushCubic(float x0, float y0,
             end = e;
         }
 
-        void pushQuad(float x0, float y0,
-                      float x1, float y1)
+        void pushQuad(double x0, double y0,
+                      double x1, double y1)
         {
             ensureSpace(4);
             curveTypes[numCurves++] = TYPE_QUADTO;
-            final float[] _curves = curves;
+            final double[] _curves = curves;
             int e = end;
             _curves[e++] = x1;    _curves[e++] = y1;
             _curves[e++] = x0;    _curves[e++] = y0;
             end = e;
         }
 
-        void pushLine(float x, float y) {
+        void pushLine(double x, double y) {
             ensureSpace(2);
             curveTypes[numCurves++] = TYPE_LINETO;
             curves[end++] = x;    curves[end++] = y;
         }
 
-        void pullAll(final PathConsumer2D io) {
+        void pullAll(final DPathConsumer2D io) {
             final int nc = numCurves;
             if (nc == 0) {
                 return;
@@ -800,7 +795,7 @@ void pullAll(final PathConsumer2D io) {
                 }
             }
             final byte[]  _curveTypes = curveTypes;
-            final float[] _curves = curves;
+            final double[] _curves = curves;
             int e = 0;
 
             for (int i = 0; i < nc; i++) {
@@ -827,7 +822,7 @@ void pullAll(final PathConsumer2D io) {
             end = 0;
         }
 
-        void popAll(final PathConsumer2D io) {
+        void popAll(final DPathConsumer2D io) {
             int nc = numCurves;
             if (nc == 0) {
                 return;
@@ -842,7 +837,7 @@ void popAll(final PathConsumer2D io) {
                 }
             }
             final byte[]  _curveTypes = curveTypes;
-            final float[] _curves = curves;
+            final double[] _curves = curves;
             int e  = end;
 
             while (nc != 0) {
@@ -997,7 +992,7 @@ void push(final int v) {
             }
         }
 
-        void pullAll(final float[] points, final PathConsumer2D io, 
+        void pullAll(final double[] points, final DPathConsumer2D io, 
                      final boolean moveFirst)
         {
             final int nc = end;
diff --git a/src/main/java/sun/java2d/marlin/IRendererContext.java b/src/main/java/sun/java2d/marlin/IRendererContext.java
deleted file mode 100644
index 7dd58dd..0000000
--- a/src/main/java/sun/java2d/marlin/IRendererContext.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-interface IRendererContext extends MarlinConst {
-
-    public RendererStats stats();
-
-    public OffHeapArray newOffHeapArray(final long initialSize);
-
-    public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize);
-
-}
diff --git a/src/main/java/sun/java2d/marlin/MarlinCache.java b/src/main/java/sun/java2d/marlin/MarlinCache.java
index 5b9ae76..e9ffbd0 100644
--- a/src/main/java/sun/java2d/marlin/MarlinCache.java
+++ b/src/main/java/sun/java2d/marlin/MarlinCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -107,7 +107,7 @@ public final class MarlinCache implements MarlinConst {
 
     boolean useRLE = false;
 
-    MarlinCache(final IRendererContext rdrCtx) {
+    MarlinCache(final RendererContext rdrCtx) {
         this.rdrStats = rdrCtx.stats();
 
         rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K
diff --git a/src/main/java/sun/java2d/marlin/MarlinProperties.java b/src/main/java/sun/java2d/marlin/MarlinProperties.java
index 4ffee64..65b3dfd 100644
--- a/src/main/java/sun/java2d/marlin/MarlinProperties.java
+++ b/src/main/java/sun/java2d/marlin/MarlinProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -317,6 +317,7 @@ public static boolean isLogUnsafeMalloc() {
     }
 
     // quality settings
+
     public static float getCurveLengthError() {
         return getFloat("sun.java2d.renderer.curve_len_err", 0.01f, 1e-6f, 1.0f);
     }
@@ -343,7 +344,7 @@ static String getString(final String key, final String def) {
     }
 
     static boolean getBoolean(final String key, final String def) {
-        return Boolean.valueOf(AccessController.doPrivileged(
+        return Boolean.parseBoolean(AccessController.doPrivileged(
                   new GetPropertyAction(key, def)));
     }
 
diff --git a/src/main/java/sun/java2d/marlin/MarlinRenderer.java b/src/main/java/sun/java2d/marlin/MarlinRenderer.java
deleted file mode 100644
index 8b4e751..0000000
--- a/src/main/java/sun/java2d/marlin/MarlinRenderer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-public interface MarlinRenderer extends MarlinConst {
-
-}
diff --git a/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java b/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java
deleted file mode 100644
index cea94e2..0000000
--- a/src/main/java/sun/java2d/marlin/MarlinRenderingEngine.java
+++ /dev/null
@@ -1,1227 +0,0 @@
-/*
- * Copyright (c) 2007, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 sun.java2d.marlin;
-
-import java.awt.BasicStroke;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Path2D;
-import java.awt.geom.PathIterator;
-import java.security.AccessController;
-import java.util.Arrays;
-import static sun.java2d.marlin.MarlinUtils.logInfo;
-import sun.awt.geom.PathConsumer2D;
-import sun.java2d.ReentrantContextProvider;
-import sun.java2d.ReentrantContextProviderCLQ;
-import sun.java2d.ReentrantContextProviderTL;
-import sun.java2d.pipe.AATileGenerator;
-import sun.java2d.pipe.Region;
-import sun.java2d.pipe.RenderingEngine;
-import sun.security.action.GetPropertyAction;
-
-/**
- * Marlin RendererEngine implementation (derived from Pisces)
- */
-public final class MarlinRenderingEngine extends RenderingEngine
-                                         implements MarlinConst
-{
-    // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases
-    static final boolean DISABLE_2ND_STROKER_CLIPPING = true;
-
-    static final boolean DO_TRACE_PATH = false;
-
-    static final boolean DO_CLIP = MarlinProperties.isDoClip();
-    static final boolean DO_CLIP_FILL = true;
-    static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
-
-    private static final float MIN_PEN_SIZE = 1.0f / MIN_SUBPIXELS;
-
-    static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
-    static final float LOWER_BND = -UPPER_BND;
-
-    private enum NormMode {
-        ON_WITH_AA {
-            @Override
-            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
-                                                    final PathIterator src)
-            {
-                // NormalizingPathIterator NearestPixelCenter:
-                return rdrCtx.nPCPathIterator.init(src);
-            }
-        },
-        ON_NO_AA{
-            @Override
-            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
-                                                    final PathIterator src)
-            {
-                // NearestPixel NormalizingPathIterator:
-                return rdrCtx.nPQPathIterator.init(src);
-            }
-        },
-        OFF{
-            @Override
-            PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
-                                                    final PathIterator src)
-            {
-                // return original path iterator if normalization is disabled:
-                return src;
-            }
-        };
-
-        abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx,
-                                                         PathIterator src);
-    }
-
-    /**
-     * Public constructor
-     */
-    public MarlinRenderingEngine() {
-        super();
-        logSettings(MarlinRenderingEngine.class.getName());
-    }
-
-    /**
-     * Create a widened path as specified by the parameters.
-     * <p>
-     * The specified {@code src} {@link Shape} is widened according
-     * to the specified attribute parameters as per the
-     * {@link BasicStroke} specification.
-     *
-     * @param src the source path to be widened
-     * @param width the width of the widened path as per {@code BasicStroke}
-     * @param caps the end cap decorations as per {@code BasicStroke}
-     * @param join the segment join decorations as per {@code BasicStroke}
-     * @param miterlimit the miter limit as per {@code BasicStroke}
-     * @param dashes the dash length array as per {@code BasicStroke}
-     * @param dashphase the initial dash phase as per {@code BasicStroke}
-     * @return the widened path stored in a new {@code Shape} object
-     * @since 1.7
-     */
-    @Override
-    public Shape createStrokedShape(Shape src,
-                                    float width,
-                                    int caps,
-                                    int join,
-                                    float miterlimit,
-                                    float[] dashes,
-                                    float dashphase)
-    {
-        final RendererContext rdrCtx = getRendererContext();
-        try {
-            // initialize a large copyable Path2D to avoid a lot of array growing:
-            final Path2D.Float p2d = rdrCtx.getPath2D();
-
-            strokeTo(rdrCtx,
-                     src,
-                     null,
-                     width,
-                     NormMode.OFF,
-                     caps,
-                     join,
-                     miterlimit,
-                     dashes,
-                     dashphase,
-                     rdrCtx.transformerPC2D.wrapPath2D(p2d)
-                    );
-
-            // Use Path2D copy constructor (trim)
-            return new Path2D.Float(p2d);
-
-        } finally {
-            // recycle the RendererContext instance
-            returnRendererContext(rdrCtx);
-        }
-    }
-
-    /**
-     * Sends the geometry for a widened path as specified by the parameters
-     * to the specified consumer.
-     * <p>
-     * The specified {@code src} {@link Shape} is widened according
-     * to the parameters specified by the {@link BasicStroke} object.
-     * Adjustments are made to the path as appropriate for the
-     * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
-     * {@code normalize} boolean parameter is true.
-     * Adjustments are made to the path as appropriate for the
-     * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
-     * {@code antialias} boolean parameter is true.
-     * <p>
-     * The geometry of the widened path is forwarded to the indicated
-     * {@link PathConsumer2D} object as it is calculated.
-     *
-     * @param src the source path to be widened
-     * @param bs the {@code BasicSroke} object specifying the
-     *           decorations to be applied to the widened path
-     * @param normalize indicates whether stroke normalization should
-     *                  be applied
-     * @param antialias indicates whether or not adjustments appropriate
-     *                  to antialiased rendering should be applied
-     * @param consumer the {@code PathConsumer2D} instance to forward
-     *                 the widened geometry to
-     * @since 1.7
-     */
-    @Override
-    public void strokeTo(Shape src,
-                         AffineTransform at,
-                         BasicStroke bs,
-                         boolean thin,
-                         boolean normalize,
-                         boolean antialias,
-                         final PathConsumer2D consumer)
-    {
-        final NormMode norm = (normalize) ?
-                ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
-                : NormMode.OFF;
-
-        final RendererContext rdrCtx = getRendererContext();
-        try {
-            strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer);
-        } finally {
-            // recycle the RendererContext instance
-            returnRendererContext(rdrCtx);
-        }
-    }
-
-    void strokeTo(final RendererContext rdrCtx,
-                  Shape src,
-                  AffineTransform at,
-                  BasicStroke bs,
-                  boolean thin,
-                  NormMode normalize,
-                  boolean antialias,
-                  PathConsumer2D pc2d)
-    {
-        float lw;
-        if (thin) {
-            if (antialias) {
-                lw = userSpaceLineWidth(at, MIN_PEN_SIZE);
-            } else {
-                lw = userSpaceLineWidth(at, 1.0f);
-            }
-        } else {
-            lw = bs.getLineWidth();
-        }
-        strokeTo(rdrCtx,
-                 src,
-                 at,
-                 lw,
-                 normalize,
-                 bs.getEndCap(),
-                 bs.getLineJoin(),
-                 bs.getMiterLimit(),
-                 bs.getDashArray(),
-                 bs.getDashPhase(),
-                 pc2d);
-    }
-
-    private float userSpaceLineWidth(AffineTransform at, float lw) {
-
-        float widthScale;
-
-        if (at == null) {
-            widthScale = 1.0f;
-        } else if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM  |
-                                    AffineTransform.TYPE_GENERAL_SCALE)) != 0) {
-            // Determinant may be negative (flip), use its absolute value:
-            widthScale = (float)Math.sqrt(Math.abs(at.getDeterminant()));
-        } else {
-            // First calculate the "maximum scale" of this transform.
-            double A = at.getScaleX();       // m00
-            double C = at.getShearX();       // m01
-            double B = at.getShearY();       // m10
-            double D = at.getScaleY();       // m11
-
-            /*
-             * Given a 2 x 2 affine matrix [ A B ] such that
-             *                             [ C D ]
-             * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
-             * find the maximum magnitude (norm) of the vector v'
-             * with the constraint (x^2 + y^2 = 1).
-             * The equation to maximize is
-             *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
-             * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
-             * Since sqrt is monotonic we can maximize |v'|^2
-             * instead and plug in the substitution y = sqrt(1 - x^2).
-             * Trigonometric equalities can then be used to get
-             * rid of most of the sqrt terms.
-             */
-
-            double EA = A*A + B*B;          // x^2 coefficient
-            double EB = 2.0d * (A*C + B*D); // xy coefficient
-            double EC = C*C + D*D;          // y^2 coefficient
-
-            /*
-             * There is a lot of calculus omitted here.
-             *
-             * Conceptually, in the interests of understanding the
-             * terms that the calculus produced we can consider
-             * that EA and EC end up providing the lengths along
-             * the major axes and the hypot term ends up being an
-             * adjustment for the additional length along the off-axis
-             * angle of rotated or sheared ellipses as well as an
-             * adjustment for the fact that the equation below
-             * averages the two major axis lengths.  (Notice that
-             * the hypot term contains a part which resolves to the
-             * difference of these two axis lengths in the absence
-             * of rotation.)
-             *
-             * In the calculus, the ratio of the EB and (EA-EC) terms
-             * ends up being the tangent of 2*theta where theta is
-             * the angle that the long axis of the ellipse makes
-             * with the horizontal axis.  Thus, this equation is
-             * calculating the length of the hypotenuse of a triangle
-             * along that axis.
-             */
-
-            double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
-            // sqrt omitted, compare to squared limits below.
-            double widthsquared = ((EA + EC + hypot) / 2.0d);
-
-            widthScale = (float)Math.sqrt(widthsquared);
-        }
-
-        return (lw / widthScale);
-    }
-
-    void strokeTo(final RendererContext rdrCtx,
-                  Shape src,
-                  AffineTransform at,
-                  float width,
-                  NormMode norm,
-                  int caps,
-                  int join,
-                  float miterlimit,
-                  float[] dashes,
-                  float dashphase,
-                  PathConsumer2D pc2d)
-    {
-        // We use strokerat so that in Stroker and Dasher we can work only
-        // with the pre-transformation coordinates. This will repeat a lot of
-        // computations done in the path iterator, but the alternative is to
-        // work with transformed paths and compute untransformed coordinates
-        // as needed. This would be faster but I do not think the complexity
-        // of working with both untransformed and transformed coordinates in
-        // the same code is worth it.
-        // However, if a path's width is constant after a transformation,
-        // we can skip all this untransforming.
-
-        // As pathTo() will check transformed coordinates for invalid values
-        // (NaN / Infinity) to ignore such points, it is necessary to apply the
-        // transformation before the path processing.
-        AffineTransform strokerat = null;
-
-        int dashLen = -1;
-        boolean recycleDashes = false;
-
-        if (at != null && !at.isIdentity()) {
-            final double a = at.getScaleX();
-            final double b = at.getShearX();
-            final double c = at.getShearY();
-            final double d = at.getScaleY();
-            final double det = a * d - c * b;
-
-            if (Math.abs(det) <= (2.0f * Float.MIN_VALUE)) {
-                // this rendering engine takes one dimensional curves and turns
-                // them into 2D shapes by giving them width.
-                // However, if everything is to be passed through a singular
-                // transformation, these 2D shapes will be squashed down to 1D
-                // again so, nothing can be drawn.
-
-                // Every path needs an initial moveTo and a pathDone. If these
-                // are not there this causes a SIGSEGV in libawt.so (at the time
-                // of writing of this comment (September 16, 2010)). Actually,
-                // I am not sure if the moveTo is necessary to avoid the SIGSEGV
-                // but the pathDone is definitely needed.
-                pc2d.moveTo(0.0f, 0.0f);
-                pc2d.pathDone();
-                return;
-            }
-
-            // If the transform is a constant multiple of an orthogonal transformation
-            // then every length is just multiplied by a constant, so we just
-            // need to transform input paths to stroker and tell stroker
-            // the scaled width. This condition is satisfied if
-            // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
-            // leave a bit of room for error.
-            if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
-                final float scale = (float) Math.sqrt(a*a + c*c);
-
-                if (dashes != null) {
-                    recycleDashes = true;
-                    dashLen = dashes.length;
-                    dashes = rdrCtx.dasher.copyDashArray(dashes);
-                    for (int i = 0; i < dashLen; i++) {
-                        dashes[i] *= scale;
-                    }
-                    dashphase *= scale;
-                }
-                width *= scale;
-
-                // by now strokerat == null. Input paths to
-                // stroker (and maybe dasher) will have the full transform at
-                // applied to them and nothing will happen to the output paths.
-            } else {
-                strokerat = at;
-
-                // by now strokerat == at. Input paths to
-                // stroker (and maybe dasher) will have the full transform at
-                // applied to them, then they will be normalized, and then
-                // the inverse of *only the non translation part of at* will
-                // be applied to the normalized paths. This won't cause problems
-                // in stroker, because, suppose at = T*A, where T is just the
-                // translation part of at, and A is the rest. T*A has already
-                // been applied to Stroker/Dasher's input. Then Ainv will be
-                // applied. Ainv*T*A is not equal to T, but it is a translation,
-                // which means that none of stroker's assumptions about its
-                // input will be violated. After all this, A will be applied
-                // to stroker's output.
-            }
-        } else {
-            // either at is null or it's the identity. In either case
-            // we don't transform the path.
-            at = null;
-        }
-
-        final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
-
-        if (DO_TRACE_PATH) {
-            // trace Stroker:
-            pc2d = transformerPC2D.traceStroker(pc2d);
-        }
-
-        if (USE_SIMPLIFIER) {
-            // Use simplifier after stroker before Renderer
-            // to remove collinear segments (notably due to cap square)
-            pc2d = rdrCtx.simplifier.init(pc2d);
-        }
-
-        // deltaTransformConsumer may adjust the clip rectangle:
-        pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
-
-        // stroker will adjust the clip rectangle (width / miter limit):
-        pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit,
-                (dashes == null));
-
-        // Curve Monotizer:
-        rdrCtx.monotonizer.init(width);
-
-        if (dashes != null) {
-            if (!recycleDashes) {
-                dashLen = dashes.length;
-            }
-            if (DO_TRACE_PATH) {
-                pc2d = transformerPC2D.traceDasher(pc2d);
-            }
-            pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
-                                      recycleDashes);
-
-            if (DISABLE_2ND_STROKER_CLIPPING) {
-                // disable stoker clipping
-                rdrCtx.stroker.disableClipping();
-            }
-
-        } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
-            if (DO_TRACE_PATH) {
-                pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
-            }
-
-            // If no dash and clip is enabled:
-            // detect closedPaths (polygons) for caps
-            pc2d = transformerPC2D.detectClosedPath(pc2d);
-        }
-        pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
-
-        if (DO_TRACE_PATH) {
-            // trace Input:
-            pc2d = transformerPC2D.traceInput(pc2d);
-        }
-
-        final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
-                                         src.getPathIterator(at));
-
-        pathTo(rdrCtx, pi, pc2d);
-
-        /*
-         * Pipeline seems to be:
-         * shape.getPathIterator(at)
-         * -> (NormalizingPathIterator)
-         * -> (inverseDeltaTransformConsumer)
-         * -> (Dasher)
-         * -> Stroker
-         * -> (deltaTransformConsumer)
-         *
-         * -> (CollinearSimplifier) to remove redundant segments
-         *
-         * -> pc2d = Renderer (bounding box)
-         */
-    }
-
-    private static boolean nearZero(final double num) {
-        return Math.abs(num) < 2.0d * Math.ulp(num);
-    }
-
-    abstract static class NormalizingPathIterator implements PathIterator {
-
-        private PathIterator src;
-
-        // the adjustment applied to the current position.
-        private float curx_adjust, cury_adjust;
-        // the adjustment applied to the last moveTo position.
-        private float movx_adjust, movy_adjust;
-
-        private final float[] tmp;
-
-        NormalizingPathIterator(final float[] tmp) {
-            this.tmp = tmp;
-        }
-
-        final NormalizingPathIterator init(final PathIterator src) {
-            this.src = src;
-            return this; // fluent API
-        }
-
-        /**
-         * Disposes this path iterator:
-         * clean up before reusing this instance
-         */
-        final void dispose() {
-            // free source PathIterator:
-            this.src = null;
-        }
-
-        @Override
-        public final int currentSegment(final float[] coords) {
-            int lastCoord;
-            final int type = src.currentSegment(coords);
-
-            switch(type) {
-                case PathIterator.SEG_MOVETO:
-                case PathIterator.SEG_LINETO:
-                    lastCoord = 0;
-                    break;
-                case PathIterator.SEG_QUADTO:
-                    lastCoord = 2;
-                    break;
-                case PathIterator.SEG_CUBICTO:
-                    lastCoord = 4;
-                    break;
-                case PathIterator.SEG_CLOSE:
-                    // we don't want to deal with this case later. We just exit now
-                    curx_adjust = movx_adjust;
-                    cury_adjust = movy_adjust;
-                    return type;
-                default:
-                    throw new InternalError("Unrecognized curve type");
-            }
-
-            // normalize endpoint
-            float coord, x_adjust, y_adjust;
-
-            coord = coords[lastCoord];
-            x_adjust = normCoord(coord); // new coord
-            coords[lastCoord] = x_adjust;
-            x_adjust -= coord;
-
-            coord = coords[lastCoord + 1];
-            y_adjust = normCoord(coord); // new coord
-            coords[lastCoord + 1] = y_adjust;
-            y_adjust -= coord;
-
-            // now that the end points are done, normalize the control points
-            switch(type) {
-                case PathIterator.SEG_MOVETO:
-                    movx_adjust = x_adjust;
-                    movy_adjust = y_adjust;
-                    break;
-                case PathIterator.SEG_LINETO:
-                    break;
-                case PathIterator.SEG_QUADTO:
-                    coords[0] += (curx_adjust + x_adjust) / 2.0f;
-                    coords[1] += (cury_adjust + y_adjust) / 2.0f;
-                    break;
-                case PathIterator.SEG_CUBICTO:
-                    coords[0] += curx_adjust;
-                    coords[1] += cury_adjust;
-                    coords[2] += x_adjust;
-                    coords[3] += y_adjust;
-                    break;
-                case PathIterator.SEG_CLOSE:
-                    // handled earlier
-                default:
-            }
-            curx_adjust = x_adjust;
-            cury_adjust = y_adjust;
-            return type;
-        }
-
-        abstract float normCoord(final float coord);
-
-        @Override
-        public final int currentSegment(final double[] coords) {
-            final float[] _tmp = tmp; // dirty
-            int type = this.currentSegment(_tmp);
-            for (int i = 0; i < 6; i++) {
-                coords[i] = _tmp[i];
-            }
-            return type;
-        }
-
-        @Override
-        public final int getWindingRule() {
-            return src.getWindingRule();
-        }
-
-        @Override
-        public final boolean isDone() {
-            if (src.isDone()) {
-                // Dispose this instance:
-                dispose();
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public final void next() {
-            src.next();
-        }
-
-        static final class NearestPixelCenter
-                                extends NormalizingPathIterator
-        {
-            NearestPixelCenter(final float[] tmp) {
-                super(tmp);
-            }
-
-            @Override
-            float normCoord(final float coord) {
-                // round to nearest pixel center
-                return FloatMath.floor_f(coord) + 0.5f;
-            }
-        }
-
-        static final class NearestPixelQuarter
-                                extends NormalizingPathIterator
-        {
-            NearestPixelQuarter(final float[] tmp) {
-                super(tmp);
-            }
-
-            @Override
-            float normCoord(final float coord) {
-                // round to nearest (0.25, 0.25) pixel quarter
-                return FloatMath.floor_f(coord + 0.25f) + 0.25f;
-            }
-        }
-    }
-
-    private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
-                               PathConsumer2D pc2d)
-    {
-        if (USE_PATH_SIMPLIFIER) {
-            // Use path simplifier at the first step
-            // to remove useless points
-            pc2d = rdrCtx.pathSimplifier.init(pc2d);
-        }
-
-        // mark context as DIRTY:
-        rdrCtx.dirty = true;
-
-        pathToLoop(rdrCtx.float6, pi, pc2d);
-
-        // mark context as CLEAN:
-        rdrCtx.dirty = false;
-    }
-
-    private static void pathToLoop(final float[] coords, final PathIterator pi,
-                                   final PathConsumer2D pc2d)
-    {
-        // ported from DuctusRenderingEngine.feedConsumer() but simplified:
-        // - removed skip flag = !subpathStarted
-        // - removed pathClosed (ie subpathStarted not set to false)
-        boolean subpathStarted = false;
-
-        for (; !pi.isDone(); pi.next()) {
-            switch (pi.currentSegment(coords)) {
-            case PathIterator.SEG_MOVETO:
-                /* Checking SEG_MOVETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Skipping next path segment in case of
-                 * invalid data.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    pc2d.moveTo(coords[0], coords[1]);
-                    subpathStarted = true;
-                }
-                break;
-            case PathIterator.SEG_LINETO:
-                /* Checking SEG_LINETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid data. If segment is skipped its endpoint
-                 * (if valid) is used to begin new subpath.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        pc2d.lineTo(coords[0], coords[1]);
-                    } else {
-                        pc2d.moveTo(coords[0], coords[1]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_QUADTO:
-                // Quadratic curves take two points
-                /* Checking SEG_QUADTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                    coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                        {
-                            pc2d.quadTo(coords[0], coords[1],
-                                        coords[2], coords[3]);
-                        } else {
-                            pc2d.lineTo(coords[2], coords[3]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[2], coords[3]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CUBICTO:
-                // Cubic curves take three points
-                /* Checking SEG_CUBICTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
-                    coords[5] < UPPER_BND && coords[5] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
-                            coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                            coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                        {
-                            pc2d.curveTo(coords[0], coords[1],
-                                         coords[2], coords[3],
-                                         coords[4], coords[5]);
-                        } else {
-                            pc2d.lineTo(coords[4], coords[5]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[4], coords[5]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CLOSE:
-                if (subpathStarted) {
-                    pc2d.closePath();
-                    // do not set subpathStarted to false
-                    // in case of missing moveTo() after close()
-                }
-                break;
-            default:
-            }
-        }
-        pc2d.pathDone();
-    }
-
-    /**
-     * Construct an antialiased tile generator for the given shape with
-     * the given rendering attributes and store the bounds of the tile
-     * iteration in the bbox parameter.
-     * The {@code at} parameter specifies a transform that should affect
-     * both the shape and the {@code BasicStroke} attributes.
-     * The {@code clip} parameter specifies the current clip in effect
-     * in device coordinates and can be used to prune the data for the
-     * operation, but the renderer is not required to perform any
-     * clipping.
-     * If the {@code BasicStroke} parameter is null then the shape
-     * should be filled as is, otherwise the attributes of the
-     * {@code BasicStroke} should be used to specify a draw operation.
-     * The {@code thin} parameter indicates whether or not the
-     * transformed {@code BasicStroke} represents coordinates smaller
-     * than the minimum resolution of the antialiasing rasterizer as
-     * specified by the {@code getMinimumAAPenWidth()} method.
-     * <p>
-     * Upon returning, this method will fill the {@code bbox} parameter
-     * with 4 values indicating the bounds of the iteration of the
-     * tile generator.
-     * The iteration order of the tiles will be as specified by the
-     * pseudo-code:
-     * <pre>
-     *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
-     *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
-     *         }
-     *     }
-     * </pre>
-     * If there is no output to be rendered, this method may return
-     * null.
-     *
-     * @param s the shape to be rendered (fill or draw)
-     * @param at the transform to be applied to the shape and the
-     *           stroke attributes
-     * @param clip the current clip in effect in device coordinates
-     * @param bs if non-null, a {@code BasicStroke} whose attributes
-     *           should be applied to this operation
-     * @param thin true if the transformed stroke attributes are smaller
-     *             than the minimum dropout pen width
-     * @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
-     *                  {@code RenderingHint} is in effect
-     * @param bbox returns the bounds of the iteration
-     * @return the {@code AATileGenerator} instance to be consulted
-     *         for tile coverages, or null if there is no output to render
-     * @since 1.7
-     */
-    @Override
-    public AATileGenerator getAATileGenerator(Shape s,
-                                              AffineTransform at,
-                                              Region clip,
-                                              BasicStroke bs,
-                                              boolean thin,
-                                              boolean normalize,
-                                              int[] bbox)
-    {
-        MarlinTileGenerator ptg = null;
-        Renderer r = null;
-
-        final RendererContext rdrCtx = getRendererContext();
-        try {
-            if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
-                // Define the initial clip bounds:
-                final float[] clipRect = rdrCtx.clipRect;
-
-                // Adjust the clipping rectangle with the renderer offsets
-                final float rdrOffX = Renderer.RDR_OFFSET_X;
-                final float rdrOffY = Renderer.RDR_OFFSET_Y;
-
-                // add a small rounding error:
-                final float margin = 1e-3f;
-
-                clipRect[0] = clip.getLoY()
-                                - margin + rdrOffY;
-                clipRect[1] = clip.getLoY() + clip.getHeight()
-                                + margin + rdrOffY;
-                clipRect[2] = clip.getLoX()
-                                - margin + rdrOffX;
-                clipRect[3] = clip.getLoX() + clip.getWidth()
-                                + margin + rdrOffX;
-
-                if (MarlinConst.DO_LOG_CLIP) {
-                    MarlinUtils.logInfo("clipRect (clip): "
-                                        + Arrays.toString(rdrCtx.clipRect));
-                }
-
-                // Enable clipping:
-                rdrCtx.doClip = true;
-            }
-
-            // Test if at is identity:
-            final AffineTransform _at = (at != null && !at.isIdentity()) ? at
-                                        : null;
-
-            final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
-
-            if (bs == null) {
-                // fill shape:
-                final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
-                                                 s.getPathIterator(_at));
-
-                // note: Winding rule may be EvenOdd ONLY for fill operations !
-                r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
-                                         clip.getWidth(), clip.getHeight(),
-                                         pi.getWindingRule());
-
-                PathConsumer2D pc2d = r;
-
-                if (DO_CLIP_FILL && rdrCtx.doClip) {
-                    if (DO_TRACE_PATH) {
-                        // trace Filler:
-                        pc2d = rdrCtx.transformerPC2D.traceFiller(pc2d);
-                    }
-                    pc2d = rdrCtx.transformerPC2D.pathClipper(pc2d);
-                }
-
-                if (DO_TRACE_PATH) {
-                    // trace Input:
-                    pc2d = rdrCtx.transformerPC2D.traceInput(pc2d);
-                }
-                pathTo(rdrCtx, pi, pc2d);
-
-            } else {
-                // draw shape with given stroke:
-                r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
-                                         clip.getWidth(), clip.getHeight(),
-                                         WIND_NON_ZERO);
-
-                strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
-            }
-            if (r.endRendering()) {
-                ptg = rdrCtx.ptg.init();
-                ptg.getBbox(bbox);
-                // note: do not returnRendererContext(rdrCtx)
-                // as it will be called later by MarlinTileGenerator.dispose()
-                r = null;
-            }
-        } finally {
-            if (r != null) {
-                // dispose renderer and recycle the RendererContext instance:
-                r.dispose();
-            }
-        }
-
-        // Return null to cancel AA tile generation (nothing to render)
-        return ptg;
-    }
-
-    @Override
-    public AATileGenerator getAATileGenerator(double x, double y,
-                                              double dx1, double dy1,
-                                              double dx2, double dy2,
-                                              double lw1, double lw2,
-                                              Region clip,
-                                              int[] bbox)
-    {
-        // REMIND: Deal with large coordinates!
-        double ldx1, ldy1, ldx2, ldy2;
-        boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
-
-        if (innerpgram) {
-            ldx1 = dx1 * lw1;
-            ldy1 = dy1 * lw1;
-            ldx2 = dx2 * lw2;
-            ldy2 = dy2 * lw2;
-            x -= (ldx1 + ldx2) / 2.0d;
-            y -= (ldy1 + ldy2) / 2.0d;
-            dx1 += ldx1;
-            dy1 += ldy1;
-            dx2 += ldx2;
-            dy2 += ldy2;
-            if (lw1 > 1.0d && lw2 > 1.0d) {
-                // Inner parallelogram was entirely consumed by stroke...
-                innerpgram = false;
-            }
-        } else {
-            ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
-        }
-
-        MarlinTileGenerator ptg = null;
-        Renderer r = null;
-
-        final RendererContext rdrCtx = getRendererContext();
-        try {
-            r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
-                                     clip.getWidth(), clip.getHeight(),
-                                     WIND_EVEN_ODD);
-
-            r.moveTo((float) x, (float) y);
-            r.lineTo((float) (x+dx1), (float) (y+dy1));
-            r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
-            r.lineTo((float) (x+dx2), (float) (y+dy2));
-            r.closePath();
-
-            if (innerpgram) {
-                x += ldx1 + ldx2;
-                y += ldy1 + ldy2;
-                dx1 -= 2.0d * ldx1;
-                dy1 -= 2.0d * ldy1;
-                dx2 -= 2.0d * ldx2;
-                dy2 -= 2.0d * ldy2;
-                r.moveTo((float) x, (float) y);
-                r.lineTo((float) (x+dx1), (float) (y+dy1));
-                r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
-                r.lineTo((float) (x+dx2), (float) (y+dy2));
-                r.closePath();
-            }
-            r.pathDone();
-
-            if (r.endRendering()) {
-                ptg = rdrCtx.ptg.init();
-                ptg.getBbox(bbox);
-                // note: do not returnRendererContext(rdrCtx)
-                // as it will be called later by MarlinTileGenerator.dispose()
-                r = null;
-            }
-        } finally {
-            if (r != null) {
-                // dispose renderer and recycle the RendererContext instance:
-                r.dispose();
-            }
-        }
-
-        // Return null to cancel AA tile generation (nothing to render)
-        return ptg;
-    }
-
-    /**
-     * Returns the minimum pen width that the antialiasing rasterizer
-     * can represent without dropouts occuring.
-     * @since 1.7
-     */
-    @Override
-    public float getMinimumAAPenSize() {
-        return MIN_PEN_SIZE;
-    }
-
-    static {
-        if (PathIterator.WIND_NON_ZERO != WIND_NON_ZERO ||
-            PathIterator.WIND_EVEN_ODD != WIND_EVEN_ODD ||
-            BasicStroke.JOIN_MITER != JOIN_MITER ||
-            BasicStroke.JOIN_ROUND != JOIN_ROUND ||
-            BasicStroke.JOIN_BEVEL != JOIN_BEVEL ||
-            BasicStroke.CAP_BUTT != CAP_BUTT ||
-            BasicStroke.CAP_ROUND != CAP_ROUND ||
-            BasicStroke.CAP_SQUARE != CAP_SQUARE)
-        {
-            throw new InternalError("mismatched renderer constants");
-        }
-    }
-
-    // --- RendererContext handling ---
-    // use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext
-    private static final boolean USE_THREAD_LOCAL;
-
-    // reference type stored in either TL or CLQ
-    static final int REF_TYPE;
-
-    // Per-thread RendererContext
-    private static final ReentrantContextProvider<RendererContext> RDR_CTX_PROVIDER;
-
-    // Static initializer to use TL or CLQ mode
-    static {
-        USE_THREAD_LOCAL = MarlinProperties.isUseThreadLocal();
-
-        // Soft reference by default:
-        final String refType = AccessController.doPrivileged(
-                            new GetPropertyAction("sun.java2d.renderer.useRef",
-                            "soft"));
-        switch (refType) {
-            default:
-            case "soft":
-                REF_TYPE = ReentrantContextProvider.REF_SOFT;
-                break;
-            case "weak":
-                REF_TYPE = ReentrantContextProvider.REF_WEAK;
-                break;
-            case "hard":
-                REF_TYPE = ReentrantContextProvider.REF_HARD;
-                break;
-        }
-
-        if (USE_THREAD_LOCAL) {
-            RDR_CTX_PROVIDER = new ReentrantContextProviderTL<RendererContext>(REF_TYPE)
-                {
-                    @Override
-                    protected RendererContext newContext() {
-                        return RendererContext.createContext();
-                    }
-                };
-        } else {
-            RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ<RendererContext>(REF_TYPE)
-                {
-                    @Override
-                    protected RendererContext newContext() {
-                        return RendererContext.createContext();
-                    }
-                };
-        }
-    }
-
-    private static boolean SETTINGS_LOGGED = !ENABLE_LOGS;
-
-    private static void logSettings(final String reClass) {
-        // log information at startup
-        if (SETTINGS_LOGGED) {
-            return;
-        }
-        SETTINGS_LOGGED = true;
-
-        String refType;
-        switch (REF_TYPE) {
-            default:
-            case ReentrantContextProvider.REF_HARD:
-                refType = "hard";
-                break;
-            case ReentrantContextProvider.REF_SOFT:
-                refType = "soft";
-                break;
-            case ReentrantContextProvider.REF_WEAK:
-                refType = "weak";
-                break;
-        }
-
-        logInfo("=========================================================="
-                + "=====================");
-
-        logInfo("Marlin software rasterizer           = ENABLED");
-        logInfo("Version                              = ["
-                + Version.getVersion() + "]");
-        logInfo("sun.java2d.renderer                  = "
-                + reClass);
-        logInfo("sun.java2d.renderer.useThreadLocal   = "
-                + USE_THREAD_LOCAL);
-        logInfo("sun.java2d.renderer.useRef           = "
-                + refType);
-
-        logInfo("sun.java2d.renderer.edges            = "
-                + MarlinConst.INITIAL_EDGES_COUNT);
-        logInfo("sun.java2d.renderer.pixelWidth       = "
-                + MarlinConst.INITIAL_PIXEL_WIDTH);
-        logInfo("sun.java2d.renderer.pixelHeight      = "
-                + MarlinConst.INITIAL_PIXEL_HEIGHT);
-
-        logInfo("sun.java2d.renderer.profile          = "
-                + (MarlinProperties.isProfileQuality() ?
-                    "quality" : "speed"));
-
-        logInfo("sun.java2d.renderer.subPixel_log2_X  = "
-                + MarlinConst.SUBPIXEL_LG_POSITIONS_X);
-        logInfo("sun.java2d.renderer.subPixel_log2_Y  = "
-                + MarlinConst.SUBPIXEL_LG_POSITIONS_Y);
-
-        logInfo("sun.java2d.renderer.tileSize_log2    = "
-                + MarlinConst.TILE_H_LG);
-        logInfo("sun.java2d.renderer.tileWidth_log2   = "
-                + MarlinConst.TILE_W_LG);
-        logInfo("sun.java2d.renderer.blockSize_log2   = "
-                + MarlinConst.BLOCK_SIZE_LG);
-
-        // RLE / blockFlags settings
-
-        logInfo("sun.java2d.renderer.forceRLE         = "
-                + MarlinProperties.isForceRLE());
-        logInfo("sun.java2d.renderer.forceNoRLE       = "
-                + MarlinProperties.isForceNoRLE());
-        logInfo("sun.java2d.renderer.useTileFlags     = "
-                + MarlinProperties.isUseTileFlags());
-        logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
-                + MarlinProperties.isUseTileFlagsWithHeuristics());
-        logInfo("sun.java2d.renderer.rleMinWidth      = "
-                + MarlinCache.RLE_MIN_WIDTH);
-
-        // optimisation parameters
-        logInfo("sun.java2d.renderer.useSimplifier    = "
-                + MarlinConst.USE_SIMPLIFIER);
-        logInfo("sun.java2d.renderer.usePathSimplifier= "
-                + MarlinConst.USE_PATH_SIMPLIFIER);
-        logInfo("sun.java2d.renderer.pathSimplifier.pixTol = "
-                + MarlinProperties.getPathSimplifierPixelTolerance());
-
-        logInfo("sun.java2d.renderer.clip             = "
-                + MarlinProperties.isDoClip());
-        logInfo("sun.java2d.renderer.clip.runtime.enable = "
-                + MarlinProperties.isDoClipRuntimeFlag());
-
-        logInfo("sun.java2d.renderer.clip.subdivider  = "
-                + MarlinProperties.isDoClipSubdivider());
-        logInfo("sun.java2d.renderer.clip.subdivider.minLength = "
-                + MarlinProperties.getSubdividerMinLength());
-
-        // debugging parameters
-        logInfo("sun.java2d.renderer.doStats          = "
-                + MarlinConst.DO_STATS);
-        logInfo("sun.java2d.renderer.doMonitors       = "
-                + MarlinConst.DO_MONITORS);
-        logInfo("sun.java2d.renderer.doChecks         = "
-                + MarlinConst.DO_CHECKS);
-
-        // logging parameters
-        logInfo("sun.java2d.renderer.useLogger        = "
-                + MarlinConst.USE_LOGGER);
-        logInfo("sun.java2d.renderer.logCreateContext = "
-                + MarlinConst.LOG_CREATE_CONTEXT);
-        logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
-                + MarlinConst.LOG_UNSAFE_MALLOC);
-
-        // quality settings
-        logInfo("sun.java2d.renderer.curve_len_err    = "
-                + MarlinProperties.getCurveLengthError());
-        logInfo("sun.java2d.renderer.cubic_dec_d2     = "
-                + MarlinProperties.getCubicDecD2());
-        logInfo("sun.java2d.renderer.cubic_inc_d1     = "
-                + MarlinProperties.getCubicIncD1());
-        logInfo("sun.java2d.renderer.quad_dec_d2      = "
-                + MarlinProperties.getQuadDecD2());
-
-        logInfo("Renderer settings:");
-        logInfo("SORT         = " + MergeSort.SORT_TYPE);
-        logInfo("CUB_DEC_BND  = " + Renderer.CUB_DEC_BND);
-        logInfo("CUB_INC_BND  = " + Renderer.CUB_INC_BND);
-        logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);
-
-        logInfo("INITIAL_EDGES_CAPACITY               = "
-                + MarlinConst.INITIAL_EDGES_CAPACITY);
-        logInfo("INITIAL_CROSSING_COUNT               = "
-                + Renderer.INITIAL_CROSSING_COUNT);
-
-        logInfo("=========================================================="
-                + "=====================");
-    }
-
-    /**
-     * Get the RendererContext instance dedicated to the current thread
-     * @return RendererContext instance
-     */
-    @SuppressWarnings({"unchecked"})
-    static RendererContext getRendererContext() {
-        final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_pre_getAATileGenerator.start();
-        }
-        return rdrCtx;
-    }
-
-    /**
-     * Reset and return the given RendererContext instance for reuse
-     * @param rdrCtx RendererContext instance
-     */
-    static void returnRendererContext(final RendererContext rdrCtx) {
-        rdrCtx.dispose();
-
-        if (DO_MONITORS) {
-            rdrCtx.stats.mon_pre_getAATileGenerator.stop();
-        }
-        RDR_CTX_PROVIDER.release(rdrCtx);
-    }
-}
diff --git a/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java b/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java
index 87b52c3..7721d78 100644
--- a/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java
+++ b/src/main/java/sun/java2d/marlin/MarlinTileGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -52,25 +52,18 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
         }
     }
 
-    private final Renderer rdrF;
-    private final DRenderer rdrD;
+    private final Renderer renderer;
     private final MarlinCache cache;
     private int x, y;
 
     // per-thread renderer stats
     final RendererStats rdrStats;
 
-    MarlinTileGenerator(final RendererStats stats, final MarlinRenderer r,
+    MarlinTileGenerator(final RendererStats stats, final Renderer r,
                         final MarlinCache cache)
     {
         this.rdrStats = stats;
-        if (r instanceof Renderer) {
-            this.rdrF = (Renderer)r;
-            this.rdrD = null;
-        } else {
-            this.rdrF = null;
-            this.rdrD = (DRenderer)r;
-        }
+        this.renderer = r;
         this.cache = cache;
     }
 
@@ -94,12 +87,7 @@ public void dispose() {
         // dispose cache:
         cache.dispose();
         // dispose renderer and recycle the RendererContext instance:
-        // bimorphic call optimization:
-        if (rdrF != null) {
-            rdrF.dispose();
-        } else if (rdrD != null) {
-            rdrD.dispose();
-        }
+        renderer.dispose();
     }
 
     void getBbox(int[] bbox) {
@@ -185,12 +173,7 @@ public void nextTile() {
             if (y < cache.bboxY1) {
                 // compute for the tile line
                 // [ y; max(y + TILE_SIZE, bboxY1) ]
-                // bimorphic call optimization:
-                if (rdrF != null) {
-                    rdrF.endRendering(y);
-                } else if (rdrD != null) {
-                    rdrD.endRendering(y);
-                }
+                renderer.endRendering(y);
             }
         }
     }
diff --git a/src/main/java/sun/java2d/marlin/MergeSort.java b/src/main/java/sun/java2d/marlin/MergeSort.java
index 969f486..0a05b60 100644
--- a/src/main/java/sun/java2d/marlin/MergeSort.java
+++ b/src/main/java/sun/java2d/marlin/MergeSort.java
@@ -22,6 +22,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 package sun.java2d.marlin;
 
 import java.util.Arrays;
diff --git a/src/main/java/sun/java2d/marlin/PathSimplifier.java b/src/main/java/sun/java2d/marlin/PathSimplifier.java
index 203cbfd..82fcda3 100644
--- a/src/main/java/sun/java2d/marlin/PathSimplifier.java
+++ b/src/main/java/sun/java2d/marlin/PathSimplifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021, 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
@@ -24,28 +24,26 @@
  */
 package sun.java2d.marlin;
 
-import sun.awt.geom.PathConsumer2D;
-
-final class PathSimplifier implements PathConsumer2D {
+final class PathSimplifier implements DPathConsumer2D {
 
     // distance threshold in pixels (device)
-    private static final float PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance();
+    private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance();
     // squared tolerance in pixels
-    private static final float SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD;
+    private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD;
 
     // members:
-    private PathConsumer2D delegate;
+    private DPathConsumer2D delegate;
     // current reference point
-    private float cx, cy;
+    private double cx, cy;
     // flag indicating if the given point was skipped
     private boolean skipped;
     // last skipped point
-    private float sx, sy;
+    private double sx, sy;
 
     PathSimplifier() {
     }
 
-    PathSimplifier init(final PathConsumer2D delegate) {
+    PathSimplifier init(final DPathConsumer2D delegate) {
         if (this.delegate != delegate) {
             this.delegate = delegate;
         }
@@ -72,7 +70,7 @@ public void closePath() {
     }
 
     @Override
-    public void moveTo(final float xe, final float ye) {
+    public void moveTo(final double xe, final double ye) {
         finishPath();
         delegate.moveTo(xe, ye);
         cx = xe;
@@ -80,10 +78,10 @@ public void moveTo(final float xe, final float ye) {
     }
 
     @Override
-    public void lineTo(final float xe, final float ye) {
+    public void lineTo(final double xe, final double ye) {
         // Test if segment is too small:
-        float dx = (xe - cx);
-        float dy = (ye - cy);
+        double dx = (xe - cx);
+        double dy = (ye - cy);
 
         if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
             skipped = true;
@@ -94,7 +92,7 @@ public void lineTo(final float xe, final float ye) {
         _lineTo(xe, ye);
     }
 
-    private void _lineTo(final float xe, final float ye) {
+    private void _lineTo(final double xe, final double ye) {
         delegate.lineTo(xe, ye);
         cx = xe;
         cy = ye;
@@ -102,12 +100,12 @@ private void _lineTo(final float xe, final float ye) {
     }
 
     @Override
-    public void quadTo(final float x1, final float y1,
-                       final float xe, final float ye)
+    public void quadTo(final double x1, final double y1,
+                       final double xe, final double ye)
     {
         // Test if curve is too small:
-        float dx = (xe - cx);
-        float dy = (ye - cy);
+        double dx = (xe - cx);
+        double dy = (ye - cy);
 
         if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
             // check control points P1:
@@ -128,13 +126,13 @@ public void quadTo(final float x1, final float y1,
     }
 
     @Override
-    public void curveTo(final float x1, final float y1,
-                        final float x2, final float y2,
-                        final float xe, final float ye)
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double xe, final double ye)
     {
         // Test if curve is too small:
-        float dx = (xe - cx);
-        float dy = (ye - cy);
+        double dx = (xe - cx);
+        double dy = (ye - cy);
 
         if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
             // check control points P1:
diff --git a/src/main/java/sun/java2d/marlin/Renderer.java b/src/main/java/sun/java2d/marlin/Renderer.java
index 410e3ca..02522fb 100644
--- a/src/main/java/sun/java2d/marlin/Renderer.java
+++ b/src/main/java/sun/java2d/marlin/Renderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -25,11 +25,10 @@
 
 package sun.java2d.marlin;
 
-import sun.awt.geom.PathConsumer2D;
 import static sun.java2d.marlin.OffHeapArray.SIZE_INT;
 import sun.misc.Unsafe;
 
-final class Renderer implements PathConsumer2D, MarlinRenderer {
+final class Renderer implements DPathConsumer2D, MarlinConst {
 
     static final boolean DISABLE_RENDER = false;
 
@@ -41,14 +40,14 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
 
     private static final double POWER_2_TO_32 = 0x1.0p32d;
 
-    // use float to make tosubpix methods faster (no int to float conversion)
-    static final float SUBPIXEL_SCALE_X = (float) SUBPIXEL_POSITIONS_X;
-    static final float SUBPIXEL_SCALE_Y = (float) SUBPIXEL_POSITIONS_Y;
+    // use double to make tosubpix methods faster (no int to double conversion)
+    static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X;
+    static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y;
     static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
     static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
 
-    static final float RDR_OFFSET_X = 0.5f / SUBPIXEL_SCALE_X;
-    static final float RDR_OFFSET_Y = 0.5f / SUBPIXEL_SCALE_Y;
+    static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X;
+    static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y;
 
     // number of subpixels corresponding to a tile line
     private static final int SUBPIXEL_TILE
@@ -76,13 +75,13 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
 
     // curve break into lines
     // cubic error in subpixels to decrement step
-    private static final float CUB_DEC_ERR_SUBPIX
-        = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 1.0 / 8th pixel
+    private static final double CUB_DEC_ERR_SUBPIX
+        = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel
     // cubic error in subpixels to increment step
-    private static final float CUB_INC_ERR_SUBPIX
-        = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.4 / 8th pixel
+    private static final double CUB_INC_ERR_SUBPIX
+        = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel
     // scale factor for Y-axis contribution to quad / cubic errors:
-    public static final float SCALE_DY = ((float) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;
+    public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;
 
     // TestNonAARasterization (JDK-8170879): cubics
     // bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
@@ -90,11 +89,11 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
     // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max =  9), 4042 warnings (avg = 0,06)
 
     // cubic bind length to decrement step
-    public static final float CUB_DEC_BND
-        = 8.0f * CUB_DEC_ERR_SUBPIX;
+    public static final double CUB_DEC_BND
+        = 8.0d * CUB_DEC_ERR_SUBPIX;
     // cubic bind length to increment step
-    public static final float CUB_INC_BND
-        = 8.0f * CUB_INC_ERR_SUBPIX;
+    public static final double CUB_INC_BND
+        = 8.0d * CUB_INC_ERR_SUBPIX;
 
     // cubic countlg
     public static final int CUB_COUNT_LG = 2;
@@ -105,16 +104,16 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
     // cubic count^3 = 8^countlg
     private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);
     // cubic dt = 1 / count
-    private static final float CUB_INV_COUNT = 1.0f / CUB_COUNT;
+    private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT;
     // cubic dt^2 = 1 / count^2 = 1 / 4^countlg
-    private static final float CUB_INV_COUNT_2 = 1.0f / CUB_COUNT_2;
+    private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2;
     // cubic dt^3 = 1 / count^3 = 1 / 8^countlg
-    private static final float CUB_INV_COUNT_3 = 1.0f / CUB_COUNT_3;
+    private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3;
 
     // quad break into lines
     // quadratic error in subpixels
-    private static final float QUAD_DEC_ERR_SUBPIX
-        = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.5 / 8th pixel
+    private static final double QUAD_DEC_ERR_SUBPIX
+        = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel
 
     // TestNonAARasterization (JDK-8170879): quads
     // bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
@@ -122,8 +121,8 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
     // 0.50px  = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10)
 
     // quadratic bind length to decrement step
-    public static final float QUAD_DEC_BND
-        = 8.0f * QUAD_DEC_ERR_SUBPIX;
+    public static final double QUAD_DEC_BND
+        = 8.0d * QUAD_DEC_ERR_SUBPIX;
 
 //////////////////////////////////////////////////////////////////////////////
 //  SCAN LINE
@@ -159,8 +158,8 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
 //////////////////////////////////////////////////////////////////////////////
     private int edgeMinY = Integer.MAX_VALUE;
     private int edgeMaxY = Integer.MIN_VALUE;
-    private float edgeMinX = Float.POSITIVE_INFINITY;
-    private float edgeMaxX = Float.NEGATIVE_INFINITY;
+    private double edgeMinX = Double.POSITIVE_INFINITY;
+    private double edgeMaxX = Double.NEGATIVE_INFINITY;
 
     // edges [ints] stored in off-heap memory
     private final OffHeapArray edges;
@@ -179,20 +178,20 @@ final class Renderer implements PathConsumer2D, MarlinRenderer {
     // Flattens using adaptive forward differencing. This only carries out
     // one iteration of the AFD loop. All it does is update AFD variables (i.e.
     // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
-    private void quadBreakIntoLinesAndAdd(float x0, float y0,
+    private void quadBreakIntoLinesAndAdd(double x0, double y0,
                                           final Curve c,
-                                          final float x2, final float y2)
+                                          final double x2, final double y2)
     {
         int count = 1; // dt = 1 / count
 
         // maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
-        float maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;
+        double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;
 
-        final float _DEC_BND = QUAD_DEC_BND;
+        final double _DEC_BND = QUAD_DEC_BND;
 
         while (maxDD >= _DEC_BND) {
             // divide step by half:
-            maxDD /= 4.0f; // error divided by 2^2 = 4
+            maxDD /= 4.0d; // error divided by 2^2 = 4
 
             count <<= 1;
             if (DO_STATS) {
@@ -203,16 +202,16 @@ private void quadBreakIntoLinesAndAdd(float x0, float y0,
         final int nL = count; // line count
 
         if (count > 1) {
-            final float icount = 1.0f / count; // dt
-            final float icount2 = icount * icount; // dt^2
+            final double icount = 1.0d / count; // dt
+            final double icount2 = icount * icount; // dt^2
 
-            final float ddx = c.dbx * icount2;
-            final float ddy = c.dby * icount2;
-            float dx = c.bx * icount2 + c.cx * icount;
-            float dy = c.by * icount2 + c.cy * icount;
+            final double ddx = c.dbx * icount2;
+            final double ddy = c.dby * icount2;
+            double dx = c.bx * icount2 + c.cx * icount;
+            double dy = c.by * icount2 + c.cy * icount;
 
             // we use x0, y0 to walk the line
-            for (float x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {
+            for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {
                 x1 += dx;
                 y1 += dy;
 
@@ -233,20 +232,20 @@ private void quadBreakIntoLinesAndAdd(float x0, float y0,
     // numerical errors, and our callers already have the exact values.
     // Another alternative would be to pass all the control points, and call
     // c.set here, but then too many numbers are passed around.
-    private void curveBreakIntoLinesAndAdd(float x0, float y0,
+    private void curveBreakIntoLinesAndAdd(double x0, double y0,
                                            final Curve c,
-                                           final float x3, final float y3)
+                                           final double x3, final double y3)
     {
-        int count           = CUB_COUNT;
-        final float icount  = CUB_INV_COUNT;   // dt
-        final float icount2 = CUB_INV_COUNT_2; // dt^2
-        final float icount3 = CUB_INV_COUNT_3; // dt^3
+        int count            = CUB_COUNT;
+        final double icount  = CUB_INV_COUNT;   // dt
+        final double icount2 = CUB_INV_COUNT_2; // dt^2
+        final double icount3 = CUB_INV_COUNT_3; // dt^3
 
         // the dx and dy refer to forward differencing variables, not the last
         // coefficients of the "points" polynomial
-        float dddx, dddy, ddx, ddy, dx, dy;
-        dddx = 2.0f * c.dax * icount3;
-        dddy = 2.0f * c.day * icount3;
+        double dddx, dddy, ddx, ddy, dx, dy;
+        dddx = 2.0d * c.dax * icount3;
+        dddy = 2.0d * c.day * icount3;
         ddx = dddx + c.dbx * icount2;
         ddy = dddy + c.dby * icount2;
         dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
@@ -254,24 +253,24 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0,
 
         int nL = 0; // line count
 
-        final float _DEC_BND = CUB_DEC_BND;
-        final float _INC_BND = CUB_INC_BND;
-        final float _SCALE_DY = SCALE_DY;
+        final double _DEC_BND = CUB_DEC_BND;
+        final double _INC_BND = CUB_INC_BND;
+        final double _SCALE_DY = SCALE_DY;
 
         // we use x0, y0 to walk the line
-        for (float x1 = x0, y1 = y0; count > 0; ) {
+        for (double x1 = x0, y1 = y0; count > 0; ) {
             // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges
 
-            // float step:
+            // double step:
             // can only do this on even "count" values, because we must divide count by 2
             while ((count % 2 == 0)
                     && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) {
-                dx = 2.0f * dx + ddx;
-                dy = 2.0f * dy + ddy;
-                ddx = 4.0f * (ddx + dddx);
-                ddy = 4.0f * (ddy + dddy);
-                dddx *= 8.0f;
-                dddy *= 8.0f;
+                dx = 2.0d * dx + ddx;
+                dy = 2.0d * dy + ddy;
+                ddx = 4.0d * (ddx + dddx);
+                ddy = 4.0d * (ddy + dddy);
+                dddx *= 8.0d;
+                dddy *= 8.0d;
 
                 count >>= 1;
                 if (DO_STATS) {
@@ -281,12 +280,12 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0,
 
             // divide step by half:
             while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) {
-                dddx /= 8.0f;
-                dddy /= 8.0f;
-                ddx = ddx / 4.0f - dddx;
-                ddy = ddy / 4.0f - dddy;
-                dx = (dx - ddx) / 2.0f;
-                dy = (dy - ddy) / 2.0f;
+                dddx /= 8.0d;
+                dddy /= 8.0d;
+                ddx = ddx / 4.0d - dddx;
+                ddy = ddy / 4.0d - dddy;
+                dx = (dx - ddx) / 2.0d;
+                dy = (dy - ddy) / 2.0d;
 
                 count <<= 1;
                 if (DO_STATS) {
@@ -315,7 +314,7 @@ private void curveBreakIntoLinesAndAdd(float x0, float y0,
         }
     }
 
-    private void addLine(float x1, float y1, float x2, float y2) {
+    private void addLine(double x1, double y1, double x2, double y2) {
         if (DO_MONITORS) {
             rdrCtx.stats.mon_rdr_addLine.start();
         }
@@ -325,7 +324,7 @@ private void addLine(float x1, float y1, float x2, float y2) {
         int or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
         if (y2 < y1) {
             or = 0;
-            float tmp = y2;
+            double tmp = y2;
             y2 = y1;
             y1 = tmp;
             tmp = x2;
@@ -333,7 +332,7 @@ private void addLine(float x1, float y1, float x2, float y2) {
             x1 = tmp;
         }
 
-        // convert subpixel coordinates [float] into pixel positions [int]
+        // convert subpixel coordinates [double] into pixel positions [int]
 
         // The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)
         // Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply
@@ -367,10 +366,7 @@ private void addLine(float x1, float y1, float x2, float y2) {
             edgeMaxY = lastCrossing;
         }
 
-        // Use double-precision for improved accuracy:
-        final double x1d   = x1;
-        final double y1d   = y1;
-        final double slope = (x1d - x2) / (y1d - y2);
+        final double slope = (x1 - x2) / (y1 - y2);
 
         if (slope >= 0.0d) { // <==> x1 < x2
             if (x1 < edgeMinX) {
@@ -437,7 +433,7 @@ private void addLine(float x1, float y1, float x2, float y2) {
         //                 = fixed_floor(x1_fixed + 2^31 - 1)
         //                 = fixed_floor(x1_fixed + 0x7FFFFFFF)
         // and error       = fixed_fract(x1_fixed + 0x7FFFFFFF)
-        final double x1_intercept = x1d + (firstCrossing - y1d) * slope;
+        final double x1_intercept = x1 + (firstCrossing - y1) * slope;
 
         // inlined scalb(x1_intercept, 32):
         final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))
@@ -501,10 +497,10 @@ private void addLine(float x1, float y1, float x2, float y2) {
     private int windingRule;
 
     // Current drawing position, i.e., final point of last segment
-    private float x0, y0;
+    private double x0, y0;
 
     // Position of most recent 'moveTo' command
-    private float sx0, sy0;
+    private double sx0, sy0;
 
     // per-thread renderer context
     final RendererContext rdrCtx;
@@ -594,8 +590,8 @@ Renderer init(final int pix_boundsX, final int pix_boundsY,
 
         edgeMinY = Integer.MAX_VALUE;
         edgeMaxY = Integer.MIN_VALUE;
-        edgeMinX = Float.POSITIVE_INFINITY;
-        edgeMaxX = Float.NEGATIVE_INFINITY;
+        edgeMinX = Double.POSITIVE_INFINITY;
+        edgeMaxX = Double.NEGATIVE_INFINITY;
 
         // reset used mark:
         edgeCount = 0;
@@ -675,23 +671,23 @@ void dispose() {
             rdrCtx.stats.mon_rdr_endRendering.stop();
         }
         // recycle the RendererContext instance
-        MarlinRenderingEngine.returnRendererContext(rdrCtx);
+        DMarlinRenderingEngine.returnRendererContext(rdrCtx);
     }
 
-    private static float tosubpixx(final float pix_x) {
+    private static double tosubpixx(final double pix_x) {
         return SUBPIXEL_SCALE_X * pix_x;
     }
 
-    private static float tosubpixy(final float pix_y) {
+    private static double tosubpixy(final double pix_y) {
         // shift y by -0.5 for fast ceil(y - 0.5):
-        return SUBPIXEL_SCALE_Y * pix_y - 0.5f;
+        return SUBPIXEL_SCALE_Y * pix_y - 0.5d;
     }
 
     @Override
-    public void moveTo(final float pix_x0, final float pix_y0) {
+    public void moveTo(final double pix_x0, final double pix_y0) {
         closePath();
-        final float sx = tosubpixx(pix_x0);
-        final float sy = tosubpixy(pix_y0);
+        final double sx = tosubpixx(pix_x0);
+        final double sy = tosubpixy(pix_y0);
         this.sx0 = sx;
         this.sy0 = sy;
         this.x0 = sx;
@@ -699,21 +695,21 @@ public void moveTo(final float pix_x0, final float pix_y0) {
     }
 
     @Override
-    public void lineTo(final float pix_x1, final float pix_y1) {
-        final float x1 = tosubpixx(pix_x1);
-        final float y1 = tosubpixy(pix_y1);
+    public void lineTo(final double pix_x1, final double pix_y1) {
+        final double x1 = tosubpixx(pix_x1);
+        final double y1 = tosubpixy(pix_y1);
         addLine(x0, y0, x1, y1);
         x0 = x1;
         y0 = y1;
     }
 
     @Override
-    public void curveTo(final float pix_x1, final float pix_y1,
-                        final float pix_x2, final float pix_y2,
-                        final float pix_x3, final float pix_y3)
+    public void curveTo(final double pix_x1, final double pix_y1,
+                        final double pix_x2, final double pix_y2,
+                        final double pix_x3, final double pix_y3)
     {
-        final float xe = tosubpixx(pix_x3);
-        final float ye = tosubpixy(pix_y3);
+        final double xe = tosubpixx(pix_x3);
+        final double ye = tosubpixy(pix_y3);
         curve.set(x0, y0,
                 tosubpixx(pix_x1), tosubpixy(pix_y1),
                 tosubpixx(pix_x2), tosubpixy(pix_y2),
@@ -724,11 +720,11 @@ public void curveTo(final float pix_x1, final float pix_y1,
     }
 
     @Override
-    public void quadTo(final float pix_x1, final float pix_y1,
-                       final float pix_x2, final float pix_y2)
+    public void quadTo(final double pix_x1, final double pix_y1,
+                       final double pix_x2, final double pix_y2)
     {
-        final float xe = tosubpixx(pix_x2);
-        final float ye = tosubpixy(pix_y2);
+        final double xe = tosubpixx(pix_x2);
+        final double ye = tosubpixy(pix_y2);
         curve.set(x0, y0,
                 tosubpixx(pix_x1), tosubpixy(pix_y1),
                 xe, ye);
@@ -739,7 +735,7 @@ public void quadTo(final float pix_x1, final float pix_y1,
 
     @Override
     public void closePath() {
-        if (x0 != sx0 || y0 != sy0) {
+        if ((x0 != sx0) || (y0 != sy0)) {
             addLine(x0, y0, sx0, sy0);
             x0 = sx0;
             y0 = sy0;
@@ -898,7 +894,7 @@ private void _endRendering(final int ymin, final int ymax) {
                             rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd);
                         }
                         this.edgePtrs = _edgePtrs
-                            = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // TODO FIX mark
+                            = edgePtrs_ref.widenArray(_edgePtrs, edgePtrsLen, // bad mark ? TODO: fix edge ptr mark
                                                       ptrEnd);
 
                         edgePtrsLen = _edgePtrs.length;
@@ -1443,8 +1439,8 @@ boolean endRendering() {
         }
 
         // bounds as half-open intervals
-        final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5f), boundsMinX);
-        final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5f), boundsMaxX);
+        final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX);
+        final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX);
 
         // edge Min/Max Y are already rounded to subpixels within bounds:
         final int spminY = edgeMinY;
diff --git a/src/main/java/sun/java2d/marlin/RendererContext.java b/src/main/java/sun/java2d/marlin/RendererContext.java
index f27c599..216c97a 100644
--- a/src/main/java/sun/java2d/marlin/RendererContext.java
+++ b/src/main/java/sun/java2d/marlin/RendererContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021, 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
@@ -30,14 +30,14 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import sun.java2d.ReentrantContext;
 import sun.java2d.marlin.ArrayCacheConst.CacheStats;
-import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
+import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
 
 /**
  * This class is a renderer context dedicated to a single thread
  */
-final class RendererContext extends ReentrantContext implements IRendererContext {
+final class RendererContext extends ReentrantContext implements MarlinConst {
 
     // RendererContext creation counter
     private static final AtomicInteger CTX_COUNT = new AtomicInteger(1);
@@ -48,16 +48,15 @@ final class RendererContext extends ReentrantContext implements IRendererContext
      * @return new RendererContext instance
      */
     static RendererContext createContext() {
-        return new RendererContext("ctx"
-                       + Integer.toString(CTX_COUNT.getAndIncrement()));
+        return new RendererContext("ctx" + CTX_COUNT.getAndIncrement());
     }
 
     // Smallest object used as Cleaner's parent reference
     private final Object cleanerObj;
-    // dirty flag indicating an exception occured during pipeline in pathTo()
+    // dirty flag indicating an exception occurred during pipeline in pathTo()
     boolean dirty = false;
     // shared data
-    final float[] float6 = new float[6];
+    final double[] double6 = new double[6];
     // shared curve (dirty) (Renderer / Stroker)
     final Curve curve = new Curve();
     // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
@@ -67,7 +66,7 @@ static RendererContext createContext() {
     // MarlinRenderingEngine.TransformingPathConsumer2D
     final TransformingPathConsumer2D transformerPC2D; // TODO: clear if dirty ?
     // recycled Path2D instance (weak)
-    private WeakReference<Path2D.Float> refPath2D = null;
+    private WeakReference<Path2D.Double> refPath2D = null;
     final Renderer renderer;
     final Stroker stroker;
     // Simplifies out collinear lines
@@ -84,9 +83,9 @@ static RendererContext createContext() {
     // flag indicating if the path is closed or not (in advance) to handle properly caps
     boolean closedPath = false;
     // clip rectangle (ymin, ymax, xmin, xmax):
-    final float[] clipRect = new float[4];
+    final double[] clipRect = new double[4];
     // clip inverse scale (mean) to adjust length checks
-    float clipInvScale = 0.0f;
+    double clipInvScale = 0.0d;
     // CurveBasicMonotonizer instance
     final CurveBasicMonotonizer monotonizer;
     // flag indicating to force the stroker to process joins
@@ -101,14 +100,16 @@ static RendererContext createContext() {
     private final ArrayCacheIntClean cleanIntCache = new ArrayCacheIntClean(5);
     /* dirty int[] cache = 5 refs */
     private final ArrayCacheInt dirtyIntCache = new ArrayCacheInt(5);
-    /* dirty float[] cache = 4 refs (2 polystack) */
-    private final ArrayCacheFloat dirtyFloatCache = new ArrayCacheFloat(4);
+    /* dirty double[] cache = 4 refs (2 polystack) */
+    private final ArrayCacheDouble dirtyDoubleCache = new ArrayCacheDouble(4);
     /* dirty byte[] cache = 2 ref (2 polystack) */
     private final ArrayCacheByte dirtyByteCache = new ArrayCacheByte(2);
 
     // RendererContext statistics
     final RendererStats stats;
 
+    final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter();
+
     /**
      * Constructor
      *
@@ -125,15 +126,15 @@ static RendererContext createContext() {
             stats = RendererStats.createInstance(cleanerObj, name);
             // push cache stats:
             stats.cacheStats = new CacheStats[] { cleanIntCache.stats,
-                dirtyIntCache.stats, dirtyFloatCache.stats, dirtyByteCache.stats
+                dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats
             };
         } else {
             stats = null;
         }
 
         // NormalizingPathIterator instances:
-        nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6);
-        nPQPathIterator  = new NormalizingPathIterator.NearestPixelQuarter(float6);
+        nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6);
+        nPQPathIterator  = new NormalizingPathIterator.NearestPixelQuarter(double6);
 
         // curve monotonizer & clip subdivider (before transformerPC2D init)
         monotonizer = new CurveBasicMonotonizer(this);
@@ -167,7 +168,7 @@ void dispose() {
         stroking   = 0;
         doClip     = false;
         closedPath = false;
-        clipInvScale = 0.0f;
+        clipInvScale = 0.0d;
         isFirstSegment = true;
 
         // if context is maked as DIRTY:
@@ -188,37 +189,34 @@ void dispose() {
         }
     }
 
-    Path2D.Float getPath2D() {
+    Path2D.Double getPath2D() {
         // resolve reference:
-        Path2D.Float p2d = (refPath2D != null) ? refPath2D.get() : null;
+        Path2D.Double p2d = (refPath2D != null) ? refPath2D.get() : null;
 
         // create a new Path2D ?
         if (p2d == null) {
-            p2d = new Path2D.Float(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
+            p2d = new Path2D.Double(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
 
             // update weak reference:
-            refPath2D = new WeakReference<Path2D.Float>(p2d);
+            refPath2D = new WeakReference<Path2D.Double>(p2d);
         }
         // reset the path anyway:
         p2d.reset();
         return p2d;
     }
 
-    @Override
-    public RendererStats stats() {
+    RendererStats stats() {
         return stats;
     }
 
-    @Override
-    public OffHeapArray newOffHeapArray(final long initialSize) {
+    OffHeapArray newOffHeapArray(final long initialSize) {
         if (DO_STATS) {
             stats.totalOffHeapInitial += initialSize;
         }
         return new OffHeapArray(cleanerObj, initialSize);
     }
 
-    @Override
-    public ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) {
+    ArrayCacheIntClean.Reference newCleanIntArrayRef(final int initialSize) {
         return cleanIntCache.createRef(initialSize);
     }
 
@@ -226,11 +224,64 @@ ArrayCacheInt.Reference newDirtyIntArrayRef(final int initialSize) {
         return dirtyIntCache.createRef(initialSize);
     }
 
-    ArrayCacheFloat.Reference newDirtyFloatArrayRef(final int initialSize) {
-        return dirtyFloatCache.createRef(initialSize);
+    ArrayCacheDouble.Reference newDirtyDoubleArrayRef(final int initialSize) {
+        return dirtyDoubleCache.createRef(initialSize);
     }
 
     ArrayCacheByte.Reference newDirtyByteArrayRef(final int initialSize) {
         return dirtyByteCache.createRef(initialSize);
     }
+
+    static final class PathConsumer2DAdapter implements DPathConsumer2D {
+        private sun.awt.geom.PathConsumer2D out;
+
+        PathConsumer2DAdapter() {}
+
+        PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) {
+            if (this.out != out) {
+                this.out = out;
+            }
+            return this;
+        }
+
+        @Override
+        public void moveTo(double x0, double y0) {
+            out.moveTo((float)x0, (float)y0);
+        }
+
+        @Override
+        public void lineTo(double x1, double y1) {
+            out.lineTo((float)x1, (float)y1);
+        }
+
+        @Override
+        public void closePath() {
+            out.closePath();
+        }
+
+        @Override
+        public void pathDone() {
+            out.pathDone();
+        }
+
+        @Override
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
+        {
+            out.curveTo((float)x1, (float)y1,
+                    (float)x2, (float)y2,
+                    (float)x3, (float)y3);
+        }
+
+        @Override
+        public void quadTo(double x1, double y1, double x2, double y2) {
+            out.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
+        }
+
+        @Override
+        public long getNativeConsumer() {
+            throw new InternalError("Not using a native peer");
+        }
+    }
 }
diff --git a/src/main/java/sun/java2d/marlin/Stroker.java b/src/main/java/sun/java2d/marlin/Stroker.java
index 9795947..d781961 100644
--- a/src/main/java/sun/java2d/marlin/Stroker.java
+++ b/src/main/java/sun/java2d/marlin/Stroker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -25,65 +25,63 @@
 
 package sun.java2d.marlin;
 
+import sun.java2d.marlin.debug.MarlinDebugThreadLocal;
 import java.util.Arrays;
-
-import sun.awt.geom.PathConsumer2D;
 import sun.java2d.marlin.Helpers.PolyStack;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
 import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
-import sun.java2d.marlin.debug.MarlinDebugThreadLocal;
 
 // TODO: some of the arithmetic here is too verbose and prone to hard to
 // debug typos. We should consider making a small Point/Vector class that
 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
-final class Stroker implements PathConsumer2D, MarlinConst {
+final class Stroker implements DPathConsumer2D, MarlinConst {
 
     private static final int MOVE_TO = 0;
     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
     private static final int CLOSE = 2;
 
     // round join threshold = 1 subpixel
-    private static final float ERR_JOIN = (1.0f / MIN_SUBPIXELS);
-    private static final float ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
+    private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS);
+    private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
 
     // kappa = (4/3) * (SQRT(2) - 1)
-    private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+    private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
 
     // SQRT(2)
-    private static final float SQRT_2 = (float)Math.sqrt(2.0d);
+    private static final double SQRT_2 = Math.sqrt(2.0d);
 
-    private PathConsumer2D out;
+    private DPathConsumer2D out;
 
     private int capStyle;
     private int joinStyle;
 
-    private float lineWidth2;
-    private float invHalfLineWidth2Sq;
+    private double lineWidth2;
+    private double invHalfLineWidth2Sq;
 
-    private final float[] offset0 = new float[2];
-    private final float[] offset1 = new float[2];
-    private final float[] offset2 = new float[2];
-    private final float[] miter = new float[2];
-    private float miterLimitSq;
+    private final double[] offset0 = new double[2];
+    private final double[] offset1 = new double[2];
+    private final double[] offset2 = new double[2];
+    private final double[] miter = new double[2];
+    private double miterLimitSq;
 
     private int prev;
 
     // The starting point of the path, and the slope there.
-    private float sx0, sy0, sdx, sdy;
+    private double sx0, sy0, sdx, sdy;
     // the current point and the slope there.
-    private float cx0, cy0, cdx, cdy; // c stands for current
+    private double cx0, cy0, cdx, cdy; // c stands for current
     // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
     // first and last points on the left parallel path. Since this path is
     // parallel, it's slope at any point is parallel to the slope of the
     // original path (thought they may have different directions), so these
     // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
     // would be error prone and hard to read, so we keep these anyway.
-    private float smx, smy, cmx, cmy;
+    private double smx, smy, cmx, cmy;
 
     private final PolyStack reverse;
 
-    private final float[] lp = new float[8];
-    private final float[] rp = new float[8];
+    private final double[] lp = new double[8];
+    private final double[] rp = new double[8];
 
     // per-thread renderer context
     final RendererContext rdrCtx;
@@ -92,7 +90,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
     final Curve curve;
 
     // Bounds of the drawing region, at pixel precision.
-    private float[] clipRect;
+    private double[] clipRect;
 
     // the outcode of the current point
     private int cOutCode = 0;
@@ -107,7 +105,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
     // flag indicating to monotonize curves
     private boolean monotonize;
 
-    private boolean subdivide = DO_CLIP_SUBDIVIDER;
+    private boolean subdivide = false;
     private final CurveClipSplitter curveSplitter;
 
     /**
@@ -133,7 +131,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
     /**
      * Inits the <code>Stroker</code>.
      *
-     * @param pc2d an output <code>PathConsumer2D</code>.
+     * @param pc2d an output <code>DPathConsumer2D</code>.
      * @param lineWidth the desired line width in pixels
      * @param capStyle the desired end cap style, one of
      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
@@ -145,25 +143,25 @@ final class Stroker implements PathConsumer2D, MarlinConst {
      * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
      * @return this instance
      */
-    Stroker init(final PathConsumer2D pc2d,
-                 final float lineWidth,
-                 final int capStyle,
-                 final int joinStyle,
-                 final float miterLimit,
-                 final boolean subdivideCurves)
+    Stroker init(final DPathConsumer2D pc2d,
+                  final double lineWidth,
+                  final int capStyle,
+                  final int joinStyle,
+                  final double miterLimit,
+                  final boolean subdivideCurves)
     {
         if (this.out != pc2d) {
             this.out = pc2d;
         }
 
-        this.lineWidth2 = lineWidth / 2.0f;
-        this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
+        this.lineWidth2 = lineWidth / 2.0d;
+        this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
         this.monotonize = subdivideCurves;
 
         this.capStyle = capStyle;
         this.joinStyle = joinStyle;
 
-        final float limit = miterLimit * lineWidth2;
+        final double limit = miterLimit * lineWidth2;
         this.miterLimitSq = limit * limit;
 
         this.prev = CLOSE;
@@ -172,7 +170,7 @@ Stroker init(final PathConsumer2D pc2d,
 
         if (rdrCtx.doClip) {
             // Adjust the clipping rectangle with the stroker margin (miter limit, width)
-            float margin = lineWidth2;
+            double margin = lineWidth2;
 
             if (capStyle == CAP_SQUARE) {
                 margin *= SQRT_2;
@@ -183,7 +181,7 @@ Stroker init(final PathConsumer2D pc2d,
 
             // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
             // adjust clip rectangle (ymin, ymax, xmin, xmax):
-            final float[] _clipRect = rdrCtx.clipRect;
+            final double[] _clipRect = rdrCtx.clipRect;
             _clipRect[0] -= margin;
             _clipRect[1] += margin;
             _clipRect[2] -= margin;
@@ -229,24 +227,24 @@ void dispose() {
 
         if (DO_CLEAN_DIRTY) {
             // Force zero-fill dirty arrays:
-            Arrays.fill(offset0, 0.0f);
-            Arrays.fill(offset1, 0.0f);
-            Arrays.fill(offset2, 0.0f);
-            Arrays.fill(miter, 0.0f);
-            Arrays.fill(lp, 0.0f);
-            Arrays.fill(rp, 0.0f);
+            Arrays.fill(offset0, 0.0d);
+            Arrays.fill(offset1, 0.0d);
+            Arrays.fill(offset2, 0.0d);
+            Arrays.fill(miter, 0.0d);
+            Arrays.fill(lp, 0.0d);
+            Arrays.fill(rp, 0.0d);
         }
     }
 
-    private static void computeOffset(final float lx, final float ly,
-                                      final float w, final float[] m)
+    private static void computeOffset(final double lx, final double ly,
+                                      final double w, final double[] m)
     {
-        float len = lx*lx + ly*ly;
-        if (len == 0.0f) {
-            m[0] = 0.0f;
-            m[1] = 0.0f;
+        double len = lx*lx + ly*ly;
+        if (len == 0.0d) {
+            m[0] = 0.0d;
+            m[1] = 0.0d;
         } else {
-            len = (float) Math.sqrt(len);
+            len = Math.sqrt(len);
             m[0] =  (ly * w) / len;
             m[1] = -(lx * w) / len;
         }
@@ -260,24 +258,24 @@ private static void computeOffset(final float lx, final float ly,
     // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
     // clockwise order.
     // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
-    private static boolean isCW(final float dx1, final float dy1,
-                                final float dx2, final float dy2)
+    private static boolean isCW(final double dx1, final double dy1,
+                                final double dx2, final double dy2)
     {
         return dx1 * dy2 <= dy1 * dx2;
     }
 
-    private void mayDrawRoundJoin(float cx, float cy,
-                                  float omx, float omy,
-                                  float mx, float my,
+    private void mayDrawRoundJoin(double cx, double cy,
+                                  double omx, double omy,
+                                  double mx, double my,
                                   boolean rev)
     {
-        if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
+        if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) {
             return;
         }
 
-        final float domx = omx - mx;
-        final float domy = omy - my;
-        final float lenSq = domx*domx + domy*domy;
+        final double domx = omx - mx;
+        final double domy = omy - my;
+        final double lenSq = domx*domx + domy*domy;
 
         if (lenSq < ROUND_JOIN_THRESHOLD) {
             return;
@@ -292,19 +290,19 @@ private void mayDrawRoundJoin(float cx, float cy,
         drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
     }
 
-    private void drawRoundJoin(float cx, float cy,
-                               float omx, float omy,
-                               float mx, float my,
+    private void drawRoundJoin(double cx, double cy,
+                               double omx, double omy,
+                               double mx, double my,
                                boolean rev)
     {
         // The sign of the dot product of mx,my and omx,omy is equal to the
         // the sign of the cosine of ext
         // (ext is the angle between omx,omy and mx,my).
-        final float cosext = omx * mx + omy * my;
+        final double cosext = omx * mx + omy * my;
         // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
         // need 1 curve to approximate the circle section that joins omx,omy
         // and mx,my.
-        if (cosext >= 0.0f) {
+        if (cosext >= 0.0d) {
             drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
         } else {
             // we need to split the arc into 2 arcs spanning the same angle.
@@ -321,10 +319,10 @@ private void drawRoundJoin(float cx, float cy,
             // have numerical problems because we know that lineWidth2 divided by
             // this normal's length is at least 0.5 and at most sqrt(2)/2 (because
             // we know the angle of the arc is > 90 degrees).
-            float nx = my - omy, ny = omx - mx;
-            float nlen = (float) Math.sqrt(nx*nx + ny*ny);
-            float scale = lineWidth2/nlen;
-            float mmx = nx * scale, mmy = ny * scale;
+            double nx = my - omy, ny = omx - mx;
+            double nlen = Math.sqrt(nx*nx + ny*ny);
+            double scale = lineWidth2/nlen;
+            double mmx = nx * scale, mmy = ny * scale;
 
             // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
             // computed the wrong intersection so we get the other one.
@@ -339,16 +337,16 @@ private void drawRoundJoin(float cx, float cy,
     }
 
     // the input arc defined by omx,omy and mx,my must span <= 90 degrees.
-    private void drawBezApproxForArc(final float cx, final float cy,
-                                     final float omx, final float omy,
-                                     final float mx, final float my,
+    private void drawBezApproxForArc(final double cx, final double cy,
+                                     final double omx, final double omy,
+                                     final double mx, final double my,
                                      boolean rev)
     {
-        final float cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq;
+        final double cosext2 = (omx * mx + omy * my) * invHalfLineWidth2Sq;
 
         // check round off errors producing cos(ext) > 1 and a NaN below
         // cos(ext) == 1 implies colinear segments and an empty join anyway
-        if (cosext2 >= 0.5f) {
+        if (cosext2 >= 0.5d) {
             // just return to avoid generating a flat curve:
             return;
         }
@@ -358,28 +356,28 @@ private void drawBezApproxForArc(final float cx, final float cy,
         // define the bezier curve we're computing.
         // It is computed using the constraints that P1-P0 and P3-P2 are parallel
         // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
-        float cv = (float) ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) /
+        double cv = ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) /
                             (1.0d + Math.sqrt(cosext2 + 0.5d)));
         // if clockwise, we need to negate cv.
         if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
             cv = -cv;
         }
-        final float x1 = cx + omx;
-        final float y1 = cy + omy;
-        final float x2 = x1 - cv * omy;
-        final float y2 = y1 + cv * omx;
+        final double x1 = cx + omx;
+        final double y1 = cy + omy;
+        final double x2 = x1 - cv * omy;
+        final double y2 = y1 + cv * omx;
 
-        final float x4 = cx + mx;
-        final float y4 = cy + my;
-        final float x3 = x4 + cv * my;
-        final float y3 = y4 - cv * mx;
+        final double x4 = cx + mx;
+        final double y4 = cy + my;
+        final double x3 = x4 + cv * my;
+        final double y3 = y4 - cv * mx;
 
         emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
     }
 
-    private void drawRoundCap(float cx, float cy, float mx, float my) {
-        final float Cmx = C * mx;
-        final float Cmy = C * my;
+    private void drawRoundCap(double cx, double cy, double mx, double my) {
+        final double Cmx = C * mx;
+        final double Cmy = C * my;
         emitCurveTo(cx + mx - Cmy, cy + my + Cmx,
                     cx - my + Cmx, cy + mx + Cmy,
                     cx - my,       cy + mx);
@@ -390,16 +388,16 @@ private void drawRoundCap(float cx, float cy, float mx, float my) {
 
     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
-    private static void computeMiter(final float x0, final float y0,
-                                     final float x1, final float y1,
-                                     final float x0p, final float y0p,
-                                     final float x1p, final float y1p,
-                                     final float[] m)
+    private static void computeMiter(final double x0, final double y0,
+                                     final double x1, final double y1,
+                                     final double x0p, final double y0p,
+                                     final double x1p, final double y1p,
+                                     final double[] m)
     {
-        float x10 = x1 - x0;
-        float y10 = y1 - y0;
-        float x10p = x1p - x0p;
-        float y10p = y1p - y0p;
+        double x10 = x1 - x0;
+        double y10 = y1 - y0;
+        double x10p = x1p - x0p;
+        double y10p = y1p - y0p;
 
         // if this is 0, the lines are parallel. If they go in the
         // same direction, there is no intersection so m[off] and
@@ -410,8 +408,8 @@ private static void computeMiter(final float x0, final float y0,
         // miter drawing because it won't be called by drawMiter (because
         // (mx == omx && my == omy) will be true, and drawMiter will return
         // immediately).
-        float den = x10*y10p - x10p*y10;
-        float t = x10p*(y0-y0p) - y10p*(x0-x0p);
+        double den = x10*y10p - x10p*y10;
+        double t = x10p*(y0-y0p) - y10p*(x0-x0p);
         t /= den;
         m[0] = x0 + t*x10;
         m[1] = y0 + t*y10;
@@ -419,16 +417,16 @@ private static void computeMiter(final float x0, final float y0,
 
     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
-    private static void safeComputeMiter(final float x0, final float y0,
-                                         final float x1, final float y1,
-                                         final float x0p, final float y0p,
-                                         final float x1p, final float y1p,
-                                         final float[] m)
+    private static void safeComputeMiter(final double x0, final double y0,
+                                         final double x1, final double y1,
+                                         final double x0p, final double y0p,
+                                         final double x1p, final double y1p,
+                                         final double[] m)
     {
-        float x10 = x1 - x0;
-        float y10 = y1 - y0;
-        float x10p = x1p - x0p;
-        float y10p = y1p - y0p;
+        double x10 = x1 - x0;
+        double y10 = y1 - y0;
+        double x10p = x1p - x0p;
+        double y10p = y1p - y0p;
 
         // if this is 0, the lines are parallel. If they go in the
         // same direction, there is no intersection so m[off] and
@@ -439,28 +437,28 @@ private static void safeComputeMiter(final float x0, final float y0,
         // miter drawing because it won't be called by drawMiter (because
         // (mx == omx && my == omy) will be true, and drawMiter will return
         // immediately).
-        float den = x10*y10p - x10p*y10;
-        if (den == 0.0f) {
-            m[2] = (x0 + x0p) / 2.0f;
-            m[3] = (y0 + y0p) / 2.0f;
+        double den = x10*y10p - x10p*y10;
+        if (den == 0.0d) {
+            m[2] = (x0 + x0p) / 2.0d;
+            m[3] = (y0 + y0p) / 2.0d;
         } else {
-            float t = x10p*(y0-y0p) - y10p*(x0-x0p);
+            double t = x10p*(y0-y0p) - y10p*(x0-x0p);
             t /= den;
             m[2] = x0 + t*x10;
             m[3] = y0 + t*y10;
         }
     }
 
-    private void drawMiter(final float pdx, final float pdy,
-                           final float x0, final float y0,
-                           final float dx, final float dy,
-                           float omx, float omy,
-                           float mx, float my,
+    private void drawMiter(final double pdx, final double pdy,
+                           final double x0, final double y0,
+                           final double dx, final double dy,
+                           double omx, double omy,
+                           double mx, double my,
                            boolean rev)
     {
         if ((mx == omx && my == omy) ||
-            (pdx == 0.0f && pdy == 0.0f) ||
-            (dx == 0.0f && dy == 0.0f))
+            (pdx == 0.0d && pdy == 0.0d) ||
+            (dx == 0.0d && dy == 0.0d))
         {
             return;
         }
@@ -475,9 +473,9 @@ private void drawMiter(final float pdx, final float pdy,
         computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
                      (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
 
-        final float miterX = miter[0];
-        final float miterY = miter[1];
-        float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
+        final double miterX = miter[0];
+        final double miterY = miter[1];
+        double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
 
         // If the lines are parallel, lenSq will be either NaN or +inf
         // (actually, I'm not sure if the latter is possible. The important
@@ -490,13 +488,13 @@ private void drawMiter(final float pdx, final float pdy,
     }
 
     @Override
-    public void moveTo(final float x0, final float y0) {
+    public void moveTo(final double x0, final double y0) {
         _moveTo(x0, y0, cOutCode);
         // update starting point:
         this.sx0 = x0;
         this.sy0 = y0;
-        this.sdx = 1.0f;
-        this.sdy = 0.0f;
+        this.sdx = 1.0d;
+        this.sdy = 0.0d;
         this.opened   = false;
         this.capStart = false;
 
@@ -507,7 +505,7 @@ public void moveTo(final float x0, final float y0) {
         }
     }
 
-    private void _moveTo(final float x0, final float y0,
+    private void _moveTo(final double x0, final double y0,
                         final int outcode)
     {
         if (prev == MOVE_TO) {
@@ -520,13 +518,13 @@ private void _moveTo(final float x0, final float y0,
             this.prev = MOVE_TO;
             this.cx0 = x0;
             this.cy0 = y0;
-            this.cdx = 1.0f;
-            this.cdy = 0.0f;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
         }
     }
 
     @Override
-    public void lineTo(final float x1, final float y1) {
+    public void lineTo(final double x1, final double y1) {
         final int outcode0 = this.cOutCode;
 
         if (clipRect != null) {
@@ -564,14 +562,14 @@ public void lineTo(final float x1, final float y1) {
             this.cOutCode = outcode1;
         }
 
-        float dx = x1 - cx0;
-        float dy = y1 - cy0;
-        if (dx == 0.0f && dy == 0.0f) {
-            dx = 1.0f;
+        double dx = x1 - cx0;
+        double dy = y1 - cy0;
+        if (dx == 0.0d && dy == 0.0d) {
+            dx = 1.0d;
         }
         computeOffset(dx, dy, lineWidth2, offset0);
-        final float mx = offset0[0];
-        final float my = offset0[1];
+        final double mx = offset0[0];
+        final double my = offset0[1];
 
         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
 
@@ -599,14 +597,14 @@ public void closePath() {
             }
             emitMoveTo(cx0, cy0 - lineWidth2);
 
-            this.sdx = 1.0f;
-            this.sdy = 0.0f;
-            this.cdx = 1.0f;
-            this.cdy = 0.0f;
+            this.sdx = 1.0d;
+            this.sdy = 0.0d;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
 
-            this.smx = 0.0f;
+            this.smx = 0.0d;
             this.smy = -lineWidth2;
-            this.cmx = 0.0f;
+            this.cmx = 0.0d;
             this.cmy = -lineWidth2;
 
             finish(cOutCode);
@@ -615,7 +613,7 @@ public void closePath() {
 
         // basic acceptance criteria
         if ((sOutCode & cOutCode) == 0) {
-            if (cx0 != sx0 || cy0 != sy0) {
+            if ((cx0 != sx0) || (cy0 != sy0)) {
                 // may subdivide line:
                 lineTo(sx0, sy0);
             }
@@ -705,19 +703,19 @@ private void finish(final int outcode) {
         emitClose();
     }
 
-    private void emitMoveTo(final float x0, final float y0) {
+    private void emitMoveTo(final double x0, final double y0) {
         out.moveTo(x0, y0);
     }
 
-    private void emitLineTo(final float x1, final float y1) {
+    private void emitLineTo(final double x1, final double y1) {
         out.lineTo(x1, y1);
     }
 
-    private void emitLineToRev(final float x1, final float y1) {
+    private void emitLineToRev(final double x1, final double y1) {
         reverse.pushLine(x1, y1);
     }
 
-    private void emitLineTo(final float x1, final float y1,
+    private void emitLineTo(final double x1, final double y1,
                             final boolean rev)
     {
         if (rev) {
@@ -727,36 +725,36 @@ private void emitLineTo(final float x1, final float y1,
         }
     }
 
-    private void emitQuadTo(final float x1, final float y1,
-                            final float x2, final float y2)
+    private void emitQuadTo(final double x1, final double y1,
+                            final double x2, final double y2)
     {
         out.quadTo(x1, y1, x2, y2);
     }
 
-    private void emitQuadToRev(final float x0, final float y0,
-                               final float x1, final float y1)
+    private void emitQuadToRev(final double x0, final double y0,
+                               final double x1, final double y1)
     {
         reverse.pushQuad(x0, y0, x1, y1);
     }
 
-    private void emitCurveTo(final float x1, final float y1,
-                             final float x2, final float y2,
-                             final float x3, final float y3)
+    private void emitCurveTo(final double x1, final double y1,
+                             final double x2, final double y2,
+                             final double x3, final double y3)
     {
         out.curveTo(x1, y1, x2, y2, x3, y3);
     }
 
-    private void emitCurveToRev(final float x0, final float y0,
-                                final float x1, final float y1,
-                                final float x2, final float y2)
+    private void emitCurveToRev(final double x0, final double y0,
+                                final double x1, final double y1,
+                                final double x2, final double y2)
     {
         reverse.pushCubic(x0, y0, x1, y1, x2, y2);
     }
 
-    private void emitCurveTo(final float x0, final float y0,
-                             final float x1, final float y1,
-                             final float x2, final float y2,
-                             final float x3, final float y3, final boolean rev)
+    private void emitCurveTo(final double x0, final double y0,
+                             final double x1, final double y1,
+                             final double x2, final double y2,
+                             final double x3, final double y3, final boolean rev)
     {
         if (rev) {
             reverse.pushCubic(x0, y0, x1, y1, x2, y2);
@@ -769,11 +767,11 @@ private void emitClose() {
         out.closePath();
     }
 
-    private void drawJoin(float pdx, float pdy,
-                          float x0, float y0,
-                          float dx, float dy,
-                          float omx, float omy,
-                          float mx, float my,
+    private void drawJoin(double pdx, double pdy,
+                          double x0, double y0,
+                          double dx, double dy,
+                          double omx, double omy,
+                          double mx, double my,
                           final int outcode)
     {
         if (prev != DRAWING_OP_TO) {
@@ -800,17 +798,16 @@ private void drawJoin(float pdx, float pdy,
             // reset trigger to process further joins (normal operations)
             rdrCtx.isFirstSegment = true;
         }
-
         prev = DRAWING_OP_TO;
     }
 
-    private int getLineOffsets(final float x1, final float y1,
-                               final float x2, final float y2,
-                               final float[] left, final float[] right)
+    private int getLineOffsets(final double x1, final double y1,
+                               final double x2, final double y2,
+                               final double[] left, final double[] right)
     {
         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
-        final float mx = offset0[0];
-        final float my = offset0[1];
+        final double mx = offset0[0];
+        final double my = offset0[1];
         left[0] = x1 + mx;
         left[1] = y1 + my;
         left[2] = x2 + mx;
@@ -824,9 +821,9 @@ private int getLineOffsets(final float x1, final float y1,
         return 4;
     }
 
-    private int computeOffsetCubic(final float[] pts, final int off,
-                                   final float[] leftOff,
-                                   final float[] rightOff)
+    private int computeOffsetCubic(final double[] pts, final int off,
+                                   final double[] leftOff,
+                                   final double[] rightOff)
     {
         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
         // vanishes, which creates problems with computeOffset. Usually
@@ -835,18 +832,18 @@ private int computeOffsetCubic(final float[] pts, final int off,
         // the input curve at the cusp, and passes it to this function.
         // because of inaccuracies in the splitting, we consider points
         // equal if they're very close to each other.
-        final float x1 = pts[off    ]; final float y1 = pts[off + 1];
-        final float x2 = pts[off + 2]; final float y2 = pts[off + 3];
-        final float x3 = pts[off + 4]; final float y3 = pts[off + 5];
-        final float x4 = pts[off + 6]; final float  y4 = pts[off + 7];
+        final double x1 = pts[off    ]; final double y1 = pts[off + 1];
+        final double x2 = pts[off + 2]; final double y2 = pts[off + 3];
+        final double x3 = pts[off + 4]; final double y3 = pts[off + 5];
+        final double x4 = pts[off + 6]; final double y4 = pts[off + 7];
 
-        float dx1 = x2 - x1; float dy1 = y2 - y1;
-        float dx4 = x4 - x3; float dy4 = y4 - y3;
+        double dx1 = x2 - x1; double dy1 = y2 - y1;
+        double dx4 = x4 - x3; double dy4 = y4 - y3;
 
         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
         // in which case ignore if p1 == p2
-        final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0f * Math.ulp(y2));
-        final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0f * Math.ulp(y4));
+        final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2));
+        final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4));
 
         if (p1eqp2 && p3eqp4) {
             return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
@@ -859,12 +856,12 @@ private int computeOffsetCubic(final float[] pts, final int off,
         }
 
         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
-        float dotsq = (dx1 * dx4 + dy1 * dy4);
+        double dotsq = (dx1 * dx4 + dy1 * dy4);
         dotsq *= dotsq;
-        final float l1sq = dx1 * dx1 + dy1 * dy1;
-        final float l4sq = dx4 * dx4 + dy4 * dy4;
+        final double l1sq = dx1 * dx1 + dy1 * dy1;
+        final double l4sq = dx4 * dx4 + dy4 * dy4;
 
-        if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) {
+        if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
             return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
         }
 
@@ -915,12 +912,12 @@ private int computeOffsetCubic(final float[] pts, final int off,
 //      getting the inverse of the matrix above. Then we use [c1,c2] to compute
 //      p2p and p3p.
 
-        final float xm = (x1 + x4 + 3.0f * (x2 + x3)) / 8.0f;
-        final float ym = (y1 + y4 + 3.0f * (y2 + y3)) / 8.0f;
+        final double xm = (x1 + x4 + 3.0d * (x2 + x3)) / 8.0d;
+        final double ym = (y1 + y4 + 3.0d * (y2 + y3)) / 8.0d;
         // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
         // c*B'(0.5) for some constant c.
-        final float dxm = x3 + x4 - (x1 + x2);
-        final float dym = y3 + y4 - (y1 + y2);
+        final double dxm = x3 + x4 - (x1 + x2);
+        final double dym = y3 + y4 - (y1 + y2);
 
         // this computes the offsets at t=0, 0.5, 1, using the property that
         // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
@@ -930,12 +927,12 @@ private int computeOffsetCubic(final float[] pts, final int off,
         computeOffset(dx4, dy4, lineWidth2, offset2);
 
         // left side:
-        float x1p = x1 + offset0[0]; // start
-        float y1p = y1 + offset0[1]; // point
-        float xi  = xm  + offset1[0]; // interpolation
-        float yi  = ym  + offset1[1]; // point
-        float x4p = x4 + offset2[0]; // end
-        float y4p = y4 + offset2[1]; // point
+        double x1p = x1 + offset0[0]; // start
+        double y1p = y1 + offset0[1]; // point
+        double xi  = xm + offset1[0]; // interpolation
+        double yi  = ym + offset1[1]; // point
+        double x4p = x4 + offset2[0]; // end
+        double y4p = y4 + offset2[1]; // point
 
 if (false) {
         final MarlinDebugThreadLocal dbgCtx = MarlinDebugThreadLocal.get();
@@ -943,15 +940,15 @@ private int computeOffsetCubic(final float[] pts, final int off,
         dbgCtx.addPoint(xi, yi);
 }
 
-        final float invdet43 = 4.0f / (3.0f * (dx1 * dy4 - dy1 * dx4));
+        final double invdet43 = 4.0d / (3.0d * (dx1 * dy4 - dy1 * dx4));
 
-        float two_pi_m_p1_m_p4x = 2.0f * xi - (x1p + x4p);
-        float two_pi_m_p1_m_p4y = 2.0f * yi - (y1p + y4p);
+        double two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p);
+        double two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p);
 
-        float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
-        float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
+        double c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
+        double c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
 
-        float x2p, y2p, x3p, y3p;
+        double x2p, y2p, x3p, y3p;
 
         if (c1 * c2 > 0.0) {
 //            System.out.println("Buggy solver (left): c1 = " + c1 + " c2 = " + c2);
@@ -992,8 +989,8 @@ private int computeOffsetCubic(final float[] pts, final int off,
         dbgCtx.addPoint(xi, yi);
 }
 
-        two_pi_m_p1_m_p4x = 2.0f * xi - (x1p + x4p);
-        two_pi_m_p1_m_p4y = 2.0f * yi - (y1p + y4p);
+        two_pi_m_p1_m_p4x = 2.0d * xi - (x1p + x4p);
+        two_pi_m_p1_m_p4y = 2.0d * yi - (y1p + y4p);
 
         c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
         c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
@@ -1029,24 +1026,24 @@ private int computeOffsetCubic(final float[] pts, final int off,
     // compute offset curves using bezier spline through t=0.5 (i.e.
     // ComputedCurve(0.5) == IdealParallelCurve(0.5))
     // return the kind of curve in the right and left arrays.
-    private int computeOffsetQuad(final float[] pts, final int off,
-                                  final float[] leftOff,
-                                  final float[] rightOff)
+    private int computeOffsetQuad(final double[] pts, final int off,
+                                  final double[] leftOff,
+                                  final double[] rightOff)
     {
         return computeOffsetQuad(pts, off, leftOff, rightOff, true);
     }
 
-    private int computeOffsetQuad(final float[] pts, final int off,
-                                  final float[] leftOff,
-                                  final float[] rightOff,
+    private int computeOffsetQuad(final double[] pts, final int off,
+                                  final double[] leftOff,
+                                  final double[] rightOff,
                                   final boolean checkCtrlPoints)
     {
-        final float x1 = pts[off    ]; final float y1 = pts[off + 1];
-        final float x2 = pts[off + 2]; final float y2 = pts[off + 3];
-        final float x3 = pts[off + 4]; final float y3 = pts[off + 5];
+        final double x1 = pts[off    ]; final double y1 = pts[off + 1];
+        final double x2 = pts[off + 2]; final double y2 = pts[off + 3];
+        final double x3 = pts[off + 4]; final double y3 = pts[off + 5];
 
-        final float dx12 = x2 - x1; final float dy12 = y2 - y1;
-        final float dx23 = x3 - x2; final float dy23 = y3 - y2;
+        final double dx12 = x2 - x1; final double dy12 = y2 - y1;
+        final double dx23 = x3 - x2; final double dy23 = y3 - y2;
 
         if (checkCtrlPoints) {
             // if p1=p2 or p2=p3 it means that the derivative at the endpoint
@@ -1058,20 +1055,20 @@ private int computeOffsetQuad(final float[] pts, final int off,
             // equal if they're very close to each other.
 
             // if p1 == p2 or p2 == p3: draw line from p1->p3
-            final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0f * Math.ulp(y2));
-            final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0f * Math.ulp(y3));
+            final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2));
+            final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3));
 
             if (p1eqp2 || p2eqp3) {
                 return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
             }
 
             // if p2-p1 and p3-p2 are parallel, that must mean this curve is a line
-            float dotsq = (dx12 * dx23 + dy12 * dy23);
+            double dotsq = (dx12 * dx23 + dy12 * dy23);
             dotsq *= dotsq;
-            final float l1sq = dx12 * dx12 + dy12 * dy12;
-            final float l3sq = dx23 * dx23 + dy23 * dy23;
+            final double l1sq = dx12 * dx12 + dy12 * dy12;
+            final double l3sq = dx23 * dx23 + dy23 * dy23;
 
-            if (Helpers.within(dotsq, l1sq * l3sq, 4.0f * Math.ulp(dotsq))) {
+            if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
                 return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
             }
         }
@@ -1082,10 +1079,10 @@ private int computeOffsetQuad(final float[] pts, final int off,
         computeOffset(dx12, dy12, lineWidth2, offset0);
         computeOffset(dx23, dy23, lineWidth2, offset1);
 
-        float x1p = x1 + offset0[0]; // start
-        float y1p = y1 + offset0[1]; // point
-        float x3p = x3 + offset1[0]; // end
-        float y3p = y3 + offset1[1]; // point
+        double x1p = x1 + offset0[0]; // start
+        double y1p = y1 + offset0[1]; // point
+        double x3p = x3 + offset1[0]; // end
+        double y3p = y3 + offset1[1]; // point
 
         safeComputeMiter(x1p, y1p, x1p+dx12, y1p+dy12, x3p, y3p, x3p-dx23, y3p-dy23, leftOff);
         leftOff[0] = x1p; leftOff[1] = y1p;
@@ -1104,9 +1101,9 @@ private int computeOffsetQuad(final float[] pts, final int off,
     }
 
     @Override
-    public void curveTo(final float x1, final float y1,
-                        final float x2, final float y2,
-                        final float x3, final float y3)
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double x3, final double y3)
     {
         final int outcode0 = this.cOutCode;
 
@@ -1150,34 +1147,34 @@ public void curveTo(final float x1, final float y1,
         _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
     }
 
-    private void _curveTo(final float x1, final float y1,
-                          final float x2, final float y2,
-                          final float x3, final float y3,
+    private void _curveTo(final double x1, final double y1,
+                          final double x2, final double y2,
+                          final double x3, final double y3,
                           final int outcode0)
     {
         // need these so we can update the state at the end of this method
-        float dxs = x1 - cx0;
-        float dys = y1 - cy0;
-        float dxf = x3 - x2;
-        float dyf = y3 - y2;
+        double dxs = x1 - cx0;
+        double dys = y1 - cy0;
+        double dxf = x3 - x2;
+        double dyf = y3 - y2;
 
-        if ((dxs == 0.0f) && (dys == 0.0f)) {
+        if ((dxs == 0.0d) && (dys == 0.0d)) {
             dxs = x2 - cx0;
             dys = y2 - cy0;
-            if ((dxs == 0.0f) && (dys == 0.0f)) {
+            if ((dxs == 0.0d) && (dys == 0.0d)) {
                 dxs = x3 - cx0;
                 dys = y3 - cy0;
             }
         }
-        if ((dxf == 0.0f) && (dyf == 0.0f)) {
+        if ((dxf == 0.0d) && (dyf == 0.0d)) {
             dxf = x3 - x1;
             dyf = y3 - y1;
-            if ((dxf == 0.0f) && (dyf == 0.0f)) {
+            if ((dxf == 0.0d) && (dyf == 0.0d)) {
                 dxf = x3 - cx0;
                 dyf = y3 - cy0;
             }
         }
-        if ((dxs == 0.0f) && (dys == 0.0f)) {
+        if ((dxs == 0.0d) && (dys == 0.0d)) {
             // this happens if the "curve" is just a point
             // fix outcode0 for lineTo() call:
             if (clipRect != null) {
@@ -1189,13 +1186,13 @@ private void _curveTo(final float x1, final float y1,
 
         // if these vectors are too small, normalize them, to avoid future
         // precision problems.
-        if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
-            final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
+        if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
+            final double len = Math.sqrt(dxs * dxs + dys * dys);
             dxs /= len;
             dys /= len;
         }
-        if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
-            final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
+        if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
+            final double len = Math.sqrt(dxf * dxf + dyf * dyf);
             dxf /= len;
             dyf /= len;
         }
@@ -1204,8 +1201,8 @@ private void _curveTo(final float x1, final float y1,
         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         int nSplits = 0;
-        final float[] mid;
-        final float[] l = lp;
+        final double[] mid;
+        final double[] l = lp;
 
         if (monotonize) {
             // monotonize curve:
@@ -1222,7 +1219,7 @@ private void _curveTo(final float x1, final float y1,
             mid[4] = x2;  mid[5] = y2;
             mid[6] = x3;  mid[7] = y3;
         }
-        final float[] r = rp;
+        final double[] r = rp;
 
         int kind = 0;
         for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
@@ -1249,13 +1246,13 @@ private void _curveTo(final float x1, final float y1,
         this.cy0 = y3;
         this.cdx = dxf;
         this.cdy = dyf;
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
+        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
     }
 
     @Override
-    public void quadTo(final float x1, final float y1,
-                       final float x2, final float y2)
+    public void quadTo(final double x1, final double y1,
+                       final double x2, final double y2)
     {
         final int outcode0 = this.cOutCode;
 
@@ -1297,21 +1294,21 @@ public void quadTo(final float x1, final float y1,
         _quadTo(x1, y1, x2, y2, outcode0);
     }
 
-    private void _quadTo(final float x1, final float y1,
-                          final float x2, final float y2,
-                          final int outcode0)
+    private void _quadTo(final double x1, final double y1,
+                         final double x2, final double y2,
+                         final int outcode0)
     {
         // need these so we can update the state at the end of this method
-        float dxs = x1 - cx0;
-        float dys = y1 - cy0;
-        float dxf = x2 - x1;
-        float dyf = y2 - y1;
+        double dxs = x1 - cx0;
+        double dys = y1 - cy0;
+        double dxf = x2 - x1;
+        double dyf = y2 - y1;
 
-        if (((dxs == 0.0f) && (dys == 0.0f)) || ((dxf == 0.0f) && (dyf == 0.0f))) {
+        if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) {
             dxs = dxf = x2 - cx0;
             dys = dyf = y2 - cy0;
         }
-        if ((dxs == 0.0f) && (dys == 0.0f)) {
+        if ((dxs == 0.0d) && (dys == 0.0d)) {
             // this happens if the "curve" is just a point
             // fix outcode0 for lineTo() call:
             if (clipRect != null) {
@@ -1322,13 +1319,13 @@ private void _quadTo(final float x1, final float y1,
         }
         // if these vectors are too small, normalize them, to avoid future
         // precision problems.
-        if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
-            final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
+        if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
+            final double len = Math.sqrt(dxs * dxs + dys * dys);
             dxs /= len;
             dys /= len;
         }
-        if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
-            final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
+        if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
+            final double len = Math.sqrt(dxf * dxf + dyf * dyf);
             dxf /= len;
             dyf /= len;
         }
@@ -1336,8 +1333,8 @@ private void _quadTo(final float x1, final float y1,
         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         int nSplits = 0;
-        final float[] mid;
-        final float[] l = lp;
+        final double[] mid;
+        final double[] l = lp;
 
         if (monotonize) {
             // monotonize quad:
@@ -1353,7 +1350,7 @@ private void _quadTo(final float x1, final float y1,
             mid[2] = x1;  mid[3] = y1;
             mid[4] = x2;  mid[5] = y2;
         }
-        final float[] r = rp;
+        final double[] r = rp;
 
         int kind = 0;
         for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
@@ -1380,8 +1377,8 @@ private void _quadTo(final float x1, final float y1,
         this.cy0 = y2;
         this.cdx = dxf;
         this.cdy = dyf;
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
+        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
     }
 
     @Override public long getNativeConsumer() {
diff --git a/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java b/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java
index 1262d01..d540472 100644
--- a/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java
+++ b/src/main/java/sun/java2d/marlin/TransformingPathConsumer2D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -25,7 +25,6 @@
 
 package sun.java2d.marlin;
 
-import sun.awt.geom.PathConsumer2D;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Path2D;
 import java.util.Arrays;
@@ -34,8 +33,8 @@
 
 final class TransformingPathConsumer2D {
 
-    // higher uncertainty in float variant for huge shapes > 10^7
-    static final float CLIP_RECT_PADDING = 1.0f;
+    // smaller uncertainty in double variant
+    static final double CLIP_RECT_PADDING = 0.25d;
 
     private final RendererContext rdrCtx;
 
@@ -45,14 +44,14 @@ final class TransformingPathConsumer2D {
     // recycled PathClipFilter instance from pathClipper()
     private final PathClipFilter       pathClipper;
 
-    // recycled PathConsumer2D instance from wrapPath2D()
+    // recycled DPathConsumer2D instance from wrapPath2D()
     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
 
-    // recycled PathConsumer2D instances from deltaTransformConsumer()
+    // recycled DPathConsumer2D instances from deltaTransformConsumer()
     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
 
-    // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
+    // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
     private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
     private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
 
@@ -70,51 +69,51 @@ final class TransformingPathConsumer2D {
         this.pathClipper = new PathClipFilter(rdrCtx);
     }
 
-    PathConsumer2D wrapPath2D(Path2D.Float p2d) {
+    DPathConsumer2D wrapPath2D(Path2D.Double p2d) {
         return wp_Path2DWrapper.init(p2d);
     }
 
-    PathConsumer2D traceInput(PathConsumer2D out) {
+    DPathConsumer2D traceInput(DPathConsumer2D out) {
         return tracerInput.init(out);
     }
 
-    PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
+    DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
         return tracerCPDetector.init(out);
     }
 
-    PathConsumer2D traceFiller(PathConsumer2D out) {
+    DPathConsumer2D traceFiller(DPathConsumer2D out) {
         return tracerFiller.init(out);
     }
 
-    PathConsumer2D traceStroker(PathConsumer2D out) {
+    DPathConsumer2D traceStroker(DPathConsumer2D out) {
         return tracerStroker.init(out);
     }
 
-    PathConsumer2D traceDasher(PathConsumer2D out) {
+    DPathConsumer2D traceDasher(DPathConsumer2D out) {
         return tracerDasher.init(out);
     }
 
-    PathConsumer2D detectClosedPath(PathConsumer2D out) {
+    DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
         return cpDetector.init(out);
     }
 
-    PathConsumer2D pathClipper(PathConsumer2D out) {
+    DPathConsumer2D pathClipper(DPathConsumer2D out) {
         return pathClipper.init(out);
     }
 
-    PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
+    DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
                                           AffineTransform at)
     {
         if (at == null) {
             return out;
         }
-        final float mxx = (float) at.getScaleX();
-        final float mxy = (float) at.getShearX();
-        final float myx = (float) at.getShearY();
-        final float myy = (float) at.getScaleY();
+        final double mxx = at.getScaleX();
+        final double mxy = at.getShearX();
+        final double myx = at.getShearY();
+        final double myy = at.getScaleY();
 
-        if (mxy == 0.0f && myx == 0.0f) {
-            if (mxx == 1.0f && myy == 1.0f) {
+        if (mxy == 0.0d && myx == 0.0d) {
+            if (mxx == 1.0d && myy == 1.0d) {
                 return out;
             } else {
                 // Scale only
@@ -135,26 +134,26 @@ PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
         }
     }
 
-    private static float adjustClipScale(final float[] clipRect,
-                                         final float mxx, final float myy)
+    private static double adjustClipScale(final double[] clipRect,
+                                          final double mxx, final double myy)
     {
         // Adjust the clipping rectangle (iv_DeltaScaleFilter):
-        final float scaleY = 1.0f / myy;
+        final double scaleY = 1.0d / myy;
         clipRect[0] *= scaleY;
         clipRect[1] *= scaleY;
 
         if (clipRect[1] < clipRect[0]) {
-            float tmp = clipRect[0];
+            double tmp = clipRect[0];
             clipRect[0] = clipRect[1];
             clipRect[1] = tmp;
         }
 
-        final float scaleX = 1.0f / mxx;
+        final double scaleX = 1.0d / mxx;
         clipRect[2] *= scaleX;
         clipRect[3] *= scaleX;
 
         if (clipRect[3] < clipRect[2]) {
-            float tmp = clipRect[2];
+            double tmp = clipRect[2];
             clipRect[2] = clipRect[3];
             clipRect[3] = tmp;
         }
@@ -163,22 +162,22 @@ private static float adjustClipScale(final float[] clipRect,
                 MarlinUtils.logInfo("clipRect (ClipScale): "
                                     + Arrays.toString(clipRect));
         }
-        return 0.5f * (Math.abs(scaleX) + Math.abs(scaleY));
+        return 0.5d * (Math.abs(scaleX) + Math.abs(scaleY));
     }
 
-    private static float adjustClipInverseDelta(final float[] clipRect,
-                                                final float mxx, final float mxy,
-                                                final float myx, final float myy)
+    private static double adjustClipInverseDelta(final double[] clipRect,
+                                                 final double mxx, final double mxy,
+                                                 final double myx, final double myy)
     {
         // Adjust the clipping rectangle (iv_DeltaTransformFilter):
-        final float det = mxx * myy - mxy * myx;
-        final float imxx =  myy / det;
-        final float imxy = -mxy / det;
-        final float imyx = -myx / det;
-        final float imyy =  mxx / det;
-
-        float xmin, xmax, ymin, ymax;
-        float x, y;
+        final double det = mxx * myy - mxy * myx;
+        final double imxx =  myy / det;
+        final double imxy = -mxy / det;
+        final double imyx = -myx / det;
+        final double imyy =  mxx / det;
+
+        double xmin, xmax, ymin, ymax;
+        double x, y;
         // xmin, ymin:
         x = clipRect[2] * imxx + clipRect[0] * imxy;
         y = clipRect[2] * imyx + clipRect[0] * imyy;
@@ -217,31 +216,31 @@ private static float adjustClipInverseDelta(final float[] clipRect,
                                     + Arrays.toString(clipRect));
         }
 
-        final float scaleX = (float) Math.sqrt(imxx * imxx + imxy * imxy);
-        final float scaleY = (float) Math.sqrt(imyx * imyx + imyy * imyy);
+        final double scaleX = Math.sqrt(imxx * imxx + imxy * imxy);
+        final double scaleY = Math.sqrt(imyx * imyx + imyy * imyy);
 
-        return 0.5f * (scaleX + scaleY);
+        return 0.5d * (scaleX + scaleY);
     }
 
-    PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
+    DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
                                                  AffineTransform at)
     {
         if (at == null) {
             return out;
         }
-        float mxx = (float) at.getScaleX();
-        float mxy = (float) at.getShearX();
-        float myx = (float) at.getShearY();
-        float myy = (float) at.getScaleY();
+        double mxx = at.getScaleX();
+        double mxy = at.getShearX();
+        double myx = at.getShearY();
+        double myy = at.getScaleY();
 
-        if (mxy == 0.0f && myx == 0.0f) {
-            if (mxx == 1.0f && myy == 1.0f) {
+        if (mxy == 0.0d && myx == 0.0d) {
+            if (mxx == 1.0d && myy == 1.0d) {
                 return out;
             } else {
-                return iv_DeltaScaleFilter.init(out, 1.0f / mxx, 1.0f / myy);
+                return iv_DeltaScaleFilter.init(out, 1.0d / mxx, 1.0d / myy);
             }
         } else {
-            final float det = mxx * myy - mxy * myx;
+            final double det = mxx * myy - mxy * myx;
             return iv_DeltaTransformFilter.init(out,
                                                 myy / det,
                                                -mxy / det,
@@ -250,14 +249,14 @@ PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
         }
     }
 
-    static final class DeltaScaleFilter implements PathConsumer2D {
-        private PathConsumer2D out;
-        private float sx, sy;
+    static final class DeltaScaleFilter implements DPathConsumer2D {
+        private DPathConsumer2D out;
+        private double sx, sy;
 
         DeltaScaleFilter() {}
 
-        DeltaScaleFilter init(PathConsumer2D out,
-                              float mxx, float myy)
+        DeltaScaleFilter init(DPathConsumer2D out,
+                              double mxx, double myy)
         {
             if (this.out != out) {
                 this.out = out;
@@ -268,27 +267,27 @@ DeltaScaleFilter init(PathConsumer2D out,
         }
 
         @Override
-        public void moveTo(float x0, float y0) {
+        public void moveTo(double x0, double y0) {
             out.moveTo(x0 * sx, y0 * sy);
         }
 
         @Override
-        public void lineTo(float x1, float y1) {
+        public void lineTo(double x1, double y1) {
             out.lineTo(x1 * sx, y1 * sy);
         }
 
         @Override
-        public void quadTo(float x1, float y1,
-                           float x2, float y2)
+        public void quadTo(double x1, double y1,
+                           double x2, double y2)
         {
             out.quadTo(x1 * sx, y1 * sy,
                        x2 * sx, y2 * sy);
         }
 
         @Override
-        public void curveTo(float x1, float y1,
-                            float x2, float y2,
-                            float x3, float y3)
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
         {
             out.curveTo(x1 * sx, y1 * sy,
                         x2 * sx, y2 * sy,
@@ -311,15 +310,15 @@ public long getNativeConsumer() {
         }
     }
 
-    static final class DeltaTransformFilter implements PathConsumer2D {
-        private PathConsumer2D out;
-        private float mxx, mxy, myx, myy;
+    static final class DeltaTransformFilter implements DPathConsumer2D {
+        private DPathConsumer2D out;
+        private double mxx, mxy, myx, myy;
 
         DeltaTransformFilter() {}
 
-        DeltaTransformFilter init(PathConsumer2D out,
-                                  float mxx, float mxy,
-                                  float myx, float myy)
+        DeltaTransformFilter init(DPathConsumer2D out,
+                                  double mxx, double mxy,
+                                  double myx, double myy)
         {
             if (this.out != out) {
                 this.out = out;
@@ -332,20 +331,20 @@ DeltaTransformFilter init(PathConsumer2D out,
         }
 
         @Override
-        public void moveTo(float x0, float y0) {
+        public void moveTo(double x0, double y0) {
             out.moveTo(x0 * mxx + y0 * mxy,
                        x0 * myx + y0 * myy);
         }
 
         @Override
-        public void lineTo(float x1, float y1) {
+        public void lineTo(double x1, double y1) {
             out.lineTo(x1 * mxx + y1 * mxy,
                        x1 * myx + y1 * myy);
         }
 
         @Override
-        public void quadTo(float x1, float y1,
-                           float x2, float y2)
+        public void quadTo(double x1, double y1,
+                           double x2, double y2)
         {
             out.quadTo(x1 * mxx + y1 * mxy,
                        x1 * myx + y1 * myy,
@@ -354,9 +353,9 @@ public void quadTo(float x1, float y1,
         }
 
         @Override
-        public void curveTo(float x1, float y1,
-                            float x2, float y2,
-                            float x3, float y3)
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
         {
             out.curveTo(x1 * mxx + y1 * mxy,
                         x1 * myx + y1 * myy,
@@ -382,12 +381,12 @@ public long getNativeConsumer() {
         }
     }
 
-    static final class Path2DWrapper implements PathConsumer2D {
-        private Path2D.Float p2d;
+    static final class Path2DWrapper implements DPathConsumer2D {
+        private Path2D.Double p2d;
 
         Path2DWrapper() {}
 
-        Path2DWrapper init(Path2D.Float p2d) {
+        Path2DWrapper init(Path2D.Double p2d) {
             if (this.p2d != p2d) {
                 this.p2d = p2d;
             }
@@ -395,12 +394,12 @@ Path2DWrapper init(Path2D.Float p2d) {
         }
 
         @Override
-        public void moveTo(float x0, float y0) {
+        public void moveTo(double x0, double y0) {
             p2d.moveTo(x0, y0);
         }
 
         @Override
-        public void lineTo(float x1, float y1) {
+        public void lineTo(double x1, double y1) {
             p2d.lineTo(x1, y1);
         }
 
@@ -413,15 +412,15 @@ public void closePath() {
         public void pathDone() {}
 
         @Override
-        public void curveTo(float x1, float y1,
-                            float x2, float y2,
-                            float x3, float y3)
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
         {
             p2d.curveTo(x1, y1, x2, y2, x3, y3);
         }
 
         @Override
-        public void quadTo(float x1, float y1, float x2, float y2) {
+        public void quadTo(double x1, double y1, double x2, double y2) {
             p2d.quadTo(x1, y1, x2, y2);
         }
 
@@ -431,12 +430,12 @@ public long getNativeConsumer() {
         }
     }
 
-    static final class ClosedPathDetector implements PathConsumer2D {
+    static final class ClosedPathDetector implements DPathConsumer2D {
 
         private final RendererContext rdrCtx;
         private final PolyStack stack;
 
-        private PathConsumer2D out;
+        private DPathConsumer2D out;
 
         ClosedPathDetector(final RendererContext rdrCtx) {
             this.rdrCtx = rdrCtx;
@@ -450,7 +449,7 @@ static final class ClosedPathDetector implements PathConsumer2D {
                 : new PolyStack(rdrCtx);
         }
 
-        ClosedPathDetector init(PathConsumer2D out) {
+        ClosedPathDetector init(DPathConsumer2D out) {
             if (this.out != out) {
                 this.out = out;
             }
@@ -484,7 +483,7 @@ public void closePath() {
         }
 
         @Override
-        public void moveTo(float x0, float y0) {
+        public void moveTo(double x0, double y0) {
             // previous path is not closed:
             finish(false);
             out.moveTo(x0, y0);
@@ -496,20 +495,20 @@ private void finish(final boolean closed) {
         }
 
         @Override
-        public void lineTo(float x1, float y1) {
+        public void lineTo(double x1, double y1) {
             stack.pushLine(x1, y1);
         }
 
         @Override
-        public void curveTo(float x3, float y3,
-                            float x2, float y2,
-                            float x1, float y1)
+        public void curveTo(double x3, double y3,
+                            double x2, double y2,
+                            double x1, double y1)
         {
             stack.pushCubic(x1, y1, x2, y2, x3, y3);
         }
 
         @Override
-        public void quadTo(float x2, float y2, float x1, float y1) {
+        public void quadTo(double x2, double y2, double x1, double y1) {
             stack.pushQuad(x1, y1, x2, y2);
         }
 
@@ -519,7 +518,7 @@ public long getNativeConsumer() {
         }
     }
 
-    static final class PathClipFilter implements PathConsumer2D {
+    static final class PathClipFilter implements DPathConsumer2D {
 
         private static final boolean TRACE = false;
 
@@ -527,14 +526,14 @@ static final class PathClipFilter implements PathConsumer2D {
         private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
         private static final int CLOSE = 2;
 
-        private PathConsumer2D out;
+        private DPathConsumer2D out;
 
         private int prev;
 
         // Bounds of the drawing region, at pixel precision.
-        private final float[] clipRect;
+        private final double[] clipRect;
 
-        private final float[] corners = new float[8];
+        private final double[] corners = new double[8];
         private boolean init_corners = false;
 
         private final IndexStack stack;
@@ -551,13 +550,10 @@ static final class PathClipFilter implements PathConsumer2D {
         private boolean outside = false;
 
         // The starting point of the path
-        private float sx0, sy0;
+        private double sx0, sy0;
 
-        // The current point (TODO stupid repeated info)
-        private float cx0, cy0;
-
-        // The current point OUTSIDE
-        private float cox0, coy0;
+        // The current point
+        private double cx0, cy0;
 
         private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
         private final CurveClipSplitter curveSplitter;
@@ -574,7 +570,7 @@ static final class PathClipFilter implements PathConsumer2D {
                 : new IndexStack(rdrCtx);
         }
 
-        PathClipFilter init(final PathConsumer2D out) {
+        PathClipFilter init(final DPathConsumer2D out) {
             if (this.out != out) {
                 this.out = out;
             }
@@ -600,14 +596,12 @@ void dispose() {
         }
 
         private void finishPath() {
-            if (outside) {
-                // criteria: inside or totally outside ?
-                if (gOutCode == 0) {
-                    finish();
-                } else {
-                    this.outside = false;
-                    stack.reset();
-                }
+            // criteria: inside or totally outside ?
+            if (gOutCode == 0) {
+                finish();
+            } else {
+                this.outside = false;
+                stack.reset();
             }
         }
 
@@ -618,8 +612,8 @@ private void finish() {
                 if (init_corners) {
                     init_corners = false;
 
-                    final float[] _corners = corners;
-                    final float[] _clipRect = clipRect;
+                    final double[] _corners = corners;
+                    final double[] _clipRect = clipRect;
                     // Top Left (0):
                     _corners[0] = _clipRect[2];
                     _corners[1] = _clipRect[0];
@@ -636,18 +630,16 @@ private void finish() {
                 stack.pullAll(corners, out, (prev == MOVE_TO));
                 prev = DRAWING_OP_TO;
             }
-            // go to the last outside point:
-            this.cx0 = cox0;
-            this.cy0 = coy0;
         }
 
         @Override
         public void pathDone() {
             if (TRACE) {
-                System.out.println("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev);
+                MarlinUtils.logInfo("PathDone(" + sx0 + ", " + sy0 + ") prev: " + prev);
             }
             _closePath();
 
+            // note: renderer's pathDone() must handle missing moveTo() if outside
             out.pathDone();
 
             // this shouldn't matter since this object won't be used
@@ -662,11 +654,13 @@ public void pathDone() {
         @Override
         public void closePath() {
             if (TRACE) {
-                System.out.println("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev);
+                MarlinUtils.logInfo("ClosePath(" + sx0 + ", " + sy0 + ") prev: " + prev);
             }
             _closePath();
 
-            out.closePath();
+            if (prev == DRAWING_OP_TO) {
+                out.closePath();
+            }
 
             // if outside, moveTo is needed
             if (sOutCode != 0) {
@@ -684,27 +678,32 @@ public void closePath() {
         private void _closePath() {
             // preserve outside flag for the lineTo call below
             final boolean prevOutside = outside;
-            finishPath();
+            if (prevOutside) {
+                finishPath();
+            }
 
             if (prev == DRAWING_OP_TO) {
                 // Should clip
                 final int orCode = (cOutCode | sOutCode);
                 if (orCode != 0) {
-                    if (cx0 != sx0 || cy0 != sy0) {
+                    if ((cx0 != sx0) || (cy0 != sy0)) {
                         // restore outside flag before lineTo:
                         this.outside = prevOutside;
                         // may subdivide line:
                         lineTo(sx0, sy0);
+                        // finish if outside caused by lineTo:
+                        if (outside) {
+                            finishPath();
+                        }
                     }
                 }
             }
-            finishPath();
         }
 
         @Override
-        public void moveTo(final float x0, final float y0) {
+        public void moveTo(final double x0, final double y0) {
             if (TRACE) {
-                System.out.println("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev);
+                MarlinUtils.logInfo("MoveTo(" + x0 + ", " + y0 + ") prev: " + prev);
             }
             _closePath();
 
@@ -716,25 +715,21 @@ public void moveTo(final float x0, final float y0) {
             this.sOutCode = outcode;
             this.cx0 = x0;
             this.cy0 = y0;
-
             this.sx0 = x0;
             this.sy0 = y0;
         }
 
         @Override
-        public void lineTo(final float xe, final float ye) {
+        public void lineTo(final double xe, final double ye) {
             final int outcode0 = this.cOutCode;
             final int outcode1 = Helpers.outcode(xe, ye, clipRect);
 
             if (TRACE) {
                 if (subdivide) {
-                    System.out.println("----------------------");
+                    MarlinUtils.logInfo("----------------------");
                 }
-                if (outside) {
-                    System.out.println("LineTo co (" + cox0 + ", " + coy0 + ")");
-                }
-                System.out.println("LineTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
+                MarlinUtils.logInfo("LineTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
+                MarlinUtils.logInfo("LineTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
             }
 
             // Should clip
@@ -750,13 +745,8 @@ public void lineTo(final float xe, final float ye) {
                         subdivide = false;
                         boolean ret;
                         // subdivide curve => callback with subdivided parts:
-                        if (outside) {
-                            ret = curveSplitter.splitLine(cox0, coy0, xe, ye,
-                                                          orCode, this);
-                        } else {
-                            ret = curveSplitter.splitLine(cx0, cy0, xe, ye,
-                                                          orCode, this);
-                        }
+                        ret = curveSplitter.splitLine(cx0, cy0, xe, ye,
+                                                      orCode, this);
                         // reentrance is done:
                         subdivide = true;
                         if (ret) {
@@ -769,11 +759,11 @@ public void lineTo(final float xe, final float ye) {
                     this.gOutCode &= sideCode;
                     // keep last point coordinate before entering the clip again:
                     this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
 
                     if (TRACE) {
-                        System.out.println("skipped: (" + cox0 + ", " + coy0 + ")");
+                        MarlinUtils.logInfo("skipped: (" + cx0 + ", " + cy0 + ")");
                     }
 
                     clip(sideCode, outcode0, outcode1);
@@ -790,12 +780,12 @@ public void lineTo(final float xe, final float ye) {
                 // emit last point outside before entering again...
                 if (outcode0 != 0) {
                     if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
+                        MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")");
                     }
                     if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
+                        out.moveTo(cx0, cy0);
                     } else {
-                        out.lineTo(cox0, coy0);
+                        out.lineTo(cx0, cy0);
                     }
                     prev = DRAWING_OP_TO;
                 }
@@ -811,7 +801,7 @@ public void lineTo(final float xe, final float ye) {
             this.cy0 = ye;
 
             if (TRACE && subdivide) {
-                System.out.println("----------------------");
+                MarlinUtils.logInfo("----------------------");
             }
         }
 
@@ -853,9 +843,9 @@ private void clip(final int sideCode,
         }
 
         @Override
-        public void curveTo(final float x1, final float y1,
-                            final float x2, final float y2,
-                            final float xe, final float ye)
+        public void curveTo(final double x1, final double y1,
+                            final double x2, final double y2,
+                            final double xe, final double ye)
         {
             final int outcode0 = this.cOutCode;
             final int outcode1 = Helpers.outcode(x1, y1, clipRect);
@@ -864,13 +854,10 @@ public void curveTo(final float x1, final float y1,
 
             if (TRACE) {
                 if (subdivide) {
-                    System.out.println("----------------------");
+                    MarlinUtils.logInfo("----------------------");
                 }
-                if (outside) {
-                    System.out.println("CurveTo co (" + cox0 + ", " + coy0 + ")");
-                }
-                System.out.println("CurveTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside);
+                MarlinUtils.logInfo("CurveTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
+                MarlinUtils.logInfo("CurveTo (" + xe + ", " + ye + ") outcode: " + outcode3 + " outside: " + outside);
             }
 
             // Should clip
@@ -886,15 +873,9 @@ public void curveTo(final float x1, final float y1,
                         subdivide = false;
                         // subdivide curve => callback with subdivided parts:
                         boolean ret;
-                        if (outside) {
-                            ret = curveSplitter.splitCurve(cox0, coy0, x1, y1,
-                                                           x2, y2, xe, ye,
-                                                           orCode, this);
-                        } else {
-                            ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
-                                                           x2, y2, xe, ye,
-                                                           orCode, this);
-                        }
+                        ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
+                                                       x2, y2, xe, ye,
+                                                       orCode, this);
                         // reentrance is done:
                         subdivide = true;
                         if (ret) {
@@ -907,11 +888,11 @@ public void curveTo(final float x1, final float y1,
                     this.gOutCode &= sideCode;
                     // keep last point coordinate before entering the clip again:
                     this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
 
                     if (TRACE) {
-                        System.out.println("skipped: (" + cox0 + ", " + coy0 + ")");
+                        MarlinUtils.logInfo("skipped: (" + cx0 + ", " + cy0 + ")");
                     }
 
                     clip(sideCode, outcode0, outcode3);
@@ -928,12 +909,12 @@ public void curveTo(final float x1, final float y1,
                 // emit last point outside before entering again...
                 if (outcode0 != 0) {
                     if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
+                        MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")");
                     }
                     if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
+                        out.moveTo(cx0, cy0);
                     } else {
-                        out.lineTo(cox0, coy0);
+                        out.lineTo(cx0, cy0);
                     }
                     prev = DRAWING_OP_TO;
                 }
@@ -949,13 +930,13 @@ public void curveTo(final float x1, final float y1,
             this.cy0 = ye;
 
             if (TRACE && subdivide) {
-                System.out.println("----------------------");
+                MarlinUtils.logInfo("----------------------");
             }
         }
 
         @Override
-        public void quadTo(final float x1, final float y1,
-                           final float xe, final float ye)
+        public void quadTo(final double x1, final double y1,
+                           final double xe, final double ye)
         {
             final int outcode0 = this.cOutCode;
             final int outcode1 = Helpers.outcode(x1, y1, clipRect);
@@ -963,13 +944,10 @@ public void quadTo(final float x1, final float y1,
 
             if (TRACE) {
                 if (subdivide) {
-                    System.out.println("----------------------");
-                }
-                if (outside) {
-                    System.out.println("QuadTo co (" + cox0 + ", " + coy0 + ")");
+                    MarlinUtils.logInfo("----------------------");
                 }
-                System.out.println("QuadTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
-                System.out.println("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
+                MarlinUtils.logInfo("QuadTo c  (" + cx0 + ", " + cy0 + ") outcode: " + outcode0);
+                MarlinUtils.logInfo("QuadTo (" + xe + ", " + ye + ") outcode: " + outcode1 + " outside: " + outside);
             }
 
             // Should clip
@@ -985,13 +963,8 @@ public void quadTo(final float x1, final float y1,
                         subdivide = false;
                         // subdivide curve => callback with subdivided parts:
                         boolean ret;
-                        if (outside) {
-                            ret = curveSplitter.splitQuad(cox0, coy0, x1, y1,
-                                                          xe, ye, orCode, this);
-                        } else {
-                            ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
-                                                          xe, ye, orCode, this);
-                        }
+                        ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+                                                      xe, ye, orCode, this);
                         // reentrance is done:
                         subdivide = true;
                         if (ret) {
@@ -1004,8 +977,8 @@ public void quadTo(final float x1, final float y1,
                     this.gOutCode &= sideCode;
                     // keep last point coordinate before entering the clip again:
                     this.outside = true;
-                    this.cox0 = xe;
-                    this.coy0 = ye;
+                    this.cx0 = xe;
+                    this.cy0 = ye;
 
                     clip(sideCode, outcode0, outcode2);
                     return;
@@ -1021,12 +994,12 @@ public void quadTo(final float x1, final float y1,
                 // emit last point outside before entering again...
                 if (outcode0 != 0) {
                     if (TRACE) {
-                        System.out.println("add last point outside: (" + cox0 + ", " + coy0 + ")");
+                        MarlinUtils.logInfo("add last point outside: (" + cx0 + ", " + cy0 + ")");
                     }
                     if (prev == MOVE_TO) {
-                        out.moveTo(cox0, coy0);
+                        out.moveTo(cx0, cy0);
                     } else {
-                        out.lineTo(cox0, coy0);
+                        out.lineTo(cx0, cy0);
                     }
                     prev = DRAWING_OP_TO;
                 }
@@ -1042,7 +1015,7 @@ public void quadTo(final float x1, final float y1,
             this.cy0 = ye;
 
             if (TRACE && subdivide) {
-                System.out.println("----------------------");
+                MarlinUtils.logInfo("----------------------");
             }
         }
 
@@ -1052,11 +1025,10 @@ public long getNativeConsumer() {
         }
     }
 
-    /* note: CurveClipSplitter uses double-precision for higher accuracy */ 
     static final class CurveClipSplitter {
 
-        static final float LEN_TH = MarlinProperties.getSubdividerMinLength();
-        static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0f);
+        static final double LEN_TH = MarlinProperties.getSubdividerMinLength();
+        static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d);
 
         private static final boolean TRACE = false;
 
@@ -1065,10 +1037,10 @@ static final class CurveClipSplitter {
         private final RendererContext rdrCtx;
 
         // scaled length threshold:
-        private float minLength;
+        private double minLength;
 
         // clip rectangle (ymin, ymax, xmin, xmax):
-        final float[] clipRect;
+        final double[] clipRect;
 
         // clip rectangle (ymin, ymax, xmin, xmax) including padding:
         final double[] clipRectPad = new double[4];
@@ -1081,19 +1053,19 @@ static final class CurveClipSplitter {
         private final double[] subdivTs = new double[MAX_N_CURVES];
 
         // dirty curve
-        private final DCurve curve;
+        private final Curve curve;
 
         CurveClipSplitter(final RendererContext rdrCtx) {
             this.rdrCtx = rdrCtx;
             this.clipRect = rdrCtx.clipRect;
-            this.curve = /* rdrCtx.curve */ new DCurve(); // double-precision curve
+            this.curve = rdrCtx.curve;
         }
 
         void init() {
             this.init_clipRectPad = true;
 
             if (DO_CHECK_LENGTH) {
-                this.minLength = (this.rdrCtx.clipInvScale == 0.0f) ? LEN_TH
+                this.minLength = (this.rdrCtx.clipInvScale == 0.0d) ? LEN_TH
                                     : (LEN_TH * this.rdrCtx.clipInvScale);
 
                 if (MarlinConst.DO_LOG_CLIP) {
@@ -1107,7 +1079,7 @@ private void initPaddedClip() {
             // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
             // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
             // add a rounding error (curve subdivision ~ 0.1px):
-            final float[] _clipRect = clipRect;
+            final double[] _clipRect = clipRect;
             final double[] _clipRectPad = clipRectPad;
 
             _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
@@ -1121,10 +1093,10 @@ private void initPaddedClip() {
             }
         }
 
-        boolean splitLine(final float x0, final float y0,
-                          final float x1, final float y1,
+        boolean splitLine(final double x0, final double y0,
+                          final double x1, final double y1,
                           final int outCodeOR,
-                          final PathConsumer2D out)
+                          final DPathConsumer2D out)
         {
             if (TRACE) {
                 MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
@@ -1141,11 +1113,11 @@ boolean splitLine(final float x0, final float y0,
             return subdivideAtIntersections(4, outCodeOR, out);
         }
 
-        boolean splitQuad(final float x0, final float y0,
-                          final float x1, final float y1,
-                          final float x2, final float y2,
+        boolean splitQuad(final double x0, final double y0,
+                          final double x1, final double y1,
+                          final double x2, final double y2,
                           final int outCodeOR,
-                          final PathConsumer2D out)
+                          final DPathConsumer2D out)
         {
             if (TRACE) {
                 MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
@@ -1163,12 +1135,12 @@ boolean splitQuad(final float x0, final float y0,
             return subdivideAtIntersections(6, outCodeOR, out);
         }
 
-        boolean splitCurve(final float x0, final float y0,
-                           final float x1, final float y1,
-                           final float x2, final float y2,
-                           final float x3, final float y3,
+        boolean splitCurve(final double x0, final double y0,
+                           final double x1, final double y1,
+                           final double x2, final double y2,
+                           final double x3, final double y3,
                            final int outCodeOR,
-                           final PathConsumer2D out)
+                           final DPathConsumer2D out)
         {
             if (TRACE) {
                 MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
@@ -1188,7 +1160,7 @@ boolean splitCurve(final float x0, final float y0,
         }
 
         private boolean subdivideAtIntersections(final int type, final int outCodeOR,
-                                                 final PathConsumer2D out)
+                                                 final DPathConsumer2D out)
         {
             final double[] mid = middle;
             final double[] subTs = subdivTs;
@@ -1198,7 +1170,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
                 initPaddedClip();
             }
 
-            final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
+            final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
                                                         outCodeOR, clipRectPad);
 
             if (TRACE) {
@@ -1214,7 +1186,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
             for (int i = 0, off = 0; i < nSplits; i++, off += type) {
                 final double t = subTs[i];
 
-                DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
+                Helpers.subdivideAt((t - prevT) / (1.0d - prevT),
                                      mid, off, mid, off, type);
                 prevT = t;
             }
@@ -1229,18 +1201,18 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
         }
 
         static void emitCurrent(final int type, final double[] pts,
-                                final int off, final PathConsumer2D out)
+                                final int off, final DPathConsumer2D out)
         {
             // if instead of switch (perf + most probable cases first)
             if (type == 8) {
-                out.curveTo((float)pts[off + 2], (float)pts[off + 3],
-                            (float)pts[off + 4], (float)pts[off + 5],
-                            (float)pts[off + 6], (float)pts[off + 7]);
+                out.curveTo(pts[off + 2], pts[off + 3],
+                            pts[off + 4], pts[off + 5],
+                            pts[off + 6], pts[off + 7]);
             } else if (type == 4) {
-                out.lineTo((float)pts[off + 2], (float)pts[off + 3]);
+                out.lineTo(pts[off + 2], pts[off + 3]);
             } else {
-                out.quadTo((float)pts[off + 2], (float)pts[off + 3],
-                           (float)pts[off + 4], (float)pts[off + 5]);
+                out.quadTo(pts[off + 2], pts[off + 3],
+                           pts[off + 4], pts[off + 5]);
             }
         }
     }
@@ -1250,16 +1222,16 @@ static final class CurveBasicMonotonizer {
         private static final int MAX_N_CURVES = 11;
 
         // squared half line width (for stroker)
-        private float lw2;
+        private double lw2;
 
         // number of splitted curves
         int nbSplits;
 
         // This is where the curve to be processed is put. We give it
         // enough room to store all curves.
-        final float[] middle = new float[MAX_N_CURVES * 6 + 2];
+        final double[] middle = new double[MAX_N_CURVES * 6 + 2];
         // t values at subdivision points
-        private final float[] subdivTs = new float[MAX_N_CURVES - 1];
+        private final double[] subdivTs = new double[MAX_N_CURVES - 1];
 
         // dirty curve
         private final Curve curve;
@@ -1268,29 +1240,29 @@ static final class CurveBasicMonotonizer {
             this.curve = rdrCtx.curve;
         }
 
-        void init(final float lineWidth) {
-            this.lw2 = (lineWidth * lineWidth) / 4.0f;
+        void init(final double lineWidth) {
+            this.lw2 = (lineWidth * lineWidth) / 4.0d;
         }
 
-        CurveBasicMonotonizer curve(final float x0, final float y0,
-                                    final float x1, final float y1,
-                                    final float x2, final float y2,
-                                    final float x3, final float y3)
+        CurveBasicMonotonizer curve(final double x0, final double y0,
+                                    final double x1, final double y1,
+                                    final double x2, final double y2,
+                                    final double x3, final double y3)
         {
-            final float[] mid = middle;
+            final double[] mid = middle;
             mid[0] = x0;  mid[1] = y0;
             mid[2] = x1;  mid[3] = y1;
             mid[4] = x2;  mid[5] = y2;
             mid[6] = x3;  mid[7] = y3;
 
-            final float[] subTs = subdivTs;
+            final double[] subTs = subdivTs;
             final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 8, lw2);
 
-            float prevT = 0.0f;
+            double prevT = 0.0d;
             for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
-                final float t = subTs[i];
+                final double t = subTs[i];
 
-                Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT),
+                Helpers.subdivideCubicAt((t - prevT) / (1.0d - prevT),
                                           mid, off, mid, off, off + 6);
                 prevT = t;
             }
@@ -1299,22 +1271,22 @@ CurveBasicMonotonizer curve(final float x0, final float y0,
             return this;
         }
 
-        CurveBasicMonotonizer quad(final float x0, final float y0,
-                                   final float x1, final float y1,
-                                   final float x2, final float y2)
+        CurveBasicMonotonizer quad(final double x0, final double y0,
+                                   final double x1, final double y1,
+                                   final double x2, final double y2)
         {
-            final float[] mid = middle;
+            final double[] mid = middle;
             mid[0] = x0;  mid[1] = y0;
             mid[2] = x1;  mid[3] = y1;
             mid[4] = x2;  mid[5] = y2;
 
-            final float[] subTs = subdivTs;
+            final double[] subTs = subdivTs;
             final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 6, lw2);
 
-            float prevt = 0.0f;
+            double prevt = 0.0d;
             for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
-                final float t = subTs[i];
-                Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt),
+                final double t = subTs[i];
+                Helpers.subdivideQuadAt((t - prevt) / (1.0d - prevt),
                                          mid, off, mid, off, off + 4);
                 prevt = t;
             }
@@ -1324,15 +1296,15 @@ CurveBasicMonotonizer quad(final float x0, final float y0,
         }
     }
 
-    static final class PathTracer implements PathConsumer2D {
+    static final class PathTracer implements DPathConsumer2D {
         private final String prefix;
-        private PathConsumer2D out;
+        private DPathConsumer2D out;
 
         PathTracer(String name) {
             this.prefix = name + ": ";
         }
 
-        PathTracer init(PathConsumer2D out) {
+        PathTracer init(DPathConsumer2D out) {
             if (this.out != out) {
                 this.out = out;
             }
@@ -1340,29 +1312,29 @@ PathTracer init(PathConsumer2D out) {
         }
 
         @Override
-        public void moveTo(float x0, float y0) {
+        public void moveTo(double x0, double y0) {
             log("p.moveTo(" + x0 + ", " + y0 + ");");
             out.moveTo(x0, y0);
         }
 
         @Override
-        public void lineTo(float x1, float y1) {
+        public void lineTo(double x1, double y1) {
             log("p.lineTo(" + x1 + ", " + y1 + ");");
             out.lineTo(x1, y1);
         }
 
         @Override
-        public void curveTo(float x1, float y1,
-                            float x2, float y2,
-                            float x3, float y3)
+        public void curveTo(double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
         {
             log("p.curveTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2  + ", " + x3 + ", " + y3 + ");");
             out.curveTo(x1, y1, x2, y2, x3, y3);
         }
 
         @Override
-        public void quadTo(float x1, float y1,
-                           float x2, float y2) {
+        public void quadTo(double x1, double y1,
+                           double x2, double y2) {
             log("p.quadTo(" + x1 + ", " + y1 + ", " + x2 + ", " + y2  + ");");
             out.quadTo(x1, y1, x2, y2);
         }
diff --git a/src/main/java/test/ClipShapeTest.java b/src/main/java/test/ClipShapeTest.java
index 23bdcb2..e931ea4 100644
--- a/src/main/java/test/ClipShapeTest.java
+++ b/src/main/java/test/ClipShapeTest.java
@@ -1,7 +1,7 @@
 package test;
 
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021, 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
@@ -63,10 +63,6 @@
  * for paths made of either 9 lines, 4 quads, 2 cubics (random)
  * Note: Use the argument -slow to run more intensive tests (too much time)
  *
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic
@@ -135,7 +131,6 @@ static enum ShapeMode {
     static final File OUTPUT_DIR = new File(".");
 
     static final AtomicBoolean isMarlin = new AtomicBoolean();
-    static final AtomicBoolean isMarlinFloat = new AtomicBoolean();
     static final AtomicBoolean isClipRuntime = new AtomicBoolean();
 
     static {
@@ -152,8 +147,7 @@ public void publish(LogRecord record) {
                 if (msg != null) {
                     // last space to avoid matching other settings:
                     if (msg.startsWith("sun.java2d.renderer ")) {
-                        isMarlin.set(msg.contains("MarlinRenderingEngine"));
-                        isMarlinFloat.set(!msg.contains("DMarlinRenderingEngine"));
+                        isMarlin.set(msg.contains("DMarlinRenderingEngine"));
                     }
                     if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) {
                         isClipRuntime.set(msg.contains("true"));
@@ -357,10 +351,7 @@ public static void main(String[] args) {
 NbPixels [All Test setups][n: 30] sum: 232 avg: 7.733 [1 | 27]
 */
                 THRESHOLD_DELTA = 2;
-                THRESHOLD_NBPIX = (USE_DASHES) ?
-                    // float variant have higher uncertainty
-                    ((isMarlinFloat.get()) ? 30 : 6) // low for double
-                    : (isMarlinFloat.get()) ? 10 : 0;
+                THRESHOLD_NBPIX = (USE_DASHES) ? 6 : 0;
         }
 
 // Visual inspection (low threshold):
diff --git a/src/main/java/test/EndlessLoop.java b/src/main/java/test/EndlessLoop.java
index 2812921..acd1e94 100644
--- a/src/main/java/test/EndlessLoop.java
+++ b/src/main/java/test/EndlessLoop.java
@@ -18,12 +18,6 @@
 drawLine(1.0E8) [AA=true]: 1.86741 ms.
 drawLine(1.0E8) [AA=true]: 4.550681 ms.
 drawLine(1.0E8) [AA=true]: 1.9914479999999999 ms.
- * - AA org.marlin.pisces.MarlinRenderingEngine:
-drawLine(1.0E8) [AA=true]: 50.357248999999996 ms.
-drawLine(1.0E8) [AA=true]: 1.935198 ms.
-
-drawLine(7.0E15) [AA=true]: 48.779185999999996 ms.
-drawLine(7.0E15) [AA=true]: 1.946686 ms.
  */
 public class EndlessLoop extends JFrame {
 
diff --git a/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine b/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine
index e760a02..2530788 100644
--- a/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine
+++ b/src/main/resources/META-INF/services/sun.java2d.pipe.RenderingEngine
@@ -1,3 +1,2 @@
 #  Marlin Rendering Engine module
-sun.java2d.marlin.MarlinRenderingEngine
 sun.java2d.marlin.DMarlinRenderingEngine
diff --git a/src/main/resources/org/marlin/pisces/Version.properties b/src/main/resources/sun/java2d/marlin/Version.properties
similarity index 100%
rename from src/main/resources/org/marlin/pisces/Version.properties
rename to src/main/resources/sun/java2d/marlin/Version.properties
diff --git a/src/test/java/ClipShapeTest.java b/src/test/java/ClipShapeTest.java
index 057bdde..c678590 100644
--- a/src/test/java/ClipShapeTest.java
+++ b/src/test/java/ClipShapeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021, 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
@@ -61,10 +61,6 @@
  * for paths made of either 9 lines, 4 quads, 2 cubics (random)
  * Note: Use the argument -slow to run more intensive tests (too much time)
  *
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic
- * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash
  * @run main/othervm/timeout=300 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic
@@ -133,7 +129,6 @@ static enum ShapeMode {
     static final File OUTPUT_DIR = new File(".");
 
     static final AtomicBoolean isMarlin = new AtomicBoolean();
-    static final AtomicBoolean isMarlinFloat = new AtomicBoolean();
     static final AtomicBoolean isClipRuntime = new AtomicBoolean();
 
     static {
@@ -150,8 +145,7 @@ public void publish(LogRecord record) {
                 if (msg != null) {
                     // last space to avoid matching other settings:
                     if (msg.startsWith("sun.java2d.renderer ")) {
-                        isMarlin.set(msg.contains("MarlinRenderingEngine"));
-                        isMarlinFloat.set(!msg.contains("DMarlinRenderingEngine"));
+                        isMarlin.set(msg.contains("DMarlinRenderingEngine"));
                     }
                     if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) {
                         isClipRuntime.set(msg.contains("true"));
@@ -355,10 +349,7 @@ public static void main(String[] args) {
 NbPixels [All Test setups][n: 30] sum: 232 avg: 7.733 [1 | 27]
 */
                 THRESHOLD_DELTA = 2;
-                THRESHOLD_NBPIX = (USE_DASHES) ?
-                    // float variant have higher uncertainty
-                    ((isMarlinFloat.get()) ? 30 : 6) // low for double
-                    : (isMarlinFloat.get()) ? 10 : 0;
+                THRESHOLD_NBPIX = (USE_DASHES) ? 6 : 0;
         }
 
 // Visual inspection (low threshold):