Nicolas Winkler 5 tahun lalu
induk
melakukan
c90d1bc108

+ 2 - 0
libmandel/include/OpenClCode.h

@@ -5,6 +5,8 @@ namespace mnd
 {
     const char* getFloat_cl();
     const char* getDoubleFloat_cl();
+    const char* getDoubleDouble_cl();
+    const char* getQuadDouble_cl();
     const char* getFixed64_cl();
     const char* getFixed128_cl();
     const char* getFixed512_cl();

+ 22 - 15
libmandel/src/ClGenerators.cpp

@@ -1,5 +1,4 @@
 #include "ClGenerators.h"
-#include "doubledouble.h"
 #include "OpenClCode.h"
 
 #ifdef WITH_OPENCL
@@ -507,7 +506,7 @@ void ClGeneratorDoubleDouble::generate(const mnd::MandelInfo& info, float* data)
 
 std::string ClGeneratorDoubleDouble::getKernelCode(bool smooth) const
 {
-    return (char*) doubledouble_cl;
+    return getDoubleDouble_cl();
 }
 
 
@@ -536,25 +535,33 @@ void ClGeneratorQuadDouble::generate(const mnd::MandelInfo& info, float* data)
 
     Buffer buffer_A(context, CL_MEM_WRITE_ONLY, bufferSize);
 
-    mnd::DoubleDouble x = mnd::convert<mnd::DoubleDouble>(info.view.x);
-    mnd::DoubleDouble y = mnd::convert<mnd::DoubleDouble>(info.view.y);
+    mnd::QuadDouble x = mnd::convert<mnd::QuadDouble>(info.view.x);
+    mnd::QuadDouble y = mnd::convert<mnd::QuadDouble>(info.view.y);
 
-    mnd::DoubleDouble psx = mnd::convert<mnd::DoubleDouble>(info.view.width / info.bWidth);
-    mnd::DoubleDouble psy = mnd::convert<mnd::DoubleDouble>(info.view.height / info.bHeight);
+    mnd::QuadDouble psx = mnd::convert<mnd::QuadDouble>(info.view.width / info.bWidth);
+    mnd::QuadDouble psy = mnd::convert<mnd::QuadDouble>(info.view.height / info.bHeight);
 
     Kernel iterate = Kernel(program, "iterate");
     iterate.setArg(0, buffer_A);
     iterate.setArg(1, int(info.bWidth));
     iterate.setArg(2, x.x[0]);
     iterate.setArg(3, x.x[1]);
-    iterate.setArg(4, y.x[0]);
-    iterate.setArg(5, y.x[1]);
-    iterate.setArg(6, psx.x[0]);
-    iterate.setArg(7, psx.x[1]);
-    iterate.setArg(8, psy.x[0]);
-    iterate.setArg(9, psy.x[1]);
-    iterate.setArg(10, int(info.maxIter));
-    iterate.setArg(11, int(info.smooth ? 1 : 0));
+    iterate.setArg(4, x.x[2]);
+    iterate.setArg(5, x.x[3]);
+    iterate.setArg(6, y.x[0]);
+    iterate.setArg(7, y.x[1]);
+    iterate.setArg(8, y.x[2]);
+    iterate.setArg(9, y.x[3]);
+    iterate.setArg(10, psx.x[0]);
+    iterate.setArg(11, psx.x[1]);
+    iterate.setArg(12, psx.x[2]);
+    iterate.setArg(13, psx.x[3]);
+    iterate.setArg(14, psy.x[0]);
+    iterate.setArg(15, psy.x[1]);
+    iterate.setArg(16, psy.x[2]);
+    iterate.setArg(17, psy.x[3]);
+    iterate.setArg(18, int(info.maxIter));
+    iterate.setArg(19, int(info.smooth ? 1 : 0));
 
     cl_int result = queue.enqueueNDRangeKernel(iterate, 0, NDRange(info.bWidth * info.bHeight));
     queue.enqueueReadBuffer(buffer_A, CL_TRUE, 0, bufferSize, data);
@@ -564,7 +571,7 @@ void ClGeneratorQuadDouble::generate(const mnd::MandelInfo& info, float* data)
 
 std::string ClGeneratorQuadDouble::getKernelCode(bool smooth) const
 {
-    return (char*) doubledouble_cl;
+    return getQuadDouble_cl();
 }
 
 

+ 4 - 4
libmandel/src/Generators.cpp

@@ -123,7 +123,7 @@ namespace mnd
     }
     template<>
     Real getPrecision<QuadDouble>() {
-        return Real("1.0e-56");
+        return Real("3.0e-64");
     }
     template<>
     Real getPrecision<Fixed64>() {
@@ -135,7 +135,7 @@ namespace mnd
     }
     template<>
     Real getPrecision<Fixed512>() {
-        return Real("1.5e-29");
+        return Real("1.5e-130");
     }
     template<>
     Real getPrecision<Float128>() {
@@ -143,11 +143,11 @@ namespace mnd
     }
     template<>
     Real getPrecision<Float256>() {
-        return Real("1.5e-29");
+        return Real("1.4e-72");
     }
     template<>
     Real getPrecision<Float512>() {
-        return Real("1.5e-29");
+        return Real("1.0e-145");
     }
 
 }

+ 9 - 6
libmandel/src/Mandel.cpp

