Переглянути джерело

adding double double cl compilation

Nicolas Winkler 4 роки тому
батько
коміт
709514e961

+ 1 - 5
libmandel/include/ClGenerators.h

@@ -111,8 +111,6 @@ public:
     virtual ~ClGeneratorDouble(void) = default;
 
     virtual void generate(const MandelInfo& info, float* data) override;
-protected:
-    virtual std::string getKernelCode(bool smooth) const;
 };
 
 
@@ -120,12 +118,10 @@ class mnd::ClGeneratorDoubleDouble : public ClGenerator
 {
     bool smooth;
 public:
-    ClGeneratorDoubleDouble(mnd::MandelDevice& device);
+    ClGeneratorDoubleDouble(mnd::MandelDevice& device, const std::string& source = getDoubleDouble_cl());
     virtual ~ClGeneratorDoubleDouble(void) = default;
 
     virtual void generate(const MandelInfo& info, float* data) override;
-protected:
-    virtual std::string getKernelCode(bool smooth) const;
 };
 
 

+ 6 - 0
libmandel/include/IterationCompiler.h

@@ -33,6 +33,12 @@ namespace mnd
     GeneratorCollection compileFormula(mnd::MandelContext& mndCtxt,
         const IterationFormula& z0,
         const IterationFormula& zi);
+
+#ifdef WITH_OPENCL
+    std::unique_ptr<MandelGenerator> compileClFloat(const ir::Formula&, MandelDevice&);
+    std::unique_ptr<MandelGenerator> compileClDouble(const ir::Formula&, MandelDevice&);
+    std::unique_ptr<MandelGenerator> compileClDoubleDouble(const ir::Formula&, MandelDevice&);
+#endif // WITH_OPENCL
 }
 //void squareTest();
 

+ 1 - 0
libmandel/include/IterationGenerator.h

@@ -104,6 +104,7 @@ class mnd::CompiledClGeneratorDoubleDouble : public mnd::ClGeneratorDoubleDouble
 public:
     CompiledClGeneratorDoubleDouble(MandelDevice& device, const std::string& code);
 };
+
 #endif // WITH_OPENCL
 
 

+ 3 - 0
libmandel/include/OpenClCode.h

@@ -17,6 +17,9 @@ namespace mnd
     std::string getFixed64_cl();
     std::string getFixed128_cl();
     std::string getFixed512_cl();
+
+
+    std::string getDoubleDouble_prelude();
 }
 
 #endif // MANDEL_OPENCLCODE_H

+ 3 - 44
libmandel/src/ClGenerators.cpp

@@ -335,44 +335,9 @@ void ClGeneratorDouble::generate(const mnd::MandelInfo& info, float* data)
 }
 
 
-std::string ClGeneratorDouble::getKernelCode(bool smooth) const
-{
-    return
-        "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"
-        "__kernel void iterate(__global float* A, const int width, double xl, double yt, double pixelScaleX, double pixelScaleY, int max, int smooth) {\n"
-        "   int index = get_global_id(0);\n"
-        "   int x = index % width;"
-        "   int y = index / width;"
-        "   double a = x * pixelScaleX + xl;"
-        "   double b = y * pixelScaleY + yt;"
-        "   double ca = a;"
-        "   double cb = b;"
-        ""
-        "   int n = 0;"
-        "   while (n < max - 1) {"
-        "       double aa = a * a;"
-        "       double bb = b * b;"
-        "       double ab = a * b;"
-        "       if (aa + bb > 16) break;"
-        "       a = aa - bb + ca;"
-        "       b = ab + ab + cb;"
-        "       n++;"
-        "   }\n"
-        // N + 1 - log (log  |Z(N)|) / log 2
-        "   if (n >= max - 1)\n"
-        "       A[index] = max;\n"
-        "   else {"
-        "       if (smooth != 0)\n"
-        "           A[index] = ((float)n) + 1 - log(log((float)(a * a + b * b)) / 2) / log(2.0f);\n"
-        "       else\n"
-        "           A[index] = ((float)n);\n"
-        "   }"
-        "}";
-}
-
-
-ClGeneratorDoubleDouble::ClGeneratorDoubleDouble(mnd::MandelDevice& device) :
-    ClGenerator{ device, getDoubleDouble_cl(), mnd::Precision::DOUBLE_DOUBLE }
+ClGeneratorDoubleDouble::ClGeneratorDoubleDouble(mnd::MandelDevice& device,
+                                                 const std::string& code) :
+    ClGenerator{ device, code, mnd::Precision::DOUBLE_DOUBLE }
 {
     kernel = Kernel(program, "iterate");
 }
@@ -416,12 +381,6 @@ void ClGeneratorDoubleDouble::generate(const mnd::MandelInfo& info, float* data)
 }
 
 
-std::string ClGeneratorDoubleDouble::getKernelCode(bool smooth) const
-{
-    return getDoubleDouble_cl();
-}
-
-
 ClGeneratorTripleDouble::ClGeneratorTripleDouble(mnd::MandelDevice& device) :
     ClGenerator{ device, getTripleDouble_cl(), mnd::Precision::TRIPLE_DOUBLE }
 {

+ 26 - 7
libmandel/src/IterationCompiler.cpp

@@ -377,7 +377,7 @@ namespace mnd
         static double myAtan2(double y, double x)
         {
             double result = ::atan2(y, x);
-            printf("atan2(%f, %f) = %f\n", y, x, result);
+            //printf("atan2(%f, %f) = %f\n", y, x, result);
             return result;
         }
 
@@ -763,10 +763,10 @@ namespace mnd
         return std::make_unique<CompiledClGenerator>(md, compileToOpenCl(formula));
     }
 
