geos/geos_remove_ttmath.patch
2020-06-17 00:56:18 +02:00

1415 lines
37 KiB
Diff

From 1111070c0a68d0a15d1488415392658a75662be4 Mon Sep 17 00:00:00 2001
From: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Thu, 16 Apr 2020 15:31:11 -0700
Subject: [PATCH] Squashed commit of the following:
commit ab1b004af900354f907f9a5d31ec514c2547ada4
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Thu Apr 16 14:40:23 2020 -0700
remove ttmath in favour of DD
commit 472c1f9a12df1a4b9628f61c93594ee162382db4
Merge: 8ccf3bf8 312c085b
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Thu Apr 16 13:24:39 2020 -0700
Merge branch 'master' of https://git.osgeo.org/gitea/geos/geos into master-dd
commit 8ccf3bf874a2887b40afbaca57ee67b59b5eb40b
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Thu Apr 16 13:10:25 2020 -0700
add informational comment
commit 8fd12e02f060f8e35d36a162d81d1ef94da1b784
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Thu Apr 16 12:15:00 2020 -0700
add in all JTS unit tests for doubledouble calculations
commit e24af3ba8ce7eca76840164c2edd5e066bbebe28
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Wed Apr 15 13:47:56 2020 -0700
autotools build
commit cb5942a13c5113ae42108e0422fed9a34a465abf
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Wed Apr 15 13:31:28 2020 -0700
fix doxygen complaint?
commit 469037a4ca75b0f4df3638256c0fff2b61d37796
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Wed Apr 15 12:47:14 2020 -0700
change name of ifdef guard
commit 00559ec59b56a0ec6a0481b54f0ab597324d9439
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Wed Apr 15 12:40:50 2020 -0700
allow DD to swap in for ttmath
commit 75e70f7f28e751f26cc3db5b4964a86f3bae518f
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Wed Apr 15 11:28:15 2020 -0700
clean build of all DD functionality from JTS
commit e42e9bc0ea3fd35bddc892fbe51b3de5677d8e57
Author: Paul Ramsey <pramsey@cleverelephant.ca>
Date: Tue Apr 14 17:27:45 2020 -0700
DD wip
---
.codecov.yml | 1 -
Makefile.am | 1 -
configure.ac | 3 +-
doc/Doxyfile.in | 1 -
include/geos/Makefile.am | 1 +
include/geos/algorithm/CGAlgorithmsDD.h | 16 +-
include/geos/algorithm/Makefile.am | 3 +-
include/geos/algorithm/RayCrossingCounterDD.h | 1 -
include/geos/algorithm/ttmath/COPYRIGHT | 28 -
include/geos/algorithm/ttmath/Makefile.am | 24 -
include/geos/algorithm/ttmath/README | 23 -
include/geos/algorithm/ttmath/ttmath.h | 2880 --------
include/geos/algorithm/ttmath/ttmathbig.h | 6093 -----------------
include/geos/algorithm/ttmath/ttmathdec.h | 419 --
include/geos/algorithm/ttmath/ttmathint.h | 1923 ------
include/geos/algorithm/ttmath/ttmathmisc.h | 250 -
include/geos/algorithm/ttmath/ttmathobjects.h | 812 ---
include/geos/algorithm/ttmath/ttmathparser.h | 2777 --------
include/geos/algorithm/ttmath/ttmaththreads.h | 252 -
include/geos/algorithm/ttmath/ttmathtypes.h | 718 --
include/geos/algorithm/ttmath/ttmathuint.h | 4189 ------------
.../geos/algorithm/ttmath/ttmathuint_noasm.h | 1038 ---
.../geos/algorithm/ttmath/ttmathuint_x86.h | 1620 -----
.../geos/algorithm/ttmath/ttmathuint_x86_64.h | 1177 ----
.../ttmath/ttmathuint_x86_64_msvc.asm | 551 --
include/geos/math/DD.h | 204 +
include/geos/math/Makefile.am | 11 +
src/Makefile.am | 2 +
src/algorithm/CGAlgorithmsDD.cpp | 7 +-
src/algorithm/InteriorPointArea.cpp | 1 -
src/geom/LineString.cpp | 2 +-
src/math/DD.cpp | 403 ++
src/math/Makefile.am | 13 +
tests/unit/Makefile.am | 1 +
tests/unit/math/DDTest.cpp | 414 ++
35 files changed, 1059 insertions(+), 24800 deletions(-)
delete mode 100644 include/geos/algorithm/ttmath/COPYRIGHT
delete mode 100644 include/geos/algorithm/ttmath/Makefile.am
delete mode 100644 include/geos/algorithm/ttmath/README
delete mode 100644 include/geos/algorithm/ttmath/ttmath.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathbig.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathdec.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathint.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathmisc.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathobjects.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathparser.h
delete mode 100644 include/geos/algorithm/ttmath/ttmaththreads.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathtypes.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathuint.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathuint_noasm.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathuint_x86.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathuint_x86_64.h
delete mode 100644 include/geos/algorithm/ttmath/ttmathuint_x86_64_msvc.asm
create mode 100644 include/geos/math/DD.h
create mode 100644 include/geos/math/Makefile.am
create mode 100644 src/math/DD.cpp
create mode 100644 src/math/Makefile.am
create mode 100644 tests/unit/math/DDTest.cpp
diff --git a/Makefile.am b/Makefile.am
index dbc74514..f064d45d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -73,5 +73,4 @@ valgrindcheck:
check-local:
! find $(srcdir) -name '*.cpp' -o -name '*.h' | \
grep -v tests/xmltester/tinyxml | \
- grep -v include/geos/algorithm/ttmath | \
xargs grep -n '[[:space:]]$$'
diff --git a/configure.ac b/configure.ac
index 86778874..80d72e36 100644
--- a/configure.ac
+++ b/configure.ac
@@ -421,7 +421,6 @@ AC_OUTPUT([
include/geos/algorithm/Makefile
include/geos/algorithm/locate/Makefile
include/geos/algorithm/distance/Makefile
- include/geos/algorithm/ttmath/Makefile
include/geos/geom/Makefile
include/geos/geom/prep/Makefile
include/geos/geom/util/Makefile
@@ -436,6 +435,7 @@ AC_OUTPUT([
include/geos/index/sweepline/Makefile
include/geos/io/Makefile
include/geos/linearref/Makefile
+ include/geos/math/Makefile
include/geos/noding/Makefile
include/geos/noding/snapround/Makefile
include/geos/operation/Makefile
@@ -468,6 +468,7 @@ AC_OUTPUT([
src/index/sweepline/Makefile
src/io/Makefile
src/linearref/Makefile
+ src/math/Makefile
src/noding/Makefile
src/noding/snapround/Makefile
src/operation/Makefile
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index c8415081..03d9e5f7 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -853,7 +853,6 @@ EXCLUDE_PATTERNS = */examples/* \
*/test/* \
*/bigtest/* \
*/io/markup/* \
- */ttmath/* \
config.h \
acconfig.h \
CoordinateList.cpp \
diff --git a/include/geos/Makefile.am b/include/geos/Makefile.am
index cc55c4ac..04e85f8f 100644
--- a/include/geos/Makefile.am
+++ b/include/geos/Makefile.am
@@ -8,6 +8,7 @@ SUBDIRS = \
index \
io \
linearref \
+ math \
noding \
operation \
planargraph \
diff --git a/include/geos/algorithm/CGAlgorithmsDD.h b/include/geos/algorithm/CGAlgorithmsDD.h
index f596466c..583532c1 100644
--- a/include/geos/algorithm/CGAlgorithmsDD.h
+++ b/include/geos/algorithm/CGAlgorithmsDD.h
@@ -19,17 +19,7 @@
#ifndef GEOS_ALGORITHM_CGALGORITHMDD_H
#define GEOS_ALGORITHM_CGALGORITHMDD_H
#include <geos/export.h>
-#include <geos/algorithm/ttmath/ttmath.h>
-
-/// \file CGAlgorithmsDD.h
-
-/// \brief Close to DoubleDouble equivalent used by JTS
-///
-/// Usage: `ttmath::Big<exponent, mantissa>`
-typedef ttmath::Big<TTMATH_BITS(32), TTMATH_BITS(128)> DD;
-//typedef ttmath::Big<TTMATH_BITS(64), TTMATH_BITS(128)> DD;
-//typedef ttmath::Big<TTMATH_BITS(32), TTMATH_BITS(256)> DD;
-//typedef ttmath::Big<TTMATH_BITS(64), TTMATH_BITS(256)> DD;
+#include <geos/math/DD.h>
// Forward declarations
namespace geos {
@@ -39,6 +29,8 @@ class CoordinateSequence;
}
}
+using namespace geos::math;
+
namespace geos {
namespace algorithm { // geos::algorithm
@@ -139,7 +131,7 @@ public:
* The circumcentre does not necessarily lie within the triangle. For example,
* the circumcentre of an obtuse isosceles triangle lies outside the triangle.
*
- * This method uses @ref DD extended-precision arithmetic to provide more accurate
+ * This method uses @ref geos::math::DD extended-precision arithmetic to provide more accurate
* results than [circumcentre(Coordinate, Coordinate, Coordinate)]
* (@ref geos::geom::Triangle::circumcentre(const Coordinate& p0, const Coordinate& p1, const Coordinate& p2)).
*
diff --git a/include/geos/algorithm/Makefile.am b/include/geos/algorithm/Makefile.am
index 717e63d8..dbd5a619 100644
--- a/include/geos/algorithm/Makefile.am
+++ b/include/geos/algorithm/Makefile.am
@@ -3,8 +3,7 @@
#
SUBDIRS = \
locate \
- distance \
- ttmath
+ distance
EXTRA_DIST =
diff --git a/include/geos/algorithm/RayCrossingCounterDD.h b/include/geos/algorithm/RayCrossingCounterDD.h
index 2328ab3e..d7ba41e3 100644
--- a/include/geos/algorithm/RayCrossingCounterDD.h
+++ b/include/geos/algorithm/RayCrossingCounterDD.h
@@ -22,7 +22,6 @@
#include <geos/export.h>
#include <geos/geom/Location.h>
-#include <geos/algorithm/ttmath/ttmath.h>
#include <vector>
diff --git a/include/geos/math/DD.h b/include/geos/math/DD.h
new file mode 100644
index 00000000..7bcbb183
--- /dev/null
+++ b/include/geos/math/DD.h
@@ -0,0 +1,204 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020 Crunchy Data
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+/**
+ * Implements extended-precision floating-point numbers
+ * which maintain 106 bits (approximately 30 decimal digits) of precision.
+ * <p>
+ * A DoubleDouble uses a representation containing two double-precision values.
+ * A number x is represented as a pair of doubles, x.hi and x.lo,
+ * such that the number represented by x is x.hi + x.lo, where
+ * <pre>
+ * |x.lo| &lt;= 0.5*ulp(x.hi)
+ * </pre>
+ * and ulp(y) means "unit in the last place of y".
+ * The basic arithmetic operations are implemented using
+ * convenient properties of IEEE-754 floating-point arithmetic.
+ * <p>
+ * The range of values which can be represented is the same as in IEEE-754.
+ * The precision of the representable numbers
+ * is twice as great as IEEE-754 double precision.
+ * <p>
+ * The correctness of the arithmetic algorithms relies on operations
+ * being performed with standard IEEE-754 double precision and rounding.
+ * This is the Java standard arithmetic model, but for performance reasons
+ * Java implementations are not
+ * constrained to using this standard by default.
+ * Some processors (notably the Intel Pentium architecture) perform
+ * floating point operations in (non-IEEE-754-standard) extended-precision.
+ * A JVM implementation may choose to use the non-standard extended-precision
+ * as its default arithmetic mode.
+ * To prevent this from happening, this code uses the
+ * Java <tt>strictfp</tt> modifier,
+ * which forces all operations to take place in the standard IEEE-754 rounding model.
+ * <p>
+ * The API provides both a set of value-oriented operations
+ * and a set of mutating operations.
+ * Value-oriented operations treat DoubleDouble values as
+ * immutable; operations on them return new objects carrying the result
+ * of the operation. This provides a simple and safe semantics for
+ * writing DoubleDouble expressions. However, there is a performance
+ * penalty for the object allocations required.
+ * The mutable interface updates object values in-place.
+ * It provides optimum memory performance, but requires
+ * care to ensure that aliasing errors are not created
+ * and constant values are not changed.
+ * <p>
+ * For example, the following code example constructs three DD instances:
+ * two to hold the input values and one to hold the result of the addition.
+ * <pre>
+ * DD a = new DD(2.0);
+ * DD b = new DD(3.0);
+ * DD c = a.add(b);
+ * </pre>
+ * In contrast, the following approach uses only one object:
+ * <pre>
+ * DD a = new DD(2.0);
+ * a.selfAdd(3.0);
+ * </pre>
+ * <p>
+ * This implementation uses algorithms originally designed variously by
+ * Knuth, Kahan, Dekker, and Linnainmaa.
+ * Douglas Priest developed the first C implementation of these techniques.
+ * Other more recent C++ implementation are due to Keith M. Briggs and David Bailey et al.
+ *
+ * <h3>References</h3>
+ * <ul>
+ * <li>Priest, D., <i>Algorithms for Arbitrary Precision Floating Point Arithmetic</i>,
+ * in P. Kornerup and D. Matula, Eds., Proc. 10th Symposium on Computer Arithmetic,
+ * IEEE Computer Society Press, Los Alamitos, Calif., 1991.
+ * <li>Yozo Hida, Xiaoye S. Li and David H. Bailey,
+ * <i>Quad-Double Arithmetic: Algorithms, Implementation, and Application</i>,
+ * manuscript, Oct 2000; Lawrence Berkeley National Laboratory Report BNL-46996.
+ * <li>David Bailey, <i>High Precision Software Directory</i>;
+ * <tt>http://crd.lbl.gov/~dhbailey/mpdist/index.html</tt>
+ * </ul>
+ *
+ *
+ * @author Martin Davis
+ *
+ */
+
+#ifndef GEOS_MATH_DD_H
+#define GEOS_MATH_DD_H
+
+#include <cmath>
+
+namespace geos {
+namespace math { // geos.math
+
+/**
+ * \class DD
+ *
+ * \brief
+ * Wrapper for DoubleDouble higher precision mathematics
+ * operations.
+ */
+class GEOS_DLL DD {
+ private:
+ static constexpr double SPLIT = 134217729.0; // 2^27+1, for IEEE double
+ double hi;
+ double lo;
+
+ int magnitude(double x) const;
+ int signum() const;
+ DD rint() const;
+
+
+ public:
+ DD(double p_hi, double p_lo) : hi(p_hi), lo(p_lo) {};
+ DD(double x) : hi(x), lo(0.0) {};
+ DD(const DD &dd) : hi(dd.hi), lo(dd.lo) {};
+ DD() : hi(0.0), lo(0.0) {};
+
+ bool operator==(const DD &rhs) const
+ {
+ return hi == rhs.hi && lo == rhs.lo;
+ }
+
+ bool operator!=(const DD &rhs) const
+ {
+ return hi != rhs.hi || lo != rhs.lo;
+ }
+
+ bool operator<(const DD &rhs) const
+ {
+ return (hi < rhs.hi) || (hi == rhs.hi && lo < rhs.lo);
+ }
+
+ bool operator<=(const DD &rhs) const
+ {
+ return (hi < rhs.hi) || (hi == rhs.hi && lo <= rhs.lo);
+ }
+
+ bool operator>(const DD &rhs) const
+ {
+ return (hi > rhs.hi) || (hi == rhs.hi && lo > rhs.lo);
+ }
+
+ bool operator>=(const DD &rhs) const
+ {
+ return (hi > rhs.hi) || (hi == rhs.hi && lo >= rhs.lo);
+ }
+
+ friend DD operator+ (const DD &lhs, const DD &rhs);
+ friend DD operator+ (const DD &lhs, double rhs);
+ friend DD operator- (const DD &lhs, const DD &rhs);
+ friend DD operator- (const DD &lhs, double rhs);
+ friend DD operator* (const DD &lhs, const DD &rhs);
+ friend DD operator* (const DD &lhs, double rhs);
+ friend DD operator/ (const DD &lhs, const DD &rhs);
+ friend DD operator/ (const DD &lhs, double rhs);
+
+ static DD determinant(const DD &x1, const DD &y1, const DD &x2, const DD &y2);
+ static DD determinant(double x1, double y1, double x2, double y2);
+ static DD abs(const DD &d);
+ static DD pow(const DD &d, int exp);
+ static DD trunc(const DD &d);
+
+ bool isNaN() const;
+ bool isNegative() const;
+ bool isPositive() const;
+ bool isZero() const;
+ double doubleValue() const;
+ double ToDouble() const { return doubleValue(); }
+ int intValue() const;
+ DD negate() const;
+ DD reciprocal() const;
+ DD floor() const;
+ DD ceil() const;
+
+ void selfAdd(const DD &d);
+ void selfAdd(double p_hi, double p_lo);
+ void selfAdd(double y);
+
+ void selfSubtract(const DD &d);
+ void selfSubtract(double p_hi, double p_lo);
+ void selfSubtract(double y);
+
+ void selfMultiply(double p_hi, double p_lo);
+ void selfMultiply(const DD &d);
+ void selfMultiply(double y);
+
+ void selfDivide(double p_hi, double p_lo);
+ void selfDivide(const DD &d);
+ void selfDivide(double y);
+};
+
+
+} // namespace geos::math
+} // namespace geos
+
+
+#endif // GEOS_MATH_DD_H
diff --git a/include/geos/math/Makefile.am b/include/geos/math/Makefile.am
new file mode 100644
index 00000000..5396b951
--- /dev/null
+++ b/include/geos/math/Makefile.am
@@ -0,0 +1,11 @@
+#
+# This file is part of project GEOS (http://trac.osgeo.org/geos/)
+#
+SUBDIRS =
+
+EXTRA_DIST =
+
+geosdir = $(includedir)/geos/math
+
+geos_HEADERS = \
+ DD.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 1bba8e87..ea465248 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,7 @@ SUBDIRS = \
index \
io \
linearref \
+ math \
noding \
operation \
planargraph \
@@ -39,6 +40,7 @@ libgeos_la_LIBADD = \
index/libindex.la \
io/libio.la \
linearref/liblinearref.la \
+ math/libmath.la \
noding/libnoding.la \
operation/liboperation.la \
planargraph/libplanargraph.la \
diff --git a/src/algorithm/CGAlgorithmsDD.cpp b/src/algorithm/CGAlgorithmsDD.cpp
index c25c1f28..c484c1f6 100644
--- a/src/algorithm/CGAlgorithmsDD.cpp
+++ b/src/algorithm/CGAlgorithmsDD.cpp
@@ -35,7 +35,7 @@ namespace {
double constexpr DP_SAFE_EPSILON = 1e-15;
inline int
-OrientationDD(DD const& dd)
+OrientationDD(const DD &dd)
{
static DD const zero(0.0);
if(dd < zero) {
@@ -49,10 +49,7 @@ OrientationDD(DD const& dd)
return CGAlgorithmsDD::STRAIGHT;
}
-// inline std::string ToStringDD(DD const& dd)
-// {
-// return dd.ToString();
-// }
+
}
namespace geos {
diff --git a/src/algorithm/InteriorPointArea.cpp b/src/algorithm/InteriorPointArea.cpp
index 1ddddac9..61c36fd0 100644
--- a/src/algorithm/InteriorPointArea.cpp
+++ b/src/algorithm/InteriorPointArea.cpp
@@ -200,7 +200,6 @@ private:
// edge intersects scan line, so add a crossing
double xInt = intersection(p0, p1, scanY);
crossings.push_back(xInt);
- //checkIntersectionDD(p0, p1, scanY, xInt);
}
void findBestMidpoint(vector<double>& crossings)
diff --git a/src/geom/LineString.cpp b/src/geom/LineString.cpp
index 65f86a0d..ac5d7fe6 100644
--- a/src/geom/LineString.cpp
+++ b/src/geom/LineString.cpp
@@ -45,7 +45,7 @@ using namespace geos::algorithm;
namespace geos {
namespace geom { // geos::geom
-LineString::~LineString(){};
+LineString::~LineString(){}
/*protected*/
LineString::LineString(const LineString& ls)
diff --git a/src/math/DD.cpp b/src/math/DD.cpp
new file mode 100644
index 00000000..673e9217
--- /dev/null
+++ b/src/math/DD.cpp
@@ -0,0 +1,403 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020 Crunchy Data
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <cmath>
+
+#include <geos/profiler.h>
+#include <geos/math/DD.h>
+
+namespace geos {
+namespace math { // geos.util
+
+
+/* private */
+int
+DD::magnitude(double x) const
+{
+ double xAbs = std::fabs(x);
+ double xLog10 = std::log(xAbs) / std::log(10);
+ int xMag = (int) std::floor(xLog10);
+ /**
+ * Since log computation is inexact, there may be an off-by-one error
+ * in the computed magnitude.
+ * Following tests that magnitude is correct, and adjusts it if not
+ */
+ double xApprox = std::pow(10, xMag);
+ if (xApprox * 10 <= xAbs)
+ xMag += 1;
+
+ return xMag;
+}
+
+/* public */
+bool DD::isNaN() const
+{
+ return std::isnan(hi);
+}
+/* public */
+bool DD::isNegative() const
+{
+ return hi < 0.0 || (hi == 0.0 && lo < 0.0);
+}
+/* public */
+bool DD::isPositive() const
+{
+ return hi > 0.0 || (hi == 0.0 && lo > 0.0);
+}
+/* public */
+bool DD::isZero() const
+{
+ return hi == 0.0 && lo == 0.0;
+}
+
+/* public */
+double DD::doubleValue() const
+{
+ return hi + lo;
+}
+
+/* public */
+int DD::intValue() const
+{
+ return (int) hi;
+}
+
+/* public */
+void DD::selfAdd(const DD &y)
+{
+ return selfAdd(y.hi, y.lo);
+}
+
+/* public */
+void DD::selfAdd(double yhi, double ylo)
+{
+ double H, h, T, t, S, s, e, f;
+ S = hi + yhi;
+ T = lo + ylo;
+ e = S - hi;
+ f = T - lo;
+ s = S-e;
+ t = T-f;
+ s = (yhi-e)+(hi-s);
+ t = (ylo-f)+(lo-t);
+ e = s+T; H = S+e; h = e+(S-H); e = t+h;
+
+ double zhi = H + e;
+ double zlo = e + (H - zhi);
+ hi = zhi;
+ lo = zlo;
+ return;
+}
+
+/* public */
+void DD::selfAdd(double y)
+{
+ double H, h, S, s, e, f;
+ S = hi + y;
+ e = S - hi;
+ s = S - e;
+ s = (y - e) + (hi - s);
+ f = s + lo;
+ H = S + f;
+ h = f + (S - H);
+ hi = H + h;
+ lo = h + (H - hi);
+ return;
+}
+
+/* public */
+DD operator+(const DD &lhs, const DD &rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfAdd(rhs);
+ return rv;
+}
+
+/* public */
+DD operator+(const DD &lhs, double rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfAdd(rhs);
+ return rv;
+}
+
+/* public */
+void DD::selfSubtract(const DD &d)
+{
+ return selfAdd(-1*d.hi, -1*d.lo);
+}
+
+/* public */
+void DD::selfSubtract(double p_hi, double p_lo)
+{
+ return selfAdd(-1*p_hi, -1*p_lo);
+}
+
+/* public */
+void DD::selfSubtract(double y)
+{
+ return selfAdd(-1*y, 0.0);
+}
+
+/* public */
+DD operator-(const DD &lhs, const DD &rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfSubtract(rhs);
+ return rv;
+}
+
+/* public */
+DD operator-(const DD &lhs, double rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfSubtract(rhs);
+ return rv;
+}
+
+/* public */
+void DD::selfMultiply(double yhi, double ylo)
+{
+ double hx, tx, hy, ty, C, c;
+ C = SPLIT * hi; hx = C-hi; c = SPLIT * yhi;
+ hx = C-hx; tx = hi-hx; hy = c-yhi;
+ C = hi*yhi; hy = c-hy; ty = yhi-hy;
+ c = ((((hx*hy-C)+hx*ty)+tx*hy)+tx*ty)+(hi*ylo+lo*yhi);
+ double zhi = C+c; hx = C-zhi;
+ double zlo = c+hx;
+ hi = zhi;
+ lo = zlo;
+ return;
+}
+
+/* public */
+void DD::selfMultiply(DD const &d)
+{
+ return selfMultiply(d.hi, d.lo);
+}
+
+/* public */
+void DD::selfMultiply(double y)
+{
+ return selfMultiply(y, 0.0);
+}
+
+/* public */
+DD operator*(const DD &lhs, const DD &rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfMultiply(rhs);
+ return rv;
+}
+
+/* public */
+DD operator*(const DD &lhs, double rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfMultiply(rhs);
+ return rv;
+}
+
+
+/* public */
+void DD::selfDivide(double yhi, double ylo)
+{
+ double hc, tc, hy, ty, C, c, U, u;
+ C = hi/yhi; c = SPLIT*C; hc =c-C;
+ u = SPLIT*yhi; hc = c-hc;
+ tc = C-hc; hy = u-yhi; U = C * yhi;
+ hy = u-hy; ty = yhi-hy;
+ u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty;
+ c = ((((hi-U)-u)+lo)-C*ylo)/yhi;
+ u = C+c;
+ hi = u;
+ lo = (C-u)+c;
+ return;
+}
+
+/* public */
+void DD::selfDivide(const DD &d)
+{
+ return selfDivide(d.hi, d.lo);
+}
+
+/* public */
+void DD::selfDivide(double y)
+{
+ return selfDivide(y, 0.0);
+}
+
+/* public */
+DD operator/(const DD &lhs, const DD &rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfDivide(rhs);
+ return rv;
+}
+
+/* public */
+DD operator/(const DD &lhs, double rhs)
+{
+ DD rv(lhs.hi, lhs.lo);
+ rv.selfDivide(rhs);
+ return rv;
+}
+
+/* public */
+DD DD::negate() const
+{
+ DD rv(hi, lo);
+ if (rv.isNaN())
+ {
+ return rv;
+ }
+ rv.hi = -hi;
+ rv.lo = -lo;
+ return rv;
+}
+
+/* public static */
+DD DD::reciprocal() const
+{
+ double hc, tc, hy, ty, C, c, U, u;
+ C = 1.0/hi;
+ c = SPLIT*C;
+ hc = c-C;
+ u = SPLIT*hi;
+ hc = c-hc; tc = C-hc; hy = u-hi; U = C*hi; hy = u-hy; ty = hi-hy;
+ u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty;
+ c = ((((1.0-U)-u))-C*lo)/hi;
+ double zhi = C+c;
+ double zlo = (C-zhi)+c;
+ return DD(zhi, zlo);
+}
+
+DD DD::floor() const
+{
+ DD rv(hi, lo);
+ if (isNaN()) return rv;
+ double fhi = std::floor(hi);
+ double flo = 0.0;
+ // Hi is already integral. Floor the low word
+ if (fhi == hi) {
+ flo = std::floor(lo);
+ }
+ // do we need to renormalize here?
+ rv.hi = fhi;
+ rv.lo = flo;
+ return rv;
+}
+
+DD DD::ceil() const
+{
+ DD rv(hi, lo);
+ if (isNaN()) return rv;
+ double fhi = std::ceil(hi);
+ double flo = 0.0;
+ // Hi is already integral. Ceil the low word
+ if (fhi == hi) {
+ flo = std::ceil(lo);
+ // do we need to renormalize here?
+ }
+ rv.hi = fhi;
+ rv.lo = flo;
+ return rv;
+}
+
+int DD::signum() const
+{
+ if (hi > 0) return 1;
+ if (hi < 0) return -1;
+ if (lo > 0) return 1;
+ if (lo < 0) return -1;
+ return 0;
+}
+
+DD DD::rint() const
+{
+ DD rv(hi, lo);
+ if (isNaN()) return rv;
+ return (rv + 0.5).floor();
+}
+
+/* public static */
+DD DD::trunc(const DD &d)
+{
+ DD rv(d);
+ if (rv.isNaN()) return rv;
+ if (rv.isPositive())
+ return rv.floor();
+ return rv.ceil();
+}
+
+/* public static */
+DD DD::abs(const DD &d)
+{
+ DD rv(d);
+ if (rv.isNaN()) return rv;
+ if (rv.isNegative())
+ return rv.negate();
+
+ return rv;
+}
+
+/* public static */
+DD DD::determinant(const DD &x1, const DD &y1, const DD &x2, const DD &y2)
+{
+ return (x1 * y2) - (y1 * x2);
+}
+
+/* public static */
+DD DD::determinant(double x1, double y1, double x2, double y2)
+{
+ return determinant(DD(x1), DD(y1), DD(x2), DD(y2) );
+}
+
+/**
+* Computes the value of this number raised to an integral power.
+* Follows semantics of Java Math.pow as closely as possible.
+*/
+/* public static */
+DD DD::pow(const DD &d, int exp)
+{
+ if (exp == 0.0)
+ return DD(1.0);
+
+ DD r(d);
+ DD s(1.0);
+ int n = std::abs(exp);
+
+ if (n > 1) {
+ /* Use binary exponentiation */
+ while (n > 0) {
+ if (n % 2 == 1) {
+ s.selfMultiply(r);
+ }
+ n /= 2;
+ if (n > 0)
+ r = r*r;
+ }
+ } else {
+ s = r;
+ }
+
+ /* Compute the reciprocal if n is negative. */
+ if (exp < 0)
+ return s.reciprocal();
+ return s;
+}
+
+
+}
+}
diff --git a/src/math/Makefile.am b/src/math/Makefile.am
new file mode 100644
index 00000000..c30e97ea
--- /dev/null
+++ b/src/math/Makefile.am
@@ -0,0 +1,13 @@
+#
+# This file is part of project GEOS (http://trac.osgeo.org/geos/)
+#
+SUBDIRS =
+
+noinst_LTLIBRARIES = libmath.la
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+libmath_la_SOURCES = \
+ DD.cpp
+
+libmath_la_LIBADD =
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index 88c4816f..3c836fc8 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -151,6 +151,7 @@ geos_unit_SOURCES = \
io/WKTWriterTest.cpp \
io/WriterTest.cpp \
linearref/LengthIndexedLineTest.cpp \
+ math/DDTest.cpp \
noding/BasicSegmentStringTest.cpp \
noding/NodedSegmentStringTest.cpp \
noding/OrientedCoordinateArrayTest.cpp \
diff --git a/tests/unit/math/DDTest.cpp b/tests/unit/math/DDTest.cpp
new file mode 100644
index 00000000..00a29b8f
--- /dev/null
+++ b/tests/unit/math/DDTest.cpp
@@ -0,0 +1,414 @@
+//
+// Test Suite for geos::util::UniqueCoordinateArrayFilter class.
+
+// geos
+
+#include <geos/profiler.h>
+#include <geos/math/DD.h>
+
+// tut
+#include <tut/tut.hpp>
+#include <utility.h>
+
+// std
+#include <memory>
+#include <string>
+
+using namespace geos::math;
+
+namespace tut {
+//
+// Test Group
+//
+
+// Common data used in test cases.
+struct test_dd_data {
+
+ double eps;
+ DD pi;
+ DD e;
+
+
+ void ensure_dd_equals(const char *str, const DD &d1, const DD &d2, double tolerance)
+ {
+ DD delta = DD::abs(d1 - d2);
+ double diff = delta.doubleValue();
+ ensure(str, diff <= tolerance);
+ }
+
+ void checkTrunc(const DD &x, const DD &expected)
+ {
+ DD trunc = DD::trunc(x);
+ ensure("checkTrunc", trunc == expected);
+ }
+
+ void checkDeterminant(double x1, double y1, double x2, double y2, double expected, double errBound)
+ {
+ DD det = DD::determinant(x1, y1, x2, y2);
+ //ensure_equals("1", Angle::angle(Coordinate(10, 0)), 0.0, TOL);
+ ensure_dd_equals("checkDeterminant", det, DD(expected), errBound);
+ }
+
+ void checkDeterminantDD(double x1, double y1, double x2, double y2, double expected, double errBound)
+ {
+ DD det = DD::determinant(DD(x1), DD(y1), DD(x2), DD(y2));
+ ensure_dd_equals("checkDeterminantDD", det, DD(expected), errBound);
+ }
+
+ void checkAddMult2(const DD &dd)
+ {
+ DD sum = dd + dd;
+ DD prod = dd * DD(2.0);
+ ensure_dd_equals("checkAddMult2", sum, prod, 0.0);
+ }
+
+ void checkMultiplyDivide(const DD &a, const DD &b, double errBound)
+ {
+ DD a2 = (a * b) / b;
+ ensure_dd_equals("checkMultiplyDivide", a, a2, errBound);
+ }
+
+ void checkDivideMultiply(const DD &a, const DD &b, double errBound)
+ {
+ DD a2 = (a / b) * b;
+ ensure_dd_equals("checkDivideMultiply", a, a2, errBound);
+ }
+
+ /**
+ * Computes (a+b)^2 in two different ways and compares the result.
+ * For correct results, a and b should be integers.
+ */
+ void checkBinomialSquare(double a, double b)
+ {
+ // binomial square
+ DD add(a);
+ DD bdd(b);
+ DD aPlusb = add + bdd;
+ DD abSq = aPlusb * aPlusb;
+
+ // expansion
+ DD a2dd = add * add;
+ DD b2dd = bdd * bdd;
+ DD ab = add * bdd;
+ DD sum = b2dd + ab + ab;
+ DD diff = abSq - a2dd;
+ DD delta = diff - sum;
+
+ ensure("isSame", diff == sum);
+ ensure("isDeltaZero", delta.isZero());
+ }
+
+ void checkBinomial2(double a, double b)
+ {
+ // binomial product
+ DD add(a);
+ DD bdd(b);
+ DD aPlusb = add + bdd;
+ DD aSubb = add - bdd;
+ DD abProd = aPlusb * aSubb;
+
+ // expansion
+ DD a2dd = add * add;
+ DD b2dd = bdd * bdd;
+
+ // this should equal b^2
+ DD diff = (abProd - a2dd).negate();
+ DD delta = diff - b2dd;
+
+ ensure("isSame", diff == b2dd);
+ ensure("isDeltaZero", delta.isZero());
+ }
+
+ void checkReciprocal(double x, double errBound)
+ {
+ DD xdd(x);
+ DD rr = xdd.reciprocal().reciprocal();
+ double err = (xdd - rr).doubleValue();
+ ensure("checkReciprocal", err <= errBound);
+ }
+
+ DD slowPow(const DD &x, int exp)
+ {
+ if (exp == 0)
+ return DD(1.0);
+
+ int n = std::abs(exp);
+ // MD - could use binary exponentiation for better precision & speed
+ DD pow(x);
+ for (int i = 1; i < n; i++) {
+ pow = pow * x;
+ }
+ if (exp < 0) {
+ return pow.reciprocal();
+ }
+ return pow;
+ }
+
+ void checkPow(double x, int exp, double errBound)
+ {
+ DD xdd(x);
+ DD pow = DD::pow(xdd, exp);
+ DD pow2 = slowPow(xdd, exp);
+ double err = (pow - pow2).doubleValue();
+ ensure("checkPow", err <= errBound);
+ }
+
+
+ DD arctan(DD x)
+ {
+ DD t = x;
+ DD t2 = t*t;
+ DD at(0.0);
+ DD two(2.0);
+ int k = 0;
+ DD d(1.0);
+ int sign = 1;
+ while (t.doubleValue() > eps) {
+ k++;
+ if (sign < 0)
+ at = at - (t / d);
+ else
+ at = at + (t / d);
+
+ d = d + two;
+ t = t * t2;
+ sign = -sign;
+ }
+ return at;
+ }
+
+ /**
+ * Uses Taylor series to compute e
+ *
+ * e = 1 + 1 + 1/2! + 1/3! + 1/4! + ...
+ */
+ DD computeEByTaylorSeries()
+ {
+ DD s(2.0);
+ DD t(1.0);
+ double n = 1.0;
+ int i = 0;
+ while(t.doubleValue() > eps)
+ {
+ i++;
+ n += 1.0;
+ t = t / DD(n);
+ s = s + t;
+ }
+ return s;
+ }
+
+ /**
+ * Uses Machin's arctangent formula to compute Pi:
+ *
+ * Pi / 4 = 4 arctan(1/5) - arctan(1/239)
+ */
+
+ DD computePiByMachin()
+ {
+ DD t1 = DD(1.0) / DD(5.0);
+ DD t2 = DD(1.0) / DD(239.0);
+ DD pi4 = (DD(4.0) * arctan(t1)) - arctan(t2);
+ DD pi = DD(4.0) * pi4;
+ return pi;
+ }
+
+ test_dd_data():
+ eps(1.23259516440783e-32), /* = 2^-106 */
+ pi(DD(3.141592653589793116e+00, 1.224646799147353207e-16)),
+ e(DD(2.718281828459045091e+00, 1.445646891729250158e-16))
+ {}
+
+};
+
+typedef test_group<test_dd_data> group;
+typedef group::object object;
+
+group test_dd_group("geos::math::DD");
+
+//
+// Test Cases
+//
+
+// Test PI calculation
+template<>
+template<>
+void object::test<1>
+()
+{
+ DD testPi = computePiByMachin();
+ double err = std::abs((testPi - pi).doubleValue());
+ // std::cout << "Difference from PI = " << err << std::endl;
+ ensure("Test PI calculation", err < 8*eps);
+}
+
+// Test E calculation
+template<>
+template<>
+void object::test<2>
+()
+{
+ DD testE = computeEByTaylorSeries();
+ double err = std::abs((testE - e).doubleValue());
+ // std::cout << "Difference from E = " << err << std::endl;
+ ensure("Test E calculation", err < eps);
+}
+
+
+// Test NaN
+template<>
+template<>
+void object::test<3>
+()
+{
+ DD nan = DD(1.0) / DD(0.0);
+ ensure("isNan", nan.isNaN());
+ ensure("isNan", (DD(1.0) * nan).isNaN());
+}
+
+// testAddMult2
+template<>
+template<>
+void object::test<4>
+()
+{
+ checkAddMult2(DD(3.0));
+ checkAddMult2(DD(pi));
+}
+
+
+// testMultiplyDivide
+template<>
+template<>
+void object::test<5>
+()
+{
+ checkMultiplyDivide(DD(pi), DD(e), 1e-30);
+ checkMultiplyDivide(DD(DD(2.0)*pi), DD(e), 1e-30);
+ checkMultiplyDivide(DD(DD(0.5)*pi), DD(e), 1e-30);
+ checkMultiplyDivide(DD(39.4), DD(10), 1e-30);
+}
+
+
+// testDivideMultiply
+template<>
+template<>
+void object::test<6>
+()
+{
+ checkDivideMultiply(DD(pi), DD(e), 1e-30);
+ checkDivideMultiply(DD(39.4), DD(10), 1e-30);
+}
+
+
+// testTrunc
+template<>
+template<>
+void object::test<7>
+()
+{
+ checkTrunc(DD(1e16) - DD(1), DD(1e16) - DD(1));
+ // the appropriate error bound is determined empirically
+ checkTrunc(DD(pi), DD(3));
+ checkTrunc(DD(999.999), DD(999));
+
+ checkTrunc(DD(e).negate(), DD(-2));
+ checkTrunc(DD(-999.999), DD(-999));
+}
+
+// testPow
+template<>
+template<>
+void object::test<8>
+()
+{
+ checkPow(0, 3, 16 * eps);
+ checkPow(0, 3, 16 * eps);
+ checkPow(14, 3, 16 * eps);
+ checkPow(3, -5, 16 * eps);
+ checkPow(-3, 5, 16 * eps);
+ checkPow(-3, -5, 16 * eps);
+ checkPow(0.12345, -5, 1e5 * eps);
+}
+
+
+// testReciprocal
+template<>
+template<>
+void object::test<9>
+()
+{
+ checkReciprocal(3.0, 0);
+ checkReciprocal(99.0, 1e-29);
+ checkReciprocal(999.0, 0);
+ checkReciprocal(314159269.0, 0);
+}
+
+// testDeterminant
+template<>
+template<>
+void object::test<10>
+()
+{
+ checkDeterminant(3, 8, 4, 6, -14, 0);
+ checkDeterminantDD(3, 8, 4, 6, -14, 0);
+}
+
+// testDeterminantRobust
+template<>
+template<>
+void object::test<11>
+()
+{
+ checkDeterminant(1.0e9, 1.0e9 - 1, 1.0e9 - 1, 1.0e9 - 2, -1, 0);
+ checkDeterminantDD(1.0e9, 1.0e9 - 1, 1.0e9 - 1, 1.0e9 - 2, -1, 0);
+}
+
+
+// testBinom
+template<>
+template<>
+void object::test<12>
+()
+{
+ checkBinomialSquare(100.0, 1.0);
+ checkBinomialSquare(1000.0, 1.0);
+ checkBinomialSquare(10000.0, 1.0);
+ checkBinomialSquare(100000.0, 1.0);
+ checkBinomialSquare(1000000.0, 1.0);
+ checkBinomialSquare(1e8, 1.0);
+ checkBinomialSquare(1e10, 1.0);
+ checkBinomialSquare(1e14, 1.0);
+ // Following call will fail, because it requires 32 digits of precision
+ // checkBinomialSquare(1e16, 1.0);
+
+ checkBinomialSquare(1e14, 291.0);
+ checkBinomialSquare(5e14, 291.0);
+ checkBinomialSquare(5e14, 345291.0);
+}
+
+// testBinom2
+template<>
+template<>
+void object::test<13>
+()
+{
+ checkBinomial2(100.0, 1.0);
+ checkBinomial2(1000.0, 1.0);
+ checkBinomial2(10000.0, 1.0);
+ checkBinomial2(100000.0, 1.0);
+ checkBinomial2(1000000.0, 1.0);
+ checkBinomial2(1e8, 1.0);
+ checkBinomial2(1e10, 1.0);
+ checkBinomial2(1e14, 1.0);
+
+ checkBinomial2(1e14, 291.0);
+
+ checkBinomial2(5e14, 291.0);
+ checkBinomial2(5e14, 345291.0);
+}
+
+
+
+} // namespace tut
+
--
2.26.2