@@ -211,6 +211,7 @@ std::unique_ptr<mnd::AdaptiveGenerator> MandelContext::createAdaptiveGenerator(v
         auto* fGen = device.getGenerator(GeneratorType::FLOAT);
         auto* dGen = device.getGenerator(GeneratorType::DOUBLE);
         auto* ddGen = device.getGenerator(GeneratorType::DOUBLE_DOUBLE);
+        auto* qdGen = device.getGenerator(GeneratorType::QUAD_DOUBLE);
 
         if (fGen)
             floatGen = fGen;
@@ -218,14 +219,16 @@ std::unique_ptr<mnd::AdaptiveGenerator> MandelContext::createAdaptiveGenerator(v
             doubleGen = dGen;
         if (ddGen)
             doubleDoubleGen = ddGen;
+        if (qdGen)
+            quadDoubleGen = qdGen;
     }
 
     auto ag = std::make_unique<AdaptiveGenerator>();
-    ag->addGenerator(Precision::FLOAT, *floatGen);
-    ag->addGenerator(Precision::DOUBLE, *doubleGen);
-    ag->addGenerator(Precision::DOUBLE_DOUBLE, *doubleDoubleGen);
-    ag->addGenerator(Precision::QUAD_DOUBLE, *quadDoubleGen);
-    ag->addGenerator(Precision::FLOAT256, *f256Gen);
+    ag->addGenerator(getPrecision<float>(), *floatGen);
+    ag->addGenerator(getPrecision<double>(), *doubleGen);
+    ag->addGenerator(getPrecision<DoubleDouble>(), *doubleDoubleGen);
+    ag->addGenerator(getPrecision<QuadDouble>(), *quadDoubleGen);
+    ag->addGenerator(getPrecision<Float256>(), *f256Gen);
     ag->addGenerator(Precision::INF_PREC, *fix512);
 
     return ag;
@@ -281,7 +284,7 @@ std::vector<MandelDevice> MandelContext::createDevices(void)
                 try {
                     md.generators.insert({ GeneratorType::DOUBLE, std::make_unique<ClGeneratorDouble>(device) });
                     md.generators.insert({ GeneratorType::DOUBLE_DOUBLE, std::make_unique<ClGeneratorDoubleDouble>(device) });
-                    //md.generators.insert({ GeneratorType::QUAD_DOUBLE, std::make_unique<ClGeneratorQuadDouble>(device) });
+                    md.generators.insert({ GeneratorType::QUAD_DOUBLE, std::make_unique<ClGeneratorQuadDouble>(device) });
                 }
                 catch (const std::string& err) {
                     printf("err: %s", err.c_str());

+ 10 - 0
libmandel/src/OpenClCode.cpp

@@ -2,6 +2,8 @@
 
 #include "opencl/float.h"
 #include "opencl/doublefloat.h"
+#include "opencl/doubledouble.h"
+#include "opencl/quaddouble.h"
 
 #include "opencl/fixed64.h"
 #include "opencl/fixed128.h"
@@ -17,6 +19,14 @@ namespace mnd
         return (char*) doublefloat_cl;
     }
 
+    const char* getDoubleDouble_cl() {
+        return (char*) doubledouble_cl;
+    }
+
+    const char* getQuadDouble_cl() {
+        return (char*) quaddouble_cl;
+    }
+
     const char* getFixed64_cl() {
         return (char*) fixed64_cl;
     }

+ 0 - 0
libmandel/src/doubledouble.cl → libmandel/src/opencl/doubledouble.cl


+ 0 - 0
libmandel/src/doubledouble.h → libmandel/src/opencl/doubledouble.h


+ 205 - 0
libmandel/src/opencl/quaddouble.cl

@@ -0,0 +1,205 @@
+#pragma OPENCL EXTENSION cl_khr_fp64 : enable
+
+inline double2 twoSum(double a, double b) {
+    double s = a + b;
+    double bb = s - a;
+    double e = (a - (s - bb)) + (b - bb);
+    return (double2)(s, e);
+}
+
+inline double two_sum(double a, double b, double* err) {
+    double s = a + b;
+    double bb = s - a;
+    *err = (a - (s - bb)) + (b - bb);
+    return s;
+}
+
+inline double2 quickTwoSum(double a, double b) {
+    double s = a + b;
+    double e = b - (s - a);
+    return (double2)(s, e);
+}
+
+inline double quick_two_sum(double a, double b, double* err) {
+  double s = a + b;
+  *err = b - (s - a);
+  return s;
+}
+
+
+inline double2 twoProd(double a, double b) {
+    double p = a * b;
+    double e = fma(a, b, -p);
+    return (double2)(p, e);
+}
+
+inline double two_prod(double a, double b, double* err) {
+    double p = a * b;
+    *err = fma(a, b, -p);
+    return p;
+}
+
+
+inline void threeSum(double* a, double* b, double* c) {
+    double2 t = twoSum(*a, *b);
+    double2 at3 = twoSum(*c, t.s0);
+    double2 bc = twoSum(t.s1, at3.s1);
+    *a = at3.s0;
+    *b = bc.s0;
+    *c = bc.s1;
+}
+
+
+inline void threeSum2(double* a, double* b, double* c) {
+    double2 t = twoSum(*a, *b);
+    double2 at3 = twoSum(*c, t.s0);
+    *a = at3.s0;
+    *b = t.s1 + at3.s1;
+}
+
+inline void renorm(double *c0, double *c1, 
+                   double *c2, double *c3, double *c4) {
+  double s0, s1, s2 = 0.0, s3 = 0.0;
+
+  s0 = quick_two_sum(*c3, *c4, c4);
+  s0 = quick_two_sum(*c2, s0, c3);
+  s0 = quick_two_sum(*c1, s0, c2);
+  *c0 = quick_two_sum(*c0, s0, c1);
+
+  s0 = *c0;
+  s1 = *c1;
+
+  if (s1 != 0.0) {
+    s1 = quick_two_sum(s1, *c2, &s2);
+    if (s2 != 0.0) {
+      s2 = quick_two_sum(s2, *c3, &s3);
+      if (s3 != 0.0)
+        s3 += *c4;
+      else
+        s2 = quick_two_sum(s2, *c4, &s3);
+    } else {
+      s1 = quick_two_sum(s1, *c3, &s2);
+      if (s2 != 0.0)
+        s2 = quick_two_sum(s2, *c4, &s3);
+      else
+        s1 = quick_two_sum(s1, *c4, &s2);
+    }
+  } else {
+    s0 = quick_two_sum(s0, *c2, &s1);
+    if (s1 != 0.0) {
+      s1 = quick_two_sum(s1, *c3, &s2);
+      if (s2 != 0.0)
+        s2 = quick_two_sum(s2, *c4, &s3);
+      else
+        s1 = quick_two_sum(s1, *c4, &s2);
+    } else {
+      s0 = quick_two_sum(s0, *c3, &s1);
+      if (s1 != 0.0)
+        s1 = quick_two_sum(s1, *c4, &s2);
+      else
+        s0 = quick_two_sum(s0, *c4, &s1);
+    }
+  }
+
+  *c0 = s0;
+  *c1 = s1;
+  *c2 = s2;
+  *c3 = s3;
+}
+
+
+inline double4 add(double4 a, double4 b) {
+    double s0, s1, s2, s3;
+    double t0, t1, t2, t3;
+    
+    s0 = two_sum(a[0], b[0], &t0);
+    s1 = two_sum(a[1], b[1], &t1);
+    s2 = two_sum(a[2], b[2], &t2);
+    s3 = two_sum(a[3], b[3], &t3);
+
+    s1 = two_sum(s1, t0, &t0);
+    threeSum(&s2, &t0, &t1);
+    threeSum2(&s3, &t0, &t2);
+    t0 = t0 + t1 + t3;
+
+    renorm(&s0, &s1, &s2, &s3, &t0);
+    return (double4)(s0, s1, s2, s3);
+}
+
+
+inline double4 mul(double4 a, double4 b) {
+    double p0, p1, p2, p3, p4, p5;
+    double q0, q1, q2, q3, q4, q5;
+    double t0, t1;
+    double s0, s1, s2;
+
+    p0 = two_prod(a[0], b[0], &q0);
+
+    p1 = two_prod(a[0], b[1], &q1);
+    p2 = two_prod(a[1], b[0], &q2);
+
+    p3 = two_prod(a[0], b[2], &q3);
+    p4 = two_prod(a[1], b[1], &q4);
+    p5 = two_prod(a[2], b[0], &q5);
+
+    /* Start Accumulation */
+    threeSum(&p1, &p2, &q0);
+
+    /* Six-Three Sum  of p2, q1, q2, p3, p4, p5. */
+    threeSum(&p2, &q1, &q2);
+    threeSum(&p3, &p4, &p5);
+    /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */
+    s0 = two_sum(p2, p3, &t0);
+    s1 = two_sum(q1, p4, &t1);
+    s2 = q2 + p5;
+    s1 = two_sum(s1, t0, &t0);
+    s2 += (t0 + t1);
+
+    /* O(eps^3) order terms */
+    s1 += a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] + q0 + q3 + q4 + q5;
+    renorm(&p0, &p1, &s0, &s1, &s2);
+    return (double4)(p0, p1, s0, s1);
+}
+
+
+__kernel void iterate(__global float* A, const int width,
+                      double x1, double x2, double x3, double x4, double y1, double y2, double y3, double y4,
+                      double pw1, double pw2, double pw3, double pw4, double ph1, double ph2, double ph3, double ph4, int max, int smooth) {
+    int index = get_global_id(0);
+    int px = index % width;
+    int py = index / width;
+
+    double4 xl = (double4)(x1, x2, x3, x4);
+    double4 yt = (double4)(y1, y2, y3, y4);
+    double4 pixelScaleX = (double4)(pw1, pw2, pw3, pw4);
+    double4 pixelScaleY = (double4)(ph1, ph2, ph3, ph4);
+
+    double4 a = add(mul(pixelScaleX, (double4) (px, 0, 0, 0)), xl); // pixelScaleX * px + xl
+    double4 b = add(mul(pixelScaleY, (double4) (py, 0, 0, 0)), yt); // pixelScaleY * py + yt
+    double4 ca = a;
+    double4 cb = b;
+
+    int n = 0;
+    while (n < max - 1) {
+        double4 aa = mul(a, a);
+        double4 bb = mul(b, b);
+        double4 ab = mul(a, b);
+        if (aa.s0 + bb.s0 > 16) break;
+        double4 minusbb = (double4)(-bb.s0, -bb.s1, -bb.s2, -bb.s3);
+        a = add(add(aa, minusbb), ca);
+        b = add(add(ab, ab), cb);
+        n++;
+    }
+
+    // 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;
+}