-    std::unique_ptr<MandelGenerator> compileClDouble(const ir::Formula& formula, MandelDevice& md)
+    /*std::unique_ptr<MandelGenerator> compileClDouble(const ir::Formula& formula, MandelDevice& md)
     {
         return std::make_unique<CompiledClGeneratorDouble>(md, compileToOpenClDouble(formula));
-    }
+    }*/
 #endif
 
     std::vector<std::unique_ptr<mnd::MandelGenerator>> compileCpu(mnd::MandelContext& mndCtxt,
@@ -818,12 +818,31 @@ namespace mnd
         printf("ir: %s\n", irf.toString().c_str()); fflush(stdout);
 
 #ifdef WITH_OPENCL
-        auto fl = compileCl(irf, dev);
-        vec.push_back(std::move(fl));
+        try {
+            auto fl = compileClFloat(irf, dev);
+            vec.push_back(std::move(fl));
+        }
+        catch(const std::string& err) {
+            printf("cl error: %s", err.c_str());
+        }
+
         if (dev.supportsDouble()) {
             irf.clearNodeData();
-            auto fld = compileClDouble(irf, dev);
-            vec.push_back(std::move(fld));
+            try {
+                auto fld = compileClDouble(irf, dev);
+                vec.push_back(std::move(fld));
+            }
+            catch(const std::string& err) {
+                printf("cl error: %s", err.c_str());
+            }
+            irf.clearNodeData();
+            try {
+                auto fldd = compileClDoubleDouble(irf, dev);
+                vec.push_back(std::move(fldd));
+            }
+            catch(const std::string& err) {
+                printf("cl error: %s", err.c_str());
+            }
         }
 #endif // WITH_OPENCL
 

+ 273 - 80
libmandel/src/IterationCompilerCl.cpp

@@ -15,25 +15,225 @@
 #include <string>
 
 using namespace std::string_literals;
+
+#ifdef WITH_OPENCL
 namespace mnd
 {
-    struct OpenClDDVisitor
+    struct FloatTraits
     {
-        int varnameCounter = 0;
-        std::stringstream code;
-        std::string floatTypeName;
+        static std::string getPrelude() {
+            return
+                "__kernel void iterate(__global float* A, const int width,"
+                "                      float xl, float yt, float pixelScaleX, float pixelScaleY,"
+                "                      int max, int smooth, int julia, float juliaX, float juliaY) {\n"
+                "   int index = get_global_id(0);\n"
+                "   int ix = index % width;\n"
+                "   int iy = index / width;\n"
+                "   float c_re = ix * pixelScaleX + xl;\n"
+                "   float c_im = iy * pixelScaleY + yt;\n";
+        }
 
-        OpenClDDVisitor(int startVarname, const std::string& floatTypeName) :
-            varnameCounter{ startVarname },
-            floatTypeName{ floatTypeName }
-        {
+        static std::string getEscapeCheck() {
+            return "   if (z_re * z_re + z_im * z_im >= 16) break;";
         }
 
-        std::string createVarname(void)
-        {
-            return "tmp"s + std::to_string(varnameCounter++);
+        static std::string getPostlude() {
+            return
+                "   if (n >= max - 1) {\n"
+                "       A[index] = max;\n"
+                "   }\n"
+                "   else {\n"
+                "       A[index] = ((float) n);\n"
+                "   }\n"
+                "}\n";
+        }
+
+        static std::string getTypeName() {
+            return "float";
+        }
+
+        static std::string constant(const mnd::Real& v) {
+            return "((float) "s + std::to_string(mnd::convert<float>(v)) + ")";
+        }
+        static std::string neg(const std::string& v) {
+            return "-"s + v;
+        }
+        static std::string add(const std::string& a, const std::string& b) {
+            return "("s + a + ") + (" + b + ")";
+        }
+        static std::string sub(const std::string& a, const std::string& b) {
+            return "("s + a + ") - (" + b + ")";
+        }
+        static std::string mul(const std::string& a, const std::string& b) {
+            return "("s + a + ") * (" + b + ")";
+        }
+        static std::string div(const std::string& a, const std::string& b) {
+            return "("s + a + ") / (" + b + ")";
+        }
+        static std::string atan2(const std::string& a, const std::string& b) {
+            return "atan2("s + a + ", " + b + ")";
+        }
+        static std::string pow(const std::string& a, const std::string& b) {
+            return "pow("s + a + ", " + b + ")";
+        }
+        static std::string cos(const std::string& x) {
+            return "cos("s + x + ")";
+        }
+        static std::string sin(const std::string& x) {
+            return "sin("s + x + ")";
+        }
+        static std::string exp(const std::string& x) {
+            return "exp("s + x + ")";
+        }
+        static std::string ln(const std::string& x) {
+            return "log("s + x + ")";
+        }
+    };
+
+    struct DoubleTraits
+    {
+        static std::string getPrelude() {
+            return
+                "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"
+                "__kernel void iterate(__global float* A, const int width,"
+                "                      double xl, double yt, double pixelScaleX, double pixelScaleY,"
+                "                      int max, int smooth, int julia, double juliaX, double juliaY) {\n"
+                "   int index = get_global_id(0);\n"
+                "   int ix = index % width;\n"
+                "   int iy = index / width;\n"
+                "   double c_re = ix * pixelScaleX + xl;\n"
+                "   double c_im = iy * pixelScaleY + yt;\n";
+        }
+
+        static std::string getEscapeCheck() {
+            return "   if (z_re * z_re + z_im * z_im >= 16) break;";
+        }
+
+        static std::string getPostlude() {
+            return
+                "   if (n >= max - 1) {\n"
+                "       A[index] = max;\n"
+                "   }\n"
+                "   else {\n"
+                "       A[index] = ((float) n);\n"
+                "   }\n"
+                "}\n";
+        }
+
+        static std::string getTypeName() {
+            return "double";
+        }
+
+        static std::string constant(const mnd::Real& v) {
+            return mnd::toString(v);
+        }
+        static std::string neg(const std::string& v) {
+            return "-"s + v;
+        }
+        static std::string add(const std::string& a, const std::string& b) {
+            return "("s + a + ") + (" + b + ")";
+        }
+        static std::string sub(const std::string& a, const std::string& b) {
+            return "("s + a + ") - (" + b + ")";
+        }
+        static std::string mul(const std::string& a, const std::string& b) {
+            return "("s + a + ") * (" + b + ")";
+        }
+        static std::string div(const std::string& a, const std::string& b) {
+            return "("s + a + ") / (" + b + ")";
+        }
+        static std::string atan2(const std::string& a, const std::string& b) {
+            return "atan2("s + a + ", " + b + ")";
+        }
+        static std::string pow(const std::string& a, const std::string& b) {
+            return "pow("s + a + ", " + b + ")";
+        }
+        static std::string cos(const std::string& x) {
+            return "cos("s + x + ")";
+        }
+        static std::string sin(const std::string& x) {
+            return "sin("s + x + ")";
+        }
+        static std::string exp(const std::string& x) {
+            return "exp("s + x + ")";
+        }
+        static std::string ln(const std::string& x) {
+            return "log("s + x + ")";
+        }
+    };
+
+    struct DoubleDoubleTraits
+    {
+        static std::string getPrelude() {
+            return getDoubleDouble_prelude();
+        }
+
+        static std::string getEscapeCheck() {
+            return "   if (z_re.s0 * z_re.s0 + z_im.s0 * z_im.s0 >= 16) break;";
         }
 
+        static std::string getPostlude() {
+            return
+                "   if (n >= max - 1) {\n"
+                "       A[index] = max;\n"
+                "   }\n"
+                "   else {\n"
+                "       A[index] = ((float) n);\n"
+                "   }\n"
+                "}\n";
+        }
+
+        static std::string getTypeName() {
+            return "double2";
+        }
+
+        static std::string constant(const mnd::Real& v) {
+            mnd::DoubleDouble dd = mnd::convert<mnd::DoubleDouble>(v);
+            return "(double2)(" + std::to_string(dd.x[0]) + ", " + std::to_string(dd.x[1]) + ")";
+        }
+        static std::string neg(const std::string& v) {
+            return "(-"s + v + ".s0, -" + v + ".s1)";
+        }
+        static std::string add(const std::string& a, const std::string& b) {
+            return "add("s + a + ", " + b + ")";
+        }
+        static std::string sub(const std::string& a, const std::string& b) {
+            return "add("s + a + ", " + neg(b) + ")";
+        }
+        static std::string mul(const std::string& a, const std::string& b) {
+            return "mul("s + a + ", " + b + ")";
+        }
+        static std::string div(const std::string& a, const std::string& b) {
+            return "div("s + a + ") / (" + b + ")";
+        }
+        static std::string atan2(const std::string& a, const std::string& b) {
+            return "atan2("s + a + ", " + b + ")";
+        }
+        static std::string pow(const std::string& a, const std::string& b) {
+            return "pow("s + a + ", " + b + ")";
+        }
+        static std::string cos(const std::string& x) {
+            return "cos("s + x + ")";
+        }
+        static std::string sin(const std::string& x) {
+            return "sin("s + x + ")";
+        }
+        static std::string exp(const std::string& x) {
+            return "exp("s + x + ")";
+        }
+        static std::string ln(const std::string& x) {
+            return "log("s + x + ")";
+        }
+    };
+
+    template<typename Traits>
+    class ClTranspiler
+    {
+        int varnameCounter = 0;
+        std::stringstream code;
+    public:
+        ClTranspiler(void) = default;
+
         std::string visitNode(ir::Node& node)
         {
             auto& nodeData = std::visit([] (auto& n) -> std::any& { return n.nodeData; }, node);
@@ -44,7 +244,7 @@ namespace mnd
                 std::string value = std::visit(*this, node);
                 if (!std::get_if<ir::Variable>(&node) && !std::get_if<ir::Constant>(&node)) {
                     std::string varname = createVarname();
-                    code << floatTypeName << " " << varname << " = " << value << ";" << std::endl;
+                    code << Traits::getTypeName() << " " << varname << " = " << value << ";" << std::endl;
                     nodeData = varname;
                     return varname;
                 }
@@ -52,113 +252,106 @@ namespace mnd
             }
         }
 
-        std::string operator()(const ir::Constant& c) {
-            return std::to_string(mnd::convert<double>(c.value)) + ((floatTypeName == "float") ? "f" : "");
+        std::string createVarname(void)
+        {
+            return "tmp"s + std::to_string(varnameCounter++);
         }
 
+        std::string compile(const ir::Formula& formula)
+        {
+            code << Traits::getPrelude();
+
+            std::string startA = visitNode(*formula.startA);
+            std::string startB = visitNode(*formula.startB);
+
+            code <<
+            "    " << Traits::getTypeName() << " z_re = " << startA << ";\n" <<
+            "    " << Traits::getTypeName() << " z_im = " << startB << ";\n" <<
+            "    int n = 0;\n"
+            "    while (n < max - 1) {\n";
+
+            std::string newA = visitNode(*formula.newA);
+            std::string newB = visitNode(*formula.newB);
+
+            code <<
+            "    z_re = " << newA << ";\n" <<
+            "    z_im = " << newB << ";\n";
+
+            code << Traits::getEscapeCheck();
+            code <<
+            "    n++;\n"
+            "}\n";
+
+            code << Traits::getPostlude();
+            return code.str();
+        }
+
+        std::string operator()(const ir::Constant& c) {
+            return Traits::constant(c.value);
+        }
         std::string operator()(const ir::Variable& v) {
             return v.name;
         }
-
         std::string operator()(const ir::Negation& n) {
-            return "-("s + visitNode(*n.value) + ")";
+            return Traits::neg(visitNode(*n.value));
         }
-
         std::string operator()(const ir::Addition& a) {
-            return "("s + visitNode(*a.left) + ") + (" + visitNode(*a.right) + ")";
+            return Traits::add(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Subtraction& a) {
-            return "("s + visitNode(*a.left) + ") - (" + visitNode(*a.right) + ")";
+            return Traits::sub(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Multiplication& a) {
-            return "("s + visitNode(*a.left) + ") * (" + visitNode(*a.right) + ")";
+            return Traits::mul(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Division& a) {
-            return "("s + visitNode(*a.left) + ") / (" + visitNode(*a.right) + ")";
+            return Traits::div(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Atan2& a) {
-            return "atan2("s + visitNode(*a.left) + ", " + visitNode(*a.right) + ")";
+            return Traits::atan2(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Pow& a) {
-            return "pow("s + visitNode(*a.left) + ", " + visitNode(*a.right) + ")";
+            return Traits::pow(visitNode(*a.left), visitNode(*a.right));
         }
-
         std::string operator()(const ir::Cos& a) {
-            return "cos("s + visitNode(*a.value) + ")";
+            return Traits::cos(visitNode(*a.value));
         }
 
         std::string operator()(const ir::Sin& a) {
-            return "sin("s + visitNode(*a.value) + ")";
+            return Traits::sin(visitNode(*a.value));
         }
 
         std::string operator()(const ir::Exp& a) {
-            return "exp("s + visitNode(*a.value) + ")";
+            return Traits::exp(visitNode(*a.value));
         }
 
         std::string operator()(const ir::Ln& a) {
-            return "log("s + visitNode(*a.value) + ")";
+            return Traits::ln(visitNode(*a.value));
         }
     };
 
-    std::string compileToOpenClDoubleDouble(const ir::Formula& formula)
+    std::unique_ptr<MandelGenerator> compileClFloat(const ir::Formula& formula, MandelDevice& md)
     {
-        OpenClDDVisitor z0Visitor{ 0, "double2" };
-        std::string startA = z0Visitor.visitNode(*formula.startA);
-        std::string startB = z0Visitor.visitNode(*formula.startB);
-
-        OpenClDDVisitor ocv{ z0Visitor.varnameCounter, "double2" };
-        std::string newA = ocv.visitNode(*formula.newA);
-        std::string newB = ocv.visitNode(*formula.newB);
-
-        std::string prelude =
-            "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"
-            "__kernel void iterate(__global float* A, const int width, double xl, double yt, double pixelScaleX, double pixelScaleY, int max, int smooth, int julia, double juliaX, double juliaY) {\n"
-            "   int index = get_global_id(0);\n"
-            "   int ix = index % width;\n"
-            "   int iy = index / width;\n"
-            "   double c_re = ix * pixelScaleX + xl;\n"
-            "   double c_im = iy * pixelScaleY + yt;\n";
-        prelude += z0Visitor.code.str() +
-            "   double z_re = " + startA + ";\n" +
-            "   double z_im = " + startB + ";\n" +
-            "\n"
-            "   int n = 0;\n"
-            "   while (n < max - 1) {\n";
-
-        std::string after = 
-            "       if (z_re * z_re + z_im * z_im > 16) break;\n"
-            "       n++;\n"
-            "   }\n"
-            "   if (n >= max - 1) {\n"
-            "       A[index] = max;\n"
-            "   }\n"
-            "   else {\n"
-            "       A[index] = ((float)n);\n"
-            "   }\n"
-            "}\n";
-
+        ClTranspiler<FloatTraits> clt;
+        std::string code = clt.compile(formula);
+        return std::make_unique<CompiledClGenerator>(md, code);
+    }
 
-        std::string code = prelude + ocv.code.str();
-        code += "z_re = " + newA + ";\n";
-        code += "z_im = " + newB + ";\n";
-        code += after;
-        //code = mnd::getFloat_cl();
-        printf("cld: %s\n", code.c_str()); fflush(stdout);
-        return code;
+    std::unique_ptr<MandelGenerator> compileClDouble(const ir::Formula& formula, MandelDevice& md)
+    {
+        ClTranspiler<DoubleTraits> clt;
+        std::string code = clt.compile(formula);
+        return std::make_unique<CompiledClGeneratorDouble>(md, code);
     }
 
-#ifdef WITH_OPENCL
     std::unique_ptr<MandelGenerator> compileClDoubleDouble(const ir::Formula& formula, MandelDevice& md)
     {
-        return nullptr;
-        //return std::make_unique<CompiledClGeneratorDouble>(md, compileToOpenClDouble(formula));
+        ClTranspiler<DoubleDoubleTraits> clt;
+        std::string code = clt.compile(formula);
+        printf("doubledouble cl code: %s", code.c_str());
+        return std::make_unique<CompiledClGeneratorDoubleDouble>(md, code);
     }
-#endif
 }
 
+#endif
 

+ 1 - 1
libmandel/src/IterationGenerator.cpp

@@ -261,7 +261,7 @@ CompiledClGeneratorDouble::CompiledClGeneratorDouble(mnd::MandelDevice& device,
 
 
 CompiledClGeneratorDoubleDouble::CompiledClGeneratorDoubleDouble(mnd::MandelDevice& device, const std::string& code) :
-    ClGeneratorDoubleDouble{ device } // TODO make this const take code as arg
+    ClGeneratorDoubleDouble{ device, code }
 {
 }
 

+ 6 - 1
libmandel/src/IterationIR.cpp

@@ -166,7 +166,12 @@ namespace mnd
 
             if (p.integerExponent) {
                 if (auto* ex = std::get_if<ir::Constant>(c)) {
-                    return intPow({ a, b }, int(ex->value));
+                    if (int(ex->value) <= 20) {
+                        return intPow({ a, b }, int(ex->value));
+                    }
+                    else {
+                        return realPow({ a, b }, c);
+                    }
                 }
             }
             if (p.realExponent) {

+ 6 - 0
libmandel/src/OpenClCode.cpp

@@ -14,6 +14,8 @@
 #include "opencl/fixed128.h"
 #include "opencl/fixed512.h"
 
+#include "opencl_template/doubledouble_prelude.h"
+
 namespace mnd
 {
     std::string getFloat_cl() {
@@ -63,6 +65,10 @@ namespace mnd
     std::string getFixed512_cl() {
         return std::string{ (char*) fixed512_cl, fixed512_cl_len };
     }
+
+    std::string getDoubleDouble_prelude() {
+        return std::string{ (char*) doubledouble_prelude_cl, doubledouble_prelude_cl_len };
+    }
 }
 
 

+ 0 - 231
libmandel/src/opencl_template/doubledouble.h

@@ -1,231 +0,0 @@
-unsigned char doubledouble_cl[] = {
-  0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x4f, 0x50, 0x45, 0x4e,
-  0x43, 0x4c, 0x20, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e,
-  0x20, 0x63, 0x6c, 0x5f, 0x6b, 0x68, 0x72, 0x5f, 0x66, 0x70, 0x36, 0x34,
-  0x20, 0x3a, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0d, 0x0a, 0x0d,
-  0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x64,
-  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75,
-  0x62, 0x6c, 0x65, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
-  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x20, 0x3d,
-  0x20, 0x61, 0x20, 0x2b, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x62, 0x20, 0x3d,
-  0x20, 0x73, 0x20, 0x2d, 0x20, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x65, 0x20, 0x3d, 0x20,
-  0x28, 0x61, 0x20, 0x2d, 0x20, 0x28, 0x73, 0x20, 0x2d, 0x20, 0x62, 0x62,
-  0x29, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x62, 0x20, 0x2d, 0x20, 0x62, 0x62,
-  0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
-  0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29,
-  0x28, 0x73, 0x2c, 0x20, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a,
-  0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75,
-  0x62, 0x6c, 0x65, 0x32, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x54, 0x77,
-  0x6f, 0x53, 0x75, 0x6d, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x29,
-  0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x2b, 0x20, 0x62,
-  0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x20, 0x2d, 0x20, 0x28, 0x73,
-  0x20, 0x2d, 0x20, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
-  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x29, 0x28, 0x73, 0x2c, 0x20, 0x65, 0x29, 0x3b, 0x0d,
-  0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
-  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x74, 0x77, 0x6f,
-  0x50, 0x72, 0x6f, 0x64, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x29,
-  0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x2a, 0x20, 0x62,
-  0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x61, 0x2c,
-  0x20, 0x62, 0x2c, 0x20, 0x2d, 0x70, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
-  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f,
-  0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x70, 0x2c, 0x20, 0x65, 0x29,
-  0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69,
-  0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x6d,
-  0x75, 0x6c, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61,
-  0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x62, 0x29,
-  0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x50,
-  0x72, 0x6f, 0x64, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x62, 0x2e,
-  0x73, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x2e,
-  0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x20,
-  0x2a, 0x20, 0x62, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x20, 0x61, 0x2e, 0x73,
-  0x31, 0x20, 0x2a, 0x20, 0x62, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71,
-  0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x70,
-  0x2e, 0x73, 0x30, 0x2c, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0d,
-  0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
-  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x73, 0x71, 0x28,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x29, 0x20, 0x7b,
-  0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
-  0x32, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x50, 0x72, 0x6f,
-  0x64, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x61, 0x2e, 0x73, 0x30,
-  0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x32, 0x20, 0x2a, 0x20, 0x61,
-  0x2e, 0x73, 0x30, 0x20, 0x2a, 0x20, 0x61, 0x2e, 0x73, 0x31, 0x3b, 0x0d,
-  0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d,
-  0x20, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
-  0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f,
-  0x53, 0x75, 0x6d, 0x28, 0x70, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x70, 0x2e,
-  0x73, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69,
-  0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
-  0x32, 0x20, 0x61, 0x64, 0x64, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
-  0x32, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
-  0x20, 0x62, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
-  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x73, 0x65, 0x20, 0x3d, 0x20,
-  0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x2c,
-  0x20, 0x62, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x73, 0x65, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x61, 0x2e,
-  0x73, 0x31, 0x20, 0x2b, 0x20, 0x62, 0x2e, 0x73, 0x31, 0x3b, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71,
-  0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x73,
-  0x65, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x73, 0x65, 0x2e, 0x73, 0x31, 0x29,
-  0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69,
-  0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x6d,
-  0x75, 0x6c, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x28, 0x64, 0x6f, 0x75,
-  0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x70, 0x20, 0x3d,
-  0x20, 0x74, 0x77, 0x6f, 0x50, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x2e, 0x73,
-  0x30, 0x2c, 0x20, 0x62, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
-  0x70, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x61, 0x2e, 0x73, 0x31,
-  0x20, 0x2a, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
-  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x54,
-  0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x70, 0x2e, 0x73, 0x30, 0x2c, 0x20,
-  0x70, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d,
-  0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f,
-  0x69, 0x64, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x28, 0x5f,
-  0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x66, 0x6c, 0x6f, 0x61,
-  0x74, 0x2a, 0x20, 0x41, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20,
-  0x69, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
-  0x75, 0x62, 0x6c, 0x65, 0x20, 0x78, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75,
-  0x62, 0x6c, 0x65, 0x20, 0x78, 0x32, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x20, 0x79, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x20, 0x79, 0x32, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x70, 0x77, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x70, 0x77, 0x32, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x70, 0x68, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
-  0x70, 0x68, 0x32, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x78,
-  0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68,
-  0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x69, 0x6e, 0x74, 0x20, 0x6a, 0x75, 0x6c, 0x69, 0x61, 0x2c, 0x20,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x78, 0x31, 0x2c, 0x20,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x78, 0x32, 0x2c, 0x20,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x79, 0x31, 0x2c, 0x20,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x79, 0x32, 0x29, 0x20,
-  0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69,
-  0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67,
-  0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b,
-  0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x78,
-  0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77,
-  0x69, 0x64, 0x74, 0x68, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
-  0x6e, 0x74, 0x20, 0x70, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x65,
-  0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0d, 0x0a,
-  0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
-  0x32, 0x20, 0x78, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x29, 0x28, 0x78, 0x31, 0x2c, 0x20, 0x78, 0x32, 0x29,
-  0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x32, 0x20, 0x79, 0x74, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75,
-  0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x79, 0x31, 0x2c, 0x20, 0x79, 0x32,
-  0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61,
-  0x6c, 0x65, 0x58, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x32, 0x29, 0x28, 0x70, 0x77, 0x31, 0x2c, 0x20, 0x70, 0x77, 0x32,
-  0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61,
-  0x6c, 0x65, 0x59, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x32, 0x29, 0x28, 0x70, 0x68, 0x31, 0x2c, 0x20, 0x70, 0x68, 0x32,
-  0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x64, 0x64, 0x28,
-  0x6d, 0x75, 0x6c, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x28, 0x70, 0x69,
-  0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x58, 0x2c, 0x20, 0x28,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x70, 0x78, 0x29, 0x2c,
-  0x20, 0x78, 0x6c, 0x29, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x69, 0x78,
-  0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x58, 0x20, 0x2a, 0x20, 0x70,
-  0x78, 0x20, 0x2b, 0x20, 0x78, 0x6c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
-  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x62, 0x20, 0x3d, 0x20,
-  0x61, 0x64, 0x64, 0x28, 0x6d, 0x75, 0x6c, 0x44, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65,
-  0x59, 0x2c, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x29, 0x20,
-  0x70, 0x79, 0x29, 0x2c, 0x20, 0x79, 0x74, 0x29, 0x3b, 0x20, 0x2f, 0x2f,
-  0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x59,
-  0x20, 0x2a, 0x20, 0x70, 0x79, 0x20, 0x2b, 0x20, 0x79, 0x74, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20,
-  0x63, 0x61, 0x20, 0x3d, 0x20, 0x6a, 0x75, 0x6c, 0x69, 0x61, 0x20, 0x21,
-  0x3d, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x28, 0x28, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x29, 0x20, 0x28, 0x6a, 0x78, 0x31, 0x2c, 0x20, 0x6a,
-  0x78, 0x32, 0x29, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x3b, 0x0d, 0x0a, 0x20,
-  0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x63,
-  0x62, 0x20, 0x3d, 0x20, 0x6a, 0x75, 0x6c, 0x69, 0x61, 0x20, 0x21, 0x3d,
-  0x20, 0x30, 0x20, 0x3f, 0x20, 0x28, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c,
-  0x65, 0x32, 0x29, 0x20, 0x28, 0x6a, 0x79, 0x31, 0x2c, 0x20, 0x6a, 0x79,
-  0x32, 0x29, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
-  0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x20,
-  0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68,
-  0x69, 0x6c, 0x65, 0x20, 0x28, 0x6e, 0x20, 0x3c, 0x20, 0x6d, 0x61, 0x78,
-  0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
-  0x20, 0x61, 0x61, 0x20, 0x3d, 0x20, 0x6d, 0x75, 0x6c, 0x28, 0x61, 0x2c,
-  0x20, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x62, 0x62,
-  0x20, 0x3d, 0x20, 0x6d, 0x75, 0x6c, 0x28, 0x62, 0x2c, 0x20, 0x62, 0x29,
-  0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
-  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x62, 0x20, 0x3d, 0x20,
-  0x6d, 0x75, 0x6c, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x29, 0x3b, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
-  0x6c, 0x65, 0x32, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x62, 0x62, 0x20,
-  0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28,
-  0x2d, 0x62, 0x62, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x2d, 0x62, 0x62, 0x2e,
-  0x73, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x64, 0x64, 0x28, 0x61, 0x64,
-  0x64, 0x28, 0x61, 0x61, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x62,
-  0x62, 0x29, 0x2c, 0x20, 0x63, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x61, 0x64,
-  0x64, 0x28, 0x61, 0x64, 0x64, 0x28, 0x61, 0x62, 0x2c, 0x20, 0x61, 0x62,
-  0x29, 0x2c, 0x20, 0x63, 0x62, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x61, 0x2e,
-  0x73, 0x30, 0x20, 0x2b, 0x20, 0x62, 0x62, 0x2e, 0x73, 0x30, 0x20, 0x3e,
-  0x20, 0x31, 0x36, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0d,
-  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x2b, 0x2b,
-  0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
-  0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x20, 0x2b, 0x20, 0x31,
-  0x20, 0x2d, 0x20, 0x6c, 0x6f, 0x67, 0x20, 0x28, 0x6c, 0x6f, 0x67, 0x20,
-  0x20, 0x7c, 0x5a, 0x28, 0x4e, 0x29, 0x7c, 0x29, 0x20, 0x2f, 0x20, 0x6c,
-  0x6f, 0x67, 0x20, 0x32, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
-  0x20, 0x28, 0x6e, 0x20, 0x3e, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x2d,
-  0x20, 0x31, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x41, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20,
-  0x6d, 0x61, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
-  0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68,
-  0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x5b, 0x69, 0x6e,
-  0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x66, 0x6c, 0x6f,
-  0x61, 0x74, 0x29, 0x20, 0x6e, 0x29, 0x20, 0x2b, 0x20, 0x31, 0x20, 0x2d,
-  0x20, 0x6c, 0x6f, 0x67, 0x28, 0x6c, 0x6f, 0x67, 0x28, 0x61, 0x2e, 0x73,
-  0x30, 0x20, 0x2a, 0x20, 0x61, 0x2e, 0x73, 0x30, 0x20, 0x2b, 0x20, 0x62,
-  0x2e, 0x73, 0x30, 0x20, 0x2a, 0x20, 0x62, 0x2e, 0x73, 0x30, 0x29, 0x20,
-  0x2f, 0x20, 0x32, 0x29, 0x20, 0x2f, 0x20, 0x6c, 0x6f, 0x67, 0x28, 0x32,
-  0x2e, 0x30, 0x66, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x5b, 0x69,
-  0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x66, 0x6c,
-  0x6f, 0x61, 0x74, 0x29, 0x20, 0x6e, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
-  0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-  0x20, 0x20, 0x41, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d,
-  0x20, 0x28, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x6e, 0x29, 0x20,
-  0x2b, 0x20, 0x31, 0x20, 0x2d, 0x20, 0x28, 0x61, 0x20, 0x2a, 0x20, 0x61,
-  0x20, 0x2b, 0x20, 0x62, 0x20, 0x2a, 0x20, 0x62, 0x20, 0x2d, 0x20, 0x31,
-  0x36, 0x29, 0x20, 0x2f, 0x20, 0x28, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20,
-  0x31, 0x36, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f,
-  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41,
-  0x5b, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
-  0x69, 0x64, 0x28, 0x30, 0x29, 0x5d, 0x20, 0x3d, 0x20, 0x35, 0x3b, 0x0d,
-  0x0a, 0x7d
-};
-unsigned int doubledouble_cl_len = 2726;

+ 12 - 29
libmandel/src/opencl_template/doubledouble.cl → libmandel/src/opencl_template/doubledouble_prelude.cl

@@ -44,6 +44,16 @@ inline double2 mulDouble(double2 a, double b) {
     return quickTwoSum(p.s0, p.s1);
 }
 
+inline double2 div(double2 a, double2 b) {
+    double q1 = a.s0 / b.s0;
+    double2 r = mulDouble(b, q1);
+    double2 s = twoSum(a.s0, -r.s0);
+    s.s1 -= r.s1;
+    s.s1 += a.s1;
+    double q2 = (s.s0 + s.s1) / b.s0;
+    return quickTwoSum(q1, q2);
+}
+
 __kernel void iterate(__global float* A, const int width,
                       double x1, double x2, double y1, double y2,
                       double pw1, double pw2, double ph1, double ph2, int max, int smooth,
@@ -56,33 +66,6 @@ __kernel void iterate(__global float* A, const int width,
     double2 yt = (double2)(y1, y2);
     double2 pixelScaleX = (double2)(pw1, pw2);
     double2 pixelScaleY = (double2)(ph1, ph2);
-    double2 a = add(mulDouble(pixelScaleX, (double) px), xl); // pixelScaleX * px + xl
-    double2 b = add(mulDouble(pixelScaleY, (double) py), yt); // pixelScaleY * py + yt
-    double2 ca = julia != 0 ? ((double2) (jx1, jx2)) : a;
-    double2 cb = julia != 0 ? ((double2) (jy1, jy2)) : b;
-
-
-    int n = 0;
-    while (n < max - 1) {
-        double2 aa = mul(a, a);
-        double2 bb = mul(b, b);
-        double2 ab = mul(a, b);
-        double2 minusbb = (double2)(-bb.s0, -bb.s1);
-        a = add(add(aa, minusbb), ca);
-        b = add(add(ab, ab), cb);
-        if (aa.s0 + bb.s0 > 16) break;
-        n++;
-    }
+    double2 c_re = add(mulDouble(pixelScaleX, (double) px), xl); // pixelScaleX * px + xl
+    double2 c_im = add(mulDouble(pixelScaleY, (double) py), yt); // pixelScaleY * py + yt
 
-    // N + 1 - log (log  |Z(N)|) / log 2
-    if (n >= max - 1)
-        A[index] = max;
-    else {
-        if (smooth != 0)
-            A[index] = ((float) n) + 1 - log(log(a.s0 * a.s0 + b.s0 * b.s0) / 2) / log(2.0f);
-        else
-            A[index] = ((float) n);
-    }
-    //               A[index] = ((float)n) + 1 - (a * a + b * b - 16) / (256 - 16);
-    //           A[get_global_id(0)] = 5;
-}

+ 175 - 0
libmandel/src/opencl_template/doubledouble_prelude.h

@@ -0,0 +1,175 @@
+unsigned char doubledouble_prelude_cl[] = {
+  0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x4f, 0x50, 0x45, 0x4e,
+  0x43, 0x4c, 0x20, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e,
+  0x20, 0x63, 0x6c, 0x5f, 0x6b, 0x68, 0x72, 0x5f, 0x66, 0x70, 0x36, 0x34,
+  0x20, 0x3a, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x0a, 0x69,
+  0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x32, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x20,
+  0x2b, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x62, 0x62, 0x20, 0x3d, 0x20, 0x73, 0x20, 0x2d,
+  0x20, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x28, 0x61, 0x20, 0x2d, 0x20,
+  0x28, 0x73, 0x20, 0x2d, 0x20, 0x62, 0x62, 0x29, 0x29, 0x20, 0x2b, 0x20,
+  0x28, 0x62, 0x20, 0x2d, 0x20, 0x62, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x73, 0x2c, 0x20, 0x65, 0x29,
+  0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x71, 0x75, 0x69, 0x63,
+  0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x2b,
+  0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x65, 0x20, 0x3d, 0x20, 0x62, 0x20, 0x2d, 0x20, 0x28,
+  0x73, 0x20, 0x2d, 0x20, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x32, 0x29, 0x28, 0x73, 0x2c, 0x20, 0x65, 0x29, 0x3b, 0x0a,
+  0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x74, 0x77, 0x6f, 0x50, 0x72, 0x6f,
+  0x64, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x70,
+  0x20, 0x3d, 0x20, 0x61, 0x20, 0x2a, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x65, 0x20, 0x3d,
+  0x20, 0x66, 0x6d, 0x61, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x2d,
+  0x70, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29,
+  0x28, 0x70, 0x2c, 0x20, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69,
+  0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x32, 0x20, 0x6d, 0x75, 0x6c, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x32, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
+  0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x77,
+  0x6f, 0x50, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x2c, 0x20,
+  0x62, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70,
+  0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x28, 0x61, 0x2e, 0x73, 0x30,
+  0x20, 0x2a, 0x20, 0x62, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x20, 0x61, 0x2e,
+  0x73, 0x31, 0x20, 0x2a, 0x20, 0x62, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71,
+  0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x70,
+  0x2e, 0x73, 0x30, 0x2c, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0a,
+  0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x73, 0x71, 0x28, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x70, 0x20,
+  0x3d, 0x20, 0x74, 0x77, 0x6f, 0x50, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x2e,
+  0x73, 0x30, 0x2c, 0x20, 0x61, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x65, 0x20,
+  0x3d, 0x20, 0x32, 0x20, 0x2a, 0x20, 0x61, 0x2e, 0x73, 0x30, 0x20, 0x2a,
+  0x20, 0x61, 0x2e, 0x73, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70,
+  0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x65, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x69,
+  0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x70, 0x2e, 0x73,
+  0x30, 0x2c, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0a, 0x7d, 0x0a,
+  0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x32, 0x20, 0x61, 0x64, 0x64, 0x28, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x32, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x32, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x73, 0x65, 0x20, 0x3d,
+  0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x61, 0x2e, 0x73, 0x30,
+  0x2c, 0x20, 0x62, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x65, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x61, 0x2e,
+  0x73, 0x31, 0x20, 0x2b, 0x20, 0x62, 0x2e, 0x73, 0x31, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75,
+  0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x73, 0x65,
+  0x2e, 0x73, 0x30, 0x2c, 0x20, 0x73, 0x65, 0x2e, 0x73, 0x31, 0x29, 0x3b,
+  0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x6d, 0x75, 0x6c, 0x44, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
+  0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62,
+  0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x32, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x50,
+  0x72, 0x6f, 0x64, 0x28, 0x61, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x62, 0x29,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x20, 0x2b,
+  0x3d, 0x20, 0x61, 0x2e, 0x73, 0x31, 0x20, 0x2a, 0x20, 0x62, 0x3b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71,
+  0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x70,
+  0x2e, 0x73, 0x30, 0x2c, 0x20, 0x70, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0a,
+  0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x64, 0x69, 0x76, 0x28, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x32, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x71, 0x31, 0x20,
+  0x3d, 0x20, 0x61, 0x2e, 0x73, 0x30, 0x20, 0x2f, 0x20, 0x62, 0x2e, 0x73,
+  0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x32, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x75, 0x6c, 0x44, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x28, 0x62, 0x2c, 0x20, 0x71, 0x31, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
+  0x20, 0x73, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28,
+  0x61, 0x2e, 0x73, 0x30, 0x2c, 0x20, 0x2d, 0x72, 0x2e, 0x73, 0x30, 0x29,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x2e, 0x73, 0x31, 0x20, 0x2d,
+  0x3d, 0x20, 0x72, 0x2e, 0x73, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x61, 0x2e, 0x73, 0x31,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x20, 0x71, 0x32, 0x20, 0x3d, 0x20, 0x28, 0x73, 0x2e, 0x73, 0x30, 0x20,
+  0x2b, 0x20, 0x73, 0x2e, 0x73, 0x31, 0x29, 0x20, 0x2f, 0x20, 0x62, 0x2e,
+  0x73, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x54, 0x77, 0x6f, 0x53,
+  0x75, 0x6d, 0x28, 0x71, 0x31, 0x2c, 0x20, 0x71, 0x32, 0x29, 0x3b, 0x0a,
+  0x7d, 0x0a, 0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20,
+  0x76, 0x6f, 0x69, 0x64, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65,
+  0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x66, 0x6c,
+  0x6f, 0x61, 0x74, 0x2a, 0x20, 0x41, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x78, 0x31, 0x2c, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x20, 0x78, 0x32, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x79, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x79, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
+  0x70, 0x77, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
+  0x70, 0x77, 0x32, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
+  0x70, 0x68, 0x31, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20,
+  0x70, 0x68, 0x32, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x78,
+  0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68,
+  0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x6e, 0x74, 0x20, 0x6a, 0x75, 0x6c, 0x69, 0x61, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x78, 0x31, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x78, 0x32, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x79, 0x31, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x6a, 0x79, 0x32, 0x29, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64,
+  0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f,
+  0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x78, 0x20, 0x3d, 0x20,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74,
+  0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x70,
+  0x79, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20,
+  0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x78, 0x6c, 0x20, 0x3d,
+  0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x78,
+  0x31, 0x2c, 0x20, 0x78, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x79, 0x74, 0x20, 0x3d,
+  0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x79,
+  0x31, 0x2c, 0x20, 0x79, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65,
+  0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x58, 0x20, 0x3d, 0x20, 0x28, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x70, 0x77, 0x31, 0x2c,
+  0x20, 0x70, 0x77, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c,
+  0x53, 0x63, 0x61, 0x6c, 0x65, 0x59, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x29, 0x28, 0x70, 0x68, 0x31, 0x2c, 0x20,
+  0x70, 0x68, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x63, 0x5f, 0x72, 0x65, 0x20, 0x3d,
+  0x20, 0x61, 0x64, 0x64, 0x28, 0x6d, 0x75, 0x6c, 0x44, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c,
+  0x65, 0x58, 0x2c, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x29,
+  0x20, 0x70, 0x78, 0x29, 0x2c, 0x20, 0x78, 0x6c, 0x29, 0x3b, 0x20, 0x2f,
+  0x2f, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65,
+  0x58, 0x20, 0x2a, 0x20, 0x70, 0x78, 0x20, 0x2b, 0x20, 0x78, 0x6c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20,
+  0x63, 0x5f, 0x69, 0x6d, 0x20, 0x3d, 0x20, 0x61, 0x64, 0x64, 0x28, 0x6d,
+  0x75, 0x6c, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x28, 0x70, 0x69, 0x78,
+  0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x59, 0x2c, 0x20, 0x28, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x70, 0x79, 0x29, 0x2c, 0x20,
+  0x79, 0x74, 0x29, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x69, 0x78, 0x65,
+  0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x59, 0x20, 0x2a, 0x20, 0x70, 0x79,
+  0x20, 0x2b, 0x20, 0x79, 0x74, 0x0a, 0x0a
+};
+unsigned int doubledouble_prelude_cl_len = 2059;