+ 448 - 0
libmandel/src/opencl/quaddouble.h

@@ -0,0 +1,448 @@
+unsigned char quaddouble_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, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73,
+  0x75, 0x6d, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x2c,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x65, 0x72, 0x72, 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, 0x2a, 0x65, 0x72, 0x72, 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,
+  0x73, 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, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f,
+  0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x20, 0x62, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20,
+  0x65, 0x72, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x20, 0x2b, 0x20,
+  0x62, 0x3b, 0x0a, 0x20, 0x20, 0x2a, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20,
+  0x62, 0x20, 0x2d, 0x20, 0x28, 0x73, 0x20, 0x2d, 0x20, 0x61, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x3b,
+  0x0a, 0x7d, 0x0a, 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, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x28,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x2c, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x2a, 0x20, 0x65, 0x72, 0x72, 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, 0x2a, 0x65, 0x72, 0x72, 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, 0x70, 0x3b,
+  0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20,
+  0x76, 0x6f, 0x69, 0x64, 0x20, 0x74, 0x68, 0x72, 0x65, 0x65, 0x53, 0x75,
+  0x6d, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x61, 0x2c,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x62, 0x2c, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x63, 0x29, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32,
+  0x20, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28,
+  0x2a, 0x61, 0x2c, 0x20, 0x2a, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x74, 0x33,
+  0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x2a, 0x63,
+  0x2c, 0x20, 0x74, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x62, 0x63, 0x20,
+  0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x74, 0x2e, 0x73,
+  0x31, 0x2c, 0x20, 0x61, 0x74, 0x33, 0x2e, 0x73, 0x31, 0x29, 0x3b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x2a, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x74, 0x33,
+  0x2e, 0x73, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x62, 0x20,
+  0x3d, 0x20, 0x62, 0x63, 0x2e, 0x73, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x2a, 0x63, 0x20, 0x3d, 0x20, 0x62, 0x63, 0x2e, 0x73, 0x31, 0x3b,
+  0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20,
+  0x76, 0x6f, 0x69, 0x64, 0x20, 0x74, 0x68, 0x72, 0x65, 0x65, 0x53, 0x75,
+  0x6d, 0x32, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x61,
+  0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x62, 0x2c,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x2a, 0x20, 0x63, 0x29, 0x20,
+  0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x32, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d,
+  0x28, 0x2a, 0x61, 0x2c, 0x20, 0x2a, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x32, 0x20, 0x61, 0x74,
+  0x33, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x53, 0x75, 0x6d, 0x28, 0x2a,
+  0x63, 0x2c, 0x20, 0x74, 0x2e, 0x73, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x2a, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x74, 0x33, 0x2e, 0x73,
+  0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x62, 0x20, 0x3d, 0x20,
+  0x74, 0x2e, 0x73, 0x31, 0x20, 0x2b, 0x20, 0x61, 0x74, 0x33, 0x2e, 0x73,
+  0x31, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
+  0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x72, 0x65, 0x6e, 0x6f, 0x72, 0x6d,
+  0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x2a, 0x63, 0x30, 0x2c,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x2a, 0x63, 0x31, 0x2c,
+  0x20, 0x0a, 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, 0x2a, 0x63, 0x32, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x2a, 0x63, 0x33, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x2a, 0x63, 0x34, 0x29, 0x20, 0x7b, 0x0a, 0x20,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x30, 0x2c, 0x20,
+  0x73, 0x31, 0x2c, 0x20, 0x73, 0x32, 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x30,
+  0x2c, 0x20, 0x73, 0x33, 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x3b, 0x0a,
+  0x0a, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63,
+  0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x2a, 0x63,
+  0x33, 0x2c, 0x20, 0x2a, 0x63, 0x34, 0x2c, 0x20, 0x63, 0x34, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63,
+  0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x2a, 0x63,
+  0x32, 0x2c, 0x20, 0x73, 0x30, 0x2c, 0x20, 0x63, 0x33, 0x29, 0x3b, 0x0a,
+  0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b,
+  0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x2a, 0x63, 0x31,
+  0x2c, 0x20, 0x73, 0x30, 0x2c, 0x20, 0x63, 0x32, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x2a, 0x63, 0x30, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b,
+  0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x2a, 0x63, 0x30,
+  0x2c, 0x20, 0x73, 0x30, 0x2c, 0x20, 0x63, 0x31, 0x29, 0x3b, 0x0a, 0x0a,
+  0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x2a, 0x63, 0x30, 0x3b, 0x0a,
+  0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x2a, 0x63, 0x31, 0x3b, 0x0a,
+  0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x31, 0x20, 0x21, 0x3d,
+  0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x31, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74,
+  0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x31, 0x2c, 0x20, 0x2a,
+  0x63, 0x32, 0x2c, 0x20, 0x26, 0x73, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x32, 0x20, 0x21, 0x3d, 0x20,
+  0x30, 0x2e, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x32, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f,
+  0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x32, 0x2c, 0x20,
+  0x2a, 0x63, 0x33, 0x2c, 0x20, 0x26, 0x73, 0x33, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x33, 0x20,
+  0x21, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x33, 0x20, 0x2b, 0x3d, 0x20, 0x2a, 0x63,
+  0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73,
+  0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x32,
+  0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f,
+  0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x32, 0x2c, 0x20, 0x2a, 0x63, 0x34,
+  0x2c, 0x20, 0x26, 0x73, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63,
+  0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x31,
+  0x2c, 0x20, 0x2a, 0x63, 0x33, 0x2c, 0x20, 0x26, 0x73, 0x32, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73,
+  0x32, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x32, 0x20, 0x3d, 0x20, 0x71,
+  0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d,
+  0x28, 0x73, 0x32, 0x2c, 0x20, 0x2a, 0x63, 0x34, 0x2c, 0x20, 0x26, 0x73,
+  0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x31, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77,
+  0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x31, 0x2c, 0x20, 0x2a, 0x63,
+  0x34, 0x2c, 0x20, 0x26, 0x73, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20,
+  0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x71,
+  0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d,
+  0x28, 0x73, 0x30, 0x2c, 0x20, 0x2a, 0x63, 0x32, 0x2c, 0x20, 0x26, 0x73,
+  0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28,
+  0x73, 0x31, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20,
+  0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75,
+  0x6d, 0x28, 0x73, 0x31, 0x2c, 0x20, 0x2a, 0x63, 0x33, 0x2c, 0x20, 0x26,
+  0x73, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69,
+  0x66, 0x20, 0x28, 0x73, 0x32, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x2e, 0x30,
+  0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x32,
+  0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f,
+  0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x32, 0x2c, 0x20, 0x2a, 0x63, 0x34,
+  0x2c, 0x20, 0x26, 0x73, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63,
+  0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x31,
+  0x2c, 0x20, 0x2a, 0x63, 0x34, 0x2c, 0x20, 0x26, 0x73, 0x32, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20,
+  0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d,
+  0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73,
+  0x75, 0x6d, 0x28, 0x73, 0x30, 0x2c, 0x20, 0x2a, 0x63, 0x33, 0x2c, 0x20,
+  0x26, 0x73, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x28, 0x73, 0x31, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x2e,
+  0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+  0x31, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x5f, 0x74, 0x77,
+  0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73, 0x31, 0x2c, 0x20, 0x2a, 0x63,
+  0x34, 0x2c, 0x20, 0x26, 0x73, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x71, 0x75, 0x69,
+  0x63, 0x6b, 0x5f, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x73,
+  0x30, 0x2c, 0x20, 0x2a, 0x63, 0x34, 0x2c, 0x20, 0x26, 0x73, 0x31, 0x29,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x0a,
+  0x0a, 0x20, 0x20, 0x2a, 0x63, 0x30, 0x20, 0x3d, 0x20, 0x73, 0x30, 0x3b,
+  0x0a, 0x20, 0x20, 0x2a, 0x63, 0x31, 0x20, 0x3d, 0x20, 0x73, 0x31, 0x3b,
+  0x0a, 0x20, 0x20, 0x2a, 0x63, 0x32, 0x20, 0x3d, 0x20, 0x73, 0x32, 0x3b,
+  0x0a, 0x20, 0x20, 0x2a, 0x63, 0x33, 0x20, 0x3d, 0x20, 0x73, 0x33, 0x3b,
+  0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x61, 0x64, 0x64, 0x28,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x61, 0x2c, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x73,
+  0x30, 0x2c, 0x20, 0x73, 0x31, 0x2c, 0x20, 0x73, 0x32, 0x2c, 0x20, 0x73,
+  0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x20, 0x74, 0x30, 0x2c, 0x20, 0x74, 0x31, 0x2c, 0x20, 0x74, 0x32,
+  0x2c, 0x20, 0x74, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f,
+  0x73, 0x75, 0x6d, 0x28, 0x61, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x62, 0x5b,
+  0x30, 0x5d, 0x2c, 0x20, 0x26, 0x74, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73,
+  0x75, 0x6d, 0x28, 0x61, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x31,
+  0x5d, 0x2c, 0x20, 0x26, 0x74, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x73, 0x32, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75,
+  0x6d, 0x28, 0x61, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x32, 0x5d,
+  0x2c, 0x20, 0x26, 0x74, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d,
+  0x28, 0x61, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x33, 0x5d, 0x2c,
+  0x20, 0x26, 0x74, 0x33, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x73, 0x31, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d,
+  0x28, 0x73, 0x31, 0x2c, 0x20, 0x74, 0x30, 0x2c, 0x20, 0x26, 0x74, 0x30,
+  0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x65, 0x65,
+  0x53, 0x75, 0x6d, 0x28, 0x26, 0x73, 0x32, 0x2c, 0x20, 0x26, 0x74, 0x30,
+  0x2c, 0x20, 0x26, 0x74, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x74, 0x68, 0x72, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x32, 0x28, 0x26, 0x73,
+  0x33, 0x2c, 0x20, 0x26, 0x74, 0x30, 0x2c, 0x20, 0x26, 0x74, 0x32, 0x29,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x30, 0x20, 0x3d, 0x20, 0x74,
+  0x30, 0x20, 0x2b, 0x20, 0x74, 0x31, 0x20, 0x2b, 0x20, 0x74, 0x33, 0x3b,
+  0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x6f, 0x72, 0x6d,
+  0x28, 0x26, 0x73, 0x30, 0x2c, 0x20, 0x26, 0x73, 0x31, 0x2c, 0x20, 0x26,
+  0x73, 0x32, 0x2c, 0x20, 0x26, 0x73, 0x33, 0x2c, 0x20, 0x26, 0x74, 0x30,
+  0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
+  0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x29, 0x28,
+  0x73, 0x30, 0x2c, 0x20, 0x73, 0x31, 0x2c, 0x20, 0x73, 0x32, 0x2c, 0x20,
+  0x73, 0x33, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, 0x6c,
+  0x69, 0x6e, 0x65, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20,
+  0x6d, 0x75, 0x6c, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20,
+  0x61, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x62,
+  0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x20, 0x70, 0x30, 0x2c, 0x20, 0x70, 0x31, 0x2c, 0x20, 0x70,
+  0x32, 0x2c, 0x20, 0x70, 0x33, 0x2c, 0x20, 0x70, 0x34, 0x2c, 0x20, 0x70,
+  0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x20, 0x71, 0x30, 0x2c, 0x20, 0x71, 0x31, 0x2c, 0x20, 0x71, 0x32,
+  0x2c, 0x20, 0x71, 0x33, 0x2c, 0x20, 0x71, 0x34, 0x2c, 0x20, 0x71, 0x35,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x20, 0x74, 0x30, 0x2c, 0x20, 0x74, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x30, 0x2c, 0x20,
+  0x73, 0x31, 0x2c, 0x20, 0x73, 0x32, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x70, 0x30, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x70, 0x72,
+  0x6f, 0x64, 0x28, 0x61, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x30,
+  0x5d, 0x2c, 0x20, 0x26, 0x71, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x70, 0x31, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x70,
+  0x72, 0x6f, 0x64, 0x28, 0x61, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x62, 0x5b,
+  0x31, 0x5d, 0x2c, 0x20, 0x26, 0x71, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x70, 0x32, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x70,
+  0x72, 0x6f, 0x64, 0x28, 0x61, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x62, 0x5b,
+  0x30, 0x5d, 0x2c, 0x20, 0x26, 0x71, 0x32, 0x29, 0x3b, 0x0a, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x70, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f,
+  0x70, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x62,
+  0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x26, 0x71, 0x33, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x70, 0x34, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f,
+  0x70, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x62,
+  0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x26, 0x71, 0x34, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x70, 0x35, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f,
+  0x70, 0x72, 0x6f, 0x64, 0x28, 0x61, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x62,
+  0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x26, 0x71, 0x35, 0x29, 0x3b, 0x0a, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74,
+  0x20, 0x41, 0x63, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f,
+  0x6e, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72,
+  0x65, 0x65, 0x53, 0x75, 0x6d, 0x28, 0x26, 0x70, 0x31, 0x2c, 0x20, 0x26,
+  0x70, 0x32, 0x2c, 0x20, 0x26, 0x71, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x53, 0x69, 0x78, 0x2d, 0x54, 0x68,
+  0x72, 0x65, 0x65, 0x20, 0x53, 0x75, 0x6d, 0x20, 0x20, 0x6f, 0x66, 0x20,
+  0x70, 0x32, 0x2c, 0x20, 0x71, 0x31, 0x2c, 0x20, 0x71, 0x32, 0x2c, 0x20,
+  0x70, 0x33, 0x2c, 0x20, 0x70, 0x34, 0x2c, 0x20, 0x70, 0x35, 0x2e, 0x20,
+  0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x72, 0x65, 0x65,
+  0x53, 0x75, 0x6d, 0x28, 0x26, 0x70, 0x32, 0x2c, 0x20, 0x26, 0x71, 0x31,
+  0x2c, 0x20, 0x26, 0x71, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x74, 0x68, 0x72, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x28, 0x26, 0x70, 0x33,
+  0x2c, 0x20, 0x26, 0x70, 0x34, 0x2c, 0x20, 0x26, 0x70, 0x35, 0x29, 0x3b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x63, 0x6f, 0x6d, 0x70,
+  0x75, 0x74, 0x65, 0x20, 0x28, 0x73, 0x30, 0x2c, 0x20, 0x73, 0x31, 0x2c,
+  0x20, 0x73, 0x32, 0x29, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x32, 0x2c, 0x20,
+  0x71, 0x31, 0x2c, 0x20, 0x71, 0x32, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x70,
+  0x33, 0x2c, 0x20, 0x70, 0x34, 0x2c, 0x20, 0x70, 0x35, 0x29, 0x2e, 0x20,
+  0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x30, 0x20, 0x3d, 0x20,
+  0x74, 0x77, 0x6f, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x70, 0x32, 0x2c, 0x20,
+  0x70, 0x33, 0x2c, 0x20, 0x26, 0x74, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f, 0x73,
+  0x75, 0x6d, 0x28, 0x71, 0x31, 0x2c, 0x20, 0x70, 0x34, 0x2c, 0x20, 0x26,
+  0x74, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x32, 0x20,
+  0x3d, 0x20, 0x71, 0x32, 0x20, 0x2b, 0x20, 0x70, 0x35, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x74, 0x77, 0x6f, 0x5f,
+  0x73, 0x75, 0x6d, 0x28, 0x73, 0x31, 0x2c, 0x20, 0x74, 0x30, 0x2c, 0x20,
+  0x26, 0x74, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x32,
+  0x20, 0x2b, 0x3d, 0x20, 0x28, 0x74, 0x30, 0x20, 0x2b, 0x20, 0x74, 0x31,
+  0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x4f,
+  0x28, 0x65, 0x70, 0x73, 0x5e, 0x33, 0x29, 0x20, 0x6f, 0x72, 0x64, 0x65,
+  0x72, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x2a, 0x2f, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x73, 0x31, 0x20, 0x2b, 0x3d, 0x20, 0x61, 0x5b, 0x30,
+  0x5d, 0x2a, 0x62, 0x5b, 0x33, 0x5d, 0x20, 0x2b, 0x20, 0x61, 0x5b, 0x31,
+  0x5d, 0x2a, 0x62, 0x5b, 0x32, 0x5d, 0x20, 0x2b, 0x20, 0x61, 0x5b, 0x32,
+  0x5d, 0x2a, 0x62, 0x5b, 0x31, 0x5d, 0x20, 0x2b, 0x20, 0x61, 0x5b, 0x33,
+  0x5d, 0x2a, 0x62, 0x5b, 0x30, 0x5d, 0x20, 0x2b, 0x20, 0x71, 0x30, 0x20,
+  0x2b, 0x20, 0x71, 0x33, 0x20, 0x2b, 0x20, 0x71, 0x34, 0x20, 0x2b, 0x20,
+  0x71, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x6f,
+  0x72, 0x6d, 0x28, 0x26, 0x70, 0x30, 0x2c, 0x20, 0x26, 0x70, 0x31, 0x2c,
+  0x20, 0x26, 0x73, 0x30, 0x2c, 0x20, 0x26, 0x73, 0x31, 0x2c, 0x20, 0x26,
+  0x73, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34,
+  0x29, 0x28, 0x70, 0x30, 0x2c, 0x20, 0x70, 0x31, 0x2c, 0x20, 0x73, 0x30,
+  0x2c, 0x20, 0x73, 0x31, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 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,
+  0x78, 0x33, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x78,
+  0x34, 0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x79, 0x31,
+  0x2c, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x79, 0x32, 0x2c,
+  0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x79, 0x33, 0x2c, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x79, 0x34, 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, 0x77, 0x33, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x70, 0x77, 0x34, 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, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x70, 0x68, 0x33, 0x2c, 0x20, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x20, 0x70, 0x68, 0x34, 0x2c, 0x20, 0x69, 0x6e, 0x74,
+  0x20, 0x6d, 0x61, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6d,
+  0x6f, 0x6f, 0x74, 0x68, 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, 0x34, 0x20, 0x78, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75,
+  0x62, 0x6c, 0x65, 0x34, 0x29, 0x28, 0x78, 0x31, 0x2c, 0x20, 0x78, 0x32,
+  0x2c, 0x20, 0x78, 0x33, 0x2c, 0x20, 0x78, 0x34, 0x29, 0x3b, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x79,
+  0x74, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34,
+  0x29, 0x28, 0x79, 0x31, 0x2c, 0x20, 0x79, 0x32, 0x2c, 0x20, 0x79, 0x33,
+  0x2c, 0x20, 0x79, 0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
+  0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c,
+  0x53, 0x63, 0x61, 0x6c, 0x65, 0x58, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x34, 0x29, 0x28, 0x70, 0x77, 0x31, 0x2c, 0x20,
+  0x70, 0x77, 0x32, 0x2c, 0x20, 0x70, 0x77, 0x33, 0x2c, 0x20, 0x70, 0x77,
+  0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x34, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61,
+  0x6c, 0x65, 0x59, 0x20, 0x3d, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+  0x65, 0x34, 0x29, 0x28, 0x70, 0x68, 0x31, 0x2c, 0x20, 0x70, 0x68, 0x32,
+  0x2c, 0x20, 0x70, 0x68, 0x33, 0x2c, 0x20, 0x70, 0x68, 0x34, 0x29, 0x3b,
+  0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x34, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x64, 0x64, 0x28, 0x6d, 0x75,
+  0x6c, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65,
+  0x58, 0x2c, 0x20, 0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x29,
+  0x20, 0x28, 0x70, 0x78, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20,
+  0x30, 0x29, 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, 0x34, 0x20, 0x62,
+  0x20, 0x3d, 0x20, 0x61, 0x64, 0x64, 0x28, 0x6d, 0x75, 0x6c, 0x28, 0x70,
+  0x69, 0x78, 0x65, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x59, 0x2c, 0x20,
+  0x28, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x29, 0x20, 0x28, 0x70,
+  0x79, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 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, 0x20, 0x20, 0x20, 0x20,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x63, 0x61, 0x20, 0x3d,
+  0x20, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62,
+  0x6c, 0x65, 0x34, 0x20, 0x63, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x3b, 0x0a,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x20, 0x3d,
+  0x20, 0x30, 0x3b, 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, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x61, 0x61,
+  0x20, 0x3d, 0x20, 0x6d, 0x75, 0x6c, 0x28, 0x61, 0x2c, 0x20, 0x61, 0x29,
+  0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f,
+  0x75, 0x62, 0x6c, 0x65, 0x34, 0x20, 0x62, 0x62, 0x20, 0x3d, 0x20, 0x6d,
+  0x75, 0x6c, 0x28, 0x62, 0x2c, 0x20, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
+  0x34, 0x20, 0x61, 0x62, 0x20, 0x3d, 0x20, 0x6d, 0x75, 0x6c, 0x28, 0x61,
+  0x2c, 0x20, 0x62, 0x29, 0x3b, 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, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34,
+  0x20, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x62, 0x62, 0x20, 0x3d, 0x20, 0x28,
+  0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x34, 0x29, 0x28, 0x2d, 0x62, 0x62,
+  0x2e, 0x73, 0x30, 0x2c, 0x20, 0x2d, 0x62, 0x62, 0x2e, 0x73, 0x31, 0x2c,
+  0x20, 0x2d, 0x62, 0x62, 0x2e, 0x73, 0x32, 0x2c, 0x20, 0x2d, 0x62, 0x62,
+  0x2e, 0x73, 0x33, 0x29, 0x3b, 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, 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, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x6e, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x7d, 0x0a, 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, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x69, 0x66, 0x20, 0x28, 0x6e, 0x20, 0x3e, 0x3d, 0x20, 0x6d, 0x61, 0x78,
+  0x20, 0x2d, 0x20, 0x31, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x41, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d,
+  0x20, 0x6d, 0x61, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c,
+  0x73, 0x65, 0x20, 0x7b, 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, 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, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x65, 0x6c, 0x73, 0x65, 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,
+  0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 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, 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, 0x0a, 0x7d, 0x0a
+};
+unsigned int quaddouble_cl_len = 5334;

+ 0 - 124
libmandel/src/quaddouble.cl

@@ -1,124 +0,0 @@
-#pragma OPENCL EXTENSION cl_khr_fp64 : enable
-
-inline double2 twoSum(double a, double b) {
-    double s = a + b;
-    double bb = s - a;
-    double e = (a - (s - bb)) + (b - bb);
-    return (double2)(s, e);
-}
-
-inline double2 quickTwoSum(double a, double b) {
-    double s = a + b;
-    double e = b - (s - a);
-    return (double2)(s, e);
-}
-
-inline double2 twoProd(double a, double b) {
-    double p = a * b;
-    double e = fma(a, b, -p);
-    return (double2)(p, e);
-}
-
-
-inline void threeSum(double* a, double* b, double* c) {
-    double2 t = twoSum(*a, *b);
-    double2 at3 = twoSum(*c, t.s0);
-    double2 bc = twoSum(t.s1, at3.s1);
-    *a = at3.s0;
-    *b = bc.s0;
-    *c = bc.s1;
-}
-
-
-inline void threeSum2(double* a, double* b, double* c) {
-    double2 t = twoSum(*a, *b);
-    double2 at3 = twoSum(*c, t.s0);
-    *a = at3.s0;
-    *b = t.s1 + at3.s1;
-}
-
-
-inline double4 add(double4 a, double4 b) {
-    double2 su0 = twoSum(a.s0, b.s0);
-    double2 su1 = twoSum(a.s1, b.s1);
-    double2 su2 = twoSum(a.s2, b.s2);
-    double2 su3 = twoSum(a.s3, b.s3);
-
-    double2 s1t0 = twoSum(su1.s0, su0.s1);
-    threeSum(&su2.s0, &su0.s1, &su1.s1);
-    threeSum2(&su3.s0, &su0.s1, &su2.s1);
-    su0.s1 = su0.s1 + su1.s1 + su3.s1;
-
-    renorm(su0.s0, su1.s0, su2.s0, su3.s0, su0.s1);
-    return (double4)(su0.s0, su1.s0, su2.s0, su3.s0);
-}
-
-
-
-
-
-
-
-
-
-
-
-inline double2 mul(double2 a, double2 b) {
-    double2 p = twoProd(a.s0, b.s0);
-    p.s1 += (a.s0 * b.s1 + a.s1 * b.s0);
-    return quickTwoSum(p.s0, p.s1);
-}
-
-inline double2 add(double2 a, double2 b) {
-    double2 se = twoSum(a.s0, b.s0);
-    se.s1 += a.s1 + b.s1;
-    return quickTwoSum(se.s0, se.s1);
-}
-
-inline double2 mulDouble(double2 a, double b) {
-    double2 p = twoProd(a.s0, b);
-    p.s1 += a.s1 * b;
-    return quickTwoSum(p.s0, p.s1);
-}
-
-__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) {
-    int index = get_global_id(0);
-    int px = index % width;
-    int py = index / width;
-
-    double2 xl = (double2)(x1, x2);
-    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 = a;
-    double2 cb = b;
-
-    int n = 0;
-    while (n < max - 1) {
-        double2 aa = mul(a, a);
-        double2 bb = mul(b, b);
-        double2 ab = mul(a, b);
-        if (aa.s0 + aa.s1 + bb.s0 + bb.s1 > 16) break;
-        double2 minusbb = (double2)(-bb.s0, -bb.s1);
-        a = add(add(aa, minusbb), ca);
-        b = add(add(ab, ab), cb);
-        n++;
-    }
-
-    // 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;
-}