[geos-commits] [SCM] GEOS branch master updated. 60dda910dc31171d4919025bc489687b36766aa9

git at osgeo.org git at osgeo.org
Sat Mar 6 03:51:39 PST 2021


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".

The branch, master has been updated
       via  60dda910dc31171d4919025bc489687b36766aa9 (commit)
       via  39a227b32fcf87af2bff9fb65ca65e2a180882d0 (commit)
       via  ee6ada22361f6b524fde6226e08e48a8c585090b (commit)
       via  9a3116d34432f75d8d343be611ae22ddbaf32035 (commit)
       via  9b0d3d81124750a3134f4e0b0ede43fb1d5bf5c6 (commit)
       via  1a5601f17db666e2e173c57df2bfaeeaa4d2181a (commit)
       via  0b2c29abd785bea95dc83f1777335ad48e744f8d (commit)
       via  6f73f02624aaff98a7289f4a481bcfc97bc845df (commit)
       via  f79a3ad98cc8bf85dc5d47f441dc6803cda3fda8 (commit)
       via  04121400d17251c23cbd55dacdea8dd6ae2e2fcd (commit)
       via  868b33cd901a22e049c60d580667ba51e5381913 (commit)
       via  41e1c326f9bd29ac1232fe88f9a0514fcf57badd (commit)
       via  00fff60636aee7931b5c80c92b84d726e014dd8d (commit)
       via  7cae4e8923984411b26d954453e1bd963a638976 (commit)
       via  e43f780745f34410a56d2ae48677017098340af7 (commit)
      from  02bde79dd08fb84b3f42b93083fa6f4ce4ca17bd (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 60dda910dc31171d4919025bc489687b36766aa9
Author: Daniel Baston <dbaston at gmail.com>
Date:   Tue Feb 9 18:59:14 2021 -0500

    Remove unused field in IndexedPointInAreaLocator

diff --git a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
index 6f5cd71..ddd5c67 100644
--- a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
+++ b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
@@ -77,7 +77,6 @@ private:
     private:
 
         index::strtree::TemplateSTRtree<SegmentView, index::strtree::IntervalTraits> index;
-        bool isEmpty;
 
         void init(const geom::Geometry& g);
         void addLine(const geom::CoordinateSequence* pts);
diff --git a/src/algorithm/locate/IndexedPointInAreaLocator.cpp b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
index 4a26175..c995c64 100644
--- a/src/algorithm/locate/IndexedPointInAreaLocator.cpp
+++ b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
@@ -39,12 +39,8 @@ namespace locate {
 // private:
 //
 IndexedPointInAreaLocator::IntervalIndexedGeometry::IntervalIndexedGeometry(const geom::Geometry& g)
-    : isEmpty(0)
 {
-    if (g.isEmpty())
-        isEmpty = true;
-    else
-        init(g);
+    init(g);
 }
 
 void

commit 39a227b32fcf87af2bff9fb65ca65e2a180882d0
Author: Daniel Baston <dbaston at gmail.com>
Date:   Tue Feb 9 18:43:04 2021 -0500

    Reduce node size in IndexedPointInAreaLocator tree

diff --git a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
index d6f781b..6f5cd71 100644
--- a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
+++ b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
@@ -55,10 +55,22 @@ namespace locate { // geos::algorithm::locate
 class IndexedPointInAreaLocator : public PointOnGeometryLocator {
 private:
     struct SegmentView {
-        SegmentView(const geom::Coordinate* p_p0, const geom::Coordinate* p_p1) : p0(p_p0), p1(p_p1) {}
+        SegmentView(const geom::Coordinate* p_p0, const geom::Coordinate* p_p1) : m_p0(p_p0) {
+            // All GEOS CoordinateSequences store their coordinates sequentially.
+            // Should that ever change, this assert will fail.
+            (void) p_p1;
+            assert(p_p0 + 1 == p_p1);
+        }
+
+        const geom::Coordinate& p0() const {
+            return *m_p0;
+        }
+
+        const geom::Coordinate& p1() const {
+            return *(m_p0 + 1);
+        }
 
-        const geom::Coordinate* p0;
-        const geom::Coordinate* p1;
+        const geom::Coordinate* m_p0;
     };
 
     class IntervalIndexedGeometry {
diff --git a/src/algorithm/locate/IndexedPointInAreaLocator.cpp b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
index db827a4..4a26175 100644
--- a/src/algorithm/locate/IndexedPointInAreaLocator.cpp
+++ b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
@@ -70,7 +70,7 @@ IndexedPointInAreaLocator::IntervalIndexedGeometry::addLine(const geom::Coordina
 {
     for(std::size_t i = 1, ni = pts->size(); i < ni; i++) {
         SegmentView seg(&pts->getAt(i-1), &pts->getAt(i));
-        auto r = std::minmax(seg.p0->y, seg.p1->y);
+        auto r = std::minmax(seg.p0().y, seg.p1().y);
 
         index.insert(index::strtree::Interval(r.first, r.second), seg);
     }
@@ -112,7 +112,7 @@ IndexedPointInAreaLocator::locate(const geom::Coordinate* /*const*/ p)
     algorithm::RayCrossingCounter rcc(*p);
 
     index->query(p->y, p->y, [&rcc](const SegmentView& ls) {
-        rcc.countSegment(*ls.p0, *ls.p1);
+        rcc.countSegment(ls.p0(), ls.p1());
     });
 
     return rcc.getLocation();

commit ee6ada22361f6b524fde6226e08e48a8c585090b
Author: Daniel Baston <dbaston at gmail.com>
Date:   Tue Feb 2 21:44:53 2021 -0500

    Improve IndexedPointInAreaLocator performance

diff --git a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
index fc5bb77..d6f781b 100644
--- a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
+++ b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
@@ -54,17 +54,22 @@ namespace locate { // geos::algorithm::locate
  */
 class IndexedPointInAreaLocator : public PointOnGeometryLocator {
 private:
+    struct SegmentView {
+        SegmentView(const geom::Coordinate* p_p0, const geom::Coordinate* p_p1) : p0(p_p0), p1(p_p1) {}
+
+        const geom::Coordinate* p0;
+        const geom::Coordinate* p1;
+    };
+
     class IntervalIndexedGeometry {
     private:
-        index::strtree::TemplateSTRtree<geom::LineSegment*, index::strtree::IntervalTraits> index;
+
+        index::strtree::TemplateSTRtree<SegmentView, index::strtree::IntervalTraits> index;
         bool isEmpty;
 
         void init(const geom::Geometry& g);
         void addLine(const geom::CoordinateSequence* pts);
 
-        // To keep track of LineSegments
-        std::vector< geom::LineSegment > segments;
-
     public:
         IntervalIndexedGeometry(const geom::Geometry& g);
 
diff --git a/src/algorithm/locate/IndexedPointInAreaLocator.cpp b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
index 5a39484..db827a4 100644
--- a/src/algorithm/locate/IndexedPointInAreaLocator.cpp
+++ b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
@@ -58,24 +58,21 @@ IndexedPointInAreaLocator::IntervalIndexedGeometry::init(const geom::Geometry& g
     for(const geom::LineString* line : lines) {
         nsegs += line->getCoordinatesRO()->size() - 1;
     }
-    segments.reserve(nsegs);
+    index = decltype(index)(10, nsegs);
 
     for(const geom::LineString* line : lines) {
         addLine(line->getCoordinatesRO());
     }
-
-    for(geom::LineSegment& seg : segments) {
-        index.insert(index::strtree::Interval(std::min(seg.p0.y, seg.p1.y),
-                                              std::max(seg.p0.y, seg.p1.y)),
-                     &seg);
-    }
 }
 
 void
 IndexedPointInAreaLocator::IntervalIndexedGeometry::addLine(const geom::CoordinateSequence* pts)
 {
     for(std::size_t i = 1, ni = pts->size(); i < ni; i++) {
-        segments.emplace_back((*pts)[i - 1], (*pts)[i]);
+        SegmentView seg(&pts->getAt(i-1), &pts->getAt(i));
+        auto r = std::minmax(seg.p0->y, seg.p1->y);
+
+        index.insert(index::strtree::Interval(r.first, r.second), seg);
     }
 }
 
@@ -114,8 +111,8 @@ IndexedPointInAreaLocator::locate(const geom::Coordinate* /*const*/ p)
 
     algorithm::RayCrossingCounter rcc(*p);
 
-    index->query(p->y, p->y, [&rcc](const geom::LineSegment* ls) {
-        rcc.countSegment(ls->p0, ls->p1);
+    index->query(p->y, p->y, [&rcc](const SegmentView& ls) {
+        rcc.countSegment(*ls.p0, *ls.p1);
     });
 
     return rcc.getLocation();

commit 9a3116d34432f75d8d343be611ae22ddbaf32035
Author: Daniel Baston <dbaston at gmail.com>
Date:   Sat Feb 6 22:34:00 2021 -0500

    Use TemplateSTRtree in C API

diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp
index c6c23a4..6bcf21d 100644
--- a/capi/geos_c.cpp
+++ b/capi/geos_c.cpp
@@ -16,7 +16,7 @@
  ***********************************************************************/
 
 #include <geos/geom/prep/PreparedGeometryFactory.h>
-#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 #include <geos/io/WKTReader.h>
 #include <geos/io/WKBReader.h>
 #include <geos/io/WKTWriter.h>
@@ -34,7 +34,7 @@
 #define GEOSGeometry geos::geom::Geometry
 #define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
 #define GEOSCoordSequence geos::geom::CoordinateSequence
-#define GEOSSTRtree geos::index::strtree::STRtree
+#define GEOSSTRtree geos::index::strtree::TemplateSTRtree<void*>
 #define GEOSWKTReader geos::io::WKTReader
 #define GEOSWKTWriter geos::io::WKTWriter
 #define GEOSWKBReader geos::io::WKBReader
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index a147d72..e448b8b 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -38,8 +38,7 @@
 #include <geos/geom/IntersectionMatrix.h>
 #include <geos/geom/Envelope.h>
 #include <geos/geom/util/Densifier.h>
-#include <geos/index/strtree/SimpleSTRtree.h>
-#include <geos/index/strtree/GeometryItemDistance.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 #include <geos/index/ItemVisitor.h>
 #include <geos/io/WKTReader.h>
 #include <geos/io/WKBReader.h>
@@ -113,7 +112,7 @@
 #define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
 #define GEOSCoordSequence geos::geom::CoordinateSequence
 #define GEOSBufferParams geos::operation::buffer::BufferParameters
-#define GEOSSTRtree geos::index::strtree::SimpleSTRtree
+#define GEOSSTRtree geos::index::strtree::TemplateSTRtree<void*>
 #define GEOSWKTReader geos::io::WKTReader
 #define GEOSWKTWriter geos::io::WKTWriter
 #define GEOSWKBReader geos::io::WKBReader
@@ -299,6 +298,11 @@ class CAPI_ItemVisitor : public geos::index::ItemVisitor {
 public:
     CAPI_ItemVisitor(GEOSQueryCallback cb, void* ud)
         : ItemVisitor(), callback(cb), userdata(ud) {}
+
+    void operator()(void* item) {
+        callback(item, userdata);
+    }
+
     void
     visitItem(void* item) override
     {
@@ -3029,18 +3033,15 @@ extern "C" {
     {
         using namespace geos::index::strtree;
 
-        struct CustomItemDistance : public ItemDistance {
+        struct CustomItemDistance {
             CustomItemDistance(GEOSDistanceCallback p_distancefn, void* p_userdata)
                 : m_distancefn(p_distancefn), m_userdata(p_userdata) {}
 
             GEOSDistanceCallback m_distancefn;
             void* m_userdata;
 
-            double
-            distance(const ItemBoundable* item1, const ItemBoundable* item2) override
+            double operator()(const void* a, const void* b) const
             {
-                const void* a = item1->getItem();
-                const void* b = item2->getItem();
                 double d;
 
                 if(!m_distancefn(a, b, &d, m_userdata)) {
@@ -3051,14 +3052,19 @@ extern "C" {
             }
         };
 
+        struct GeometryDistance {
+            double operator()(void* a, void* b) const {
+                return static_cast<const Geometry*>(a)->distance(static_cast<const Geometry*>(b));
+            }
+        };
+
         return execute(extHandle, [&]() {
             if(distancefn) {
                 CustomItemDistance itemDistance(distancefn, userdata);
-                return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance);
+                return tree->nearestNeighbour(*itemEnvelope->getEnvelopeInternal(), (void*) item, itemDistance);
             }
             else {
-                GeometryItemDistance itemDistance = GeometryItemDistance();
-                return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance);
+                return tree->nearestNeighbour<GeometryDistance>(*itemEnvelope->getEnvelopeInternal(), (void*) item);
             }
         });
     }
diff --git a/include/geos/operation/distance/FacetSequenceTreeBuilder.h b/include/geos/operation/distance/FacetSequenceTreeBuilder.h
index 7cafec7..b20b21d 100644
--- a/include/geos/operation/distance/FacetSequenceTreeBuilder.h
+++ b/include/geos/operation/distance/FacetSequenceTreeBuilder.h
@@ -43,7 +43,7 @@ private:
 
     class FacetSequenceTree : public geos::index::strtree::TemplateSTRtree<const FacetSequence*> {
     public:
-        // TODO support TemplateSTRtree<std::unique_ptr<FacetSequence>> and dispence with holding vector.
+        // TODO support TemplateSTRtree<std::unique_ptr<FacetSequence>> and dispense with holding vector.
         FacetSequenceTree(std::vector<FacetSequence> &&seq) :
             TemplateSTRtree(STR_TREE_NODE_CAPACITY, seq.size()), sequences(seq) {
             for (auto& fs : sequences) {

commit 9b0d3d81124750a3134f4e0b0ede43fb1d5bf5c6
Author: Daniel Baston <dbaston at gmail.com>
Date:   Sat Feb 6 20:20:19 2021 -0500

    Use TemplateSTRtree in IndexedNestedRingTester

diff --git a/src/operation/valid/IndexedNestedRingTester.cpp b/src/operation/valid/IndexedNestedRingTester.cpp
index f5c1fe6..ba81bde 100644
--- a/src/operation/valid/IndexedNestedRingTester.cpp
+++ b/src/operation/valid/IndexedNestedRingTester.cpp
@@ -41,17 +41,14 @@ IndexedNestedRingTester::isNonNested()
 {
     buildIndex();
 
-    std::vector<void*> results;
-    for(std::size_t i = 0, n = rings.size(); i < n; ++i) {
+    std::vector<const geom::LinearRing*> results;
+    for(const auto& outerRing : rings) {
         results.clear();
 
-        const geom::LinearRing* outerRing = rings[i];
-
         geos::algorithm::locate::IndexedPointInAreaLocator locator(*outerRing);
 
-        index->query(outerRing->getEnvelopeInternal(), results);
-        for(const auto& result : results) {
-            const geom::LinearRing* possibleInnerRing = static_cast<const geom::LinearRing*>(result);
+        index->query(*outerRing->getEnvelopeInternal(), results);
+        for(const auto& possibleInnerRing : results) {
             const geom::CoordinateSequence* possibleInnerRingPts = possibleInnerRing->getCoordinatesRO();
 
             if(outerRing == possibleInnerRing) {
@@ -94,21 +91,14 @@ IndexedNestedRingTester::isNonNested()
     return true;
 }
 
-IndexedNestedRingTester::~IndexedNestedRingTester()
-{
-    delete index;
-}
+IndexedNestedRingTester::~IndexedNestedRingTester() = default;
 
 void
 IndexedNestedRingTester::buildIndex()
 {
-    delete index;
-
-    index = new index::strtree::STRtree();
-    for(std::size_t i = 0, n = rings.size(); i < n; ++i) {
-        const geom::LinearRing* ring = rings[i];
-        const geom::Envelope* env = ring->getEnvelopeInternal();
-        index->insert(env, (void*)ring);
+    index.reset(new index::strtree::TemplateSTRtree<const geom::LinearRing*>(10, rings.size()));
+    for(const auto& ring : rings) {
+        index->insert(ring);
     }
 }
 
diff --git a/src/operation/valid/IndexedNestedRingTester.h b/src/operation/valid/IndexedNestedRingTester.h
index 118e305..7d5e557 100644
--- a/src/operation/valid/IndexedNestedRingTester.h
+++ b/src/operation/valid/IndexedNestedRingTester.h
@@ -22,6 +22,9 @@
 #include <cstddef>
 #include <vector> // for composition
 
+#include <geos/index/strtree/TemplateSTRtree.h>
+#include <memory>
+
 // Forward declarations
 namespace geos {
 namespace geom {
@@ -90,8 +93,8 @@ private:
     /// Ownership of this vector elements are externally owned
     std::vector<const geom::LinearRing*> rings;
 
-    // Owned by us (use unique_ptr ?)
-    geos::index::SpatialIndex* index; // 'index' in JTS
+    // Owned by us
+    std::unique_ptr<geos::index::strtree::TemplateSTRtree<const geom::LinearRing*>> index;
 
     // Externally owned, if not null
     const geom::Coordinate* nestedPt;

commit 1a5601f17db666e2e173c57df2bfaeeaa4d2181a
Author: Daniel Baston <dbaston at gmail.com>
Date:   Sat Feb 6 20:11:42 2021 -0500

    Use TemplateSTRtree in IndexedFacetDistance, MinimumClearance

diff --git a/include/geos/operation/distance/FacetSequenceTreeBuilder.h b/include/geos/operation/distance/FacetSequenceTreeBuilder.h
index 5e559ad..7cafec7 100644
--- a/include/geos/operation/distance/FacetSequenceTreeBuilder.h
+++ b/include/geos/operation/distance/FacetSequenceTreeBuilder.h
@@ -20,7 +20,7 @@
 #define GEOS_OPERATION_DISTANCE_FACETSEQUENCETREEBUILDER_H
 
 #include <geos/index/ItemVisitor.h>
-#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 #include <geos/geom/Geometry.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/operation/distance/FacetSequence.h>
@@ -41,11 +41,13 @@ private:
                                   std::vector<FacetSequence> & sections);
     static std::vector<FacetSequence> computeFacetSequences(const geom::Geometry* g);
 
-    class FacetSequenceTree : public geos::index::strtree::STRtree {
+    class FacetSequenceTree : public geos::index::strtree::TemplateSTRtree<const FacetSequence*> {
     public:
-        FacetSequenceTree(std::vector<FacetSequence> &&seq) : STRtree(STR_TREE_NODE_CAPACITY), sequences(seq) {
+        // TODO support TemplateSTRtree<std::unique_ptr<FacetSequence>> and dispence with holding vector.
+        FacetSequenceTree(std::vector<FacetSequence> &&seq) :
+            TemplateSTRtree(STR_TREE_NODE_CAPACITY, seq.size()), sequences(seq) {
             for (auto& fs : sequences) {
-                STRtree::insert(fs.getEnvelope(), &fs);
+                TemplateSTRtree::insert(fs.getEnvelope(), &fs);
             }
         }
 
@@ -60,7 +62,7 @@ public:
      * The FacetSequences are owned by the tree and are automatically deleted by
      * the tree on destruction.
      */
-    static std::unique_ptr<geos::index::strtree::STRtree> build(const geom::Geometry* g);
+    static std::unique_ptr<geos::index::strtree::TemplateSTRtree<const FacetSequence*>> build(const geom::Geometry* g);
 };
 }
 }
diff --git a/include/geos/operation/distance/IndexedFacetDistance.h b/include/geos/operation/distance/IndexedFacetDistance.h
index 19d7e5b..bd4e23f 100644
--- a/include/geos/operation/distance/IndexedFacetDistance.h
+++ b/include/geos/operation/distance/IndexedFacetDistance.h
@@ -99,7 +99,7 @@ public:
     std::vector<geom::Coordinate> nearestPoints(const geom::Geometry* g) const;
 
 private:
-    std::unique_ptr<geos::index::strtree::STRtree> cachedTree;
+    std::unique_ptr<geos::index::strtree::TemplateSTRtree<const FacetSequence*>> cachedTree;
 
 };
 }
diff --git a/src/operation/distance/FacetSequenceTreeBuilder.cpp b/src/operation/distance/FacetSequenceTreeBuilder.cpp
index c9adca2..da91bb6 100644
--- a/src/operation/distance/FacetSequenceTreeBuilder.cpp
+++ b/src/operation/distance/FacetSequenceTreeBuilder.cpp
@@ -27,10 +27,10 @@ namespace geos {
 namespace operation {
 namespace distance {
 
-std::unique_ptr<STRtree>
+std::unique_ptr<TemplateSTRtree<const FacetSequence*>>
 FacetSequenceTreeBuilder::build(const Geometry* g)
 {
-    auto tree = std::unique_ptr<STRtree>(new FacetSequenceTree(computeFacetSequences(g)));
+    std::unique_ptr<TemplateSTRtree<const FacetSequence*>> tree(new FacetSequenceTree(computeFacetSequences(g)));
 
     tree->build();
     return tree;
diff --git a/src/operation/distance/IndexedFacetDistance.cpp b/src/operation/distance/IndexedFacetDistance.cpp
index ccbf78f..5a4c81f 100644
--- a/src/operation/distance/IndexedFacetDistance.cpp
+++ b/src/operation/distance/IndexedFacetDistance.cpp
@@ -46,47 +46,40 @@ IndexedFacetDistance::nearestPoints(const geom::Geometry* g1, const geom::Geomet
 double
 IndexedFacetDistance::distance(const Geometry* g) const
 {
-    struct : public ItemDistance {
-        double
-        distance(const ItemBoundable* item1, const ItemBoundable* item2) override
-        {
-            return static_cast<const FacetSequence*>(item1->getItem())->distance(*static_cast<const FacetSequence*>
-                    (item2->getItem()));
+    struct FacetDistance {
+        double operator()(const FacetSequence* a, const FacetSequence* b) {
+            return a->distance(*b);
         }
-    } itemDistance;
-
-    std::unique_ptr<STRtree> tree2(FacetSequenceTreeBuilder::build(g));
-
-    std::pair<const void*, const void*> obj = cachedTree->nearestNeighbour(tree2.get(),
-            dynamic_cast<ItemDistance*>(&itemDistance));
+    };
 
-    const FacetSequence *fs1 = static_cast<const FacetSequence*>(obj.first);
-    const FacetSequence *fs2 = static_cast<const FacetSequence*>(obj.second);
+    auto tree2 = FacetSequenceTreeBuilder::build(g);
+    auto nearest = cachedTree->nearestNeighbour<FacetDistance>(*tree2);
 
-    double p_distance = fs1->distance(*fs2);
+    if (!nearest.first) {
+        throw util::GEOSException("Cannot calculate IndexedFacetDistance on empty geometries.");
+    }
 
-    return p_distance;
+    return nearest.first->distance(*nearest.second);
 }
 
 std::vector<GeometryLocation>
 IndexedFacetDistance::nearestLocations(const geom::Geometry* g) const
 {
-    struct : public ItemDistance {
-        double
-        distance(const ItemBoundable* item1, const ItemBoundable* item2) override
+    struct FacetDistance {
+        double operator()(const FacetSequence* a, const FacetSequence* b) const
         {
-            return static_cast<const FacetSequence*>(item1->getItem())->distance(*static_cast<const FacetSequence*>
-                    (item2->getItem()));
+            return a->distance(*b);
         }
-    } itemDistance;
-    std::unique_ptr<STRtree> tree2(FacetSequenceTreeBuilder::build(g));
-    std::pair<const void*, const void*> obj = cachedTree->nearestNeighbour(tree2.get(),
-            dynamic_cast<ItemDistance*>(&itemDistance));
-    const FacetSequence *fs1 = static_cast<const FacetSequence*>(obj.first);
-    const FacetSequence *fs2 = static_cast<const FacetSequence*>(obj.second);
-    std::vector<GeometryLocation> locs;
-    locs = fs1->nearestLocations(*fs2);
-    return locs;
+    };
+
+    auto tree2 = FacetSequenceTreeBuilder::build(g);
+    auto nearest = cachedTree->nearestNeighbour<FacetDistance>(*tree2);
+
+    if (!nearest.first) {
+        throw util::GEOSException("Cannot calculate IndexedFacetDistance on empty geometries.");
+    }
+
+    return nearest.first->nearestLocations(*nearest.second);
 }
 
 std::vector<Coordinate>
diff --git a/src/precision/MinimumClearance.cpp b/src/precision/MinimumClearance.cpp
index 0c06705..06bd9bf 100644
--- a/src/precision/MinimumClearance.cpp
+++ b/src/precision/MinimumClearance.cpp
@@ -56,7 +56,7 @@ MinimumClearance::getLine()
 void
 MinimumClearance::compute()
 {
-    class MinClearanceDistance : public ItemDistance {
+    class MinClearanceDistance {
     private:
         double minDist;
         std::vector<Coordinate> minPts;
@@ -82,14 +82,9 @@ MinimumClearance::compute()
             return &minPts;
         }
 
-        double
-        distance(const ItemBoundable* b1, const ItemBoundable* b2) override
+        double operator()(const FacetSequence* fs1, const FacetSequence* fs2)
         {
-            FacetSequence* fs1 = static_cast<FacetSequence*>(b1->getItem());
-            FacetSequence* fs2 = static_cast<FacetSequence*>(b2->getItem());
-
             minDist = std::numeric_limits<double>::infinity();
-
             return distance(fs1, fs2);
         }
 
@@ -165,7 +160,7 @@ MinimumClearance::compute()
     };
 
     // already computed
-    if(minClearancePts.get() != nullptr) {
+    if(minClearancePts != nullptr) {
         return;
     }
 
@@ -181,11 +176,9 @@ MinimumClearance::compute()
 
     auto tree = FacetSequenceTreeBuilder::build(inputGeom);
     MinClearanceDistance mcd;
-    std::pair<const void*, const void*> nearest = tree->nearestNeighbour(&mcd);
+    auto nearest = tree->nearestNeighbour(mcd);
 
-    minClearance = mcd.distance(
-                       static_cast<const FacetSequence*>(nearest.first),
-                       static_cast<const FacetSequence*>(nearest.second));
+    minClearance = mcd.distance(nearest.first, nearest.second);
 
     const std::vector<Coordinate>* minClearancePtsVec = mcd.getCoordinates();
     minClearancePts->setAt((*minClearancePtsVec)[0], 0);
diff --git a/tests/unit/operation/distance/IndexedFacetDistanceTest.cpp b/tests/unit/operation/distance/IndexedFacetDistanceTest.cpp
index b5bc8de..cb9c603 100644
--- a/tests/unit/operation/distance/IndexedFacetDistanceTest.cpp
+++ b/tests/unit/operation/distance/IndexedFacetDistanceTest.cpp
@@ -351,16 +351,29 @@ void object::test<11>
     std::string wkt1("POINT(150 150)");
     GeomPtr g0(_wktreader.read(wkt0));
     GeomPtr g1(_wktreader.read(wkt1));
-    IndexedFacetDistance ifd(g0.get());
+    IndexedFacetDistance ifd0(g0.get());
+    IndexedFacetDistance ifd1(g1.get());
+
+    try {
+        ifd0.distance(g1.get());
+        fail("IndexedFacedDistance::distance did not throw on empty input");
+    }
+    catch (const GEOSException&) { }
+
+    try {
+        ifd0.nearestPoints(g1.get());
+        fail("IndexedFacedDistance::nearestPoints did not throw on empty input");
+    }
+    catch (const GEOSException&) { }
 
     try {
-        ifd.distance(g1.get());
+        ifd1.distance(g0.get());
         fail("IndexedFacedDistance::distance did not throw on empty input");
     }
     catch (const GEOSException&) { }
 
     try {
-        ifd.nearestPoints(g1.get());
+        ifd1.nearestPoints(g0.get());
         fail("IndexedFacedDistance::nearestPoints did not throw on empty input");
     }
     catch (const GEOSException&) { }

commit 0b2c29abd785bea95dc83f1777335ad48e744f8d
Author: Daniel Baston <dbaston at gmail.com>
Date:   Sat Feb 6 17:06:02 2021 -0500

    Use TemplateSTRtree in CascadedPolygonUnion

diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index c36fcf0..a147d72 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -1424,9 +1424,9 @@ extern "C" {
                 throw IllegalArgumentException("Invalid argument (must be a MultiPolygon)");
             }
 
-            Geometry* g3 = CascadedPolygonUnion::Union(p);
+            auto g3 = CascadedPolygonUnion::Union(p);
             g3->setSRID(g1->getSRID());
-            return g3;
+            return g3.release();
         });
     }
 
diff --git a/include/geos/operation/polygonize/EdgeRing.h b/include/geos/operation/polygonize/EdgeRing.h
index 211bd11..ca0e268 100644
--- a/include/geos/operation/polygonize/EdgeRing.h
+++ b/include/geos/operation/polygonize/EdgeRing.h
@@ -48,11 +48,6 @@ class Coordinate;
 namespace planargraph {
 class DirectedEdge;
 }
-namespace index {
-namespace strtree {
-class STRtree;
-}
-}
 }
 
 namespace geos {
diff --git a/include/geos/operation/union/CascadedPolygonUnion.h b/include/geos/operation/union/CascadedPolygonUnion.h
index 95e907e..b7c5d9f 100644
--- a/include/geos/operation/union/CascadedPolygonUnion.h
+++ b/include/geos/operation/union/CascadedPolygonUnion.h
@@ -148,8 +148,8 @@ public:
      * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry).
      *              ownership of elements *and* vector are left to caller.
      */
-    static geom::Geometry* Union(std::vector<geom::Polygon*>* polys);
-    static geom::Geometry* Union(std::vector<geom::Polygon*>* polys, UnionStrategy* unionFun);
+    static std::unique_ptr<geom::Geometry> Union(std::vector<geom::Polygon*>* polys);
+    static std::unique_ptr<geom::Geometry> Union(std::vector<geom::Polygon*>* polys, UnionStrategy* unionFun);
 
     /** \brief
      * Computes the union of a set of polygonal [Geometrys](@ref geom::Geometry).
@@ -160,7 +160,7 @@ public:
      * @param unionStrategy strategy to apply
      */
     template <class T>
-    static geom::Geometry*
+    static std::unique_ptr<geom::Geometry>
     Union(T start, T end, UnionStrategy *unionStrategy)
     {
         std::vector<geom::Polygon*> polys;
@@ -177,7 +177,7 @@ public:
      * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry).
      *              Ownership of elements *and* vector are left to caller.
      */
-    static geom::Geometry* Union(const geom::MultiPolygon* polys);
+    static std::unique_ptr<geom::Geometry> Union(const geom::MultiPolygon* polys);
 
     /** \brief
      * Creates a new instance to union the given collection of
@@ -204,22 +204,13 @@ public:
      * @return the union of the input geometries
      * @return `null` if no input geometries were provided
      */
-    geom::Geometry* Union();
+    std::unique_ptr<geom::Geometry> Union();
 
 private:
 
     UnionStrategy* unionFunction;
     ClassicUnionStrategy defaultUnionFunction;
 
-    geom::Geometry* unionTree(index::strtree::ItemsList* geomTree);
-
-    /**
-     * Unions a list of geometries
-     * by treating the list as a flattened binary tree,
-     * and performing a cascaded union on the tree.
-     */
-    geom::Geometry* binaryUnion(GeometryListHolder* geoms);
-
     /**
      * Unions a section of a list using a recursive binary union on each half
      * of the section.
@@ -229,17 +220,7 @@ private:
      * @param end the index after the end of the section
      * @return the union of the list section
      */
-    geom::Geometry* binaryUnion(GeometryListHolder* geoms, std::size_t start,
-                                std::size_t end);
-
-    /**
-     * Reduces a tree of geometries to a list of geometries
-     * by recursively unioning the subtrees in the list.
-     *
-     * @param geomTree a tree-structured list of geometries
-     * @return a list of Geometrys
-     */
-    GeometryListHolder* reduceToGeometries(index::strtree::ItemsList* geomTree);
+    std::unique_ptr<geom::Geometry> binaryUnion(const std::vector<const geom::Geometry*> & geoms, std::size_t start, std::size_t end);
 
     /**
      * Computes the union of two geometries,
@@ -250,7 +231,9 @@ private:
      * @return the union of the input(s)
      * @return null if both inputs are null
      */
-    geom::Geometry* unionSafe(geom::Geometry* g0, geom::Geometry* g1);
+    std::unique_ptr<geom::Geometry> unionSafe(const geom::Geometry* g0, const geom::Geometry* g1) const;
+
+    std::unique_ptr<geom::Geometry> unionSafe(std::unique_ptr<geom::Geometry> &&, std::unique_ptr<geom::Geometry> &&);
 
     /**
      * Encapsulates the actual unioning of two polygonal geometries.
@@ -259,7 +242,9 @@ private:
      * @param g1
      * @return
      */
-    geom::Geometry* unionActual(geom::Geometry* g0, geom::Geometry* g1);
+    std::unique_ptr<geom::Geometry> unionActual(const geom::Geometry* g0, const geom::Geometry* g1) const;
+
+    std::unique_ptr<geom::Geometry> unionActual(std::unique_ptr<geom::Geometry> &&, std::unique_ptr<geom::Geometry> &&) const;
 };
 
 
diff --git a/include/geos/operation/union/UnionStrategy.h b/include/geos/operation/union/UnionStrategy.h
index b68d090..3195565 100644
--- a/include/geos/operation/union/UnionStrategy.h
+++ b/include/geos/operation/union/UnionStrategy.h
@@ -50,6 +50,9 @@ public:
     */
     virtual std::unique_ptr<geom::Geometry> Union(const geom::Geometry*, const geom::Geometry*) = 0;
 
+    virtual std::unique_ptr<geom::Geometry> Union(std::unique_ptr<geom::Geometry> &&,
+                                                  std::unique_ptr<geom::Geometry> &&);
+
     /**
     * Indicates whether the union function operates using
     * a floating (full) precision model.
diff --git a/src/operation/union/CascadedPolygonUnion.cpp b/src/operation/union/CascadedPolygonUnion.cpp
index 7e00d73..4fed1f5 100644
--- a/src/operation/union/CascadedPolygonUnion.cpp
+++ b/src/operation/union/CascadedPolygonUnion.cpp
@@ -29,7 +29,7 @@
 #include <geos/geom/Polygon.h>
 #include <geos/geom/MultiPolygon.h>
 #include <geos/geom/util/PolygonExtracter.h>
-#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 
 // std
 #include <cassert>
@@ -113,21 +113,21 @@ GeometryListHolder::deleteItem(geom::Geometry* item)
 }
 
 // ////////////////////////////////////////////////////////////////////////////
-geom::Geometry*
+std::unique_ptr<geom::Geometry>
 CascadedPolygonUnion::Union(std::vector<geom::Polygon*>* polys)
 {
     CascadedPolygonUnion op(polys);
     return op.Union();
 }
 
-geom::Geometry*
+std::unique_ptr<geom::Geometry>
 CascadedPolygonUnion::Union(std::vector<geom::Polygon*>* polys, UnionStrategy* unionFun)
 {
     CascadedPolygonUnion op(polys, unionFun);
     return op.Union();
 }
 
-geom::Geometry*
+std::unique_ptr<geom::Geometry>
 CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly)
 {
     std::vector<geom::Polygon*> polys;
@@ -140,7 +140,7 @@ CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly)
     return op.Union();
 }
 
-geom::Geometry*
+std::unique_ptr<geom::Geometry>
 CascadedPolygonUnion::Union()
 {
     if(inputPolys->empty()) {
@@ -156,114 +156,85 @@ CascadedPolygonUnion::Union()
      * to be eliminated on each round.
      */
 
-    index::strtree::STRtree index(STRTREE_NODE_CAPACITY);
-
-    typedef std::vector<geom::Polygon*>::iterator iterator_type;
-    iterator_type end = inputPolys->end();
-    for(iterator_type i = inputPolys->begin(); i != end; ++i) {
-        geom::Geometry* g = dynamic_cast<geom::Geometry*>(*i);
-        index.insert(g->getEnvelopeInternal(), g);
+    index::strtree::TemplateSTRtree<const geom::Geometry*> index(10, inputPolys->size());
+    for (const auto& p : *inputPolys) {
+        index.insert(p);
     }
 
-    std::unique_ptr<index::strtree::ItemsList> itemTree(index.itemsTree());
+    // TODO avoid creating this vector and run binaryUnion off the iterators directly
+    std::vector<const geom::Geometry*> geoms(index.items().begin(), index.items().end());
 
-    return unionTree(itemTree.get());
+    return binaryUnion(geoms, 0, geoms.size());
 }
 
-geom::Geometry*
-CascadedPolygonUnion::unionTree(
-    index::strtree::ItemsList* geomTree)
-{
-    /*
-     * Recursively unions all subtrees in the list into single geometries.
-     * The result is a list of Geometry's only
-     */
-    std::unique_ptr<GeometryListHolder> geoms(reduceToGeometries(geomTree));
-    return binaryUnion(geoms.get());
-}
 
-geom::Geometry*
-CascadedPolygonUnion::binaryUnion(GeometryListHolder* geoms)
-{
-    return binaryUnion(geoms, 0, geoms->size());
-}
-
-geom::Geometry*
-CascadedPolygonUnion::binaryUnion(GeometryListHolder* geoms,
+std::unique_ptr<geom::Geometry>
+CascadedPolygonUnion::binaryUnion(const std::vector<const geom::Geometry*> & geoms,
                                   std::size_t start, std::size_t end)
 {
     if(end - start <= 1) {
-        return unionSafe(geoms->getGeometry(start), nullptr);
+        return unionSafe(geoms[start], nullptr);
     }
     else if(end - start == 2) {
-        return unionSafe(geoms->getGeometry(start), geoms->getGeometry(start + 1));
+        return unionSafe(geoms[start], geoms[start + 1]);
     }
     else {
         // recurse on both halves of the list
         std::size_t mid = (end + start) / 2;
         std::unique_ptr<geom::Geometry> g0(binaryUnion(geoms, start, mid));
         std::unique_ptr<geom::Geometry> g1(binaryUnion(geoms, mid, end));
-        return unionSafe(g0.get(), g1.get());
+        return unionSafe(std::move(g0), std::move(g1));
     }
 }
 
-GeometryListHolder*
-CascadedPolygonUnion::reduceToGeometries(index::strtree::ItemsList* geomTree)
+std::unique_ptr<geom::Geometry>
+CascadedPolygonUnion::unionSafe(const geom::Geometry* g0, const geom::Geometry* g1) const
 {
-    std::unique_ptr<GeometryListHolder> geoms(new GeometryListHolder());
-
-    typedef index::strtree::ItemsList::iterator iterator_type;
-    iterator_type end = geomTree->end();
-    for(iterator_type i = geomTree->begin(); i != end; ++i) {
-        if((*i).get_type() == index::strtree::ItemsListItem::item_is_list) {
-            std::unique_ptr<geom::Geometry> geom(unionTree((*i).get_itemslist()));
-            geoms->push_back_owned(geom.get());
-            geom.release();
-        }
-        else if((*i).get_type() == index::strtree::ItemsListItem::item_is_geometry) {
-            geoms->push_back(reinterpret_cast<geom::Geometry*>((*i).get_geometry()));
-        }
-        else {
-            assert(!static_cast<bool>("should never be reached"));
-        }
+    if(g0 == nullptr && g1 == nullptr) {
+        return nullptr;
     }
 
-    return geoms.release();
+    if(g0 == nullptr) {
+        return g1->clone();
+    }
+    if(g1 == nullptr) {
+        return g0->clone();
+    }
+
+    return unionActual(g0, g1);
 }
 
-geom::Geometry*
-CascadedPolygonUnion::unionSafe(geom::Geometry* g0, geom::Geometry* g1)
+std::unique_ptr<geom::Geometry>
+CascadedPolygonUnion::unionSafe(std::unique_ptr<geom::Geometry> && g0, std::unique_ptr<geom::Geometry> && g1)
 {
     if(g0 == nullptr && g1 == nullptr) {
         return nullptr;
     }
 
     if(g0 == nullptr) {
-        return g1->clone().release();
+        return std::move(g1);
     }
     if(g1 == nullptr) {
-        return g0->clone().release();
+        return std::move(g0);
     }
 
-    return unionActual(g0, g1);
+    return unionActual(std::move(g0), std::move(g1));
 }
 
-// geom::Geometry*
-// CascadedPolygonUnion::unionActual(geom::Geometry* g0, geom::Geometry* g1)
-// {
-//     OverlapUnion unionOp(g0, g1);
-//     geom::Geometry* justPolys = restrictToPolygons(
-//         std::unique_ptr<geom::Geometry>(unionOp.doUnion())
-//         ).release();
-//     return justPolys;
-// }
-
-geom::Geometry*
-CascadedPolygonUnion::unionActual(geom::Geometry* g0, geom::Geometry* g1)
+std::unique_ptr<geom::Geometry>
+CascadedPolygonUnion::unionActual(const geom::Geometry* g0, const geom::Geometry* g1) const
 {
     std::unique_ptr<geom::Geometry> ug;
     ug = unionFunction->Union(g0, g1);
-    return restrictToPolygons(std::move(ug)).release();
+    return restrictToPolygons(std::move(ug));
+}
+
+std::unique_ptr<geom::Geometry>
+CascadedPolygonUnion::unionActual(std::unique_ptr<geom::Geometry> && g0, std::unique_ptr<geom::Geometry> && g1) const
+{
+    std::unique_ptr<geom::Geometry> ug;
+    ug = unionFunction->Union(std::move(g0), std::move(g1));
+    return restrictToPolygons(std::move(ug));
 }
 
 std::unique_ptr<geom::Geometry>
@@ -271,26 +242,26 @@ CascadedPolygonUnion::restrictToPolygons(std::unique_ptr<geom::Geometry> g)
 {
     using namespace geom;
 
-
     if(g->isPolygonal()) {
         return g;
     }
 
-    Polygon::ConstVect polygons;
-    geom::util::PolygonExtracter::getPolygons(*g, polygons);
-
-    if(polygons.size() == 1) {
-        return std::unique_ptr<Geometry>(polygons[0]->clone());
-    }
-
-    typedef std::vector<Geometry*> GeomVect;
-
-    Polygon::ConstVect::size_type n = polygons.size();
-    GeomVect* newpolys = new GeomVect(n);
-    for(Polygon::ConstVect::size_type i = 0; i < n; ++i) {
-        (*newpolys)[i] = polygons[i]->clone().release();
+    auto gfact = g->getFactory();
+    auto coordDim = g->getCoordinateDimension();
+
+    auto coll = dynamic_cast<GeometryCollection*>(g.get());
+    if (coll) {
+        // Release polygons from the collection and re-form into MultiPolygon
+        auto components = coll->releaseGeometries();
+        components.erase(std::remove_if(components.begin(), components.end(), [](const std::unique_ptr<Geometry> & cmp) {
+            return !cmp->isPolygonal();
+        }), components.end());
+
+        return gfact->createMultiPolygon(std::move(components));
+    } else {
+        // Not polygonal and not a collection? No polygons here.
+        return gfact->createPolygon(coordDim);
     }
-    return std::unique_ptr<Geometry>(g->getFactory()->createMultiPolygon(newpolys));
 }
 
 /************************************************************************/
@@ -300,6 +271,8 @@ using operation::overlay::OverlayOp;
 std::unique_ptr<geom::Geometry>
 ClassicUnionStrategy::Union(const geom::Geometry* g0, const geom::Geometry* g1)
 {
+    // TODO make an rvalue overload for this that can consume its inputs.
+    // At a minimum, a copy in the buffer fallback can be eliminated.
     try {
         // return SnapIfNeededOverlayOp.union(g0, g1);
         return geom::HeuristicOverlay(g0, g1, overlay::OverlayOp::opUNION);
diff --git a/src/operation/union/UnaryUnionOp.cpp b/src/operation/union/UnaryUnionOp.cpp
index ae18770..8bd96b8 100644
--- a/src/operation/union/UnaryUnionOp.cpp
+++ b/src/operation/union/UnaryUnionOp.cpp
@@ -93,8 +93,7 @@ UnaryUnionOp::Union()
 
     GeomPtr unionPolygons;
     if(!polygons.empty()) {
-        unionPolygons.reset(CascadedPolygonUnion::Union(polygons.begin(),
-                            polygons.end(), unionFunction));
+        unionPolygons = CascadedPolygonUnion::Union(polygons.begin(), polygons.end(), unionFunction);
     }
 
     /*
diff --git a/src/operation/union/UnionStrategy.cpp b/src/operation/union/UnionStrategy.cpp
new file mode 100644
index 0000000..8515069
--- /dev/null
+++ b/src/operation/union/UnionStrategy.cpp
@@ -0,0 +1,29 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2021 Daniel Baston
+ *
+ * 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 <geos/operation/union/UnionStrategy.h>
+#include <geos/geom/Geometry.h>
+
+namespace geos {
+namespace operation {
+namespace geounion {
+
+std::unique_ptr<geom::Geometry> UnionStrategy::Union(std::unique_ptr<geom::Geometry> && g0, std::unique_ptr<geom::Geometry> && g1) {
+    // Default implementation just copies the inputs.
+    return Union(g0.get(), g1.get());
+}
+
+}
+}
+}
\ No newline at end of file
diff --git a/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp b/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp
index 3e009c1..adf5b26 100644
--- a/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp
+++ b/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp
@@ -59,7 +59,7 @@ unionIterated(
     return unionAll.release();
 }
 
-geos::geom::Geometry*
+std::unique_ptr<geos::geom::Geometry>
 unionCascaded(
     std::vector<geos::geom::Polygon*>* geoms)
 {
diff --git a/tests/xmltester/tests/issue/issue-geos-837.xml b/tests/xmltester/tests/issue/issue-geos-837.xml
index 1c3df7f..b92d65c 100644
--- a/tests/xmltester/tests/issue/issue-geos-837.xml
+++ b/tests/xmltester/tests/issue/issue-geos-837.xml
@@ -6,11 +6,7 @@
   <a>
 0106000000E7010000010300000001000000050000006766666658012A41CDCCCC4C25D53B41CDCCCCCC56012A416766666689D53B4133333333E7022A419A9999198BD53B41CDCCCCCCE8022A410000000027D53B416766666658012A41CDCCCC4C25D53B410103000000010000000500000067666666D9022A4100000000ACD83B419A999999D7022A419A99991910D93B41CDCCCCCC9F032A41676666E610D93B419A999999A1032A41CDCCCCCCACD83B4167666666D9022A4100000000ACD83B4101030000000100000007000000CDCCCCCC0D022A41CDCCCC4C73D93B41000000000C022A4167666666D7D93B41CDCCCCCC43012A419A999999D6D93B410000000042012A41333333B33ADA3B419A999999D2022A41676666663CDA3B4100000000D6022A413333333374D93B41CDCCCCCC0D022A41CDCCCC4C73D93B410103000000010000000600000024CF5A3D3B012A41317FAAC3C8DB3B41E17A142E3E012A41F6285C0FC8DB3B415559285104022A41D9C503FA95DB3B41CDCCCCCC06022A41333333B303DB3B419A9999993E012A41676666E602DB3B4124CF5A3D3B012A41317FAAC3C8DB3B4101030000000100000005000000676666661B022A410000008052D63B410000000018022A41333333B31AD73B4133333333E0022A41000000801BD73B419A999999E3022A41C
 DCCCC4C53D63B41676666661B022A410000008052D63B41010300000001000000050000000000000061012A41333333B330D33B41333333335F012A41676666E694D33B419A99999927022A41333333B395D33B413333333329022A419A99999931D33B410000000061012A41333333B330D33B4101030000000100000005000000CDCCCCCCF4022A41CDCCCC4C6AD23B4100000000BD032A419A9999196BD23B41CDCCCCCCBE032A410000000007D23B419A999999F6022A419A99991906D23B41CDCCCCCCF4022A41CDCCCC4C6AD23B4101030000000100000005000000000000001F022A41333333338AD53B41333333331D022A4167666666EED53B4167666666E5022A4133333333EFD53B4133333333E7022A419A9999198BD53B41000000001F022A41333333338AD53B41010300000001000000050000000000000030022A4133333333A1D13B41676666662E022A41CDCCCC4C05D23B419A999999F6022A419A99991906D23B4167666666F8022A4100000000A2D13B410000000030022A4133333333A1D13B41010300000001000000050000009A999999D0FF2941000000002FD33B41CDCCCCCCCEFF29419A99991993D33B410000000097002A410000000094D33B41CDCCCCCC98002A41676666E62FD33B419A999999D0FF2941000000002FD33B4101030000000100000005
 0000009A9999992C022A416766666669D23B41000000002B022A4100000080CDD23B4133333333F3022A4167666666CED23B41CDCCCCCCF4022A41CDCCCC4C6AD23B419A9999992C022A416766666669D23B4101030000000100000005000000333333335A012A4133333333C1D43B416766666658012A41CDCCCC4C25D53B419A99999920022A419A99991926D53B416766666622022A4100000000C2D43B41333333335A012A4133333333C1D43B4101030000000100000005000000333333339C002A41333333B367D23B419A9999999A002A41CDCCCCCCCBD23B41CDCCCCCC62012A419A999999CCD23B416766666664012A410000008068D23B41333333339C002A41333333B367D23B410103000000010000000700000006C9049260022A418AABDE4368DB3B41A4703D0A9F022A4190C2F56839DB3B41F12AE880CE022A41DC5887B827DB3B41CDCCCCCCD0022A4100000080A0DA3B419A99999908022A419A9999999FDA3B413333333305022A41676666E667DB3B4106C9049260022A418AABDE4368DB3B410103000000010000000500000067666666E5022A4133333333EFD53B419A999999E3022A41CDCCCC4C53D63B4100000000AC032A413333333354D63B419A999999AD032A419A999919F0D53B4167666666E5022A4133333333EFD53B4101030000000100000005000
 00000000000E2022A4167666666B7D63B4133333333E0022A41000000801BD73B4167666666A8032A41676666661CD73B4133333333AA032A41CDCCCC4CB8D63B4100000000E2022A4167666666B7D63B4101030000000100000005000000F0C50F1998032A41ED967BD6D9DA3B417B14AEC7D3032A411F85EB91ADDA3B410CD07853F4032A410610D3C3A1DA3B410000000099032A4167666666A1DA3B41F0C50F1998032A41ED967BD6D9DA3B4101030000000100000005000000CDCCCCCCE8022A410000000027D53B4133333333E7022A419A9999198BD53B4167666666AF032A41000000008CD53B4100000000B1032A41676666E627D53B41CDCCCCCCE8022A410000000027D53B410103000000010000000F000000AEB054951B032A4150DCA5F104DB3B415C8FC27546032A41A4703D0AF1DA3B4152B81E8578032A41295C8F42F1DA3B41F0C50F1998032A41ED967BD6D9DA3B410000000099032A4167666666A1DA3B410CD07853F4032A410610D3C3A1DA3B41A0D9DBE161042A41E927E1067ADA3B419A99999964042A4100000000DAD93B4133333333D4022A41CDCCCC4CD8D93B419A999999D2022A41676666663CDA3B41676666660A022A41000000803BDA3B419A99999908022A419A9999999FDA3B41CDCCCCCCD0022A4100000080A0DA3B4100000000CF022A419A99
 999904DB3B41AEB054951B032A4150DCA5F104DB3B41010300000001000000070000009A999999E3022A41CDCCCC4C53D63B4100000000E2022A4167666666B7D63B419A9999993A052A4100000000BAD63B416766666641052A419A99999929D53B413333333379042A41333333B328D53B413333333374042A410000000055D63B419A999999E3022A41CDCCCC4C53D63B4101030000000100000007000000CDCCCCCC4F012A41CDCCCCCC19D73B410000000018022A41333333B31AD73B41676666661B022A410000008052D63B419A999999E3022A41CDCCCC4C53D63B4167666666E5022A4133333333EFD53B410000000055012A4100000080EDD53B41CDCCCCCC4F012A41CDCCCCCC19D73B410103000000010000000700000067666666B4032A419A9999995FD43B41CDCCCCCCB2032A41CDCCCCCCC3D43B419A999999EA022A41676666E6C2D43B41CDCCCCCCE8022A410000000027D53B413333333379042A41333333B328D53B41CDCCCCCC7C042A410000008060D43B4167666666B4032A419A9999995FD43B4101030000000100000005000000CDCCCCCC0D022A41CDCCCC4C73D93B41333333339E032A410000000075D93B41CDCCCCCC9F032A41676666E610D93B41676666660F022A41333333330FD93B41CDCCCCCC0D022A41CDCCCC4C73D93B4101030000000100000
 00500000033333333F3022A4167666666CED23B41CDCCCCCCEF022A419A99999996D33B4100000000B8032A416766666697D33B4167666666BB032A4133333333CFD23B4133333333F3022A4167666666CED23B4101030000000100000005000000CDCCCCCCDC022A41333333B3E3D73B4133333333DB022A41676666E647D83B4167666666A3032A41333333B348D83B4100000000A5032A419A999999E4D73B41CDCCCCCCDC022A41333333B3E3D73B41010300000001000000050000002198275F04022A41E1077AF695DB3B41A4703D8A33022A41A4703D0A8ADB3B4106C9049260022A418AABDE4368DB3B413333333305022A41676666E667DB3B412198275F04022A41E1077AF695DB3B41010300000001000000050000009A99999987002A410000000019D73B410000000086002A419A9999197DD73B41333333334E012A41676666E67DD73B41CDCCCCCC4F012A41CDCCCCCC19D73B419A99999987002A410000000019D73B41010300000001000000070000000000000018022A41333333B31AD73B416766666616022A41CDCCCCCC7ED73B419A999999DE022A419A9999997FD73B41CDCCCCCCDC022A41333333B3E3D73B4100000000A5032A419A999999E4D73B4167666666A8032A41676666661CD73B410000000018022A41333333B31AD73B4101030000000100000005
 000000CDCCCCCC67012A41CDCCCC4CA0D13B416766666664012A410000008068D23B419A9999992C022A416766666669D23B410000000030022A4133333333A1D13B41CDCCCCCC67012A41CDCCCC4CA0D13B41010300000001000000050000009A9999999A002A41CDCCCCCCCBD23B41CDCCCCCC98002A41676666E62FD33B413333333329022A419A99999931D33B41000000002B022A4100000080CDD23B419A9999999A002A41CDCCCCCCCBD23B41010300000001000000050000003333333390002A416766666624D53B41676666668E002A410000008088D53B41CDCCCCCC56012A416766666689D53B416766666658012A41CDCCCC4C25D53B413333333390002A416766666624D53B4101030000000100000005000000CDCCCCCC25022A41CDCCCCCCF9D33B410000000024022A41676666E65DD43B4133333333EC022A41CDCCCCCC5ED43B4100000000EE022A41333333B3FAD33B41CDCCCCCC25022A41CDCCCCCCF9D33B4101030000000100000005000000333333339C002A41333333B367D23B416766666664012A410000008068D23B41CDCCCCCC67012A41CDCCCC4CA0D13B419A9999999F002A41000000809FD13B41333333339C002A41333333B367D23B41010300000001000000050000006766666695002A419A999919F8D33B419A99999993002A41333333335CD43
 B41CDCCCCCC5B012A419A9999195DD43B419A9999995D012A4100000000F9D33B416766666695002A419A999919F8D33B41010300000001000000050000003333333311022A419A999919ABD83B41676666660F022A41333333330FD93B419A999999D7022A419A99991910D93B4167666666D9022A4100000000ACD83B413333333311022A419A999919ABD83B4101030000000100000005000000333333334E012A41676666E67DD73B41676666664C012A4100000000E2D73B419A99999914022A41676666E6E2D73B416766666616022A41CDCCCCCC7ED73B41333333334E012A41676666E67DD73B4101030000000100000005000000CDCCCCCCEF022A419A99999996D33B4133333333EC022A41CDCCCCCC5ED43B4167666666B4032A419A9999995FD43B4100000000B8032A416766666697D33B41CDCCCCCCEF022A419A99999996D33B410103000000010000000500000033333333E7022A419A9999198BD53B4167666666E5022A4133333333EFD53B419A999999AD032A419A999919F0D53B4167666666AF032A41000000008CD53B4133333333E7022A419A9999198BD53B41010300000001000000050000000000000042012A41333333B33ADA3B419A9999993E012A41676666E602DB3B41CDCCCCCC06022A41333333B303DB3B41676666660A022A41000000803BDA3B41
 0000000042012A41333333B33ADA3B410103000000010000000500000067666666D7FF29419A9999999ED13B4100000000D4FF2941CDCCCCCC66D23B41333333339C002A41333333B367D23B419A9999999F002A41000000809FD13B4167666666D7FF29419A9999999ED13B4101030000000100000005000000676666662E022A41CDCCCC4C05D23B419A9999992C022A416766666669D23B41CDCCCCCCF4022A41CDCCCC4C6AD23B419A999999F6022A419A99991906D23B41676666662E022A41CDCCCC4C05D23B4101030000000100000005000000333333335F012A41676666E694D33B419A9999995D012A4100000000F9D33B41CDCCCCCC25022A41CDCCCCCCF9D33B419A99999927022A41333333B395D33B41333333335F012A41676666E694D33B4101030000000100000005000000000000002B022A4100000080CDD23B413333333329022A419A99999931D33B4167666666F1022A410000008032D33B4133333333F3022A4167666666CED23B41000000002B022A4100000080CDD23B410103000000010000000500000033333333C6FF2941333333B387D53B41676666668E002A410000008088D53B413333333390002A416766666624D53B4100000000C8FF29419A99999923D53B4133333333C6FF2941333333B387D53B410103000000010000000500000000000000F
 A022A41676666E63DD13B4167666666F8022A4100000000A2D13B419A999999C0032A41676666E6A2D13B4133333333C2032A41CDCCCCCC3ED13B4100000000FA022A41676666E63DD13B41010300000001000000050000000000000049012A4133333333AAD83B413333333347012A41676666660ED93B41676666660F022A41333333330FD93B413333333311022A419A999919ABD83B410000000049012A4133333333AAD83B410103000000010000000500000000000000B0FF29419A9999199DDA3B4133333333AEFF29413333333301DB3B416766666676002A410000000002DB3B413333333378002A41676666E69DDA3B4100000000B0FF29419A9999199DDA3B41010300000001000000050000006766666616022A41CDCCCCCC7ED73B419A99999914022A41676666E6E2D73B41CDCCCCCCDC022A41333333B3E3D73B419A999999DE022A419A9999997FD73B416766666616022A41CDCCCCCC7ED73B41010300000001000000090000000850CA06B4042A412F6CD9763EDA3B419A9999192A052A4152B81EC5C8D93B41FC955A532D052A41DCDDF0D6C1D93B419A9999992E052A41CDCCCCCC76D93B41333333339E032A410000000075D93B41676666669C032A419A999919D9D93B419A99999964042A4100000000DAD93B410000000063042A419A9999193EDA3B410850CA
 06B4042A412F6CD9763EDA3B4101030000000100000005000000CDCCCCCC12022A410000000047D83B413333333311022A419A999919ABD83B4167666666D9022A4100000000ACD83B4133333333DB022A41676666E647D83B41CDCCCCCC12022A410000000047D83B4101030000000100000005000000CDCCCCCC8C002A419A999999ECD53B41000000008B002A41333333B350D63B413333333353012A419A99999951D63B410000000055012A4100000080EDD53B41CDCCCCCC8C002A419A999999ECD53B410103000000010000000500000094733070CE022A41B35AC2BE27DB3B41AE47E17AD9022A4115AE47A123DB3B41AEB054951B032A4150DCA5F104DB3B4100000000CF022A419A99999904DB3B4194733070CE022A41B35AC2BE27DB3B410103000000010000000500000033333333D2FF2941676666E6CAD23B419A999999D0FF2941000000002FD33B41CDCCCCCC98002A41676666E62FD33B419A9999999A002A41CDCCCCCCCBD23B4133333333D2FF2941676666E6CAD23B4101030000000100000005000000676666666B012A419A999919D8D03B419A99999969012A41333333333CD13B41CDCCCCCC31022A419A9999193DD13B419A99999933022A4100000000D9D03B41676666666B012A419A999919D8D03B4101030000000100000005000000CDCCCCCCC9FF294
 100000080BFD43B4100000000C8FF29419A99999923D53B413333333390002A416766666624D53B410000000092002A41CDCCCC4CC0D43B41CDCCCCCCC9FF294100000080BFD43B41010300000001000000050000006766666664012A410000008068D23B41CDCCCCCC62012A419A999999CCD23B41000000002B022A4100000080CDD23B419A9999992C022A416766666669D23B416766666664012A410000008068D23B41010300000001000000050000009A999999BDFF2941333333337CD73B4100000000BCFF2941CDCCCC4CE0D73B41676666664C012A4100000000E2D73B41333333334E012A41676666E67DD73B419A999999BDFF2941333333337CD73B4101030000000100000005000000333333334E012A41676666E67DD73B416766666616022A41CDCCCCCC7ED73B410000000018022A41333333B31AD73B41CDCCCCCC4F012A41CDCCCCCC19D73B41333333334E012A41676666E67DD73B410103000000010000000500000000000000D6022A413333333374D93B4133333333D4022A41CDCCCC4CD8D93B41676666669C032A419A999919D9D93B41333333339E032A410000000075D93B4100000000D6022A413333333374D93B410103000000010000000500000033333333DB022A41676666E647D83B4167666666D9022A4100000000ACD83B419A999999A1032A41CD
 CCCCCCACD83B4167666666A3032A41333333B348D83B4133333333DB022A41676666E647D83B41010300000001000000050000009A999999A1032A41CDCCCCCCACD83B41333333339E032A410000000075D93B416766666666042A41676666E675D93B41CDCCCCCC69042A41333333B3ADD83B419A999999A1032A41CDCCCCCCACD83B41010300000001000000050000009A99999927022A41333333B395D33B41CDCCCCCC25022A41CDCCCCCCF9D33B4100000000EE022A41333333B3FAD33B41CDCCCCCCEF022A419A99999996D33B419A99999927022A41333333B395D33B4101030000000100000005000000CDCCCCCC5B012A419A9999195DD43B41333333335A012A4133333333C1D43B416766666622022A4100000000C2D43B410000000024022A41676666E65DD43B41CDCCCCCC5B012A419A9999195DD43B4101030000000100000005000000CDCCCCCC56012A416766666689D53B410000000055012A4100000080EDD53B41333333331D022A4167666666EED53B41000000001F022A41333333338AD53B41CDCCCCCC56012A416766666689D53B41010300000001000000070000000000000048DA2941333333B3A1D23B413333333310DB29419A999999A2D23B410000000012DB2941000000803ED23B4133333333DADB2941CDCCCC4C3FD23B41CDCCCCCCDBDB294133333
 333DBD13B41676666664BDA294100000080D9D13B410000000048DA2941333333B3A1D23B410103000000010000000800000057A6F58CF4D82941E77E3F42A0D23B410AD7A3700AD929411F85EB519DD23B41FFD8CDD939D929417640928DA0D23B410000000048DA2941333333B3A1D23B41CDCCCCCC49DA29419A9999993DD23B4167666666B9D82941676666E63BD23B419A999999B7D8294100000000A0D23B4157A6F58CF4D82941E77E3F42A0D23B41010300000001000000050000000000000012DB2941000000803ED23B413333333310DB29419A999999A2D23B4167666666D8DB294167666666A3D23B4133333333DADB2941CDCCCC4C3FD23B410000000012DB2941000000803ED23B41010300000001000000080000004A863DE425D7294160874710E7D23B4190C2F5A838D7294190C2F568E5D23B419A99999980D72941AE47E1BAEED23B419A999919E1D72941C3F5281CD8D23B419094A37DEED7294142734CC3D1D23B4167666666EFD72941333333339FD23B413333333327D72941CDCCCC4C9ED23B414A863DE425D7294160874710E7D23B4101030000000100000007000000EFFDCAE546DA2941B484C7AAE6D23B410AD7A37053DA29411F85EBD1EED23B41E68D68B472DA2941CF93F4FC05D33B41CDCCCCCCD6DB29410000008007D33B4167666666D8DB294167
 666666A3D23B410000000048DA2941333333B3A1D23B41EFFDCAE546DA2941B484C7AAE6D23B4101030000000100000005000000CDCCCCCC09D429419A999999D2D13B410000000008D42941CDCCCCCC36D23B4133333333D0D429419A99999937D23B4100000000D2D4294100000080D3D13B41CDCCCCCC09D429419A999999D2D13B410103000000010000000500000033333333DADB2941CDCCCC4C3FD23B4167666666A2DC29413333333340D23B4133333333A4DC29419A999919DCD13B41CDCCCCCCDBDB294133333333DBD13B4133333333DADB2941CDCCCC4C3FD23B41010300000001000000050000009A99999977D229410000000035D23B41CDCCCCCC75D229419A99991999D23B416766666606D42941676666E69AD23B410000000008D42941CDCCCCCC36D23B419A99999977D229410000000035D23B41010300000001000000050000000000000038DE29419A99999915D13B413333333336DE2941333333B379D13B4167666666FEDE29419A9999997AD13B413333333300DF29416766666616D13B410000000038DE29419A99999915D13B410103000000010000000500000000000000FBDE2941CDCCCCCC42D23B4167666666F9DE2941676666E6A6D23B419A999999C1DF2941333333B3A7D23B4133333333C3DF29419A99999943D23B4100000000FBDE2941CDCCC
 CCC42D23B4101030000000100000005000000CDCCCCCCFCDE2941333333B3DED13B4100000000C5DF294100000080DFD13B419A999999C6DF2941676666667BD13B4167666666FEDE29419A9999997AD13B41CDCCCCCCFCDE2941333333B3DED13B410103000000010000000500000067666666C8DF2941CDCCCC4C17D13B419A999999C6DF2941676666667BD13B41CDCCCCCC8EE02941CDCCCC4C7CD13B419A99999990E029413333333318D13B4167666666C8DF2941CDCCCC4C17D13B4101030000000100000009000000CDCCCCCCFCDE2941333333B3DED13B4100000000FBDE2941CDCCCCCC42D23B4133333333C3DF29419A99999943D23B419A999999C1DF2941333333B3A7D23B410000000052E1294167666666A9D23B413333333357E129419A9999197DD13B419A999999C6DF2941676666667BD13B4100000000C5DF294100000080DFD13B41CDCCCCCCFCDE2941333333B3DED13B41010300000001000000050000006766666655E1294133333333E1D13B419A99999953E12941CDCCCC4C45D23B41CDCCCCCC1BE229413333333346D23B419A9999991DE229419A999919E2D13B416766666655E1294133333333E1D13B41010300000001000000050000003333333357E129419A9999197DD13B416766666655E1294133333333E1D13B419A9999991DE229419A999919
 E2D13B41676666661FE22941000000007ED13B413333333357E129419A9999197DD13B41010300000001000000050000009A99999953E12941CDCCCC4C45D23B410000000052E1294167666666A9D23B4167666666E2E2294133333333ABD23B4133333333E4E229419A99991947D23B419A99999953E12941CDCCCC4C45D23B41010300000001000000050000009A9999991DE229419A999919E2D13B41CDCCCCCC1BE229413333333346D23B4133333333E4E229419A99991947D23B41CDCCCCCCE5E2294100000000E3D13B419A9999991DE229419A999919E2D13B410103000000010000000500000033333333E4E229419A99991947D23B4167666666E2E2294133333333ABD23B419A999999AAE3294100000000ACD23B4167666666ACE32941676666E647D23B4133333333E4E229419A99991947D23B410103000000010000000500000000000000AEE32941CDCCCCCCE3D13B4167666666ACE32941676666E647D23B419A99999974E42941CDCCCCCC48D23B413333333376E42941333333B3E4D13B4100000000AEE32941CDCCCCCCE3D13B41010300000001000000050000009A99999974E42941CDCCCCCC48D23B41CDCCCCCC72E42941676666E6ACD23B41000000003BE52941333333B3ADD23B41CDCCCCCC3CE529419A99999949D23B419A99999974E42941CDCCCCCC48D
 23B41010300000001000000050000003333333376E42941333333B3E4D13B419A99999974E42941CDCCCCCC48D23B41CDCCCCCC3CE529419A99999949D23B41676666663EE5294100000080E5D13B413333333376E42941333333B3E4D13B4101030000000100000005000000CDCCCCCC3CE529419A99999949D23B41000000003BE52941333333B3ADD23B413333333303E629419A999999AED23B410000000005E62941000000804AD23B41CDCCCCCC3CE529419A99999949D23B4101030000000100000005000000333333330AE62941333333331ED13B416766666608E62941CDCCCC4C82D13B419A999999D0E629419A99991983D13B4167666666D2E62941000000001FD13B41333333330AE62941333333331ED13B410103000000010000000500000067666666CBE6294100000080AFD23B419A99999993E72941CDCCCC4CB0D23B416766666695E72941333333334CD23B4133333333CDE62941CDCCCC4C4BD23B4167666666CBE6294100000080AFD23B410103000000010000000500000067666666D2E62941000000001FD13B41CDCCCCCCCEE6294133333333E7D13B413333333397E729419A999919E8D13B419A9999999AE72941676666E61FD13B4167666666D2E62941000000001FD13B41010300000001000000050000006766666695E72941333333334CD23B419A99
 999993E72941CDCCCC4CB0D23B41CDCCCCCC5BE8294133333333B1D23B419A9999995DE829419A9999194DD23B416766666695E72941333333334CD23B41010300000001000000050000003333333300DF29416766666616D13B4167666666FEDE29419A9999997AD13B419A999999C6DF2941676666667BD13B4167666666C8DF2941CDCCCC4C17D13B413333333300DF29416766666616D13B4101030000000100000005000000CDCCCCCCBFC629410000000060D13B4133333333BEC629419A999919C4D13B416766666686C72941676666E6C4D13B410000000088C72941CDCCCCCC60D13B41CDCCCCCCBFC629410000000060D13B410103000000010000000F00000067666666BCC629413333333328D23B419A999999BAC62941CDCCCC4C8CD23B4167666666F2C52941676666668BD23B41CDCCCCCCF0C5294100000080EFD23B413333333398C32941676666E6ECD23B410000000093C32941CDCCCC4C19D43B41333333335BC429419A9999191AD43B416766666659C42941333333337ED43B41CDCCCCCCE9C52941676666E67FD43B419A999999EBC52941CDCCCCCC1BD43B41000000007CC729419A9999991DD43B413333333381C7294133333333F1D23B416766666649C829419A999919F2D23B41CDCCCCCC4CC82941676666E629D23B4167666666BCC629413333333328D
 23B41010300000001000000080000008FACC04B7BC72941166E53A649D43B410AD7A3F081C72941713D0A9747D43B4100000000E8C729417B14AE074BD43B41295C8F423AC82941EC51B85E41D43B415D3D57AA43C8294176AAA7D83FD43B413333333344C82941676666661ED43B41000000007CC729419A9999991DD43B418FACC04B7BC72941166E53A649D43B410103000000010000000B000000A47CE0D12DC62941D26BBB2AA1D73B4190C2F5A820C629411F85EB11ADD73B419A99991914C62941CDCCCC0CCDD73B4148E17A941BC62941EC51B81EEBD73B41676666E6A6C6294152B81EC51CD83B41B81E856B2CC729413E0AD76342D83B4169089E8569C729414878F78E56D83B41CDCCCCCC6AC729419A99999906D83B419A999999A2C62941CDCCCCCC05D83B4167666666A4C62941333333B3A1D73B41A47CE0D12DC62941D26BBB2AA1D73B41010300000001000000040000004CBAA863A7C72941303FC5FA6AD83B416F80C59830C82941F971D44498D83B416766666631C829419A9999996BD83B414CBAA863A7C72941303FC5FA6AD83B4101030000000100000005000000802C2FFBB8C8294111164E51D0D83B41CDCCCCCCCBC82941EC51B85EF7D83B41D6C46925F7C829415887DADEF7D83B41CDCCCCCCF7C829419A999999D0D83B41802C2FFBB8C8294111164E51
 D0D83B41010300000001000000040000009DA6E911B2C62941109EF10788D43B4184F15710C9C62941A1E21CE780D43B4133333333B2C62941CDCCCCCC80D43B419DA6E911B2C62941109EF10788D43B410103000000010000000800000090AEF26A3EC82941FD06003976D53B41AE47E17A1EC829413E0AD7A391D53B415C8FC2F5E1C7294190C2F568ABD53B419A999919B7C729417B14AE47DAD53B4115AE476186C72941C3F5281C01D63B41A6EE472379C72941790F342012D63B419A9999993BC829410000000013D63B4190AEF26A3EC82941FD06003976D53B41010300000001000000080000004E9B2C4673C729415D3014A919D63B41CDCCCC4C2CC72941C3F528DC74D63B41B81E856B0FC72941A4703DCAA6D63B411F85EBD1FAC62941D7A370BDC8D63B415C8FC2F597C62941EC51B85EF3D63B41CB2F566B6AC629413FF2F64B3DD73B41676666666EC72941676666663ED73B414E9B2C4673C729415D3014A919D63B4101030000000100000006000000A6EE472379C72941790F342012D63B41A9847D4773C729419F4463A719D63B41CDCCCCCC71C729413333333376D63B41000000003AC829419A99991977D63B419A9999993BC829410000000013D63B41A6EE472379C72941790F342012D63B410103000000010000000F000000CB2F566B6AC629413FF2F64B3DD
 73B419A99999968C629410000004040D73B41AE47E1FA67C62941A4703D4A64D73B41A4703D8A55C62941D7A3703D7DD73B41A47CE0D12DC62941D26BBB2AA1D73B4167666666A4C62941333333B3A1D73B419A999999A2C62941CDCCCCCC05D83B41CDCCCCCC6AC729419A99999906D83B4169089E8569C729414878F78E56D83B414CBAA863A7C72941303FC5FA6AD83B416766666631C829419A9999996BD83B41000000003AC829419A99991977D63B41CDCCCCCC71C729413333333376D63B41676666666EC72941676666663ED73B41CB2F566B6AC629413FF2F64B3DD73B410103000000010000000C000000B56494BE58C4294141BD7536A7D43B4115AE4761A7C4294148E17A94ABD43B411F85EB5165C5294190C2F568BBD43B41F6285C8F85C529417B14AE87AFD43B41AE47E1FA4FC629413E0AD763A5D43B41CDCCCC4C86C629419A99999995D43B41244A09FAB1C62941007D580F88D43B41CDCCCCCCB3C62941333333B31CD43B419A999999EBC52941CDCCCCCC1BD43B41CDCCCCCCE9C52941676666E67FD43B416766666659C42941333333337ED43B41B56494BE58C4294141BD7536A7D43B41010300000001000000070000009A9999992DC52941CDCCCC4CC2D13B41000000002CC529416766666626D23B4133333333F4C52941CDCCCC4C27D23B4167666666F2C5
 2941676666668BD23B419A999999BAC62941CDCCCC4C8CD23B4133333333BEC629419A999919C4D13B419A9999992DC52941CDCCCC4CC2D13B4101030000000100000005000000333333332AC529419A9999998AD23B419A99999928C52941333333B3EED23B41CDCCCCCCF0C5294100000080EFD23B4167666666F2C52941676666668BD23B41333333332AC529419A9999998AD23B41010300000001000000050000009A999999F7C529419A9999195FD13B4100000000F6C5294133333333C3D13B4133333333BEC629419A999919C4D13B41CDCCCCCCBFC629410000000060D13B419A999999F7C529419A9999195FD13B410103000000010000000600000084F15710C9C62941A1E21CE780D43B418FACC04B7BC72941166E53A649D43B41000000007CC729419A9999991DD43B41CDCCCCCCB3C62941333333B31CD43B4133333333B2C62941CDCCCCCC80D43B4184F15710C9C62941A1E21CE780D43B410103000000010000000500000033333333BEC629419A999919C4D13B4167666666BCC629413333333328D23B419A99999984C729410000000029D23B416766666686C72941676666E6C4D13B4133333333BEC629419A999919C4D13B41010300000001000000050000000000000009C9294100000080E7D43B41CDCCCCCC03C92941CDCCCCCC13D63B4100000000CCC9294
 1333333B314D63B4133333333D1C92941CDCCCC4CE8D43B410000000009C9294100000080E7D43B4101030000000100000005000000000000003AC829419A99991977D63B413333333338C8294133333333DBD63B416766666600C9294100000000DCD63B413333333302C92941676666E677D63B41000000003AC829419A99991977D63B4101030000000100000005000000000000002CC529416766666626D23B41333333332AC529419A9999998AD23B4167666666F2C52941676666668BD23B4133333333F4C52941CDCCCC4C27D23B41000000002CC529416766666626D23B41010300000001000000050000009A99999907C2294133333333EBD23B410000000006C22941CDCCCC4C4FD33B4133333333CEC229413333333350D33B41CDCCCCCCCFC229419A999919ECD23B419A99999907C2294133333333EBD23B41010300000001000000050000000000000006C22941CDCCCC4C4FD33B413333333304C2294167666666B3D33B4167666666CCC22941CDCCCC4CB4D33B4133333333CEC229413333333350D33B410000000006C22941CDCCCC4C4FD33B41010300000001000000060000008459F83600C22941B59FEE50A0D43B41713D0A5781C22941E17A142E88D43B4113E070BBC8C229415A3D3A418DD43B4100000000C9C22941000000807CD43B41CDCCCCCC00C2294133
 3333B37BD43B418459F83600C22941B59FEE50A0D43B410103000000010000000700000067666666CCC22941CDCCCC4CB4D33B4100000000C9C22941000000807CD43B416766666659C42941333333337ED43B41333333335BC429419A9999191AD43B410000000093C32941CDCCCC4C19D43B419A99999994C3294133333333B5D33B4167666666CCC22941CDCCCC4CB4D33B410103000000020000001A000000A13AB048D0B52941616D149715D23B415C8FC275CFB52941C3F5289C45D23B4115AE4761EAB52941CDCCCCCC86D23B41B81E85EB1BB62941CDCCCC0CA5D23B41676666667DB62941295C8F82C9D23B41F6285C8F6CB72941C3F5289C0AD33B4115AE476192B729417B14AEC717D33B41713D0A57B4B72941E17A14EE1CD33B4152B81E85F6B729415C8FC23517D33B4148E17A1402B9294152B81E45CED23B4185EB51B866B92941B81E85ABAFD23B417B14AE47B9B92941EC51B81E09D33B410AD7A370E7B92941F6285C4F02D33B41AE47E1FA4DBA294185EB51B8E9D23B417B14AEC7FABA2941B81E856BC7D23B41562CE01DFEBA29410767AFF6CAD23B4167666666FFBA2941676666667FD23B413333333337BA2941000000807ED23B410000000039BA2941676666661AD23B41CDCCCCCC70B929419A99999919D23B416766666672B9294100000080B5D13B41CDC
 CCCCC19B72941676666E6B2D13B416766666616B729419A9999197BD23B41333333334EB62941CDCCCC4C7AD23B410000000050B629419A99991916D23B41A13AB048D0B52941616D149715D23B41050000006766666616B729419A9999197BD23B41CDCCCCCCA6B82941CDCCCCCC7CD23B4133333333A5B82941676666E6E0D23B419A99999914B7294133333333DFD23B416766666616B729419A9999197BD23B410103000000010000000600000091066719BBA429413D124827D5D93B41D7A3703D02A529419A999959A2D93B4171465D753BA52941E27218A071D93B416766666632A429410000008070D93B419A99999930A429419A999999D4D93B4191066719BBA429413D124827D5D93B4101030000000100000010000000B044C838C4A52941586FBD2123D93B4185EB51B864A62941AE47E1BA28D93B414632857E8CA62941E369B1E720D93B41CDCCCCCC8CA62941676666E60ED93B410000000055A72941CDCCCCCC0FD93B41861AC49B54A729413AE0CFDA26D93B41AE47E1FA6CA72941EC51B8DE2AD93B419A99991905A829413E0AD763B9D83B41333333333AA82941CDCCCC8C7DD83B4194ADAD9392A829413D3C15EF48D83B416766666658A729419A99999947D83B419A99999956A72941333333B3ABD83B4100000000FEA429419A999919A9D83B4167666666FCA4
 2941333333330DD93B419A999999C4A529419A9999190ED93B41B044C838C4A52941586FBD2123D93B410103000000010000000500000037A2DF4EB3B429410B3E6C2A4CD13B41F6285C8F16B529419A99999983D13B41EE3218E488B5294154D3A181CDD13B41333333338BB529419A9999194DD13B4137A2DF4EB3B429410B3E6C2A4CD13B41010300000001000000040000004C01D3677BBB29415F7E392C48D33B410E14BB70C3BB29418A1C7B0678D33B4133333333C4BB29410000008048D33B414C01D3677BBB29415F7E392C48D33B410103000000010000000700000083F6941334BC29416210AF0DADD33B4190C2F5A8A0BC2941A4703D8AD4D33B41167FD1F250BD2941F5E2573521D43B419A99999954BD2941333333334AD33B41676666668CBC2941CDCCCC4C49D33B41CDCCCCCC8ABC294167666666ADD33B4183F6941334BC29416210AF0DADD33B41010300000001000000050000009A9999993ABA2941CDCCCC4CB6D13B410000000039BA2941676666661AD23B413333333301BB2941CDCCCC4C1BD23B410000000003BB294133333333B7D13B419A9999993ABA2941CDCCCC4CB6D13B4101030000000100000005000000333333338EBC294133333333E5D23B41676666668CBC2941CDCCCC4C49D33B419A99999954BD2941333333334AD33B416766666656BD294
 19A999919E6D23B41333333338EBC294133333333E5D23B4101030000000100000009000000CDCCCCCCB0A62941333333B3D8D03B4100000000AFA62941CDCCCCCC3CD13B41333333338EA32941CDCCCC4C39D13B41CDCCCCCC8AA329419A99999901D23B4167666666E3A529419A99991904D23B4133333333E5A5294100000000A0D13B419A99999975A72941333333B3A1D13B410000000079A7294100000080D9D03B41CDCCCCCCB0A62941333333B3D8D03B41010300000001000000050000009A9999999FA62941333333B3C1D43B41000000009EA62941CDCCCCCC25D53B413333333366A72941333333B326D53B41CDCCCCCC67A729419A999999C2D43B419A9999999FA62941333333B3C1D43B4101030000000100000005000000676666669AA629419A999919EED53B41CDCCCCCC98A629413333333352D63B410000000061A729410000000053D63B419A99999962A72941676666E6EED53B41676666669AA629419A999919EED53B41010300000001000000060000000D36E6798CA62941B4259AE820D93B41D7A3703DD7A629413333333312D93B410E28E39554A72941B7EED7D926D93B410000000055A72941CDCCCCCC0FD93B41CDCCCCCC8CA62941676666E60ED93B410D36E6798CA62941B4259AE820D93B4101030000000100000009000000562CE01DFEBA294107
 67AFF6CAD23B41676666E62DBB29415C8FC2B5FDD23B41C3F5285C57BB2941B81E85EB20D33B417B14AEC776BB29419A99991945D33B414C01D3677BBB29415F7E392C48D33B4133333333C4BB29410000008048D33B419A999999C7BB2941CDCCCC4C80D23B4167666666FFBA2941676666667FD23B41562CE01DFEBA29410767AFF6CAD23B41010300000001000000050000006766666656BD29419A999919E6D23B419A99999954BD2941333333334AD33B41000000001DBE2941000000004BD33B419A9999991EBE2941676666E6E6D23B416766666656BD29419A999919E6D23B41010300000001000000050000003333333374B929416766666651D13B416766666672B9294100000080B5D13B419A9999993ABA2941CDCCCC4CB6D13B41676666663CBA29413333333352D13B413333333374B929416766666651D13B41010300000001000000050000009A999999DEB72941000000007CD23B4100000000DDB729419A999919E0D23B4133333333A5B82941676666E6E0D23B41CDCCCCCCA6B82941CDCCCCCC7CD23B419A999999DEB72941000000007CD23B4101030000000100000008000000EE3218E488B5294154D3A181CDD13B41EC51B81EA9B52941713D0A57E2D13B41B81E85EBD0B5294152B81E85F0D13B41A13AB048D0B52941616D149715D23B410000000050B6294
 19A99991916D23B416766666653B62941676666E64DD13B41333333338BB529419A9999194DD13B41EE3218E488B5294154D3A181CDD13B41010300000001000000050000000000000039BA2941676666661AD23B413333333337BA2941000000807ED23B4167666666FFBA2941676666667FD23B413333333301BB2941CDCCCC4C1BD23B410000000039BA2941676666661AD23B41010300000001000000050000009A999999CEBB2941CDCCCCCCEFD03B41CDCCCCCCCCBB2941676666E653D13B410000000095BC2941CDCCCCCC54D13B41CDCCCCCC96BC2941333333B3F0D03B419A999999CEBB2941CDCCCCCCEFD03B410103000000010000000500000000000000ACB829410000008050D13B4133333333AAB829419A999999B4D13B416766666672B9294100000080B5D13B413333333374B929416766666651D13B4100000000ACB829410000008050D13B41010300000001000000050000003333333358BD29410000000082D23B416766666656BD29419A999919E6D23B419A9999991EBE2941676666E6E6D23B416766666620BE2941CDCCCCCC82D23B413333333358BD29410000000082D23B41010300000001000000050000006766666672B9294100000080B5D13B41CDCCCCCC70B929419A99999919D23B410000000039BA2941676666661AD23B419A9999993ABA2941CD
 CCCC4CB6D13B416766666672B9294100000080B5D13B410103000000010000000500000033333333CBBB294100000000B8D13B4167666666C9BB2941333333331CD23B419A99999991BC2941000000001DD23B416766666693BC2941676666E6B8D13B4133333333CBBB294100000000B8D13B4101030000000100000005000000676666663CBA29413333333352D13B419A9999993ABA2941CDCCCC4CB6D13B410000000003BB294133333333B7D13B419A99999904BB29419A99991953D13B41676666663CBA29413333333352D13B41010300000001000000050000006766666653B62941676666E64DD13B419A99999951B6294100000000B2D13B41CDCCCCCC19B72941676666E6B2D13B419A9999991BB72941CDCCCCCC4ED13B416766666653B62941676666E64DD13B41010300000001000000050000000000000090BC29419A99991981D23B41333333338EBC294133333333E5D23B416766666656BD29419A999919E6D23B413333333358BD29410000000082D23B410000000090BC29419A99991981D23B41010300000001000000070000000E14BB70C3BB29418A1C7B0678D33B4148E17A94EBBB29410AD7A3B092D33B4183F6941334BC29416210AF0DADD33B41CDCCCCCC8ABC294167666666ADD33B41676666668CBC2941CDCCCC4C49D33B4133333333C4BB294100000
 08048D33B410E14BB70C3BB29418A1C7B0678D33B410103000000010000000700000009F5662588BD29413D60BFDCDAD43B4148E17A1469BD29417B14AEC728D53B41B81E85EBFEBD2941333333733CD53B41A52BB10510BE294191D58F943FD53B416766666614BE29419A9999993FD53B410000000016BE294100000080DBD43B4109F5662588BD29413D60BFDCDAD43B41010300000001000000050000009A999999C9A52941333333B3E1D73B4100000000C8A52941CDCCCCCC45D83B416766666658A729419A99999947D83B41333333335AA7294100000080E3D73B419A999999C9A52941333333B3E1D73B4101030000000100000005000000CDCCCCCCDAA52941333333B3F8D33B4133333333D9A52941CDCCCCCC5CD43B4167666666A1A629419A9999995DD43B4100000000A3A6294100000080F9D33B41CDCCCCCCDAA52941333333B3F8D33B410103000000010000000700000067666666E5B7294100000080EBD03B4100000000E2B72941CDCCCCCCB3D13B4133333333AAB829419A999999B4D13B4100000000ACB829410000008050D13B413333333374B929416766666651D13B410000000076B92941CDCCCC4CEDD03B4167666666E5B7294100000080EBD03B41010300000001000000050000009A999999C4B429419A999919E8D03B4100000000C3B4294133333333
 4CD13B41333333338BB529419A9999194DD13B41CDCCCCCC8CB5294100000000E9D03B419A999999C4B429419A999919E8D03B41010300000001000000050000000000000016A529419A9999992FD33B416766666614A52941333333B393D33B419A999999DCA529419A99999994D33B4133333333DEA529410000008030D33B410000000016A529419A9999992FD33B4101030000000100000009000000B7FF787F3BA529417ADC7C9771D93B410AD7A3704FA52941C3F5289C60D93B410AD7A3705EA52941C3F5289C27D93B41EC51B89EA0A529413E0AD7E321D93B41B044C838C4A52941586FBD2123D93B419A999999C4A529419A9999190ED93B4167666666FCA42941333333330DD93B419A999999FAA42941CDCCCC4C71D93B41B7FF787F3BA529417ADC7C9771D93B410103000000010000000600000092F27C42B4A82941D19BA9F4E4D73B41AE47E1FA87A8294115AE47A1A5D73B41120E470090A829415E327EAF80D73B410000000024A829413333333380D73B416766666622A82941CDCCCC4CE4D73B4192F27C42B4A82941D19BA9F4E4D73B410103000000010000000B0000005559FCD60FBE2941124F038C3FD53B4152B81E85C8BE29419A99995961D53B411F85EBD126BF2941D7A370BD55D53B415C8FC275EFBF29411F85EB913ED53B41E17A14AE09C02941B81E8
 5AB33D53B410AD7A37062C02941295C8F020DD53B4152B81E05CBC02941E17A146EF2D43B415C19B5701AC12941E1FA5DCFDED43B4167666666A6BF294133333333DDD43B41CDCCCCCCA4BF2941CDCCCC4C41D53B415559FCD60FBE2941124F038C3FD53B4101030000000100000007000000120E470090A829415E327EAF80D73B41E17A142E97A82941EC51B89E5FD73B4133333333CEA82941A4703DCA28D73B414F6D11BECCA82941A9BFB9D91CD73B41CDCCCCCC25A829419A9999191CD73B410000000024A829413333333380D73B41120E470090A829415E327EAF80D73B410103000000010000000700000013E070BBC8C229415A3D3A418DD43B41713D0A57F9C229415C8FC2B590D43B411F85EB51A9C3294185EB51789DD43B41B56494BE58C4294141BD7536A7D43B416766666659C42941333333337ED43B4100000000C9C22941000000807CD43B4113E070BBC8C229415A3D3A418DD43B4101030000000100000007000000F27B9CED36C1294183A1BAC5D7D43B4190C2F5289EC1294152B81E45BED43B41333333B3EEC12941713D0A97A3D43B418459F83600C22941B59FEE50A0D43B41CDCCCCCC00C22941333333B37BD43B419A99999938C12941CDCCCCCC7AD43B41F27B9CED36C1294183A1BAC5D7D43B4101030000000100000005000000CDCCCCCC3DC1294100
 0000804ED33B41000000003CC129419A999999B2D33B413333333304C2294167666666B3D33B410000000006C22941CDCCCC4C4FD33B41CDCCCCCC3DC12941000000804ED33B410103000000010000000B000000CDCCCCCC0BA529413333333388D53B41000000000AA52941CDCCCC4CECD53B41676666669AA629419A999919EED53B41333333339CA62941000000008AD53B416766666664A72941CDCCCCCC8AD53B413333333366A72941333333B326D53B41000000009EA62941CDCCCCCC25D53B419A9999999FA62941333333B3C1D43B4167666666D7A52941676666E6C0D43B4100000000D4A529419A99991989D53B41CDCCCCCC0BA529413333333388D53B4101030000000100000005000000CDCCCCCCDAA52941333333B3F8D33B4100000000A3A6294100000080F9D33B4167666666A6A62941CDCCCC4C31D33B4133333333DEA529410000008030D33B41CDCCCCCCDAA52941333333B3F8D33B41010300000001000000050000009A9999999FA62941333333B3C1D43B41CDCCCCCC67A729419A999999C2D43B419A99999969A72941000000805ED43B4167666666A1A629419A9999995DD43B419A9999999FA62941333333B3C1D43B41010300000001000000050000009A99999912A52941CDCCCCCCF7D33B410000000011A52941676666E65BD43B4133333333D9A5294
 1CDCCCCCC5CD43B41CDCCCCCCDAA52941333333B3F8D33B419A99999912A52941CDCCCCCCF7D33B41010300000001000000050000009A999999ABA629410000000005D23B4100000000AAA629419A99991969D23B413333333372A72941000000006AD23B41CDCCCCCC73A72941676666E605D23B419A999999ABA629410000000005D23B41010300000001000000050000000000000003BB294133333333B7D13B4167666666FFBA2941676666667FD23B419A999999C7BB2941CDCCCC4C80D23B4133333333CBBB294100000000B8D13B410000000003BB294133333333B7D13B41010300000001000000050000000000000076B92941CDCCCC4CEDD03B413333333374B929416766666651D13B419A99999904BB29419A99991953D13B416766666606BB294100000000EFD03B410000000076B92941CDCCCC4CEDD03B41010300000001000000050000003333333304C2294167666666B3D33B419A99999902C229419A99999917D43B41CDCCCCCCCAC229416766666618D43B4167666666CCC22941CDCCCC4CB4D33B413333333304C2294167666666B3D33B41010300000001000000050000006766666616B729419A9999197BD23B419A99999914B7294133333333DFD23B4100000000DDB729419A999919E0D23B419A999999DEB72941000000007CD23B416766666616B729419A
 9999197BD23B41010300000001000000050000009A99999951B6294100000000B2D13B41333333334EB62941CDCCCC4C7AD23B416766666616B729419A9999197BD23B41CDCCCCCC19B72941676666E6B2D13B419A99999951B6294100000000B2D13B4101030000000100000005000000CDCCCCCC19B72941676666E6B2D13B4100000000E2B72941CDCCCCCCB3D13B41CDCCCCCCE3B72941333333B34FD13B419A9999991BB72941CDCCCCCC4ED13B41CDCCCCCC19B72941676666E6B2D13B41010300000001000000070000003333333382A329419A999919F6D33B416766666680A32941333333335AD43B419A99999948A429419A9999195BD43B410000000047A4294133333333BFD43B41333333330FA5294100000000C0D43B419A99999912A52941CDCCCCCCF7D33B413333333382A329419A999919F6D33B410103000000010000000500000067666666ADA62941676666E6A0D13B419A999999ABA629410000000005D23B41CDCCCCCC73A72941676666E605D23B419A99999975A72941333333B3A1D13B4167666666ADA62941676666E6A0D13B4101030000000100000009000000855BDE9C92A82941148B9CE948D83B4115AE4761A3A82941E17A14EE3ED83B41EC51B89EA5A82941B81E85EB30D83B41CDCCCC4C94A82941CDCCCCCC08D83B41B81E85EBB4A8294167666
 6E6E5D73B4192F27C42B4A82941D19BA9F4E4D73B416766666622A82941CDCCCC4CE4D73B419A99999920A829416766666648D83B41855BDE9C92A82941148B9CE948D83B4101030000000100000004000000195D796E9CF12941FA34DBD8D0D73B41340A5B1FA8F129411EC0F735D5D73B4133333333A8F12941676666E6D0D73B41195D796E9CF12941FA34DBD8D0D73B410103000000010000000500000000000000ECF02941CDCCCC4C13D53B4167666666E8F0294100000080DBD53B41CDCCCCCCB0F12941CDCCCC4CDCD53B4133333333B4F129419A99991914D53B4100000000ECF02941CDCCCC4C13D53B4101030000000100000006000000BD2783378CEE2941FF99028FACD63B415C8FC27506EF2941676666A6D6D63B411AFE884C53EF294160005F65F3D63B419A99999954EF294100000000A2D63B41676666668CEE29419A999919A1D63B41BD2783378CEE2941FF99028FACD63B4101030000000100000005000000CDCCCCCC96EE29410000008048D43B410000000095EE29419A999999ACD43B41333333335DEF294167666666ADD43B41000000005FEF2941CDCCCC4C49D43B41CDCCCCCC96EE29410000008048D43B41010300000001000000050000009A9999995BEF29410000008011D53B41CDCCCCCC59EF29419A99999975D53B410000000022F0294100000080
 76D53B41CDCCCCCC23F029416766666612D53B419A9999995BEF29410000008011D53B4101030000000100000007000000632DB122E1F02941975893F588D73B41D7A3703D63F1294100000080BBD73B41195D796E9CF12941FA34DBD8D0D73B4133333333A8F12941676666E6D0D73B41CDCCCCCCA9F12941CDCCCCCC6CD73B419A999999E1F02941676666E66BD73B41632DB122E1F02941975893F588D73B41010300000001000000050000000000000058EF2941CDCCCCCCD9D53B416766666656EF2941676666E63DD63B419A9999991EF02941333333B33ED63B413333333320F029419A999999DAD53B410000000058EF2941CDCCCCCCD9D53B41010300000001000000060000001AFE884C53EF294160005F65F3D63B412B5DA5D685EF2941A6489B4D06D73B41333333331BF02941676666E606D73B41CDCCCCCC1CF02941CDCCCCCCA2D63B419A99999954EF294100000000A2D63B411AFE884C53EF294160005F65F3D63B41010300000001000000050000009A999999EDF029419A999919AFD43B4100000000ECF02941CDCCCC4C13D53B4133333333B4F129419A99991914D53B41CDCCCCCCB5F1294100000000B0D43B419A999999EDF029419A999919AFD43B41010300000001000000060000002B5DA5D685EF2941A6489B4D06D73B419A999999ABEF2941E17A146E14D
 73B4185EB5138FDEF2941F6285CCF2FD73B412DBAF5481AF02941EB8702C73CD73B41333333331BF02941676666E606D73B412B5DA5D685EF2941A6489B4D06D73B410103000000010000000500000000000000E5F02941333333B3A3D63B419A999999E1F02941676666E66BD73B41CDCCCCCCA9F12941CDCCCCCC6CD73B4133333333ADF1294100000080A4D63B4100000000E5F02941333333B3A3D63B410103000000010000000500000067666666E8F0294100000080DBD53B41CDCCCCCCE6F029419A9999993FD63B4100000000AFF129416766666640D63B41CDCCCCCCB0F12941CDCCCC4CDCD53B4167666666E8F0294100000080DBD53B4101030000000100000005000000CDCCCCCC8FEE2941676666E6D8D53B41333333338EEE2941000000003DD63B416766666656EF2941676666E63DD63B410000000058EF2941CDCCCCCCD9D53B41CDCCCCCC8FEE2941676666E6D8D53B4101030000000100000007000000FE8E2EBA20EE2941836E03823CD63B4190C2F5A828EE2941A4703D8A47D63B411F85EBD169EE2941EC51B8DE7CD63B412EC1654977EE294118680004A1D63B41676666668CEE29419A999919A1D63B41333333338EEE2941000000003DD63B41FE8E2EBA20EE2941836E03823CD63B4101030000000100000005000000CDCCCCCCE8E929419A9999197BD33B
 4133333333E7E9294133333333DFD33B4167666666AFEA29419A999919E0D33B4100000000B1EA2941000000007CD33B41CDCCCCCCE8E929419A9999197BD33B41010300000001000000050000000000000055E829419A99999941D43B413333333353E82941333333B3A5D43B419A9999991BE929419A999999A6D43B41333333331DE929410000008042D43B410000000055E829419A99999941D43B4101030000000100000005000000676666663CEC294100000000AAD43B419A9999993AEC29419A9999190ED53B41CDCCCCCC02ED2941000000000FD53B419A99999904ED2941676666E6AAD43B41676666663CEC294100000000AAD43B4101030000000100000005000000CDCCCCCC59EF29419A99999975D53B410000000058EF2941CDCCCCCCD9D53B413333333320F029419A999999DAD53B410000000022F029410000008076D53B41CDCCCCCC59EF29419A99999975D53B4101030000000100000005000000333333338EEE2941000000003DD63B41676666668CEE29419A999919A1D63B419A99999954EF294100000000A2D63B416766666656EF2941676666E63DD63B41333333338EEE2941000000003DD63B41010300000001000000050000003333333353E82941333333B3A5D43B419A99999951E82941CDCCCCCC09D53B41CDCCCCCC19E92941333333B30AD53B419
 A9999991BE929419A999999A6D43B413333333353E82941333333B3A5D43B41010300000001000000050000002EC1654977EE294118680004A1D63B413E0AD72379EE2941D7A370FDA5D63B41BD2783378CEE2941FF99028FACD63B41676666668CEE29419A999919A1D63B412EC1654977EE294118680004A1D63B4101030000000100000006000000804CAF3638EC2941526A90EE9DD53B411F85EB51ABEC2941333333B392D53B41A956ED7F00ED2941053B2A0B9AD53B413333333301ED29419A99991973D53B410000000039EC29413333333372D53B41804CAF3638EC2941526A90EE9DD53B41010300000001000000050000006766666656EF2941676666E63DD63B419A99999954EF294100000000A2D63B41CDCCCCCC1CF02941CDCCCCCCA2D63B419A9999991EF02941333333B33ED63B416766666656EF2941676666E63DD63B4101030000000100000005000000000000001FE9294167666666DED33B41333333331DE929410000008042D43B4167666666E5E92941CDCCCC4C43D43B4133333333E7E9294133333333DFD33B41000000001FE9294167666666DED33B41010300000001000000050000000000000024E9294100000000B2D23B416766666622E929419A99991916D33B419A999999EAE929410000000017D33B4167666666ECE92941676666E6B2D23B410000
 000024E9294100000000B2D23B41010300000001000000100000001445DF8900ED29412EB9050C9AD53B417B14AE4719ED2941E17A142E9CD53B41C3F528DC64ED29417B14AE87B9D53B4190C2F528B6ED29410AD7A3F0E7D53B4185EB51B8F9ED2941295C8F4206D63B417EEA3ABF20EE294163BA08893CD63B41333333338EEE2941000000003DD63B41CDCCCCCC8FEE2941676666E6D8D53B410000000058EF2941CDCCCCCCD9D53B41CDCCCCCC59EF29419A99999975D53B419A99999991EE2941CDCCCCCC74D53B410000000095EE29419A999999ACD43B41CDCCCCCCCCED2941333333B3ABD43B4167666666C9ED2941676666E673D53B413333333301ED29419A99991973D53B411445DF8900ED29412EB9050C9AD53B4101030000000100000005000000333333335AE82941CDCCCC4C15D33B416766666658E829416766666679D33B419A99999920E92941333333337AD33B416766666622E929419A99991916D33B41333333335AE82941CDCCCC4C15D33B41010300000001000000050000006766666622E929419A99991916D33B419A99999920E92941333333337AD33B41CDCCCCCCE8E929419A9999197BD33B419A999999EAE929410000000017D33B416766666622E929419A99991916D33B410103000000010000000500000000000000EEE92941CDCCCCCC4ED23B416
 7666666ECE92941676666E6B2D23B419A999999B4EA2941333333B3B3D23B4133333333B6EA29419A9999994FD23B4100000000EEE92941CDCCCCCC4ED23B41010300000001000000050000009A9999995DE829419A9999194DD23B41333333335AE82941CDCCCC4C15D33B416766666622E929419A99991916D33B41CDCCCCCC25E92941676666E64DD23B419A9999995DE829419A9999194DD23B4101030000000100000005000000000000003EEC2941676666E645D43B41676666663CEC294100000000AAD43B419A99999904ED2941676666E6AAD43B413333333306ED2941CDCCCCCC46D43B41000000003EEC2941676666E645D43B410103000000010000000500000067666666F1E929419A99999986D13B41CDCCCCCCEFE92941333333B3EAD13B4100000000B8EA294100000080EBD13B419A999999B9EA29416766666687D13B4167666666F1E929419A99999986D13B410103000000010000000500000033333333AAEA2941676666660CD53B416766666672EB2941333333330DD53B413333333374EB29419A999919A9D43B4100000000ACEA2941CDCCCC4CA8D43B4133333333AAEA2941676666660CD53B410103000000010000000500000033333333F3E929410000008022D13B4167666666F1E929419A99999986D13B419A999999B9EA29416766666687D13B416766
 6666BBEA2941CDCCCC4C23D13B4133333333F3E929410000008022D13B410103000000010000000500000067666666ECE92941676666E6B2D23B419A999999EAE929410000000017D33B41CDCCCCCCB2EA2941676666E617D33B419A999999B4EA2941333333B3B3D23B4167666666ECE92941676666E6B2D23B41010300000001000000050000009A999999EAE929410000000017D33B41CDCCCCCCE8E929419A9999197BD33B4100000000B1EA2941000000007CD33B41CDCCCCCCB2EA2941676666E617D33B419A999999EAE929410000000017D33B410103000000010000000500000033333333E7E9294133333333DFD33B4167666666E5E92941CDCCCC4C43D43B419A999999ADEA29413333333344D43B4167666666AFEA29419A999919E0D33B4133333333E7E9294133333333DFD33B410103000000010000000500000067666666E5E92941CDCCCC4C43D43B41CDCCCCCCE3E9294167666666A7D43B4100000000ACEA2941CDCCCC4CA8D43B419A999999ADEA29413333333344D43B4167666666E5E92941CDCCCC4C43D43B410103000000010000000500000000000000E2E92941000000800BD53B4133333333E0E929419A9999996FD53B419A999999A8EA29410000008070D53B4133333333AAEA2941676666660CD53B4100000000E2E92941000000800BD53B410103000
 00001000000060000007CD264D096EA29419CDB3387D4D53B41C758A3EAA6EA2941AC86451ECED53B419A999999A8EA29410000008070D53B4133333333E0E929419A9999996FD53B419A999999DEE92941333333B3D3D53B417CD264D096EA29419CDB3387D4D53B4101030000000100000005000000000000002BE929419A99999921D13B413333333329E92941333333B385D13B4167666666F1E929419A99999986D13B4133333333F3E929410000008022D13B41000000002BE929419A99999921D13B4101030000000100000005000000CDCCCCCCE3E9294167666666A7D43B4100000000E2E92941000000800BD53B4133333333AAEA2941676666660CD53B4100000000ACEA2941CDCCCC4CA8D43B41CDCCCCCCE3E9294167666666A7D43B41010300000001000000050000004C00C769C9A3294112F9D822D4D93B415C8FC2750CA4294167666666F9D93B41EC3D9D1030A4294154C1C513F6D93B419A99999930A429419A999999D4D93B414C00C769C9A3294112F9D822D4D93B4101030000000100000006000000EC3D9D1030A4294154C1C513F6D93B4185EB51B868A42941A4703DCAF0D93B41A4703D0AA9A42941CDCCCC0CE2D93B4191066719BBA429413D124827D5D93B419A99999930A429419A999999D4D93B41EC3D9D1030A4294154C1C513F6D93B410103000000
 0100000005000000676666667BA329410000008086D53B419A99999979A329419A999999EAD53B41CDCCCCCC41A4294100000080EBD53B419A99999943A429416766666687D53B41676666667BA329410000008086D53B41010300000001000000050000006766666680A32941333333335AD43B41CDCCCCCC7EA32941CDCCCC4CBED43B410000000047A4294133333333BFD43B419A99999948A429419A9999195BD43B416766666680A32941333333335AD43B41010300000001000000050000000000000084A329410000000092D33B413333333382A329419A999919F6D33B41676666664AA4294100000000F7D33B41333333334CA42941CDCCCCCC92D33B410000000084A329410000000092D33B41010300000001000000050000009A99999943A429416766666687D53B41CDCCCCCC0BA529413333333388D53B41676666660DA529419A99991924D53B413333333345A42941CDCCCC4C23D53B419A99999943A429416766666687D53B41010300000001000000050000009A999999C1C62941CDCCCCCCFBD03B41CDCCCCCCBFC629410000000060D13B410000000088C72941CDCCCCCC60D13B41CDCCCCCC89C72941333333B3FCD03B419A999999C1C62941CDCCCCCCFBD03B4101030000000100000005000000075380D0B1D7294177FC9B7993D43B4175CCA98A24D7294158A
 69BC42ED43B410000000058D62941676666E62DD43B416766666656D629410000000092D43B41075380D0B1D7294177FC9B7993D43B4101030000000100000008000000856556A9C1E629416A9D9F3AEBD43B41713D0A57EAE629415C8FC235F3D43B41295C8F4238E72941CDCCCC8CFDD43B416C30D37D3CE729411D6E9B9F08D53B419A99999951E82941CDCCCCCC09D53B413333333353E82941333333B3A5D43B41CDCCCCCCC2E6294100000000A4D43B41856556A9C1E629416A9D9F3AEBD43B410103000000010000000500000000000000DFE229416766666673D33B4133333333DDE2294100000080D7D33B4167666666A5E32941CDCCCC4CD8D33B4133333333A7E329413333333374D33B4100000000DFE229416766666673D33B410103000000010000001400000090D9117220D7294167DE1CD92BD43B41C3F5285C05D72941A4703D8A18D43B415C8FC2F501D72941AE47E17AF4D33B41E42E0F89D5D62941ADE2604BCAD33B41CDCCCCCC59D62941CDCCCCCCC9D33B419259E8B55AD629414BDB791C97D33B41F6285C0FBBD52941AE47E1FA9DD33B419A999999EBD42941A4703D0A71D33B41295C8FC203D529413E0AD72369D33B410C94DDF238D529413D19FC6364D33B41CDCCCCCC02D429419A99991963D33B413333333301D4294133333333C7D33B4167666666C9
 D4294100000000C8D33B419A999999C7D429419A9999192CD43B41CDCCCCCC8FD52941000000002DD43B41333333338ED529419A99991991D43B416766666656D629410000000092D43B410000000058D62941676666E62DD43B416766666620D72941333333B32ED43B4190D9117220D7294167DE1CD92BD43B410103000000010000000B000000CDCCCCCC02D429419A99991963D33B419A99999904D4294100000000FFD23B410000000095D52941333333B300D33B41CDCCCCCC96D529419A9999999CD23B419A999999CED42941333333B39BD23B4133333333D0D429419A99999937D23B410000000008D42941CDCCCCCC36D23B416766666606D42941676666E69AD23B41333333333ED32941000000009AD23B419A9999993AD329413333333362D33B41CDCCCCCC02D429419A99991963D33B41010300000001000000060000006F1301C46CD729413C9C82DAEBD63B41676666667BD729419A9999D9DBD63B41295C8FC22DD72941D7A3707DC1D63B4142245A0315D72941C1F76D48BED63B413333333314D7294100000080EBD63B416F1301C46CD729413C9C82DAEBD63B41010300000001000000050000000000000088E02941333333B30CD33B416766666686E02941CDCCCCCC70D33B419A9999994EE12941333333B371D33B413333333350E129419A9999990DD33B410
 000000088E02941333333B30CD33B41010300000001000000050000006766666656D629410000000092D43B419A99999954D629419A999919F6D43B41CDCCCCCC1CD72941676666E6F6D43B419A9999991ED72941CDCCCCCC92D43B416766666656D629410000000092D43B4101030000000100000008000000C05006F49ED529413D301B9B21D63B41B81E856BCED529417B14AEC70DD63B411F85EBD128D6294167666626FCD53B410B135E6F50D62941C2789228EED53B413333333351D62941CDCCCC4CBED53B410000000089D5294167666666BDD53B416766666687D529410000008021D63B41C05006F49ED529413D301B9B21D63B41010300000001000000070000009A99999993E72941CDCCCC4CB0D23B410000000092E729416766666614D33B41CDCCCCCCC9E629419A99999913D33B4100000000C8E62941333333B377D33B416766666658E829416766666679D33B41CDCCCCCC5BE8294133333333B1D23B419A99999993E72941CDCCCC4CB0D23B410103000000010000000500000067666666C6E62941CDCCCCCCDBD33B419A999999C4E62941676666E63FD43B41CDCCCCCC8CE72941333333B340D43B419A9999998EE729419A999999DCD33B4167666666C6E62941CDCCCCCCDBD33B4101030000000100000005000000CDCCCCCCEAD329419A999999DCD83B413333
 3333E9D32941333333B340D93B4167666666B1D429410000008041D93B4100000000B3D4294167666666DDD83B41CDCCCCCCEAD329419A999999DCD83B41010300000001000000060000003685289693D529412111AED059D33B4185EB513851D62941D7A3707D2AD33B4151DA56AA5CD6294156F311DE26D33B41000000005FD62941676666669DD23B41CDCCCCCC96D529419A9999999CD23B413685289693D529412111AED059D33B41010300000001000000050000000000000071E429410000000011D33B41676666666FE429419A99991975D33B419A99999937E529410000000076D33B416766666639E52941CDCCCCCC11D33B410000000071E429410000000011D33B4101030000000100000006000000E42E0F89D5D62941ADE2604BCAD33B41D7A3703DADD629417B14AE07A4D33B41B81E856B79D62941A4703DCA95D33B419259E8B55AD629414BDB791C97D33B41CDCCCCCC59D62941CDCCCCCCC9D33B41E42E0F89D5D62941ADE2604BCAD33B4101030000000100000005000000CDCCCCCC4CE12941CDCCCCCCD5D33B41000000004BE12941676666E639D43B416766666613E22941333333B33AD43B410000000015E229419A999999D6D33B41CDCCCCCC4CE12941CDCCCCCCD5D33B410103000000010000000500000033333333EED329416766666614D83B419A99999
 9ECD329410000008078D83B41CDCCCCCCB4D42941CDCCCC4C79D83B419A999999B6D429413333333315D83B4133333333EED329416766666614D83B41010300000001000000050000009A99999901E62941333333B312D33B41CDCCCCCCFFE52941CDCCCCCC76D33B4100000000C8E62941333333B377D33B41CDCCCCCCC9E629419A99999913D33B419A99999901E62941333333B312D33B41010300000001000000050000006766666639E52941CDCCCCCC11D33B419A99999901E62941333333B312D33B413333333303E629419A999999AED23B41000000003BE52941333333B3ADD23B416766666639E52941CDCCCCCC11D33B41010300000001000000050000003333333334E52941333333333ED43B416766666632E52941CDCCCC4CA2D43B419A999999FAE529419A999919A3D43B4167666666FCE52941000000003FD43B413333333334E52941333333333ED43B4101030000000100000005000000CDCCCCCC19E92941333333B30AD53B410000000018E92941CDCCCCCC6ED53B4133333333E0E929419A9999996FD53B4100000000E2E92941000000800BD53B41CDCCCCCC19E92941333333B30AD53B4101030000000100000005000000333333331DE929410000008042D43B41CDCCCCCC19E92941333333B30AD53B4100000000E2E92941000000800BD53B4167666666E5
 E92941CDCCCC4C43D43B41333333331DE929410000008042D43B410103000000010000000500000067666666CBE6294100000080AFD23B41CDCCCCCCC9E629419A99999913D33B410000000092E729416766666614D33B419A99999993E72941CDCCCC4CB0D23B4167666666CBE6294100000080AFD23B41010300000001000000050000006766666658E829416766666679D33B41CDCCCCCC56E8294100000080DDD33B41000000001FE9294167666666DED33B419A99999920E92941333333337AD33B416766666658E829416766666679D33B410103000000010000002D0000005E73F39332CF29412B3F652974DA3B41F6285C0F8DCF294185EB513882DA3B4190C2F5A819D02941CDCCCCCC68DA3B41B81E85EB9ED02941E17A146E9DDA3B41713D0AD7FBD02941676666E6E2DA3B41F6285C0F3FD129410000004014DB3B4115AE47E156D12941C3F5285C20DB3B415C8FC2F5BAD12941A4703DCA22DB3B41F6285C0F57D2294185EB517827DB3B4152B81E85E7D229411F85EB1116DB3B410AD7A3F00FD3294185EB513801DB3B4185EB513806D3294167666626EFDA3B41D7A370BD41D52941A4703D8ABDDA3B41F6285C0F6CD529415C8FC2B5ADDA3B417B14AEC79CD52941EC51B8DE86DA3B410AD7A370B7D529410AD7A3F061DA3B41E17A14AE20D629411F85EB5121DA3B414
 8E17A94BED6294115AE47E1BCD93B4190C2F5A839D729419A99995985D93B410AD7A37072D72941A4703D8A5BD93B41676666E6BBD72941295C8FC20BD93B41B81E856BC4D7294152B81EC5EDD83B415C8FC27554D729417B14AE47E3D83B413E0AD7A30AD729410AD7A3F0D3D83B410AD7A3F0D6D62941333333B3BED83B411F85EB51C1D6294148E17A94A7D83B418E1DD7F0C9D62941ECBDC3987BD83B413333333345D62941000000007BD83B419A99999943D629419A999919DFD83B41333333337BD52941CDCCCC4CDED83B419A99999979D529416766666642D93B4167666666B1D429410000008041D93B419A999999AFD429419A999999A5D93B4167666666E7D32941CDCCCCCCA4D93B4100000000E4D32941000000006DDA3B41CDCCCCCC1BD329419A9999196CDA3B41676666661DD329410000000008DA3B413333333355D229413333333307DA3B410000000057D229419A999919A3D93B419A999999C6D0294167666666A1D93B41CDCCCCCCC4D029410000008005DA3B419A999999FCCF29419A99999904DA3B4100000000FBCF2941333333B368DA3B41CDCCCCCC32CF2941CDCCCCCC67DA3B415E73F39332CF29412B3F652974DA3B41010300000001000000070000009148E9E4D4D62941AEE4B28917D83B41D7A370BDDAD629415C8FC275E1D73B41D163D14311D7
 294184D501C89BD73B413333333314D7294100000080EBD63B41000000004CD629419A999999EAD63B410000000047D62941676666E616D83B419148E9E4D4D62941AEE4B28917D83B4101030000000100000005000000CDCCCCCC89E029419A999999A8D23B410000000088E02941333333B30CD33B413333333350E129419A9999990DD33B410000000052E1294167666666A9D23B41CDCCCCCC89E029419A999999A8D23B4101030000000100000005000000CDCCCCCC72E42941676666E6ACD23B410000000071E429410000000011D33B416766666639E52941CDCCCCCC11D33B41000000003BE52941333333B3ADD23B41CDCCCCCC72E42941676666E6ACD23B4101030000000100000005000000000000006CE42941CDCCCC4C3DD43B41333333336AE4294167666666A1D43B416766666632E52941CDCCCC4CA2D43B413333333334E52941333333333ED43B41000000006CE42941CDCCCC4C3DD43B4101030000000100000005000000CDCCCCCCB4D42941CDCCCC4C79D83B4100000000B3D4294167666666DDD83B41333333337BD52941CDCCCC4CDED83B41000000007DD52941333333337AD83B41CDCCCCCCB4D42941CDCCCC4C79D83B41010300000001000000050000000000000021D32941CDCCCCCC3FD93B41333333331FD32941676666E6A3D93B4167666666E7D3294
 1CDCCCCCCA4D93B4133333333E9D32941333333B340D93B410000000021D32941CDCCCCCC3FD93B41010300000001000000050000003333333350E129419A9999990DD33B419A9999994EE12941333333B371D33B41CDCCCCCC16E229410000008072D33B416766666618E22941676666660ED33B413333333350E129419A9999990DD33B41010300000001000000050000000000000052E1294167666666A9D23B413333333350E129419A9999990DD33B419A999999E0E22941CDCCCC4C0FD33B4167666666E2E2294133333333ABD23B410000000052E1294167666666A9D23B4101030000000100000005000000CDCCCCCC01D02941CDCCCC4CD8D83B410000000000D02941676666663CD93B4133333333C8D02941333333333DD93B4100000000CAD029419A999919D9D83B41CDCCCCCC01D02941CDCCCC4CD8D83B410103000000050000002F0000008E1DD7F0C9D62941ECBDC3987BD83B41CDCCCC4CCCD62941F6285C8F6FD83B419A999999D3D62941295C8F8223D83B41323AE4E5D4D62941A57CA18017D83B410000000047D62941676666E616D83B41676666664AD62941333333B34ED73B413333333382D52941676666E64DD73B41CDCCCCCC83D52941CDCCCCCCE9D63B419A999999BBD42941676666E6E8D63B4167666666BDD42941CDCCCCCC84D63B41000000002DD3
 29419A99991983D63B419A99999929D32941CDCCCC4C4BD73B413333333361D22941676666664AD73B410000000063D22941CDCCCC4CE6D63B41CDCCCCCC9AD1294100000080E5D63B419A9999999CD129416766666681D63B4167666666D4D029410000008080D63B419A999999D2D029419A999999E4D63B41676666660AD02941333333B3E3D63B41CDCCCCCC0DD02941000000801BD63B419A99999945CF2941333333B31AD63B41CDCCCCCC43CF2941CDCCCCCC7ED63B419A9999997BCE2941676666E67DD63B41676666667DCE2941CDCCCCCC19D63B4133333333B5CD29410000000019D63B4100000000B0CD2941CDCCCC4C45D73B41CDCCCCCCE7CC29416766666644D73B4100000000E6CC294100000080A8D73B419A99999955CB2941CDCCCCCCA6D73B410000000054CB2941676666E60AD83B4167666666E4CC29419A9999990CD83B4100000000E1CC2941CDCCCCCCD4D83B419A99999939CF294167666666D7D83B41CDCCCCCC37CF2941000000803BD93B410000000000D02941676666663CD93B41CDCCCCCC01D02941CDCCCC4CD8D83B41676666665AD22941676666E6DAD83B41333333335CD22941333333B376D83B419A999999ECD329410000008078D83B4133333333EED329416766666614D83B419A999999B6D429413333333315D83B41CDCCCCCCB4D42941C
 DCCCC4C79D83B41000000007DD52941333333337AD83B41333333337BD52941CDCCCC4CDED83B419A99999943D629419A999919DFD83B413333333345D62941000000007BD83B418E1DD7F0C9D62941ECBDC3987BD83B41050000009A999999BBD42941676666E6E8D63B4100000000BAD42941000000004DD73B41CDCCCCCCF1D329419A9999194CD73B4167666666F3D3294100000000E8D63B419A999999BBD42941676666E6E8D63B41050000003333333361D22941676666664AD73B419A9999995FD2294100000080AED73B4133333333CFD02941CDCCCCCCACD73B41CDCCCCCCD0D02941333333B348D73B413333333361D22941676666664AD73B410500000000000000B0CD2941CDCCCC4C45D73B413333333378CE29419A99991946D73B419A99999976CE294133333333AAD73B4133333333AECD294167666666A9D73B4100000000B0CD2941CDCCCC4C45D73B410500000033333333EED329416766666614D83B410000000026D329410000008013D83B41CDCCCCCC27D3294167666666AFD73B4100000000F0D3294133333333B0D73B4133333333EED329416766666614D83B4101030000000100000006000000D17610E919D72941FA5D36B7A2D53B41676666E674D729413333337399D53B41F6285C8FE1D7294115AE47E17AD53B41F801297BE1D729419D0E31E45BD5
 3B41333333331BD72941000000005BD53B41D17610E919D72941FA5D36B7A2D53B4101030000000100000004000000FF534E9AE5D72941C1294A32DAD43B4152B81E85E6D729415C8FC2B5CDD43B416BAA39D6E5D7294196DF196ACCD43B41FF534E9AE5D72941C1294A32DAD43B41010300000001000000060000006164E4EB37D7294102492AC24FD73B41A4703D8A50D7294185EB51B80AD73B410FBCCEBE6CD729413D6B31E0EBD63B413333333314D7294100000080EBD63B419A99999912D729419A9999994FD73B416164E4EB37D7294102492AC24FD73B4101030000000100000005000000CDCCCCCC33D329419A999999F2D43B416766666630D32941676666E6BAD53B419A999999F8D32941333333B3BBD53B4100000000FCD3294100000080F3D43B41CDCCCCCC33D329419A999999F2D43B410103000000010000000B000000BD1A22D40DDB294151098C9831D33B413E0AD7A32ADB2941F6285CCF2BD33B4185EB51B84EDB29415C8FC2F528D33B41333333B378DB2941676666262DD33B4148E17A148DDB29415C8FC23518D33B411F85EB519DDB294152B81E450CD33B417B14AE47C9DB29415C8FC27510D33B413384FEA1D6DB2941D71EDBCE10D33B41CDCCCCCCD6DB29410000008007D33B419A9999990EDB2941333333B306D33B41BD1A22D40DDB294151098C983
 1D33B41010300000001000000050000000B135E6F50D62941C2789228EED53B4190C2F5A89BD62941713D0A97D3D53B4183658A05B8D629411CF3FBB5BED53B413333333351D62941CDCCCC4CBED53B410B135E6F50D62941C2789228EED53B41010300000001000000080000006D158A1387D52941A97AF87F33D63B41295C8FC27DD5294185EB51782FD63B412C86473887D52941CA7CD1842BD63B416766666687D529410000008021D63B4100000000BFD42941333333B320D63B4167666666BDD42941CDCCCCCC84D63B419A99999985D529419A99999985D63B416D158A1387D52941A97AF87F33D63B4101030000000100000005000000333333338ED529419A99991991D43B41676666668CD5294133333333F5D43B419A99999954D629419A999919F6D43B416766666656D629410000000092D43B41333333338ED529419A99991991D43B41010300000001000000050000009A99999922D32941333333B3DBD83B410000000021D32941CDCCCCCC3FD93B4133333333E9D32941333333B340D93B41CDCCCCCCEAD329419A999999DCD83B419A99999922D32941333333B3DBD83B4101030000000100000007000000333333335CD22941333333B376D83B41676666665AD22941676666E6DAD83B413333333392D1294100000000DAD83B419A99999990D129419A9999193ED9
 3B410000000021D32941CDCCCCCC3FD93B416766666624D329419A99999977D83B41333333335CD22941333333B376D83B410103000000010000000500000000000000B0CD2941CDCCCC4C45D73B4133333333AECD294167666666A9D73B419A99999976CE294133333333AAD73B413333333378CE29419A99991946D73B4100000000B0CD2941CDCCCC4C45D73B410103000000010000000500000000000000B3D4294167666666DDD83B4167666666B1D429410000008041D93B419A99999979D529416766666642D93B41333333337BD52941CDCCCC4CDED83B4100000000B3D4294167666666DDD83B41010300000001000000050000000000000057D229419A999919A3D93B413333333355D229413333333307DA3B41676666661DD329410000000008DA3B41333333331FD32941676666E6A3D93B410000000057D229419A999919A3D93B41010300000001000000040000002826D17824D729415DDDE2B72ED43B410155CA7220D72941B960A0D92BD43B416766666620D72941333333B32ED43B412826D17824D729415DDDE2B72ED43B410103000000010000000500000022D6369A5CD62941827E2CE326D33B41EC51B89E8FD62941D7A370BD16D33B41CDEAE2DDB1D62941604B70E101D33B41333333335DD629410000008001D33B4122D6369A5CD62941827E2CE326D33B4
 1010300000001000000070000009094A37DEED7294142734CC3D1D23B41EC51B89E19D829411F85EB51BDD23B411F85EBD147D82941295C8F82B4D23B410AD7A3F079D8294185EB51B8B0D23B41A7FBB158F4D8294108CC4349A0D23B4167666666EFD72941333333339FD23B419094A37DEED7294142734CC3D1D23B4101030000000100000007000000E51FB9333AD9294178F8B393A0D23B410AD7A37068D92941AE47E1BAA3D23B41A4703D0AE0D929417B14AE47C3D23B417B14AEC72FDA29413E0AD7A3D7D23B412DFB58D446DA2941737C709FE6D23B410000000048DA2941333333B3A1D23B41E51FB9333AD9294178F8B393A0D23B410103000000010000001D0000002BACC992D6DB294100A875CE10D33B411F85EB51F7DB294190C2F5A811D33B411F85EB5196DC29417B14AE47E2D23B417B14AE47B6DC2941E17A146EE6D23B410AD7A3F0CFDC2941F6285C8FFCD23B4190C2F528B3DC2941D7A3707D28D33B4152B81E8596DC294190C2F5684BD33B413333333384DC29419A9999595CD33B41C3F528DC95DC29413333337372D33B417A7D08189DDC294130AEA5BF72D33B41333333339DDC2941000000806CD33B416766666686E02941CDCCCCCC70D33B41CDCCCCCC89E029419A999999A8D23B4167666666F9DE2941676666E6A6D23B41CDCCCCCCFCDE2941333333
 B3DED13B419A99999934DE2941CDCCCCCCDDD13B41CDCCCCCC32DE2941676666E641D23B419A9999996ADD29419A99991941D23B41000000006EDD2941CDCCCCCC78D13B41CDCCCCCCA5DC29410000000078D13B419A999999A7DC2941676666E613D13B413333333317DB29413333333312D13B416766666615DB2941CDCCCC4C76D13B419A999999DDDB29419A99991977D13B41CDCCCCCCDBDB294133333333DBD13B4133333333A4DC29419A999919DCD13B4167666666A2DC29413333333340D23B4133333333DADB2941CDCCCC4C3FD23B412BACC992D6DB294100A875CE10D33B4101030000000100000005000000A9DE638EFADF294146332F59D4D33B410000000007E029419A999959E0D33B41F6285C0F1BE02941E17A146EDDD33B41F26064F12DE029410E77C08DD4D33B41A9DE638EFADF294146332F59D4D33B410103000000010000000B00000033333333A7E329413333333374D33B4167666666A5E32941CDCCCC4CD8D33B410000000015E229419A999999D6D33B419A99999911E22941CDCCCCCC9ED43B41CDCCCCCCD9E22941333333B39FD43B419A999999DBE229419A9999993BD43B419A999999C4E62941676666E63FD43B4167666666C6E62941CDCCCCCCDBD33B419A9999996DE4294133333333D9D33B41676666666FE429419A99991975D33B413333333
 3A7E329413333333374D33B410103000000010000000900000000000000FCD3294100000080F3D43B41CDCCCCCCF6D32941CDCCCCCC1FD63B419A9999992ED32941000000001FD63B41000000002DD329419A99991983D63B4167666666BDD42941CDCCCCCC84D63B419A999999C2D429410000008058D53B41CDCCCCCC8AD52941CDCCCC4C59D53B41676666668CD5294133333333F5D43B4100000000FCD3294100000080F3D43B410103000000010000000B0000007216297BE1D72941227750E45BD53B41CDCCCC4CE1D7294152B81EC514D53B41FF534E9AE5D72941C1294A32DAD43B416BAA39D6E5D7294196DF196ACCD43B41F6285C0FD5D729419A999999ACD43B410F05B8CBB1D72941053C337693D43B419A9999991ED72941CDCCCCCC92D43B41CDCCCCCC1CD72941676666E6F6D43B419A99999954D629419A999919F6D43B410000000053D62941333333335AD53B417216297BE1D72941227750E45BD53B4101030000000100000007000000F1B2D2AC38D529413B383D6A64D33B419A99991984D52941E17A14AE5DD33B41DD74569393D529411C4462D159D33B410000000095D52941333333B300D33B419A99999904D4294100000000FFD23B41CDCCCCCC02D429419A99991963D33B41F1B2D2AC38D529413B383D6A64D33B41010300000001000000050000009A99
 99998EE729419A999999DCD33B41CDCCCCCC8CE72941333333B340D43B41333333331DE929410000008042D43B41000000001FE9294167666666DED33B419A9999998EE729419A999999DCD33B410103000000010000000500000067666666BDD42941CDCCCCCC84D63B419A999999BBD42941676666E6E8D63B41CDCCCCCC83D52941CDCCCCCCE9D63B419A99999985D529419A99999985D63B4167666666BDD42941CDCCCCCC84D63B41010300000001000000050000006766666639E52941CDCCCCCC11D33B419A99999937E529410000000076D33B41CDCCCCCCFFE52941CDCCCCCC76D33B419A99999901E62941333333B312D33B416766666639E52941CDCCCCCC11D33B4101030000000100000005000000CDCCCCCC8AD52941CDCCCC4C59D53B410000000089D5294167666666BDD53B413333333351D62941CDCCCC4CBED53B410000000053D62941333333335AD53B41CDCCCCCC8AD52941CDCCCC4C59D53B41010300000001000000050000006766666632CC294167666666F6D23B41CDCCCCCC30CC2941000000805AD33B4100000000F9CC2941676666665BD33B419A999999FACC294133333333F7D23B416766666632CC294167666666F6D23B4101030000000100000005000000E0F49F4E11D72941EE5E32BA9BD73B41AE47E17A26D72941B81E85AB80D73B4142E9BDE
 C37D72941624AC8BF4FD73B419A99999912D729419A9999994FD73B41E0F49F4E11D72941EE5E32BA9BD73B410103000000010000000700000015D475A14DD629412035782094D63B41AE47E17A18D62941AE47E13A84D63B4100000080A5D5294190C2F5A840D63B41A7A29C1687D5294113B64C8133D63B413333333382D52941676666E64DD73B41676666664AD62941333333B34ED73B4115D475A14DD629412035782094D63B41010300000001000000040000001AD0033A87D52941D4EA17842BD63B41C05006F49ED529413D301B9B21D63B416766666687D529410000008021D63B411AD0033A87D52941D4EA17842BD63B410103000000010000000500000000000000CAD029419A999919D9D83B419A999999C6D0294167666666A1D93B41CDCCCCCC8ED1294133333333A2D93B413333333392D1294100000000DAD83B4100000000CAD029419A999919D9D83B4101030000000100000005000000CDCCCCCCD0D02941333333B348D73B4133333333CFD02941CDCCCCCCACD73B419A9999995FD2294100000080AED73B413333333361D22941676666664AD73B41CDCCCCCCD0D02941333333B348D73B4101030000000100000005000000CDCCCCCC27D3294167666666AFD73B410000000026D329410000008013D83B4133333333EED329416766666614D83B4100000000F0
 D3294133333333B0D73B41CDCCCCCC27D3294167666666AFD73B410103000000010000000500000000000000CCC92941333333B314D63B4167666666CAC92941CDCCCCCC78D63B419A99999992CA29419A99999979D63B416766666694CA29410000008015D63B4100000000CCC92941333333B314D63B410103000000010000000500000000000000EDCC29419A99991918D63B4133333333EBCC2941333333337CD63B4167666666B3CD29419A9999197DD63B4133333333B5CD29410000000019D63B4100000000EDCC29419A99991918D63B41010300000001000000050000006766666624D329419A99999977D83B419A99999922D32941333333B3DBD83B41CDCCCCCCEAD329419A999999DCD83B419A999999ECD329410000008078D83B416766666624D329419A99999977D83B41010300000001000000050000009A999999C2D429410000008058D53B41CDCCCCCCC0D429419A999999BCD53B410000000089D5294167666666BDD53B41CDCCCCCC8AD52941CDCCCC4C59D53B419A999999C2D429410000008058D53B4101030000000100000005000000CDCCCCCC58D22941000000003FD93B410000000057D229419A999919A3D93B41333333331FD32941676666E6A3D93B410000000021D32941CDCCCCCC3FD93B41CDCCCCCC58D22941000000003FD93B410103000000010
 000000700000083658A05B8D629411CF3FBB5BED53B410AD7A370D4D62941A4703DCAA9D53B41D17610E919D72941FA5D36B7A2D53B41333333331BD72941000000005BD53B410000000053D62941333333335AD53B413333333351D62941CDCCCC4CBED53B4183658A05B8D629411CF3FBB5BED53B410103000000010000000500000067666666E7D32941CDCCCCCCA4D93B419A999999AFD429419A999999A5D93B4167666666B1D429410000008041D93B4133333333E9D32941333333B340D93B4167666666E7D32941CDCCCCCCA4D93B410103000000010000000500000067666666F3D3294100000000E8D63B41CDCCCCCCF1D329419A9999194CD73B4100000000BAD42941000000004DD73B419A999999BBD42941676666E6E8D63B4167666666F3D3294100000000E8D63B4101030000000100000005000000CDCCCCCC8AD52941CDCCCC4C59D53B410000000053D62941333333335AD53B419A99999954D629419A999919F6D43B41676666668CD5294133333333F5D43B41CDCCCCCC8AD52941CDCCCC4C59D53B4101030000000100000005000000CDCCCCCC8CE72941333333B340D43B41000000008BE72941CDCCCCCCA4D43B413333333353E82941333333B3A5D43B410000000055E829419A99999941D43B41CDCCCCCC8CE72941333333B340D43B410103000000010000
 00050000009A999999ECD329410000008078D83B41CDCCCCCCEAD329419A999999DCD83B4100000000B3D4294167666666DDD83B41CDCCCCCCB4D42941CDCCCC4C79D83B419A999999ECD329410000008078D83B41010300000001000000050000003333333303E629419A999999AED23B419A99999901E62941333333B312D33B41CDCCCCCCC9E629419A99999913D33B4167666666CBE6294100000080AFD23B413333333303E629419A999999AED23B41010300000001000000050000009A999999C4E62941676666E63FD43B41CDCCCCCCC2E6294100000000A4D43B41000000008BE72941CDCCCCCCA4D43B41CDCCCCCC8CE72941333333B340D43B419A999999C4E62941676666E63FD43B410103000000010000000500000000000000ACEA2941CDCCCC4CA8D43B413333333374EB29419A999919A9D43B41CDCCCCCC75EB29410000000045D43B419A999999ADEA29413333333344D43B4100000000ACEA2941CDCCCC4CA8D43B4101030000000100000005000000CDCCCCCC48FE2941CDCCCCCC38D13B410000000047FE2941676666E69CD13B41333333330FFF2941333333B39DD13B410000000011FF29419A99999939D13B41CDCCCCCC48FE2941CDCCCCCC38D13B41010300000001000000050000009A99999969012A41333333333CD13B41CDCCCCCC67012A41CDCCCC4CA
 0D13B410000000030022A4133333333A1D13B41CDCCCCCC31022A419A9999193DD13B419A99999969012A41333333333CD13B410103000000010000000500000033333333D9FF2941000000803AD13B4167666666D7FF29419A9999999ED13B419A9999999F002A41000000809FD13B4167666666A1002A41676666663BD13B4133333333D9FF2941000000803AD13B4101030000000100000005000000CDCCCCCC31022A419A9999193DD13B4100000000FA022A41676666E63DD13B41CDCCCCCCFB022A41CDCCCCCCD9D03B419A99999933022A4100000000D9D03B41CDCCCCCC31022A419A9999193DD13B41010300000001000000050000000000000030022A4133333333A1D13B4167666666F8022A4100000000A2D13B4100000000FA022A41676666E63DD13B41CDCCCCCC31022A419A9999193DD13B410000000030022A4133333333A1D13B410103000000010000000500000000000000DEE32941CDCCCCCCF0C63B4167666666DCE32941676666E654C73B41CDCCCCCC6CE529419A99999956C73B419A9999996EE5294100000080F2C63B4100000000DEE32941CDCCCCCCF0C63B410103000000010000000C0000005D3D57AA43C8294176AAA7D83FD43B41B81E856B64C82941A4703D8A3AD43B41295C8F42F6C829410AD7A3304ED43B41F6285C0FF6C829413333333359D4
 3B41DE0906760BC92941FE69590B5AD43B41676666660CC92941CDCCCC4C1FD43B41CDCCCCCC9CCA29410000000021D43B4100000000A2CA2941333333B3F4D23B413333333381C7294133333333F1D23B41000000007CC729419A9999991DD43B413333333344C82941676666661ED43B415D3D57AA43C8294176AAA7D83FD43B4101030000000100000034000000579702670BC9294161C7C10A5AD43B41EC51B81E68C92941333333B35DD43B415C8FC2F589C92941C3F528DC69D43B411F85EBD1B1C92941A4703D0A74D43B41C3F528DC1AC929410AD7A370A2D43B416B98348509C9294102B58CDCC8D43B410000000009C9294100000080E7D43B4133333333D1C92941CDCCCC4CE8D43B4100000000CCC92941333333B314D63B416766666694CA29410000008015D63B419A99999992CA29419A99999979D63B4167666666CAC92941CDCCCCCC78D63B419A999999C8C92941676666E6DCD63B413333333338C8294133333333DBD63B4140977E8D30C8294169891B4198D83B4190C2F5A8B2C8294133333333C3D83B41802C2FFBB8C8294111164E51D0D83B41CDCCCCCCF7C829419A999999D0D83B4193EA1022F7C8294171A2D0DEF7D83B41F6285C0FE0C92941F6285C8FFAD83B41D7A370BDF7C92941E17A14AE0FD93B41295C8FC221CA2941C3F528DC10D93B41F6285C8
 F34CA29413E0AD7E3E3D83B41F6285C8F50CA2941295C8F02E5D83B4185EB51385ACA294148E17A14FAD83B415C8FC27546CB2941713D0A17FCD83B41CDCCCCCC4BCB294190C2F52823D93B41F61B1C4351CB29416C97083637D93B4133333333DFCC29410000000039D93B4100000000E1CC2941CDCCCCCCD4D83B419A99999950CB29419A999919D3D83B413333333352CB2941000000006FD83B419A999999E2CC2941333333B370D83B4167666666E4CC29419A9999990CD83B410000000054CB2941676666E60AD83B419A99999955CB2941CDCCCCCCA6D73B41CDCCCCCC1DCC2941333333B3A7D73B419A9999991FCC29419A99999943D73B41CDCCCCCCE7CC29416766666644D73B4133333333EBCC2941333333337CD63B410000000023CC2941CDCCCC4C7BD63B41CDCCCCCC24CC29413333333317D63B419A9999995CCB29416766666616D63B41333333335ECB2941CDCCCC4CB2D53B416766666626CC29419A999919B3D53B413333333328CC2941000000004FD53B410000000060CB2941333333334ED53B416766666663CB29410000000086D43B41333333339BCA29419A99991985D43B41CDCCCCCC9CCA29410000000021D43B41676666660CC92941CDCCCC4C1FD43B41579702670BC9294161C7C10A5AD43B41010300000001000000050000009A9999999ECA294167
 6666E6BCD33B41CDCCCCCC9CCA29410000000021D43B413333333365CB2941CDCCCCCC21D43B41CDCCCCCC66CB2941333333B3BDD33B419A9999999ECA2941676666E6BCD33B410103000000010000000500000067666666E2C92941CDCCCC4CFFD03B419A999999E0C929416766666663D13B4100000000A9CA29413333333364D13B419A999999AACA29419A99991900D13B4167666666E2C92941CDCCCC4CFFD03B41010300000001000000050000009A9999996DCB2941CDCCCC4C2DD23B41333333336ACB294100000080F5D23B416766666632CC294167666666F6D23B41CDCCCCCC35CC2941333333332ED23B419A9999996DCB2941CDCCCC4C2DD23B41010300000001000000050000006766666686C72941676666E6C4D13B419A9999994EC82941CDCCCCCCC5D13B410000000052C829419A999999FDD03B41CDCCCCCC89C72941333333B3FCD03B416766666686C72941676666E6C4D13B410103000000010000000500000049C15D8D09C92941228377CAC8D43B41CDCCCCCCFFC8294167666666DED43B4124A2ED64FCC8294192207E71E7D43B410000000009C9294100000080E7D43B4149C15D8D09C92941228377CAC8D43B41010300000001000000050000009A9999993BC829410000000013D63B41CDCCCCCC03C92941CDCCCCCC13D63B419A99999905C9294133333
 3B3AFD53B41676666663DC82941CDCCCCCCAED53B419A9999993BC829410000000013D63B410103000000010000000A00000024A2ED64FCC8294192207E71E7D43B41EC51B81EF1C8294115AE476105D53B41F6285C8FE4C82941C3F5285C25D53B41D7A3703DC8C82941295C8F4235D53B41AE47E1FA3EC82941D7A370BD75D53B41B9BE295C3EC829410240B14576D53B41676666663DC82941CDCCCCCCAED53B419A99999905C92941333333B3AFD53B410000000009C9294100000080E7D43B4124A2ED64FCC8294192207E71E7D43B41010300000001000000050000009A9999993BC829410000000013D63B41000000003AC829419A99991977D63B413333333302C92941676666E677D63B41CDCCCCCC03C92941CDCCCCCC13D63B419A9999993BC829410000000013D63B4101030000000100000005000000CDCCCCCC03C92941CDCCCCCC13D63B416766666600C9294100000000DCD63B419A999999C8C92941676666E6DCD63B4100000000CCC92941333333B314D63B41CDCCCCCC03C92941CDCCCCCC13D63B41010300000001000000050000006766666686C72941676666E6C4D13B419A99999984C729410000000029D23B41CDCCCCCC4CC82941676666E629D23B419A9999994EC82941CDCCCCCCC5D13B416766666686C72941676666E6C4D13B41010300000001000000
 0D000000000000002FCC29419A999999BED33B419A99999987CE294133333333C1D33B410000000086CE2941CDCCCC4C25D43B41333333334ECF29419A99991926D43B416766666653CF2941CDCCCCCCF9D23B41CDCCCCCCC2CD29419A999919F8D23B4167666666C6CD2941676666E62FD23B41CDCCCCCC35CC2941333333332ED23B416766666632CC294167666666F6D23B419A999999FACC294133333333F7D23B4100000000F9CC2941676666665BD33B41CDCCCCCC30CC2941000000805AD33B41000000002FCC29419A999999BED33B4101030000000100000005000000CDCCCCCCF3CC2941333333B387D43B4100000000F2CC2941CDCCCCCCEBD43B4167666666BACD29419A999999ECD43B4100000000BCCD29410000008088D43B41CDCCCCCCF3CC2941333333B387D43B410103000000010000000500000000000000BCCD29410000008088D43B413333333384CE29416766666689D43B410000000086CE2941CDCCCC4C25D43B41CDCCCCCCBDCD29416766666624D43B4100000000BCCD29410000008088D43B41010300000001000000050000006766666647CF29419A999999B6D53B419A99999945CF2941333333B31AD63B41CDCCCCCC0DD02941000000801BD63B419A9999990FD0294167666666B7D53B416766666647CF29419A999999B6D53B41010300000001000
 0000700000033333333A9CD2941333333B3D5D83B41CDCCCCCCA5CD2941676666E69DD93B413333333336CF29419A9999999FD93B41CDCCCCCC37CF2941000000803BD93B419A9999996FCE2941333333B33AD93B416766666671CE29419A999999D6D83B4133333333A9CD2941333333B3D5D83B41010300000001000000050000003847E2B3EFCE2941A235298867DA3B417B14AE47FBCE29411F85EB916BDA3B415E73F39332CF29412B3F652974DA3B41CDCCCCCC32CF2941CDCCCCCC67DA3B413847E2B3EFCE2941A235298867DA3B410103000000010000000500000000000000EDCC29419A99991918D63B4133333333B5CD29410000000019D63B41CDCCCCCCB6CD2941CDCCCCCCB4D53B419A999999EECC294100000000B4D53B4100000000EDCC29419A99991918D63B4101030000000100000005000000CDCCCCCC24CC29413333333317D63B410000000023CC2941CDCCCC4C7BD63B4133333333EBCC2941333333337CD63B4100000000EDCC29419A99991918D63B41CDCCCCCC24CC29413333333317D63B41010300000001000000050000006766666626CC29419A999919B3D53B419A999999EECC294100000000B4D53B4167666666F0CC2941676666E64FD53B413333333328CC2941000000004FD53B416766666626CC29419A999919B3D53B41010300000001000000
 050000009A99999990D129419A9999193ED93B41CDCCCCCC8ED1294133333333A2D93B410000000057D229419A999919A3D93B41CDCCCCCC58D22941000000003FD93B419A99999990D129419A9999193ED93B4101030000000100000005000000CDCCCCCC32CF2941CDCCCCCC67DA3B4100000000FBCF2941333333B368DA3B419A999999FCCF29419A99999904DA3B416766666634CF2941333333B303DA3B41CDCCCCCC32CF2941CDCCCCCC67DA3B41010300000001000000050000009A9999999CD129416766666681D63B41CDCCCCCC9AD1294100000080E5D63B410000000063D22941CDCCCC4CE6D63B41CDCCCCCC64D229413333333382D63B419A9999999CD129416766666681D63B4101030000000100000005000000CDCCCCCC37CF2941000000803BD93B416766666634CF2941333333B303DA3B419A999999FCCF29419A99999904DA3B410000000000D02941676666663CD93B41CDCCCCCC37CF2941000000803BD93B4101030000000100000005000000CDCCCCCC72CB29410000000001D13B413333333371CB29419A99991965D13B41CDCCCCCCC9CD2941333333B367D13B4167666666CBCD29419A99999903D13B41CDCCCCCC72CB29410000000001D13B41010300000001000000050000009A99999990D129419A9999193ED93B41CDCCCCCC8ED1294133333333A2D
 93B410000000057D229419A999919A3D93B41CDCCCCCC58D22941000000003FD93B419A99999990D129419A9999193ED93B41010300000001000000050000006766666671CE29419A999999D6D83B419A9999996FCE2941333333B33AD93B41CDCCCCCC37CF2941000000803BD93B419A99999939CF294167666666D7D83B416766666671CE29419A999999D6D83B41010300000001000000050000000000000000D02941676666663CD93B4167666666FECF294100000080A0D93B419A999999C6D0294167666666A1D93B4133333333C8D02941333333333DD93B410000000000D02941676666663CD93B4101030000000100000013000000F61B1C4351CB29416C97083637D93B413E0AD7A35ECB29411F85EB5168D93B41B81E85EB7CCB2941E17A146E59D93B4190C2F528BFCB2941333333B34FD93B41F6285C0FE1CB2941C3F528DC58D93B41CDCCCCCC02CC294152B81E056AD93B41713D0AD780CC29411F85EB9170D93B41AE47E17A8ACC294152B81EC5FAD93B4148E17A94CACC2941A4703D0AF9D93B411F85EBD10ACE2941B81E856B01DA3B41AE47E1FA69CE29413E0AD7E338DA3B4198EE89A7EFCE294109E5DA8367DA3B41CDCCCCCC32CF2941CDCCCCCC67DA3B413333333336CF29419A9999999FD93B41CDCCCCCCA5CD2941676666E69DD93B4133333333A9CD294133
 3333B3D5D83B4100000000E1CC2941CDCCCCCCD4D83B4133333333DFCC29410000000039D93B41F61B1C4351CB29416C97083637D93B4101030000000100000005000000CDCCCCCCE7CC29416766666644D73B4100000000B0CD2941CDCCCC4C45D73B4167666666B3CD29419A9999197DD63B4133333333EBCC2941333333337CD63B41CDCCCCCCE7CC29416766666644D73B410103000000010000000500000067666666FECF294100000080A0D93B419A999999FCCF29419A99999904DA3B41CDCCCCCCC4D029410000008005DA3B419A999999C6D0294167666666A1D93B4167666666FECF294100000080A0D93B41010300000001000000050000009A99999968CB29419A99999959D33B41CDCCCCCC66CB2941333333B3BDD33B41000000002FCC29419A999999BED33B41CDCCCCCC30CC2941000000805AD33B419A99999968CB29419A99999959D33B4101030000000100000005000000CDCCCCCC66CB2941333333B3BDD33B413333333365CB2941CDCCCCCC21D43B41676666662DCC2941333333B322D43B41000000002FCC29419A999999BED33B41CDCCCCCC66CB2941333333B3BDD33B4101030000000100000005000000333333335ECB2941CDCCCC4CB2D53B419A9999995CCB29416766666616D63B41CDCCCCCC24CC29413333333317D63B416766666626CC29419A999
 919B3D53B41333333335ECB2941CDCCCC4CB2D53B4101030000000100000005000000676666661ACC2941676666E66FD83B41CDCCCCCC18CC294100000000D4D83B4100000000E1CC2941CDCCCCCCD4D83B419A999999E2CC2941333333B370D83B41676666661ACC2941676666E66FD83B41010300000001000000050000003333333352CB2941000000006FD83B419A99999950CB29419A999919D3D83B41CDCCCCCC18CC294100000000D4D83B41676666661ACC2941676666E66FD83B413333333352CB2941000000006FD83B41010300000001000000050000009A9999991FCC29419A99999943D73B41CDCCCCCC1DCC2941333333B3A7D73B4100000000E6CC294100000080A8D73B41CDCCCCCCE7CC29416766666644D73B419A9999991FCC29419A99999943D73B41010300000001000000050000006766666626CC29419A999919B3D53B41CDCCCCCC24CC29413333333317D63B4100000000EDCC29419A99991918D63B419A999999EECC294100000000B4D53B416766666626CC29419A999919B3D53B41010300000001000000050000000000000045D329419A99999909D13B41CDCCCCCC3FD32941676666E635D23B410000000008D42941CDCCCCCC36D23B41333333330DD42941676666660AD13B410000000045D329419A99999909D13B41010300000001000000050000
 00CDCCCCCC25E92941676666E64DD23B410000000024E9294100000000B2D23B4167666666ECE92941676666E6B2D23B4100000000EEE92941CDCCCCCC4ED23B41CDCCCCCC25E92941676666E64DD23B41010300000001000000090000000000000061E82941676666E684D13B419A9999995DE829419A9999194DD23B41CDCCCCCC25E92941676666E64DD23B410000000024E9294100000000B2D23B4167666666ECE92941676666E6B2D23B41CDCCCCCCEFE92941333333B3EAD13B419A99999927E92941CDCCCCCCE9D13B413333333329E92941333333B385D13B410000000061E82941676666E684D13B410103000000010000000500000033333333F4F729419A9999197FD53B4167666666F2F7294133333333E3D53B419A999999BAF829419A999919E4D53B4167666666BCF82941676666E67FD53B4133333333F4F729419A9999197FD53B41010300000001000000050000009A99999937FE2941676666E621D53B41CDCCCCCC35FE29410000000086D53B4100000000FEFE2941CDCCCCCC86D53B41CDCCCCCCFFFE2941333333B322D53B419A99999937FE2941676666E621D53B4101030000000100000005000000CDCCCCCCB3F829410000008074D73B4100000000B2F829419A999999D8D73B41333333337AF9294167666666D9D73B41000000007CF92941CDCCCC4C75D
 73B41CDCCCCCCB3F829410000008074D73B410103000000010000000500000033333333B7F82941CDCCCC4CACD63B4167666666B5F829416766666610D73B419A9999997DF929413333333311D73B41676666667FF929419A999919ADD63B4133333333B7F82941CDCCCC4CACD63B41010300000001000000050000009A9999990EF429419A999999B2D43B41CDCCCCCC0CF42941333333B316D53B4100000000D5F429410000008017D53B41CDCCCCCCD6F4294167666666B3D43B419A9999990EF429419A999999B2D43B41010300000001000000060000003FEEBC828BF52941D106B41C20D93B415C8FC2751EF629419A99991925D93B419142BA4F53F629415C1B0BE439D93B413333333354F62941CDCCCC4C02D93B41000000008CF529410000008001D93B413FEEBC828BF52941D106B41C20D93B41010300000001000000050000006766666645FE29410000000001D23B41CDCCCCCC41FE294133333333C9D23B41000000000AFF29419A999919CAD23B419A9999990DFF2941676666E601D23B416766666645FE29410000000001D23B41010300000001000000090000006766666626FE2941676666E60AD93B419A99999924FE2941000000006FD93B41676666665CFD2941333333336ED93B410000000059FD29416766666636DA3B413333333321FE29413333333337DA3B
 410000000023FE29419A999919D3D93B4133333333EBFE294100000000D4D93B419A999999EEFE2941CDCCCCCC0BD93B416766666626FE2941676666E60AD93B41010300000001000000070000009A999999AAFC2941676666E657D43B4167666666A5FC29413333333384D53B419A9999996DFD29419A99991985D53B410000000071FD2941676666E6BCD43B413333333339FE2941333333B3BDD43B41000000003BFE29419A99999959D43B419A999999AAFC2941676666E657D43B41010300000001000000050000009A9999999EFC2941333333B314D73B416766666699FC29410000000041D83B419A99999961FD2941CDCCCCCC41D83B41CDCCCCCC66FD29410000008015D73B419A9999999EFC2941333333B314D73B410103000000010000000500000033333333C1FF294100000000B4D63B4167666666BFFF29419A99991918D73B41CDCCCCCC4F012A41CDCCCCCC19D73B419A99999951012A41333333B3B5D63B4133333333C1FF294100000000B4D63B4101030000000100000005000000CDCCCCCCCAF429413333333370D73B4100000000C9F42941CDCCCC4CD4D73B413333333391F5294133333333D5D73B410000000093F529410000000071D73B41CDCCCCCCCAF429413333333370D73B4101030000000100000005000000676666660CFB29419A99991977D73B419
 A9999990AFB294133333333DBD73B4100000000D3FB294100000000DCD73B419A999999D4FB2941676666E677D73B41676666660CFB29419A99991977D73B410103000000010000000500000000000000EFF7294167666666ABD63B4133333333EDF72941000000800FD73B4167666666B5F829416766666610D73B4133333333B7F82941CDCCCC4CACD63B4100000000EFF7294167666666ABD63B41010300000001000000050000000000000088F929419A999999B8D43B413333333386F92941333333B31CD53B41676666664EFA29419A9999991DD53B413333333350FA294100000080B9D43B410000000088F929419A999999B8D43B4101030000000100000005000000CDCCCCCC29FE2941333333B342D83B413333333328FE2941CDCCCCCCA6D83B4167666666F0FE2941333333B3A7D83B4100000000F2FE29419A99999943D83B41CDCCCCCC29FE2941333333B342D83B4101030000000100000005000000CDCCCCCC66FD29410000008015D73B41000000002FFE29416766666616D73B419A99999930FE2941CDCCCC4CB2D63B416766666668FD294167666666B1D63B41CDCCCCCC66FD29410000008015D73B41010300000001000000050000003333333340FE2941CDCCCC4C2DD33B416766666608FF2941333333332ED33B41000000000AFF29419A999919CAD23B41CDCC
 CCCC41FE294133333333C9D23B413333333340FE2941CDCCCC4C2DD33B410103000000010000000500000067666666CFFB294133333333A4D83B41CDCCCCCCCDFB2941CDCCCC4C08D93B410000000096FC29413333333309D93B419A99999997FC29419A999919A5D83B4167666666CFFB294133333333A4D83B4101030000000100000005000000CDCCCCCC03FB29419A9999996BD93B413333333302FB2941333333B3CFD93B4167666666CAFB29419A999999D0D93B4100000000CCFB2941000000806CD93B41CDCCCCCC03FB29419A9999996BD93B4101030000000100000005000000CDCCCCCCE9F72941333333B3D7D73B4133333333E8F72941CDCCCCCC3BD83B4167666666B0F82941333333B33CD83B4100000000B2F829419A999999D8D73B41CDCCCCCCE9F72941333333B3D7D73B410103000000010000000500000033333333C6FF2941333333B387D53B419A999999C4FF2941CDCCCCCCEBD53B41CDCCCCCC8C002A419A999999ECD53B41676666668E002A410000008088D53B4133333333C6FF2941333333B387D53B410103000000010000000500000067666666B5F829416766666610D73B41CDCCCCCCB3F829410000008074D73B41000000007CF92941CDCCCC4C75D73B419A9999997DF929413333333311D73B4167666666B5F829416766666610D73B410103000
 000010000000500000067666666F5FE2941676666667BD73B41CDCCCCCCF3FE294100000080DFD73B4100000000BCFF2941CDCCCC4CE0D73B419A999999BDFF2941333333337CD73B4167666666F5FE2941676666667BD73B410103000000010000000500000085525C03E1F729415815C0FDD7D93B41A74E7FD6A8F829417A578344F0D93B4167666666A9F829419A999919CDD93B4133333333E1F72941CDCCCC4CCCD93B4185525C03E1F729415815C0FDD7D93B41010300000001000000050000008C30C5903DF92941E4B88EDF31DA3B4190C2F5A84CF92941676666E639DA3B4180005DA16FF9294157E173A846DA3B410000000070F929419A99991932DA3B418C30C5903DF92941E4B88EDF31DA3B4101030000000100000004000000B50F21852BFA2941642C140D97DA3B4162DE6B5136FA29415677133A9CDA3B416766666636FA29419A99991997DA3B41B50F21852BFA2941642C140D97DA3B410103000000010000000700000067666666F0FE2941333333B3A7D83B41CDCCCCCCECFE2941676666E66FD93B4133333333B5FF2941333333B370D93B41CDCCCCCCB6FF29419A9999990CD93B41000000007F002A41000000800DD93B41CDCCCCCC80002A4167666666A9D83B4167666666F0FE2941333333B3A7D83B4101030000000100000005000000333333339DF52941
 6766666618D53B41676666669BF52941000000807CD53B41000000002CF72941333333337ED53B419A9999992DF729419A9999191AD53B41333333339DF529416766666618D53B41010300000001000000050000006766666601FF29419A999999BED43B41CDCCCCCCFFFE2941333333B322D53B4100000000C8FF29419A99999923D53B41CDCCCCCCC9FF294100000080BFD43B416766666601FF29419A999999BED43B41010300000001000000050000000000000048F329419A9999994DD43B413333333346F32941333333B3B1D43B419A9999990EF429419A999999B2D43B413333333310F42941000000804ED43B410000000048F329419A9999994DD43B4101030000000100000005000000CDCCCCCCF0F72941CDCCCC4C47D63B4100000000EFF7294167666666ABD63B4133333333B7F82941CDCCCC4CACD63B4100000000B9F829413333333348D63B41CDCCCCCCF0F72941CDCCCC4C47D63B41010300000001000000050000000000000020F72941000000003BD83B41333333331EF729419A9999199FD83B4167666666E6F72941676666E69FD83B4133333333E8F72941CDCCCCCC3BD83B410000000020F72941000000003BD83B4101030000000100000005000000676666667CF229410000000015D53B419A9999997AF229419A99991979D53B41CDCCCCCC42F32941676
 666E679D53B419A99999944F32941CDCCCCCC15D53B41676666667CF229410000000015D53B4101030000000100000005000000676666663FF329413333333342D63B41CDCCCCCC3DF32941CDCCCC4CA6D63B410000000006F429419A999919A7D63B419A99999907F429410000000043D63B41676666663FF329413333333342D63B41010300000001000000050000003333333384002A4133333333E1D73B416766666682002A41CDCCCC4C45D83B419A9999994A012A419A99991946D83B41676666664C012A4100000000E2D73B413333333384002A4133333333E1D73B4101030000000100000008000000952BAF0D6FF2294112B888761FD83B41A4703D8AABF22941A4703D0A36D83B41DDE5450736F32941F27BB10B69D83B41333333333AF32941000000806ED73B41CDCCCCCCA9F12941CDCCCCCC6CD73B4133333333A8F12941676666E6D0D73B416766666670F22941333333B3D1D73B41952BAF0D6FF2294112B888761FD83B4101030000000100000005000000A3AA38FFB6F329417B3D2D629BD83B41676666E6D9F3294185EB5178A9D83B41A58D8AEDFCF3294113744AF4B5D83B4167666666FDF32941333333B39BD83B41A3AA38FFB6F329417B3D2D629BD83B41010300000001000000080000001C87B5D6C3F42941BA598B72FED83B41CDCCCC4C10F52941E17A14
 EE1BD93B413FEEBC828BF52941D106B41C20D93B41000000008CF529410000008001D93B413333333354F62941CDCCCC4C02D93B410000000056F62941333333339ED83B419A999999C5F42941000000809CD83B411C87B5D6C3F42941BA598B72FED83B41010300000001000000050000003333333346F32941333333B3B1D43B419A99999944F32941CDCCCCCC15D53B41CDCCCCCC0CF42941333333B316D53B419A9999990EF429419A999999B2D43B413333333346F32941333333B3B1D43B4101030000000100000005000000CDCCCCCCBFF82941333333B3B7D43B4100000000BEF82941CDCCCCCC1BD53B413333333386F92941333333B31CD53B410000000088F929419A999999B8D43B41CDCCCCCCBFF82941333333B3B7D43B4101030000000100000005000000000000003BFE29419A99999959D43B413333333339FE2941333333B3BDD43B416766666601FF29419A999999BED43B413333333303FF2941000000805AD43B41000000003BFE29419A99999959D43B41010300000001000000050000009A9999997AF229419A99991979D53B410000000079F2294133333333DDD53B413333333341F3294100000000DED53B41CDCCCCCC42F32941676666E679D53B419A9999997AF229419A99991979D53B4101030000000100000005000000340A5B1FA8F129411EC0F735D
 5D73B41D7AC83006FF22941A6499E711FD83B416766666670F22941333333B3D1D73B4133333333A8F12941676666E6D0D73B41340A5B1FA8F129411EC0F735D5D73B41010300000001000000050000003333333310F42941000000804ED43B419A9999990EF429419A999999B2D43B41CDCCCCCCD6F4294167666666B3D43B4167666666D8F42941CDCCCC4C4FD43B413333333310F42941000000804ED43B4101030000000100000005000000333333332AF72941CDCCCC4CE2D53B416766666628F729416766666646D63B41CDCCCCCCF0F72941CDCCCC4C47D63B4167666666F2F7294133333333E3D53B41333333332AF72941CDCCCC4CE2D53B41010300000001000000050000000000000095EE29419A999999ACD43B419A99999991EE2941CDCCCCCC74D53B41CDCCCCCC59EF29419A99999975D53B41333333335DEF294167666666ADD43B410000000095EE29419A999999ACD43B4101030000000300000034000000D1205A6136FA29413A48B6419CDA3B419A99999992FA29415C8FC275C8DA3B41676666E6CAFA2941E17A14AEBADA3B41B81E85EB0CFB294185EB51F8BDDA3B41E17A14AE64FB2941EC51B85ED1DA3B410AD7A3F064FB29419A999959C2DA3B41295C8FC2C8FB2941CDCCCCCCD3DA3B41C3F5285C0CFC2941EC51B81EF0DA3B410AD7A37037FC2941EC51B8
 5E28DB3B41CDCCCCCC9AFC2941713D0AD754DB3B41295C8F42D0FC2941C3F5281C77DB3B41EC51B89E07FD294167666666A0DB3B410C26313B1DFD2941BB46D78FC6DB3B413333333352FD2941CDCCCCCCC6DB3B410E89AC2551FD2941A434715901DC3B4152B81E0554FD2941713D0AD703DC3B41F6285C0F6FFD294152B81E053DDC3B41713D0AD7C5FD2941AE47E17A8ADC3B41905FB07D12FE2941A4C37FE18FDC3B41694AC41FEBFE2941F1DFF5CC90DC3B419A999919F0FE2941295C8FC28FDC3B41333333332DFF2941333333F352DC3B41AE47E1FA8DFF29411F85EB512CDC3B4190C2F5A81A002A413E0AD7E30DDC3B41B1AE034A72002A4160B347E6F8DB3B416766666676002A410000000002DB3B4133333333AEFF29413333333301DB3B4100000000B0FF29419A9999199DDA3B413333333378002A41676666E69DDA3B41CDCCCCCC79002A41CDCCCCCC39DA3B410000000042012A41333333B33ADA3B41CDCCCCCC43012A419A999999D6D93B41000000000C022A4167666666D7D93B41CDCCCCCC0D022A41CDCCCC4C73D93B419A99999945012A410000008072D93B410000000049012A4133333333AAD83B413333333311022A419A999919ABD83B41CDCCCCCC12022A410000000047D83B416766666682002A41CDCCCC4C45D83B413333333384002A4133333333E1D
 73B41CDCCCCCCF3FE294100000080DFD73B4167666666F5FE2941676666667BD73B41333333332DFE2941000000807AD73B41000000002FFE29416766666616D73B41CDCCCCCC66FD29410000008015D73B419A99999961FD2941CDCCCCCC41D83B416766666699FC29410000000041D83B410000000096FC29413333333309D93B41CDCCCCCCCDFB2941CDCCCC4C08D93B4167666666CAFB29419A999999D0D93B41CDCCCCCC39FA2941CDCCCCCCCED93B41D1205A6136FA29413A48B6419CDA3B4105000000CDCCCCCCF3FE294100000080DFD73B4167666666F0FE2941333333B3A7D83B413333333328FE2941CDCCCCCCA6D83B419A9999992BFE29419A999999DED73B41CDCCCCCCF3FE294100000080DFD73B410F00000067666666F0FE2941333333B3A7D83B41CDCCCCCC80002A4167666666A9D83B41000000007F002A41000000800DD93B41CDCCCCCCB6FF29419A9999990CD93B4133333333B5FF2941333333B370D93B41CDCCCCCCECFE2941676666E66FD93B4133333333EBFE294100000000D4D93B410000000023FE29419A999919D3D93B413333333321FE29413333333337DA3B410000000059FD29416766666636DA3B41676666665CFD2941333333336ED93B419A99999924FE2941000000006FD93B416766666626FE2941676666E60AD93B419A999999EEFE2941
 CDCCCCCC0BD93B4167666666F0FE2941333333B3A7D83B410103000000010000001700000067666666D6FB2941CDCCCCCC13D73B4100000000D3FB294100000000DCD73B416766666642FA2941CDCCCC4CDAD73B413333333344FA29413333333376D73B41000000007CF92941CDCCCC4C75D73B41333333337AF9294167666666D9D73B4100000000B2F829419A999999D8D73B4167666666B0F82941333333B33CD83B4133333333E8F72941CDCCCCCC3BD83B4167666666E6F72941676666E69FD83B41333333331EF729419A9999199FD83B41676666661CF729413333333303D93B419A999999E4F729419A99991904D93B4100000000E3F729413333333368D93B4133333333ABF829410000000069D93B4100000000ADF82941676666E604D93B41676666663DFA29419A99999906D93B41000000003FFA294100000080A2D83B4167666666CFFB294133333333A4D83B4133333333D1FB29419A99991940D83B416766666699FC29410000000041D83B419A9999999EFC2941333333B314D73B4167666666D6FB2941CDCCCCCC13D73B4101030000000100000009000000000000004BFA2941CDCCCCCCE5D53B413333333313FB29419A999999E6D53B419A99999911FB2941333333B34AD63B4100000000A2FC2941000000804CD63B41CDCCCCCCA3FC2941CDCCCC4CE8D53B410
 00000006CFD294133333333E9D53B419A9999996DFD29419A99991985D53B41CDCCCCCC4CFA2941333333B381D53B41000000004BFA2941CDCCCCCCE5D53B4101030000000100000007000000CDCCCCCC0BFF29410000000066D23B416766666608FF2941333333332ED33B413333333340FE2941CDCCCC4C2DD33B41CDCCCCCC3CFE294100000080F5D33B4133333333CDFF294133333333F7D33B4100000000D4FF2941CDCCCCCC66D23B41CDCCCCCC0BFF29410000000066D23B410103000000010000000700000000000000C9F42941CDCCCC4CD4D73B4133333333C7F429416766666638D83B4100000000FFF329410000008037D83B4167666666FDF32941333333B39BD83B41CDCCCCCC8DF52941676666669DD83B413333333391F5294133333333D5D73B4100000000C9F42941CDCCCC4CD4D73B4101030000000100000005000000CDCCCCCCB0F12941CDCCCC4CDCD53B410000000079F2294133333333DDD53B41676666667CF229410000000015D53B4133333333B4F129419A99991914D53B41CDCCCCCCB0F12941CDCCCC4CDCD53B4101030000000100000005000000333333331AFB29413333333356D43B41CDCCCCCC16FB2941676666661ED53B4100000000DFFB2941CDCCCC4C1FD53B4167666666E2FB29419A99991957D43B41333333331AFB29413333333356D43B
 4101030000000100000005000000676666667CF229410000000015D53B419A99999944F32941CDCCCCCC15D53B410000000048F329419A9999994DD43B41CDCCCCCC7FF22941CDCCCCCC4CD43B41676666667CF229410000000015D53B41010300000001000000050000000000000071FD2941676666E6BCD43B41676666666FFD29410000000021D53B41CDCCCCCCFFFE2941333333B322D53B416766666601FF29419A999999BED43B410000000071FD2941676666E6BCD43B41010300000001000000050000000000000047FE2941676666E69CD13B416766666645FE29410000000001D23B419A9999990DFF2941676666E601D23B41333333330FFF2941333333B39DD13B410000000047FE2941676666E69CD13B41010300000001000000050000006766666632FE2941333333334ED63B419A99999930FE2941CDCCCC4CB2D63B4100000000F9FE29419A999919B3D63B419A999999FAFE2941000000004FD63B416766666632FE2941333333334ED63B4101030000000100000005000000000000003CF32941676666660AD73B41333333333AF32941000000806ED73B416766666602F42941CDCCCC4C6FD73B413333333304F42941333333330BD73B41000000003CF32941676666660AD73B41010300000001000000050000006766666673F92941676666E669D93B419A99999
 971F9294100000000CED93B41CDCCCCCC39FA2941CDCCCCCCCED93B419A9999993BFA2941333333B36AD93B416766666673F92941676666E669D93B41010300000001000000050000006766666649FA2941676666E649D63B419A99999947FA294100000000AED63B41CDCCCCCC0FFB2941CDCCCCCCAED63B419A99999911FB2941333333B34AD63B416766666649FA2941676666E649D63B410103000000010000000500000067666666CBFF2941676666665BD43B41CDCCCCCCC9FF294100000080BFD43B410000000092002A41CDCCCC4CC0D43B419A99999993002A41333333335CD43B4167666666CBFF2941676666665BD43B4101030000000100000005000000676666664EFA29419A9999991DD53B41CDCCCCCC4CFA2941333333B381D53B4133333333DDFB29416766666683D53B4100000000DFFB2941CDCCCC4C1FD53B41676666664EFA29419A9999991DD53B41010300000001000000050000009A9999993BFA2941333333B36AD93B41CDCCCCCC39FA2941CDCCCCCCCED93B413333333302FB2941333333B3CFD93B41CDCCCCCC03FB29419A9999996BD93B419A9999993BFA2941333333B36AD93B41010300000001000000050000003333333344FA29413333333376D73B416766666642FA2941CDCCCC4CDAD73B419A9999990AFB294133333333DBD73B41676666660C
 FB29419A99991977D73B413333333344FA29413333333376D73B41010300000001000000050000003333333347012A41676666660ED93B419A99999945012A410000008072D93B41CDCCCCCC0D022A41CDCCCC4C73D93B41676666660F022A41333333330FD93B413333333347012A41676666660ED93B4101030000000100000005000000676666668E002A410000008088D53B41CDCCCCCC8C002A419A999999ECD53B410000000055012A4100000080EDD53B41CDCCCCCC56012A416766666689D53B41676666668E002A410000008088D53B4101030000000100000005000000676666663DFA29419A99999906D93B419A9999993BFA2941333333B36AD93B4100000000CCFB2941000000806CD93B41CDCCCCCCCDFB2941CDCCCC4C08D93B41676666663DFA29419A99999906D93B4101030000000100000005000000676666667BFD29413333333364D23B419A99999979FD294167666666C8D23B41CDCCCCCC41FE294133333333C9D23B419A99999943FE29419A99991965D23B41676666667BFD29413333333364D23B4101030000000100000005000000CDCCCCCCA3FC2941CDCCCC4CE8D53B4100000000A2FC2941000000804CD63B41333333336AFD2941CDCCCC4C4DD63B41000000006CFD294133333333E9D53B41CDCCCCCCA3FC2941CDCCCC4CE8D53B410103000000010
 000000500000000000000D4FF2941CDCCCCCC66D23B4133333333D2FF2941676666E6CAD23B419A9999999A002A41CDCCCCCCCBD23B41333333339C002A41333333B367D23B4100000000D4FF2941CDCCCCCC66D23B4101030000000100000005000000000000008B002A41333333B350D63B416766666689002A41676666E6B4D63B419A99999951012A41333333B3B5D63B413333333353012A419A99999951D63B41000000008B002A41333333B350D63B41010300000001000000050000009A9999992BFE29419A999999DED73B41CDCCCCCC29FE2941333333B342D83B4100000000F2FE29419A99999943D83B41CDCCCCCCF3FE294100000080DFD73B419A9999992BFE29419A999999DED73B4101030000000100000005000000333333330EFB29410000000013D73B41676666660CFB29419A99991977D73B419A999999D4FB2941676666E677D73B4167666666D6FB2941CDCCCCCC13D73B41333333330EFB29410000000013D73B410103000000010000000500000033333333A0FC29419A999999B0D63B419A9999999EFC2941333333B314D73B41CDCCCCCC66FD29410000008015D73B416766666668FD294167666666B1D63B4133333333A0FC29419A999999B0D63B41010300000001000000050000009A999999E0FB294133333333BBD43B4100000000DFFB2941CDCCCC
 4C1FD53B4133333333A7FC29419A99991920D53B41CDCCCCCCA8FC294100000000BCD43B419A999999E0FB294133333333BBD43B4101030000000100000005000000676666667FF929419A999919ADD63B419A9999997DF929413333333311D73B410000000046FA29419A99991912D73B419A99999947FA294100000000AED63B41676666667FF929419A999919ADD63B41010300000001000000050000009A99999907F429410000000043D63B410000000006F429419A999919A7D63B4133333333CEF4294100000000A8D63B41CDCCCCCCCFF42941676666E643D63B419A99999907F429410000000043D63B41010300000001000000050000000000000062F6294100000080E1D53B413333333360F629419A99999945D63B416766666628F729416766666646D63B41333333332AF72941CDCCCC4CE2D53B410000000062F6294100000080E1D53B41010300000001000000050000009A9999996DFD29419A99991985D53B41000000006CFD294133333333E9D53B413333333334FE29419A999919EAD53B41CDCCCCCC35FE29410000000086D53B419A9999996DFD29419A99991985D53B41010300000001000000050000000000000046FA29419A99991912D73B413333333344FA29413333333376D73B41676666660CFB29419A99991977D73B41333333330EFB2941000000001
 3D73B410000000046FA29419A99991912D73B41010300000001000000070000006766666632FE2941333333334ED63B41CDCCCCCCC2FF2941676666E64FD63B4133333333C6FF2941333333B387D53B4100000000FEFE2941CDCCCCCC86D53B4167666666FCFE2941676666E6EAD53B413333333334FE29419A999919EAD53B416766666632FE2941333333334ED63B410103000000010000000500000000000000F9FE29419A999919B3D63B4167666666F5FE2941676666667BD73B419A999999BDFF2941333333337CD73B4133333333C1FF294100000000B4D63B4100000000F9FE29419A999919B3D63B4101030000000100000005000000CDCCCCCC89F929410000008054D43B410000000088F929419A999999B8D43B413333333350FA294100000080B9D43B410000000052FA2941CDCCCC4C55D43B41CDCCCCCC89F929410000008054D43B4101030000000100000005000000333333336AFD2941CDCCCC4C4DD63B416766666668FD294167666666B1D63B419A99999930FE2941CDCCCC4CB2D63B416766666632FE2941333333334ED63B41333333336AFD2941CDCCCC4C4DD63B410103000000010000000500000067666666F2F7294133333333E3D53B41CDCCCCCCF0F72941CDCCCC4C47D63B4100000000B9F829413333333348D63B419A999999BAF829419A999919E4D5
 3B4167666666F2F7294133333333E3D53B41010300000001000000050000009A9999999F002A41000000809FD13B41CDCCCCCC67012A41CDCCCC4CA0D13B419A99999969012A41333333333CD13B4167666666A1002A41676666663BD13B419A9999999F002A41000000809FD13B410103000000010000000500000033333333CEF4294100000000A8D63B4167666666CCF429419A9999190CD73B419A99999994F52941676666E60CD73B416766666696F52941CDCCCCCCA8D63B4133333333CEF4294100000000A8D63B41010300000001000000050000009A99999979FD294167666666C8D23B410000000078FD2941000000802CD33B413333333340FE2941CDCCCC4C2DD33B41CDCCCCCC41FE294133333333C9D23B419A99999979FD294167666666C8D23B410103000000010000000500000009D4954E72002A4121682FE5F8DB3B41B62D0F573B012A41404082BDC8DB3B41000000003D012A410000000067DB3B41CDCCCCCC74002A419A99991966DB3B4109D4954E72002A4121682FE5F8DB3B41010300000001000000050000009A99999911FB2941333333B34AD63B41CDCCCCCC0FFB2941CDCCCCCCAED63B4100000000D8FB2941333333B3AFD63B41CDCCCCCCD9FB29419A9999994BD63B419A99999911FB2941333333B34AD63B410103000000010000000500000033333
 333EDF72941000000800FD73B419A999999EBF729419A99999973D73B41CDCCCCCCB3F829410000008074D73B4167666666B5F829416766666610D73B4133333333EDF72941000000800FD73B41010300000001000000050000009A99999930FE2941CDCCCC4CB2D63B41333333332DFE2941000000807AD73B4167666666F5FE2941676666667BD73B4100000000F9FE29419A999919B3D63B419A99999930FE2941CDCCCC4CB2D63B410103000000010000000500000067666666BFFF29419A99991918D73B419A999999BDFF2941333333337CD73B410000000086002A419A9999197DD73B419A99999987002A410000000019D73B4167666666BFFF29419A99991918D73B4101030000000100000005000000CDCCCCCC3CFE294100000080F5D33B41000000003BFE29419A99999959D43B413333333303FF2941000000805AD43B410000000005FF294167666666F6D33B41CDCCCCCC3CFE294100000080F5D33B4101030000000100000005000000676666666FFD29410000000021D53B419A9999996DFD29419A99991985D53B41CDCCCCCC35FE29410000000086D53B419A99999937FE2941676666E621D53B41676666666FFD29410000000021D53B410103000000010000000500000067666666E2FB29419A99991957D43B419A999999E0FB294133333333BBD43B41CDCCCCCC
 A8FC294100000000BCD43B419A999999AAFC2941676666E657D43B4167666666E2FB29419A99991957D43B410103000000010000000500000000000000F9FE29419A999919B3D63B4133333333C1FF294100000000B4D63B41CDCCCCCCC2FF2941676666E64FD63B419A999999FAFE2941000000004FD63B4100000000F9FE29419A999919B3D63B41010300000001000000050000009A9999990DFF2941676666E601D23B41CDCCCCCC0BFF29410000000066D23B4100000000D4FF2941CDCCCCCC66D23B41CDCCCCCCD5FF2941333333B302D23B419A9999990DFF2941676666E601D23B410103000000010000001C0000005BF6085B53F6294185D47DE839D93B4103E4CCC5C5F629415744F2EA66D93B41CDCCCCCC1AF72941CDCCCC4C67D93B417945A7351AF729415AEB3A2288D93B41676666E636F72941B81E856B93D93B41291E8C89BAF729419F1CFA15CCD93B4167666666A9F829419A999919CDD93B41A74E7FD6A8F829417A578344F0D93B41333333B3C9F82941295C8F42F4D93B418C30C5903DF92941E4B88EDF31DA3B410000000070F929419A99991932DA3B4180005DA16FF9294157E173A846DA3B41F6285C0FC4F929415C8FC27565DA3B41B50F21852BFA2941642C140D97DA3B416766666636FA29419A99991997DA3B41CDCCCCCC39FA2941CDCCCCCCCED93B4
 19A99999971F9294100000000CED93B416766666673F92941676666E669D93B419A9999993BFA2941333333B36AD93B41676666663DFA29419A99999906D93B4100000000ADF82941676666E604D93B4133333333ABF829410000000069D93B4100000000E3F729413333333368D93B419A999999E4F729419A99991904D93B41676666661CF729413333333303D93B41333333331EF729419A9999199FD83B410000000056F62941333333339ED83B415BF6085B53F6294185D47DE839D93B4101030000000100000009000000DDE5450736F32941F27BB10B69D83B41F6285C8F66F32941B81E85EB7AD83B41A3AA38FFB6F329417B3D2D629BD83B4167666666FDF32941333333B39BD83B41CDCCCCCC00F4294167666666D3D73B4100000000C9F42941CDCCCC4CD4D73B41CDCCCCCCCAF429413333333370D73B41333333333AF32941000000806ED73B41DDE5450736F32941F27BB10B69D83B4101030000000100000005000000CDCCCCCC7EFD2941000000009CD13B41676666667BFD29413333333364D23B419A99999943FE29419A99991965D23B410000000047FE2941676666E69CD13B41CDCCCCCC7EFD2941000000009CD13B4101030000000100000005000000333333330FFF2941333333B39DD13B419A9999990DFF2941676666E601D23B41CDCCCCCCD5FF2941333333
 B302D23B4167666666D7FF29419A9999999ED13B41333333330FFF2941333333B39DD13B41010300000001000000050000009A99999984F92941CDCCCCCC80D53B41CDCCCCCC82F92941676666E6E4D53B41000000004BFA2941CDCCCCCCE5D53B41CDCCCCCC4CFA2941333333B381D53B419A99999984F92941CDCCCCCC80D53B4101030000000100000006000000A58D8AEDFCF3294113744AF4B5D83B4152B81E058DF42941F6285C4FE9D83B411C87B5D6C3F42941BA598B72FED83B419A999999C5F42941000000809CD83B4167666666FDF32941333333B39BD83B41A58D8AEDFCF3294113744AF4B5D83B4101030000000100000005000000000000004BFA2941CDCCCCCCE5D53B416766666649FA2941676666E649D63B419A99999911FB2941333333B34AD63B413333333313FB29419A999999E6D53B41000000004BFA2941CDCCCCCCE5D53B4101030000000100000005000000676666668FF52941CDCCCC4C39D83B41CDCCCCCC8DF52941676666669DD83B410000000056F62941333333339ED83B419A99999957F629419A9999193AD83B41676666668FF52941CDCCCC4C39D83B4101030000000100000005000000CDCCCCCCD9FB29419A9999994BD63B4100000000D8FB2941333333B3AFD63B4133333333A0FC29419A999999B0D63B4100000000A2FC2941000000804
 CD63B41CDCCCCCCD9FB29419A9999994BD63B41010300000001000000050000000C26313B1DFD2941BB46D78FC6DB3B41F6285C8F2CFD294115AE47A1E1DB3B410E89AC2551FD2941A434715901DC3B413333333352FD2941CDCCCCCCC6DB3B410C26313B1DFD2941BB46D78FC6DB3B4101030000000100000005000000CDCCCCCC98002A41676666E62FD33B410000000097002A410000000094D33B41333333335F012A41676666E694D33B410000000061012A41333333B330D33B41CDCCCCCC98002A41676666E62FD33B410103000000010000000500000033333333CDFF294133333333F7D33B4167666666CBFF2941676666665BD43B419A99999993002A41333333335CD43B416766666695002A419A999919F8D33B4133333333CDFF294133333333F7D33B41010300000001000000070000006766666623F72941CDCCCCCC72D73B410000000020F72941000000003BD83B4133333333E8F72941CDCCCCCC3BD83B41CDCCCCCCE9F72941333333B3D7D73B4100000000B2F829419A999999D8D73B41CDCCCCCCB3F829410000008074D73B416766666623F72941CDCCCCCC72D73B41010300000001000000050000003333333378002A41676666E69DDA3B41CDCCCCCC74002A419A99991966DB3B41000000003D012A410000000067DB3B416766666640012A41CDCCCCCC9EDA
 3B413333333378002A41676666E69DDA3B4101030000000100000005000000000000003FFA294100000080A2D83B41676666663DFA29419A99999906D93B41CDCCCCCCCDFB2941CDCCCC4C08D93B4167666666CFFB294133333333A4D83B41000000003FFA294100000080A2D83B410103000000010000000500000067666666CFFB294133333333A4D83B419A99999997FC29419A999919A5D83B416766666699FC29410000000041D83B4133333333D1FB29419A99991940D83B4167666666CFFB294133333333A4D83B410103000000010000000500000067666666CCF429419A9999190CD73B41CDCCCCCCCAF429413333333370D73B410000000093F529410000000071D73B419A99999994F52941676666E60CD73B4167666666CCF429419A9999190CD73B410103000000010000000400000082A61DCFC5F629415A639CEE66D93B417945A7351AF729415AEB3A2288D93B41CDCCCCCC1AF72941CDCCCC4C67D93B4182A61DCFC5F629415A639CEE66D93B41010300000001000000050000006307CBA1BAF729417FFF6920CCD93B410AD7A3F0D1F7294190C2F528D6D93B4185525C03E1F729415815C0FDD7D93B4133333333E1F72941CDCCCC4CCCD93B416307CBA1BAF729417FFF6920CCD93B4101030000000100000005000000CDCCCCCCC2FF2941676666E64FD63B4133333
 333C1FF294100000000B4D63B416766666689002A41676666E6B4D63B41000000008B002A41333333B350D63B41CDCCCCCCC2FF2941676666E64FD63B4101030000000100000005000000333333336AFD2941CDCCCC4C4DD63B416766666632FE2941333333334ED63B413333333334FE29419A999919EAD53B41000000006CFD294133333333E9D53B41333333336AFD2941CDCCCC4C4DD63B410103000000010000000500000033333333A0FC29419A999999B0D63B416766666668FD294167666666B1D63B41333333336AFD2941CDCCCC4C4DD63B4100000000A2FC2941000000804CD63B4133333333A0FC29419A999999B0D63B410103000000010000000500000000000000DFFB2941CDCCCC4C1FD53B4133333333DDFB29416766666683D53B4167666666A5FC29413333333384D53B4133333333A7FC29419A99991920D53B4100000000DFFB2941CDCCCC4C1FD53B410103000000010000000500000000000000D8FB2941333333B3AFD63B4167666666D6FB2941CDCCCCCC13D73B419A9999999EFC2941333333B314D73B4133333333A0FC29419A999999B0D63B4100000000D8FB2941333333B3AFD63B41010300000001000000050000003333333334FE29419A999919EAD53B4167666666FCFE2941676666E6EAD53B4100000000FEFE2941CDCCCCCC86D53B41CDCCCCCC
 35FE29410000000086D53B413333333334FE29419A999919EAD53B410103000000010000001700000011698D2F1AF02941649FACBB3CD73B41000000806EF029419A99995962D73B41CE7AB6AA8AF02941CA615A826BD73B419A999999E1F02941676666E66BD73B4100000000E5F02941333333B3A3D63B4133333333ADF1294100000080A4D63B41CDCCCCCCA9F12941CDCCCCCC6CD73B41333333333AF32941000000806ED73B41000000003CF32941676666660AD73B413333333304F42941333333330BD73B416766666602F42941CDCCCC4C6FD73B41CDCCCCCCCAF429413333333370D73B4133333333CEF4294100000000A8D63B41CDCCCCCC3DF32941CDCCCC4CA6D63B41676666663FF329413333333342D63B413333333377F22941CDCCCC4C41D63B410000000079F2294133333333DDD53B41CDCCCCCCB0F12941CDCCCC4CDCD53B4100000000AFF129416766666640D63B41CDCCCCCCE6F029419A9999993FD63B4167666666E8F0294100000080DBD53B413333333320F029419A999999DAD53B4111698D2F1AF02941649FACBB3CD73B4101030000000100000007000000676666663FF329413333333342D63B410000000098F52941333333B344D63B41676666669BF52941000000807CD53B4133333333D3F42941333333B37BD53B4100000000D5F42941000000801
 7D53B419A99999944F32941CDCCCCCC15D53B41676666663FF329413333333342D63B4101030000000100000009000000CDCCCCCC5CF62941CDCCCCCC0DD73B416766666659F6294100000000D6D73B413333333391F5294133333333D5D73B41676666668FF52941CDCCCC4C39D83B419A99999957F629419A9999193AD83B410000000056F62941333333339ED83B41333333331EF729419A9999199FD83B410000000025F72941333333B30ED73B41CDCCCCCC5CF62941CDCCCCCC0DD73B41010300000001000000050000000000000052FA2941CDCCCC4C55D43B41676666664EFA29419A9999991DD53B41CDCCCCCC16FB2941676666661ED53B41333333331AFB29413333333356D43B410000000052FA2941CDCCCC4C55D43B41010300000001000000050000000000000005FF294167666666F6D33B416766666601FF29419A999999BED43B41CDCCCCCCC9FF294100000080BFD43B4133333333CDFF294133333333F7D33B410000000005FF294167666666F6D33B41010300000001000000050000003333333360F629419A99999945D63B419A9999995EF62941333333B3A9D63B4100000000EFF7294167666666ABD63B41CDCCCCCCF0F72941CDCCCC4C47D63B413333333360F629419A99999945D63B410103000000010000000500000033333333CDFF294133333333F7D3
 3B419A9999995D012A4100000000F9D33B41333333335F012A41676666E694D33B41CDCCCCCCCEFF29419A99991993D33B4133333333CDFF294133333333F7D33B4101030000000100000005000000CDCCCCCC00F4294167666666D3D73B4100000000FFF329410000008037D83B4133333333C7F429416766666638D83B4100000000C9F42941CDCCCC4CD4D73B41CDCCCCCC00F4294167666666D3D73B41010300000001000000050000003333333381F929410000000049D63B41676666667FF929419A999919ADD63B419A99999947FA294100000000AED63B416766666649FA2941676666E649D63B413333333381F929410000000049D63B41010300000001000000050000009A999999F7F72941676666E6B6D43B41CDCCCCCCF5F72941000000001BD53B4100000000BEF82941CDCCCCCC1BD53B41CDCCCCCCBFF82941333333B3B7D43B419A999999F7F72941676666E6B6D43B41010300000001000000050000000000000079F2294133333333DDD53B413333333377F22941CDCCCC4C41D63B41676666663FF329413333333342D63B413333333341F3294100000000DED53B410000000079F2294133333333DDD53B4101030000000100000005000000CE7AB6AA8AF02941CA615A826BD73B4148E17A14CCF029417B14AEC780D73B41632DB122E1F02941975893F588D73B4
 19A999999E1F02941676666E66BD73B41CE7AB6AA8AF02941CA615A826BD73B410103000000010000000500000000000000B9F829413333333348D63B4133333333B7F82941CDCCCC4CACD63B41676666667FF929419A999919ADD63B413333333381F929410000000049D63B4100000000B9F829413333333348D63B41010300000001000000050000009A99999974FD2941333333B3F4D33B41CDCCCCCC72FD2941CDCCCCCC58D43B41000000003BFE29419A99999959D43B41CDCCCCCC3CFE294100000080F5D33B419A99999974FD2941333333B3F4D33B41
   </a>
-<test>
-  <op name="union" arg1="A">
-01060000002E000000010300000001000000050000009A99999979A329419A999999EAD53B41CDCCCCCC41A4294100000080EBD53B419A99999943A429416766666687D53B41676666667BA329410000008086D53B419A99999979A329419A999999EAD53B410103000000010000000A0000003333333382A329419A999919F6D33B416766666680A32941333333335AD43B41CDCCCCCC7EA32941CDCCCC4CBED43B410000000047A4294133333333BFD43B41333333330FA5294100000000C0D43B419A99999912A52941CDCCCCCCF7D33B419C42A1664AA429412D7333F3F6D33B41333333334CA42941CDCCCCCC92D33B410000000084A329410000000092D33B413333333382A329419A999919F6D33B41010300000001000000050000003333333345A42941CDCCCC4C23D53B419A99999943A429416766666687D53B41CDCCCCCC0BA529413333333388D53B41676666660DA529419A99991924D53B413333333345A42941CDCCCC4C23D53B410103000000010000000B000000A4703D0AA9A42941CDCCCC0CE2D93B4191066719BBA429413D124827D5D93B41D7A3703D02A529419A999959A2D93B4171465D753BA52941E27218A071D93B416766666632A429410000008070D93B419A99999930A429419A999999D4D93B414C00C769C9A3294112F9D822D4D93B415C8FC2750CA
 4294167666666F9D93B41EC3D9D1030A4294154C1C513F6D93B4185EB51B868A42941A4703DCAF0D93B41A4703D0AA9A42941CDCCCC0CE2D93B41010300000001000000160000009A999999FAA42941CDCCCC4C71D93B41B7FF787F3BA529417ADC7C9771D93B410AD7A3704FA52941C3F5289C60D93B410AD7A3705EA52941C3F5289C27D93B41EC51B89EA0A529413E0AD7E321D93B41B044C838C4A52941586FBD2123D93B4185EB51B864A62941AE47E1BA28D93B410D36E6798CA62941B3259AE820D93B410D36E6798CA62941B4259AE820D93B41D7A3703DD7A629413333333312D93B410E28E39554A72941B7EED7D926D93B410000000055A72941CDCCCCCC0FD93B41861AC49B54A729413AE0CFDA26D93B41AE47E1FA6CA72941EC51B8DE2AD93B419A99991905A829413E0AD763B9D83B41333333333AA82941CDCCCC8C7DD83B4194ADAD9392A829413D3C15EF48D83B416766666658A729419A99999947D83B419A99999956A72941333333B3ABD83B4100000000FEA429419A999919A9D83B4167666666FCA42941333333330DD93B419A999999FAA42941CDCCCC4C71D93B410103000000010000000D000000000000000AA52941CDCCCC4CECD53B41676666669AA629419A999919EED53B41333333339CA62941000000008AD53B416766666664A72941CDCCCCCC8AD5
 3B413333333366A72941333333B326D53B41CDCCCCCC67A729419A999999C2D43B419A99999969A72941000000805ED43B4167666666A1A629419A9999995DD43B419A9999999FA62941333333B3C1D43B4167666666D7A52941676666E6C0D43B4100000000D4A529419A99991989D53B41CDCCCCCC0BA529413333333388D53B41000000000AA52941CDCCCC4CECD53B410103000000010000000C0000000000000011A52941676666E65BD43B4133333333D9A52941CDCCCCCC5CD43B4167666666A1A629419A9999995DD43B4100000000A3A6294100000080F9D33B4167666666A6A62941CDCCCC4C31D33B4133333333DEA529410000008030D33B410000000016A529419A9999992FD33B416766666614A52941333333B393D33B4114800080DCA52941F9237C9994D33B41CDCCCCCCDAA52941333333B3F8D33B419A99999912A52941CDCCCCCCF7D33B410000000011A52941676666E65BD43B410103000000010000000500000000000000C8A52941CDCCCCCC45D83B416766666658A729419A99999947D83B41333333335AA7294100000080E3D73B419A999999C9A52941333333B3E1D73B4100000000C8A52941CDCCCCCC45D83B4101030000000100000005000000CDCCCCCC98A629413333333352D63B410000000061A729410000000053D63B419A99999962A72941676
 666E6EED53B41676666669AA629419A999919EED53B41CDCCCCCC98A629413333333352D63B410103000000010000000700000000000000AAA629419A99991969D23B413333333372A72941000000006AD23B41CDCCCCCC73A72941676666E605D23B419A99999975A72941333333B3A1D13B4167666666ADA62941676666E6A0D13B419A999999ABA629410000000005D23B4100000000AAA629419A99991969D23B4101030000000100000009000000333333338EA32941CDCCCC4C39D13B41CDCCCCCC8AA329419A99999901D23B4167666666E3A529419A99991904D23B4133333333E5A5294100000000A0D13B419A99999975A72941333333B3A1D13B410000000079A7294100000080D9D03B41CDCCCCCCB0A62941333333B3D8D03B4100000000AFA62941CDCCCCCC3CD13B41333333338EA32941CDCCCC4C39D13B4101030000000100000010000000CDCCCC4C94A82941CDCCCCCC08D83B41B81E85EBB4A82941676666E6E5D73B4192F27C42B4A82941D19BA9F4E4D73B41AE47E1FA87A8294115AE47A1A5D73B41120E470090A829415E327EAF80D73B41E17A142E97A82941EC51B89E5FD73B4133333333CEA82941A4703DCA28D73B414F6D11BECCA82941A9BFB9D91CD73B41CDCCCCCC25A829419A9999191CD73B410000000024A829413333333380D73B416766666622
 A82941CDCCCC4CE4D73B419A99999920A829416766666648D83B41855BDE9C92A82941148B9CE948D83B4115AE4761A3A82941E17A14EE3ED83B41EC51B89EA5A82941B81E85EB30D83B41CDCCCC4C94A82941CDCCCCCC08D83B410103000000040000003300000037A2DF4EB3B429410B3E6C2A4CD13B41F6285C8F16B529419A99999983D13B41EE3218E488B5294154D3A181CDD13B41EC51B81EA9B52941713D0A57E2D13B41B81E85EBD0B5294152B81E85F0D13B41A13AB048D0B52941616D149715D23B415C8FC275CFB52941C3F5289C45D23B4115AE4761EAB52941CDCCCCCC86D23B41B81E85EB1BB62941CDCCCC0CA5D23B41676666667DB62941295C8F82C9D23B41F6285C8F6CB72941C3F5289C0AD33B4115AE476192B729417B14AEC717D33B41713D0A57B4B72941E17A14EE1CD33B4152B81E85F6B729415C8FC23517D33B4148E17A1402B9294152B81E45CED23B4185EB51B866B92941B81E85ABAFD23B417B14AE47B9B92941EC51B81E09D33B410AD7A370E7B92941F6285C4F02D33B41AE47E1FA4DBA294185EB51B8E9D23B417B14AEC7FABA2941B81E856BC7D23B41562CE01DFEBA29410767AFF6CAD23B41676666E62DBB29415C8FC2B5FDD23B41C3F5285C57BB2941B81E85EB20D33B417B14AEC776BB29419A99991945D33B414C01D3677BBB29415F7E3
 92C48D33B410E14BB70C3BB29418A1C7B0678D33B4148E17A94EBBB29410AD7A3B092D33B4183F6941334BC29416210AF0DADD33B4190C2F5A8A0BC2941A4703D8AD4D33B41167FD1F250BD2941F5E2573521D43B419A99999954BD2941333333334AD33B41000000001DBE2941000000004BD33B419A9999991EBE2941676666E6E6D23B416766666620BE2941CDCCCCCC82D23B413333333358BD29410000000082D23B410000000090BC29419A99991981D23B41333333338EBC294133333333E5D23B41676666668CBC2941CDCCCC4C49D33B4133333333C4BB29410000008048D33B419A999999C7BB2941CDCCCC4C80D23B4133333333CBBB294100000000B8D13B410000000003BB294133333333B7D13B419A99999904BB29419A99991953D13B416766666606BB294100000000EFD03B410000000076B92941CDCCCC4CEDD03B4167666666E5B7294100000080EBD03B410715FCB2E3B72941907D15B34FD13B419A9999991BB72941CDCCCCCC4ED13B416766666653B62941676666E64DD13B41333333338BB529419A9999194DD13B4137A2DF4EB3B429410B3E6C2A4CD13B410400000067666666FFBA2941676666667FD23B413333333301BB2941CDCCCC4C1BD23B410000000003BB294133333333B7D13B4167666666FFBA2941676666667FD23B4104000000CDCCCCCCA6
 B82941CDCCCCCC7CD23B419A999999DEB72941000000007CD23B416766666616B729419A9999197BD23B41CDCCCCCCA6B82941CDCCCCCC7CD23B410400000033333333AAB829419A999999B4D13B416766666672B9294100000080B5D13B41A099991946B8294134333333B4D13B4133333333AAB829419A999999B4D13B41010300000001000000050000009A999999C4B429419A999919E8D03B4100000000C3B42941333333334CD13B41333333338BB529419A9999194DD13B41CDCCCCCC8CB5294100000000E9D03B419A999999C4B429419A999919E8D03B410103000000010000000500000067666666C9BB2941333333331CD23B419A99999991BC2941000000001DD23B416766666693BC2941676666E6B8D13B4133333333CBBB294100000000B8D13B4167666666C9BB2941333333331CD23B4101030000000100000005000000CDCCCCCC96BC2941333333B3F0D03B419A999999CEBB2941CDCCCCCCEFD03B41CDCCCCCCCCBB2941676666E653D13B410000000095BC2941CDCCCCCC54D13B41CDCCCCCC96BC2941333333B3F0D03B41010300000001000000110000001F85EBD126BF2941D7A370BD55D53B415C8FC275EFBF29411F85EB913ED53B41E17A14AE09C02941B81E85AB33D53B410AD7A37062C02941295C8F020DD53B4152B81E05CBC02941E17A146EF2D43B4
 15C19B5701AC12941E1FA5DCFDED43B4167666666A6BF294133333333DDD43B41CDCCCCCCA4BF2941CDCCCC4C41D53B41B250896614BE2941C33711913FD53B410000000016BE294100000080DBD43B4109F5662588BD29413D60BFDCDAD43B4148E17A1469BD29417B14AEC728D53B41B81E85EBFEBD2941333333733CD53B41A52BB10510BE294191D58F943FD53B41A72BB10510BE294191D58F943FD53B4152B81E85C8BE29419A99995961D53B411F85EBD126BF2941D7A370BD55D53B4101030000000F000000000100009A99999938C12941CDCCCCCC7AD43B41F27B9CED36C1294183A1BAC5D7D43B4190C2F5289EC1294152B81E45BED43B41333333B3EEC12941713D0A97A3D43B418459F83600C22941B59FEE50A0D43B41713D0A5781C22941E17A142E88D43B4113E070BBC8C229415A3D3A418DD43B41713D0A57F9C229415C8FC2B590D43B411F85EB51A9C3294185EB51789DD43B41B56494BE58C4294141BD7536A7D43B4115AE4761A7C4294148E17A94ABD43B411F85EB5165C5294190C2F568BBD43B41F6285C8F85C529417B14AE87AFD43B41AE47E1FA4FC629413E0AD763A5D43B41CDCCCC4C86C629419A99999995D43B41244A09FAB1C62941007D580F88D43B41CDCCCCCCB3C62941333333B31CD43B4133333333B2C62941CDCCCCCC80D43B419DA6E911B2
 C62941109EF10788D43B4184F15710C9C62941A1E21CE780D43B418FACC04B7BC72941166E53A649D43B410AD7A3F081C72941713D0A9747D43B4100000000E8C729417B14AE074BD43B41295C8F423AC82941EC51B85E41D43B415D3D57AA43C8294176AAA7D83FD43B41B81E856B64C82941A4703D8A3AD43B41295C8F42F6C829410AD7A3304ED43B41F6285C0FF6C829413333333359D43B41579702670BC9294160C7C10A5AD43B41579702670BC9294161C7C10A5AD43B41EC51B81E68C92941333333B35DD43B415C8FC2F589C92941C3F528DC69D43B411F85EBD1B1C92941A4703D0A74D43B41C3F528DC1AC929410AD7A370A2D43B4149C15D8D09C92941228377CAC8D43B41CDCCCCCCFFC8294167666666DED43B4124A2ED64FCC8294192207E71E7D43B41EC51B81EF1C8294115AE476105D53B41F6285C8FE4C82941C3F5285C25D53B41D7A3703DC8C82941295C8F4235D53B41AE47E1FA3EC82941D7A370BD75D53B4190AEF26A3EC82941FD06003976D53B41AE47E17A1EC829413E0AD7A391D53B415C8FC2F5E1C7294190C2F568ABD53B419A999919B7C729417B14AE47DAD53B4115AE476186C72941C3F5281C01D63B41A6EE472379C72941790F342012D63B41A9847D4773C729419F4463A719D63B41CDCCCCCC71C729413333333376D63B41676666666EC7294
 1676666663ED73B414E9B2C4673C729415D3014A919D63B41CDCCCC4C2CC72941C3F528DC74D63B41B81E856B0FC72941A4703DCAA6D63B411F85EBD1FAC62941D7A370BDC8D63B415C8FC2F597C62941EC51B85EF3D63B41CB2F566B6AC629413FF2F64B3DD73B419A99999968C629410000004040D73B41AE47E1FA67C62941A4703D4A64D73B41A4703D8A55C62941D7A3703D7DD73B41A47CE0D12DC62941D26BBB2AA1D73B4190C2F5A820C629411F85EB11ADD73B419A99991914C62941CDCCCC0CCDD73B4148E17A941BC62941EC51B81EEBD73B41676666E6A6C6294152B81EC51CD83B41B81E856B2CC729413E0AD76342D83B4169089E8569C729414878F78E56D83B414CBAA863A7C72941303FC5FA6AD83B416F80C59830C82941F971D44498D83B4190C2F5A8B2C8294133333333C3D83B41802C2FFBB8C8294111164E51D0D83B41CDCCCCCCCBC82941EC51B85EF7D83B4193EA1022F7C8294171A2D0DEF7D83B41F6285C0FE0C92941F6285C8FFAD83B41D7A370BDF7C92941E17A14AE0FD93B41295C8FC221CA2941C3F528DC10D93B41F6285C8F34CA29413E0AD7E3E3D83B41F6285C8F50CA2941295C8F02E5D83B4185EB51385ACA294148E17A14FAD83B415C8FC27546CB2941713D0A17FCD83B41CDCCCCCC4BCB294190C2F52823D93B41F61B1C4351CB29416C97
 083637D93B413E0AD7A35ECB29411F85EB5168D93B41B81E85EB7CCB2941E17A146E59D93B4190C2F528BFCB2941333333B34FD93B41F6285C0FE1CB2941C3F528DC58D93B41CDCCCCCC02CC294152B81E056AD93B41713D0AD780CC29411F85EB9170D93B41AE47E17A8ACC294152B81EC5FAD93B4148E17A94CACC2941A4703D0AF9D93B411F85EBD10ACE2941B81E856B01DA3B41AE47E1FA69CE29413E0AD7E338DA3B4198EE89A7EFCE294109E5DA8367DA3B41CDCCCCCC32CF2941CDCCCCCC67DA3B413847E2B3EFCE2941A235298867DA3B417B14AE47FBCE29411F85EB916BDA3B415E73F39332CF29412B3F652974DA3B41F6285C0F8DCF294185EB513882DA3B4190C2F5A819D02941CDCCCCCC68DA3B41B81E85EB9ED02941E17A146E9DDA3B41713D0AD7FBD02941676666E6E2DA3B41F6285C0F3FD129410000004014DB3B4115AE47E156D12941C3F5285C20DB3B415C8FC2F5BAD12941A4703DCA22DB3B41F6285C0F57D2294185EB517827DB3B4152B81E85E7D229411F85EB1116DB3B410AD7A3F00FD3294185EB513801DB3B4185EB513806D3294167666626EFDA3B41D7A370BD41D52941A4703D8ABDDA3B41F6285C0F6CD529415C8FC2B5ADDA3B417B14AEC79CD52941EC51B8DE86DA3B410AD7A370B7D529410AD7A3F061DA3B41E17A14AE20D629411F85EB512
 1DA3B4148E17A94BED6294115AE47E1BCD93B4190C2F5A839D729419A99995985D93B410AD7A37072D72941A4703D8A5BD93B41676666E6BBD72941295C8FC20BD93B41B81E856BC4D7294152B81EC5EDD83B415C8FC27554D729417B14AE47E3D83B413E0AD7A30AD729410AD7A3F0D3D83B410AD7A3F0D6D62941333333B3BED83B411F85EB51C1D6294148E17A94A7D83B418E1DD7F0C9D62941ECBDC3987BD83B41CDCCCC4CCCD62941F6285C8F6FD83B419A999999D3D62941295C8F8223D83B41323AE4E5D4D62941A57CA18017D83B41313AE4E5D4D62941A57CA18017D83B41D7A370BDDAD629415C8FC275E1D73B41D163D14311D7294184D501C89BD73B413333333314D7294100000080EBD63B41047421224CD6294118E1C099EAD63B4115D475A14DD629412035782094D63B41AE47E17A18D62941AE47E13A84D63B4100000080A5D5294190C2F5A840D63B41A7A29C1687D5294113B64C8133D63B413333333382D52941676666E64DD73B41CDCCCCCC83D52941CDCCCCCCE9D63B419A99999985D529419A99999985D63B416D158A1387D52941A97AF87F33D63B41295C8FC27DD5294185EB51782FD63B412C86473887D52941CA7CD1842BD63B416766666687D529410000008021D63B411AD0033A87D52941D4EA17842BD63B41C05006F49ED529413D301B9B21D63B
 41B81E856BCED529417B14AEC70DD63B411F85EBD128D6294167666626FCD53B410B135E6F50D62941C2789228EED53B4190C2F5A89BD62941713D0A97D3D53B4183658A05B8D629411CF3FBB5BED53B410AD7A370D4D62941A4703DCAA9D53B41D17610E919D72941FA5D36B7A2D53B41676666E674D729413333337399D53B41F6285C8FE1D7294115AE47E17AD53B417116297BE1D72941227750E45BD53B417216297BE1D72941227750E45BD53B41CDCCCC4CE1D7294152B81EC514D53B41FF534E9AE5D72941C1294A32DAD43B4152B81E85E6D729415C8FC2B5CDD43B416BAA39D6E5D7294196DF196ACCD43B41F6285C0FD5D729419A999999ACD43B410F05B8CBB1D72941053C337693D43B410E05B8CBB1D72941053C337693D43B4175CCA98A24D7294158A69BC42ED43B410000000058D62941676666E62DD43B416766666620D72941333333B32ED43B4190D9117220D7294167DE1CD92BD43B41C3F5285C05D72941A4703D8A18D43B415C8FC2F501D72941AE47E17AF4D33B41E42E0F89D5D62941ADE2604BCAD33B41D7A3703DADD629417B14AE07A4D33B41B81E856B79D62941A4703DCA95D33B419259E8B55AD629414BDB791C97D33B41F6285C0FBBD52941AE47E1FA9DD33B419A999999EBD42941A4703D0A71D33B41295C8FC203D529413E0AD72369D33B41F0B
 2D2AC38D529413B383D6A64D33B41F1B2D2AC38D529413B383D6A64D33B419A99991984D52941E17A14AE5DD33B41DD74569393D529411C4462D159D33B410000000095D52941333333B300D33B41CDCCCCCC96D529419A9999999CD23B419A999999CED42941333333B39BD23B4133333333D0D429419A99999937D23B4100000000D2D4294100000080D3D13B41CDCCCCCC09D429419A999999D2D13B410000000008D42941CDCCCCCC36D23B41333333330DD42941676666660AD13B410000000045D329419A99999909D13B41CDCCCCCC3FD32941676666E635D23B419A99999977D229410000000035D23B41CDCCCCCC75D229419A99991999D23B41A7AB32333ED32941D5711D009AD23B419A9999993AD329413333333362D33B41CDCCCCCC02D429419A99991963D33B413333333301D4294133333333C7D33B4167666666C9D4294100000000C8D33B419A999999C7D429419A9999192CD43B41CDCCCCCC8FD52941000000002DD43B41333333338ED529419A99991991D43B41676666668CD5294133333333F5D43B4100000000FCD3294100000080F3D43B41CDCCCCCC33D329419A999999F2D43B416766666630D32941676666E6BAD53B41ECD58888F8D329410EBE21B3BBD53B41CDCCCCCCF6D32941CDCCCCCC1FD63B419A9999992ED32941000000001FD63B4100000000
 2DD329419A99991983D63B419A99999929D32941CDCCCC4C4BD73B413333333361D22941676666664AD73B410000000063D22941CDCCCC4CE6D63B41CDCCCCCC64D229413333333382D63B419A9999999CD129416766666681D63B4167666666D4D029410000008080D63B419A999999D2D029419A999999E4D63B41676666660AD02941333333B3E3D63B41CDCCCCCC0DD02941000000801BD63B419A9999990FD0294167666666B7D53B416766666647CF29419A999999B6D53B419A99999945CF2941333333B31AD63B41CDCCCCCC43CF2941CDCCCCCC7ED63B419A9999997BCE2941676666E67DD63B41676666667DCE2941CDCCCCCC19D63B4133333333B5CD29410000000019D63B41CDCCCCCCB6CD2941CDCCCCCCB4D53B419A999999EECC294100000000B4D53B4167666666F0CC2941676666E64FD53B413333333328CC2941000000004FD53B410000000060CB2941333333334ED53B416766666663CB29410000000086D43B41333333339BCA29419A99991985D43B41CDCCCCCC9CCA29410000000021D43B4100000000A2CA2941333333B3F4D23B411139826649C8294154523313F2D23B41CDCCCCCC4CC82941676666E629D23B4167666666BCC629413333333328D23B419A999999BAC62941CDCCCC4C8CD23B4133333333BEC629419A999919C4D13B419A9999992DC52
 941CDCCCC4CC2D13B41000000002CC529416766666626D23B41333333332AC529419A9999998AD23B412F6EDF9928C52941866F22A2EED23B413333333398C32941676666E6ECD23B410000000093C32941CDCCCC4C19D43B419A99999994C3294133333333B5D33B4167666666CCC22941CDCCCC4CB4D33B4133333333CEC229413333333350D33B41CDCCCCCCCFC229419A999919ECD23B419A99999907C2294133333333EBD23B410000000006C22941CDCCCC4C4FD33B41CDCCCCCC3DC12941000000804ED33B41000000003CC129419A999999B2D33B413333333304C2294167666666B3D33B419A99999902C229419A99999917D43B410DA533B3CAC22941AD364C6618D43B4100000000C9C22941000000807CD43B41CDCCCCCC00C22941333333B37BD43B419A99999938C12941CDCCCCCC7AD43B410500000084872122BFD42941921456B320D63B417B90DDDDC0D42941BF0EAB99BCD53B410000000089D5294167666666BDD53B416766666687D529410000008021D63B4184872122BFD42941921456B320D63B410400000000000000B0CD2941CDCCCC4C45D73B4167666666B3CD29419A9999197DD63B4133333333B5CD29410000000019D63B4100000000B0CD2941CDCCCC4C45D73B410500000092B2AD4738C8294157754933DBD63B4154B8AD4738C829412D264833DB
 D63B416766666600C9294100000000DCD63B419A999999C8C92941676666E6DCD63B4192B2AD4738C8294157754933DBD63B410400000000000000CCC92941333333B314D63B4167666666CAC92941CDCCCCCC78D63B419A999999C8C92941676666E6DCD63B4100000000CCC92941333333B314D63B4104000000676666661ACC2941676666E66FD83B413333333352CB2941000000006FD83B419A999999E2CC2941333333B370D83B41676666661ACC2941676666E66FD83B410400000067666666E4CC29419A9999990CD83B4100000000E1CC2941CDCCCCCCD4D83B419A999999E2CC2941333333B370D83B4167666666E4CC29419A9999990CD83B41050000009A99999939CF294167666666D7D83B416766666671CE29419A999999D6D83B4133333333A9CD2941333333B3D5D83B4100000000E1CC2941CDCCCCCCD4D83B419A99999939CF294167666666D7D83B410400000067666666FECF294100000080A0D93B419A999999FCCF29419A99999904DA3B410000000000D02941676666663CD93B4167666666FECF294100000080A0D93B410500000033333333C8D02941333333333DD93B410571B1FFC9D029419C53AA2AD9D83B41B2D7B5FFC9D029417D58AA2AD9D83B419A999999C6D0294167666666A1D93B4133333333C8D02941333333333DD93B4104000000CDCCCCC
 C8ED1294133333333A2D93B410000000057D229419A999919A3D93B419A999999C6D0294167666666A1D93B41CDCCCCCC8ED1294133333333A2D93B41050000004E1A0E3392D129416C5F8808DAD83B41E848103392D12941D7618808DAD83B419A99999990D129419A9999193ED93B41CDCCCCCC8ED1294133333333A2D93B414E1A0E3392D129416C5F8808DAD83B41040000000000000021D32941CDCCCCCC3FD93B41CDCCCCCC58D22941000000003FD93B419A99999990D129419A9999193ED93B410000000021D32941CDCCCCCC3FD93B4106000000676666661DD329410000000008DA3B41333333331FD32941676666E6A3D93B4167666666E7D32941CDCCCCCCA4D93B4100000000E4D32941000000006DDA3B41CDCCCCCC1BD329419A9999196CDA3B41676666661DD329410000000008DA3B4104000000333333335CD22941333333B376D83B419A999999ECD329410000008078D83B416766666624D329419A99999977D83B41333333335CD22941333333B376D83B410103000000020000000C00000067666666BCC629413333333328D23B419A99999984C729410000000029D23B41CDCCCCCC4CC82941676666E629D23B419A9999994EC82941CDCCCCCCC5D13B410000000052C829419A999999FDD03B41CDCCCCCC89C72941333333B3FCD03B419A999999C1C62941CD
 CCCCCCFBD03B41CDCCCCCCBFC629410000000060D13B419A999999F7C529419A9999195FD13B4100000000F6C5294133333333C3D13B4133333333BEC629419A999919C4D13B4167666666BCC629413333333328D23B41040000006766666686C72941676666E6C4D13B410000000088C72941CDCCCCCC60D13B41CDCCCCCC89C72941333333B3FCD03B416766666686C72941676666E6C4D13B410103000000010000000500000000000000A9CA29413333333364D13B419A999999AACA29419A99991900D13B4167666666E2C92941CDCCCC4CFFD03B419A999999E0C929416766666663D13B4100000000A9CA29413333333364D13B4101030000000100000013000000CDCCCCCC9CCA29410000000021D43B413333333365CB2941CDCCCCCC21D43B41676666662DCC2941333333B322D43B41000000002FCC29419A999999BED33B419A99999987CE294133333333C1D33B410000000086CE2941CDCCCC4C25D43B41333333334ECF29419A99991926D43B416766666653CF2941CDCCCCCCF9D23B41CDCCCCCCC2CD29419A999919F8D23B4167666666C6CD2941676666E62FD23B41CDCCCCCC35CC2941333333332ED23B419A9999996DCB2941CDCCCC4C2DD23B41333333336ACB294100000080F5D23B416766666632CC294167666666F6D23B41CDCCCCCC30CC2941000000805AD
 33B419A99999968CB29419A99999959D33B41CDCCCCCC66CB2941333333B3BDD33B419A9999999ECA2941676666E6BCD33B41CDCCCCCC9CCA29410000000021D43B4101030000000100000005000000CDCCCCCCC9CD2941333333B367D13B4167666666CBCD29419A99999903D13B41CDCCCCCC72CB29410000000001D13B413333333371CB29419A99991965D13B41CDCCCCCCC9CD2941333333B367D13B4101030000000100000005000000CDCCCCCCF3CC2941333333B387D43B4100000000F2CC2941CDCCCCCCEBD43B4167666666BACD29419A999999ECD43B4100000000BCCD29410000008088D43B41CDCCCCCCF3CC2941333333B387D43B410103000000010000000500000000000000BCCD29410000008088D43B413333333384CE29416766666689D43B410000000086CE2941CDCCCC4C25D43B41CDCCCCCCBDCD29416766666624D43B4100000000BCCD29410000008088D43B41010300000001000000090000003685289693D529412111AED059D33B4185EB513851D62941D7A3707D2AD33B4122D6369A5CD62941827E2CE326D33B41EC51B89E8FD62941D7A370BD16D33B41CDEAE2DDB1D62941604B70E101D33B41B94CCC4C5DD62941A1751D8001D33B41000000005FD62941676666669DD23B41CDCCCCCC96D529419A9999999CD23B413685289693D529412111AED0
 59D33B41010300000001000000090000009A99999912D729419A9999994FD73B41E0F49F4E11D72941EE5E32BA9BD73B41AE47E17A26D72941B81E85AB80D73B416064E4EB37D7294102492AC24FD73B416164E4EB37D7294102492AC24FD73B41A4703D8A50D7294185EB51B80AD73B410FBCCEBE6CD729413D6B31E0EBD63B413333333314D7294100000080EBD63B419A99999912D729419A9999994FD73B4101030000000100000006000000295C8FC22DD72941D7A3707DC1D63B4142245A0315D72941C1F76D48BED63B413333333314D7294100000080EBD63B416F1301C46CD729413C9C82DAEBD63B41676666667BD729419A9999D9DBD63B41295C8FC22DD72941D7A3707DC1D63B41010300000001000000040000000155CA7220D72941B960A0D92BD43B416766666620D72941333333B32ED43B412826D17824D729415DDDE2B72ED43B410155CA7220D72941B960A0D92BD43B410103000000010000000C00000090C2F5A838D7294190C2F568E5D23B419A99999980D72941AE47E1BAEED23B419A999919E1D72941C3F5281CD8D23B419094A37DEED7294142734CC3D1D23B41EC51B89E19D829411F85EB51BDD23B411F85EBD147D82941295C8F82B4D23B410AD7A3F079D8294185EB51B8B0D23B41A7FBB158F4D8294108CC4349A0D23B4167666666EFD7294133333
 3339FD23B413333333327D72941CDCCCC4C9ED23B414A863DE425D7294160874710E7D23B4190C2F5A838D7294190C2F568E5D23B4101030000000100000007000000E51FB9333AD9294178F8B393A0D23B410AD7A37068D92941AE47E1BAA3D23B41A4703D0AE0D929417B14AE47C3D23B417B14AEC72FDA29413E0AD7A3D7D23B412DFB58D446DA2941737C709FE6D23B410000000048DA2941333333B3A1D23B41E51FB9333AD9294178F8B393A0D23B4101030000000300000040000000EFFDCAE546DA2941B484C7AAE6D23B410AD7A37053DA29411F85EBD1EED23B41E68D68B472DA2941CF93F4FC05D33B41B3AD07BCD6DB2941C0C5ED7F07D33B41F2A807BCD6DB294137D8EE7F07D33B419A9999990EDB2941333333B306D33B41BD1A22D40DDB294151098C9831D33B413E0AD7A32ADB2941F6285CCF2BD33B4185EB51B84EDB29415C8FC2F528D33B41333333B378DB2941676666262DD33B4148E17A148DDB29415C8FC23518D33B411F85EB519DDB294152B81E450CD33B417B14AE47C9DB29415C8FC27510D33B412BACC992D6DB294100A875CE10D33B411F85EB51F7DB294190C2F5A811D33B411F85EB5196DC29417B14AE47E2D23B417B14AE47B6DC2941E17A146EE6D23B410AD7A3F0CFDC2941F6285C8FFCD23B4190C2F528B3DC2941D7A3707D28D33B4152B81E
 8596DC294190C2F5684BD33B413333333384DC29419A9999595CD33B41C3F528DC95DC29413333337372D33B417A7D08189DDC294130AEA5BF72D33B41333333339DDC2941000000806CD33B416766666686E02941CDCCCCCC70D33B419A9999994EE12941333333B371D33B41CDCCCCCC16E229410000008072D33B41F306326618E2294147FA32730ED33B419A999999E0E22941CDCCCC4C0FD33B4167666666E2E2294133333333ABD23B419A999999AAE3294100000000ACD23B4167666666ACE32941676666E647D23B4133333333E4E229419A99991947D23B41CDCCCCCCE5E2294100000000E3D13B419A9999991DE229419A999919E2D13B41676666661FE22941000000007ED13B413333333357E129419A9999197DD13B41133808CD8EE02941EA70E43F7CD13B419A99999990E029413333333318D13B4167666666C8DF2941CDCCCC4C17D13B413333333300DF29416766666616D13B410000000038DE29419A99999915D13B413333333336DE2941333333B379D13B4167666666FEDE29419A9999997AD13B41CDCCCCCCFCDE2941333333B3DED13B419A99999934DE2941CDCCCCCCDDD13B41CDCCCCCC32DE2941676666E641D23B419A9999996ADD29419A99991941D23B41000000006EDD2941CDCCCCCC78D13B41CDCCCCCCA5DC29410000000078D13B419A999999A7D
 C2941676666E613D13B413333333317DB29413333333312D13B416766666615DB2941CDCCCC4C76D13B419A999999DDDB29419A99991977D13B41CDCCCCCCDBDB294133333333DBD13B41676666664BDA294100000080D9D13B412AAC33B349DA2941ECC67D993DD23B4167666666B9D82941676666E63BD23B419A999999B7D8294100000000A0D23B4157A6F58CF4D82941E77E3F42A0D23B410AD7A3700AD929411F85EB519DD23B41FFD8CDD939D929417640928DA0D23B410000000048DA2941333333B3A1D23B41EFFDCAE546DA2941B484C7AAE6D23B41040000008CF9ED96D7DB2941AA80441AD6D23B4167666666D8DB294167666666A3D23B4133333333DADB2941CDCCCC4C3FD23B418CF9ED96D7DB2941AA80441AD6D23B41050000000000000052E1294167666666A9D23B41CDCCCCCC89E029419A999999A8D23B4167666666F9DE2941676666E6A6D23B419A999999C1DF2941333333B3A7D23B410000000052E1294167666666A9D23B4101030000000100000005000000F6285C0F1BE02941E17A146EDDD33B41F26064F12DE029410E77C08DD4D33B41A9DE638EFADF294146332F59D4D33B410000000007E029419A999959E0D33B41F6285C0F1BE02941E17A146EDDD33B410103000000060000003600000000000000DFE229416766666673D33B41751E6E33DDE2
 29413E733373D7D33B410000000015E229419A999999D6D33B41CDCCCCCC4CE12941CDCCCCCCD5D33B41000000004BE12941676666E639D43B41893ECD4C13E22941290A19B33AD43B419A99999911E22941CDCCCCCC9ED43B41CDCCCCCCD9E22941333333B39FD43B419A999999DBE229419A9999993BD43B41B46EE8FF6BE42941346BEB513DD43B41333333336AE4294167666666A1D43B416766666632E52941CDCCCC4CA2D43B419A999999FAE529419A999919A3D43B41CE433766FCE52941CF3C3D0A3FD43B419A999999C4E62941676666E63FD43B41CDCCCCCCC2E6294100000000A4D43B41856556A9C1E629416A9D9F3AEBD43B41713D0A57EAE629415C8FC235F3D43B41295C8F4238E72941CDCCCC8CFDD43B416C30D37D3CE729411D6E9B9F08D53B419A99999951E82941CDCCCCCC09D53B41CDCCCCCC19E92941333333B30AD53B410000000018E92941CDCCCCCC6ED53B4133333333E0E929419A9999996FD53B419A999999DEE92941333333B3D3D53B417CD264D096EA29419CDB3387D4D53B41C758A3EAA6EA2941AC86451ECED53B419A999999A8EA29410000008070D53B4133333333AAEA2941676666660CD53B416766666672EB2941333333330DD53B413333333374EB29419A999919A9D43B41CDCCCCCC75EB29410000000045D43B419A999999ADEA29413
 333333344D43B4167666666AFEA29419A999919E0D33B4100000000B1EA2941000000007CD33B41CDCCCCCCB2EA2941676666E617D33B419A999999B4EA2941333333B3B3D23B4133333333B6EA29419A9999994FD23B41C1279919EEE9294187FCE6CC4ED23B41CDCCCCCCEFE92941333333B3EAD13B419A99999927E92941CDCCCCCCE9D13B413333333329E92941333333B385D13B410000000061E82941676666E684D13B419A9999995DE829419A9999194DD23B41333333335AE82941CDCCCC4C15D33B416766666658E829416766666679D33B41CDCCCCCC56E8294100000080DDD33B41000000001FE9294167666666DED33B419A9999998EE729419A999999DCD33B4167666666C6E62941CDCCCCCCDBD33B419A9999996DE4294133333333D9D33B41676666666FE429419A99991975D33B4133333333A7E329413333333374D33B4100000000DFE229416766666673D33B4104000000000000008BE72941CDCCCCCCA4D43B413333333353E82941333333B3A5D43B41CDCCCCCCC2E6294100000000A4D43B41000000008BE72941CDCCCCCCA4D43B41050000009A99999920E92941333333337AD33B41CDCCCCCCE8E929419A9999197BD33B4133333333E7E9294133333333DFD33B41000000001FE9294167666666DED33B419A99999920E92941333333337AD33B41040000
 0002E8EEEE76E52941707777773ED43B413333333334E52941333333333ED43B4155A39919D0E429410B0000C03DD43B4102E8EEEE76E52941707777773ED43B4104000000CDCCCCCC8CE72941333333B340D43B41333333331DE929410000008042D43B410000000055E829419A99999941D43B41CDCCCCCC8CE72941333333B340D43B410400000000000000E2E92941000000800BD53B4167666666E5E92941CDCCCC4C43D43B41CDCCCCCCE3E9294167666666A7D43B4100000000E2E92941000000800BD53B410103000000010000001500000067666666ACE32941676666E647D23B419A99999974E42941CDCCCCCC48D23B41CDCCCCCC72E42941676666E6ACD23B410000000071E429410000000011D33B41676666666FE429419A99991975D33B419A99999937E529410000000076D33B41CDCCCCCCFFE52941CDCCCCCC76D33B4100000000C8E62941333333B377D33B416766666658E829416766666679D33B41CDCCCCCC5BE8294133333333B1D23B419A9999995DE829419A9999194DD23B416766666695E72941333333334CD23B4133333333CDE62941CDCCCC4C4BD23B4167666666CBE6294100000080AFD23B413333333303E629419A999999AED23B410000000005E62941000000804AD23B41CDCCCCCC3CE529419A99999949D23B41676666663EE5294100000080E
 5D13B413333333376E42941333333B3E4D13B4100000000AEE32941CDCCCCCCE3D13B4167666666ACE32941676666E647D23B4101030000000100000005000000CDCCCCCC6CE529419A99999956C73B419A9999996EE5294100000080F2C63B4100000000DEE32941CDCCCCCCF0C63B4167666666DCE32941676666E654C73B41CDCCCCCC6CE529419A99999956C73B410103000000010000000800000067666666D2E62941000000001FD13B41333333330AE62941333333331ED13B416766666608E62941CDCCCC4C82D13B419A999999D0E629419A99991983D13B41CDCCCCCCCEE6294133333333E7D13B413333333397E729419A999919E8D13B419A9999999AE72941676666E61FD13B4167666666D2E62941000000001FD13B410103000000010000000900000000000000B8EA294100000080EBD13B419A999999B9EA29416766666687D13B4167666666BBEA2941CDCCCC4C23D13B4133333333F3E929410000008022D13B41000000002BE929419A99999921D13B413333333329E92941333333B385D13B4167666666F1E929419A99999986D13B41CDCCCCCCEFE92941333333B3EAD13B4100000000B8EA294100000080EBD13B41010300000001000000060000000000000039EC29413333333372D53B41804CAF3638EC2941526A90EE9DD53B411F85EB51ABEC2941333333
 B392D53B41A956ED7F00ED2941053B2A0B9AD53B413333333301ED29419A99991973D53B410000000039EC29413333333372D53B4101030000000100000007000000CDCCCCCC02ED2941000000000FD53B419A99999904ED2941676666E6AAD43B413333333306ED2941CDCCCCCC46D43B41000000003EEC2941676666E645D43B41676666663CEC294100000000AAD43B419A9999993AEC29419A9999190ED53B41CDCCCCCC02ED2941000000000FD53B41010300000021000000DC0000001445DF8900ED29412EB9050C9AD53B417B14AE4719ED2941E17A142E9CD53B41C3F528DC64ED29417B14AE87B9D53B4190C2F528B6ED29410AD7A3F0E7D53B4185EB51B8F9ED2941295C8F4206D63B41FE8E2EBA20EE2941836E03823CD63B4190C2F5A828EE2941A4703D8A47D63B411F85EBD169EE2941EC51B8DE7CD63B412EC1654977EE294118680004A1D63B413E0AD72379EE2941D7A370FDA5D63B41BD2783378CEE2941FF99028FACD63B415C8FC27506EF2941676666A6D6D63B411AFE884C53EF294160005F65F3D63B412B5DA5D685EF2941A6489B4D06D73B419A999999ABEF2941E17A146E14D73B4185EB5138FDEF2941F6285CCF2FD73B412DBAF5481AF02941EB8702C73CD73B41000000806EF029419A99995962D73B41CE7AB6AA8AF02941CA615A826BD73B4148E17A1
 4CCF029417B14AEC780D73B41632DB122E1F02941975893F588D73B41D7A3703D63F1294100000080BBD73B41195D796E9CF12941FA34DBD8D0D73B41340A5B1FA8F129411EC0F735D5D73B41D7AC83006FF22941A6499E711FD83B416766666670F22941333333B3D1D73B41952BAF0D6FF2294112B888761FD83B41A4703D8AABF22941A4703D0A36D83B41DDE5450736F32941F27BB10B69D83B41F6285C8F66F32941B81E85EB7AD83B41A3AA38FFB6F329417B3D2D629BD83B41676666E6D9F3294185EB5178A9D83B41A58D8AEDFCF3294113744AF4B5D83B4152B81E058DF42941F6285C4FE9D83B411C87B5D6C3F42941BA598B72FED83B41CDCCCC4C10F52941E17A14EE1BD93B413FEEBC828BF52941D106B41C20D93B415C8FC2751EF629419A99991925D93B419142BA4F53F629415C1B0BE439D93B413333333354F62941CDCCCC4C02D93B410000000056F62941333333339ED83B415BF6085B53F6294185D47DE839D93B4103E4CCC5C5F629415744F2EA66D93B41CDCCCCCC1AF72941CDCCCC4C67D93B4182A61DCFC5F629415A639CEE66D93B417945A7351AF729415AEB3A2288D93B41676666E636F72941B81E856B93D93B41291E8C89BAF729419F1CFA15CCD93B4167666666A9F829419A999919CDD93B4133333333E1F72941CDCCCC4CCCD93B416307CBA1BAF7
 29417FFF6920CCD93B410AD7A3F0D1F7294190C2F528D6D93B4185525C03E1F729415815C0FDD7D93B41A74E7FD6A8F829417A578344F0D93B41333333B3C9F82941295C8F42F4D93B418C30C5903DF92941E4B88EDF31DA3B4190C2F5A84CF92941676666E639DA3B4180005DA16FF9294157E173A846DA3B41F6285C0FC4F929415C8FC27565DA3B41B50F21852BFA2941642C140D97DA3B4162DE6B5136FA29415677133A9CDA3B416766666636FA29419A99991997DA3B41CDCCCCCC39FA2941CDCCCCCCCED93B41D1205A6136FA29413A48B6419CDA3B419A99999992FA29415C8FC275C8DA3B41676666E6CAFA2941E17A14AEBADA3B41B81E85EB0CFB294185EB51F8BDDA3B41E17A14AE64FB2941EC51B85ED1DA3B410AD7A3F064FB29419A999959C2DA3B41295C8FC2C8FB2941CDCCCCCCD3DA3B41C3F5285C0CFC2941EC51B81EF0DA3B410AD7A37037FC2941EC51B85E28DB3B41CDCCCCCC9AFC2941713D0AD754DB3B41295C8F42D0FC2941C3F5281C77DB3B41EC51B89E07FD294167666666A0DB3B410C26313B1DFD2941BB46D78FC6DB3B41F6285C8F2CFD294115AE47A1E1DB3B410E89AC2551FD2941A434715901DC3B4152B81E0554FD2941713D0AD703DC3B41F6285C0F6FFD294152B81E053DDC3B41713D0AD7C5FD2941AE47E17A8ADC3B41905FB07D12FE2941A
 4C37FE18FDC3B41694AC41FEBFE2941F1DFF5CC90DC3B419A999919F0FE2941295C8FC28FDC3B41333333332DFF2941333333F352DC3B41AE47E1FA8DFF29411F85EB512CDC3B4190C2F5A81A002A413E0AD7E30DDC3B41B1AE034A72002A4160B347E6F8DB3B416766666676002A410000000002DB3B413333333378002A41676666E69DDA3B41CDCCCCCC74002A419A99991966DB3B4109D4954E72002A4121682FE5F8DB3B41B62D0F573B012A41404082BDC8DB3B41E17A142E3E012A41F6285C0FC8DB3B415559285104022A41D9C503FA95DB3B41CDCCCCCC06022A41333333B303DB3B419A99999908022A419A9999999FDA3B413333333305022A41676666E667DB3B412198275F04022A41E1077AF695DB3B41A4703D8A33022A41A4703D0A8ADB3B4106C9049260022A418AABDE4368DB3B41A4703D0A9F022A4190C2F56839DB3B41F12AE880CE022A41DC5887B827DB3B41AE47E17AD9022A4115AE47A123DB3B41AEB054951B032A4150DCA5F104DB3B415C8FC27546032A41A4703D0AF1DA3B4152B81E8578032A41295C8F42F1DA3B41F0C50F1998032A41ED967BD6D9DA3B417B14AEC7D3032A411F85EB91ADDA3B410CD07853F4032A410610D3C3A1DA3B41A0D9DBE161042A41E927E1067ADA3B419A99999964042A4100000000DAD93B410000000063042A419A9999
 193EDA3B410850CA06B4042A412F6CD9763EDA3B419A9999192A052A4152B81EC5C8D93B41FC955A532D052A41DCDDF0D6C1D93B419A9999992E052A41CDCCCCCC76D93B416766666666042A41676666E675D93B41CDCCCCCC69042A41333333B3ADD83B419A999999A1032A41CDCCCCCCACD83B4167666666A3032A41333333B348D83B4100000000A5032A419A999999E4D73B4167666666A8032A41676666661CD73B410000000018022A41333333B31AD73B4133333333E0022A41000000801BD73B419A999999E3022A41CDCCCC4C53D63B4100000000E2022A4167666666B7D63B419A9999993A052A4100000000BAD63B416766666641052A419A99999929D53B413333333379042A41333333B328D53B413333333374042A410000000055D63B41AAED3300AC032A41746E822654D63B419A999999AD032A419A999919F0D53B4167666666AF032A41000000008CD53B4100000000B1032A41676666E627D53B41CDCCCCCCE8022A410000000027D53B416766666658012A41CDCCCC4C25D53B413333333390002A416766666624D53B410000000092002A41CDCCCC4CC0D43B419A99999993002A41333333335CD43B41CDCCCCCC5B012A419A9999195DD43B419A9999995D012A4100000000F9D33B416766666695002A419A999919F8D33B4133333333CDFF294133333333F7D
 33B410000000005FF294167666666F6D33B41CDCCCCCC3CFE294100000080F5D33B419A99999974FD2941333333B3F4D33B410FB807CD72FD29410B4000C058D43B419A999999AAFC2941676666E657D43B4167666666E2FB29419A99991957D43B41333333331AFB29413333333356D43B410000000052FA2941CDCCCC4C55D43B41CDCCCCCC89F929410000008054D43B410000000088F929419A999999B8D43B41CDCCCCCCBFF82941333333B3B7D43B419A999999F7F72941676666E6B6D43B41CDCCCCCCF5F72941000000001BD53B4100000000BEF82941CDCCCCCC1BD53B413333333386F92941333333B31CD53B41676666664EFA29419A9999991DD53B41CDCCCCCC4CFA2941333333B381D53B419A99999984F92941CDCCCCCC80D53B41CDCCCCCC82F92941676666E6E4D53B41000000004BFA2941CDCCCCCCE5D53B416766666649FA2941676666E649D63B413333333381F929410000000049D63B4100000000B9F829413333333348D63B419A999999BAF829419A999919E4D53B4167666666BCF82941676666E67FD53B4133333333F4F729419A9999197FD53B4167666666F2F7294133333333E3D53B41333333332AF72941CDCCCC4CE2D53B410000000062F6294100000080E1D53B413333333360F629419A99999945D63B419A9999995EF62941333333B3A9D63B41
 00000000EFF7294167666666ABD63B4133333333EDF72941000000800FD73B419A999999EBF729419A99999973D73B41CDCCCCCCB3F829410000008074D73B416766666623F72941CDCCCCCC72D73B410000000020F72941000000003BD83B41333333331EF729419A9999199FD83B410000000025F72941333333B30ED73B41CDCCCCCC5CF62941CDCCCCCC0DD73B416766666659F6294100000000D6D73B413333333391F5294133333333D5D73B410000000093F529410000000071D73B419A99999994F52941676666E60CD73B416766666696F52941CDCCCCCCA8D63B4133333333CEF4294100000000A8D63B41CDCCCCCCCFF42941676666E643D63B41562D33B36BF429412D33337343D63B410000000098F52941333333B344D63B41676666669BF52941000000807CD53B4133333333D3F42941333333B37BD53B4100000000D5F429410000008017D53B41CDCCCCCCD6F4294167666666B3D43B4167666666D8F42941CDCCCC4C4FD43B413333333310F42941000000804ED43B410000000048F329419A9999994DD43B41CDCCCCCC7FF22941CDCCCCCC4CD43B41676666667CF229410000000015D53B4133333333B4F129419A99991914D53B41CDCCCCCCB5F1294100000000B0D43B419A999999EDF029419A999919AFD43B4100000000ECF02941CDCCCC4C13D53B4167666
 666E8F0294100000080DBD53B413333333320F029419A999999DAD53B410000000022F029410000008076D53B41CDCCCCCC23F029416766666612D53B419A9999995BEF29410000008011D53B41CDCCCCCC59EF29419A99999975D53B41333333335DEF294167666666ADD43B41000000005FEF2941CDCCCC4C49D43B41CDCCCCCC96EE29410000008048D43B410000000095EE29419A999999ACD43B41CDCCCCCCCCED2941333333B3ABD43B4167666666C9ED2941676666E673D53B413333333301ED29419A99991973D53B411445DF8900ED29412EB9050C9AD53B41040000003333333302FB2941333333B3CFD93B4167666666CAFB29419A999999D0D93B41CDCCCCCC39FA2941CDCCCCCCCED93B413333333302FB2941333333B3CFD93B410400000000000000CCFB2941000000806CD93B41CDCCCCCCCDFB2941CDCCCC4C08D93B4167666666CAFB29419A999999D0D93B4100000000CCFB2941000000806CD93B4104000000CDCCCCCC03FB29419A9999996BD93B419A9999993BFA2941333333B36AD93B4100000000CCFB2941000000806CD93B41CDCCCCCC03FB29419A9999996BD93B4105000000CDCCCCCC79002A41CDCCCCCC39DA3B410000000042012A41333333B33ADA3B41E14CCD4C40012A412C57AFCC9EDA3B413333333378002A41676666E69DDA3B41CDCCCCCC79
 002A41CDCCCCCC39DA3B4105000000D653CC4CD4022A417B9FE84CD8D93B41F45ACC4CD4022A4187FCE64CD8D93B41676666669C032A419A999919D9D93B419A99999964042A4100000000DAD93B41D653CC4CD4022A417B9FE84CD8D93B4104000000333333339E032A410000000075D93B41CDCCCCCC9F032A41676666E610D93B419A999999A1032A41CDCCCCCCACD83B41333333339E032A410000000075D93B410400000000000000D6022A413333333374D93B41CDCCCCCC0D022A41CDCCCC4C73D93B41333333339E032A410000000075D93B4100000000D6022A413333333374D93B41040000006766666601FF29419A999999BED43B413333333303FF2941000000805AD43B410000000005FF294167666666F6D33B416766666601FF29419A999999BED43B41040000000000000071FD2941676666E6BCD43B413333333339FE2941333333B3BDD43B416766666601FF29419A999999BED43B410000000071FD2941676666E6BCD43B4104000000676666666FFD29410000000021D53B419A9999996DFD29419A99991985D53B410000000071FD2941676666E6BCD43B41676666666FFD29410000000021D53B4104000000CDCCCCCCFFFE2941333333B322D53B419A99999937FE2941676666E621D53B41676666666FFD29410000000021D53B41CDCCCCCCFFFE2941333333B
 322D53B410500000000000000C8FF29419A99999923D53B4133333333C6FF2941333333B387D53B4100000000FEFE2941CDCCCCCC86D53B41CDCCCCCCFFFE2941333333B322D53B4100000000C8FF29419A99999923D53B41060000009A999999C4FF2941CDCCCCCCEBD53B41CDCCCCCC8C002A419A999999ECD53B41000000008B002A41333333B350D63B41CDCCCCCCC2FF2941676666E64FD63B4133333333C6FF2941333333B387D53B419A999999C4FF2941CDCCCCCCEBD53B41040000000E3B3333DDFB29416F66666683D53B4167666666A5FC29413333333384D53B419A9999996DFD29419A99991985D53B410E3B3333DDFB29416F66666683D53B4104000000FAFFFFFFA7FC294144CECC0CEED43B41CDCCCCCCA8FC294100000000BCD43B419A999999AAFC2941676666E657D43B41FAFFFFFFA7FC294144CECC0CEED43B4104000000CDCCCCCC16FB2941676666661ED53B4100000000DFFB2941CDCCCC4C1FD53B41676666664EFA29419A9999991DD53B41CDCCCCCC16FB2941676666661ED53B41040000006766666628F729416766666646D63B41CDCCCCCCF0F72941CDCCCC4C47D63B413333333360F629419A99999945D63B416766666628F729416766666646D63B4104000000CDCCCCCC3DF32941CDCCCC4CA6D63B410000000006F429419A999919A7D63B413333
 3333CEF4294100000000A8D63B41CDCCCCCC3DF32941CDCCCC4CA6D63B4104000000CDCCCCCC1CF02941CDCCCCCCA2D63B41CFCCCCCC1CF0294164CCCCCCA2D63B41CACCCCCC1CF02941A6CDCCCCA2D63B41CDCCCCCC1CF02941CDCCCCCCA2D63B41040000006766666602F42941CDCCCC4C6FD73B41CDCCCCCCCAF429413333333370D73B41333333333AF32941000000806ED73B416766666602F42941CDCCCC4C6FD73B41040000009A99999944F32941CDCCCCCC15D53B41E85F54FD41F329411683449AACD53B41CDCCCCCC42F32941676666E679D53B419A99999944F32941CDCCCCCC15D53B4105000000000000007CF92941CDCCCC4C75D73B419A9999997DF929413333333311D73B410000000046FA29419A99991912D73B413333333344FA29413333333376D73B41000000007CF92941CDCCCC4C75D73B41070000009A99999947FA294100000000AED63B41CDCCCCCC0FFB2941CDCCCCCCAED63B4100000000D8FB2941333333B3AFD63B4167666666D6FB2941CDCCCCCC13D73B41333333330EFB29410000000013D73B410000000046FA29419A99991912D73B419A99999947FA294100000000AED63B410400000000000000D3FB294100000000DCD73B419A999999D4FB2941676666E677D73B4167666666D6FB2941CDCCCCCC13D73B4100000000D3FB294100000000D
 CD73B410400000000000000A2FC2941000000804CD63B41CDCCCCCCD9FB29419A9999994BD63B419A99999911FB2941333333B34AD63B4100000000A2FC2941000000804CD63B41040000000000000096FC29413333333309D93B419A99999997FC29419A999919A5D83B416766666699FC29410000000041D83B410000000096FC29413333333309D93B410400000000000000F2FE29419A99999943D83B41CDCCCCCCF3FE294100000080DFD73B4167666666F0FE2941333333B3A7D83B4100000000F2FE29419A99999943D83B410500000000000000BCFF2941CDCCCC4CE0D73B41676666664C012A4100000000E2D73B413333333384002A4133333333E1D73B41CDCCCCCCF3FE294100000080DFD73B4100000000BCFF2941CDCCCC4CE0D73B41080000009A99999914022A41676666E6E2D73B41CDCCCCCCDC022A41333333B3E3D73B4133333333DB022A41676666E647D83B41CDCCCCCC12022A410000000047D83B416766666682002A41CDCCCC4C45D83B419A9999994A012A419A99991946D83B41676666664C012A4100000000E2D73B419A99999914022A41676666E6E2D73B41040000009A999999BDFF2941333333337CD73B4133333333C1FF294100000000B4D63B4167666666BFFF29419A99991918D73B419A999999BDFF2941333333337CD73B4104000000CDCCCC
 CC4F012A41CDCCCCCC19D73B419A99999987002A410000000019D73B4167666666BFFF29419A99991918D73B41CDCCCCCC4F012A41CDCCCCCC19D73B41040000006766666652012A41666666A683D63B413333333353012A419A99999951D63B410000000055012A4100000080EDD53B416766666652012A41666666A683D63B41010300000001000000050000009A9999992DF729419A9999191AD53B41333333339DF529416766666618D53B41676666669BF52941000000807CD53B41000000002CF72941333333337ED53B419A9999992DF729419A9999191AD53B4101030000000700000025000000676666667BFD29413333333364D23B419A99999979FD294167666666C8D23B410000000078FD2941000000802CD33B413333333340FE2941CDCCCC4C2DD33B41CDCCCCCC3CFE294100000080F5D33B4133333333CDFF294133333333F7D33B419A9999995D012A4100000000F9D33B41CDCCCCCC25022A41CDCCCCCCF9D33B410000000024022A41676666E65DD43B4133333333EC022A41CDCCCCCC5ED43B4100000000EE022A41333333B3FAD33B41CDCCCCCCEF022A419A99999996D33B419A99999927022A41333333B395D33B413333333329022A419A99999931D33B4167666666F1022A410000008032D33B4133333333F3022A4167666666CED23B41CDCCCCCCF4022A4
 1CDCCCC4C6AD23B4100000000BD032A419A9999196BD23B41CDCCCCCCBE032A410000000007D23B419A999999F6022A419A99991906D23B4167666666F8022A4100000000A2D13B419A999999C0032A41676666E6A2D13B4133333333C2032A41CDCCCCCC3ED13B4100000000FA022A41676666E63DD13B41CDCCCCCCFB022A41CDCCCCCCD9D03B419A99999933022A4100000000D9D03B41676666666B012A419A999919D8D03B419A99999969012A41333333333CD13B4167666666A1002A41676666663BD13B4133333333D9FF2941000000803AD13B4167666666D7FF29419A9999999ED13B41333333330FFF2941333333B39DD13B410000000011FF29419A99999939D13B41CDCCCCCC48FE2941CDCCCCCC38D13B410000000047FE2941676666E69CD13B41CDCCCCCC7EFD2941000000009CD13B41676666667BFD29413333333364D23B41040000006766666645FE29410000000001D23B419A99999943FE29419A99991965D23B410000000047FE2941676666E69CD13B416766666645FE29410000000001D23B41040000006766666608FF2941333333332ED33B41000000000AFF29419A999919CAD23B41CDCCCCCC0BFF29410000000066D23B416766666608FF2941333333332ED33B410400000090999999D0FF29412F0200002FD33B41A3999999D0FF2941D1FDFFFF2ED3
 3B419A999999D0FF2941000000002FD33B4190999999D0FF29412F0200002FD33B4104000000333333335F012A41676666E694D33B4152E665E6CEFF29413B0FB71993D33B410000000097002A410000000094D33B41333333335F012A41676666E694D33B4104000000000000002B022A4100000080CDD23B419A9999999A002A41CDCCCCCCCBD23B41CDCCCCCC62012A419A999999CCD23B41000000002B022A4100000080CDD23B41040000009A9999992C022A416766666669D23B410000000030022A4133333333A1D13B41676666662E022A41CDCCCC4C05D23B419A9999992C022A416766666669D23B4101030000000100000007000000333333335A012A4133333333C1D43B416766666658012A41CDCCCC4C25D53B419A99999920022A419A99991926D53B416766666622022A4100000000C2D43B410000000024022A41676666E65DD43B41CDCCCCCC5B012A419A9999195DD43B41333333335A012A4133333333C1D43B410103000000010000000500000033333333E0022A41000000801BD73B4167666666A8032A41676666661CD73B4133333333AA032A41CDCCCC4CB8D63B4100000000E2022A4167666666B7D63B4133333333E0022A41000000801BD73B4101030000000100000007000000CDCCCCCCB2032A41CDCCCCCCC3D43B419A999999EA022A41676666E6C2D
 43B41CDCCCCCCE8022A410000000027D53B413333333379042A41333333B328D53B41CDCCCCCC7C042A410000008060D43B4167666666B4032A419A9999995FD43B41CDCCCCCCB2032A41CDCCCCCCC3D43B410103000000010000000700000033333333EC022A41CDCCCCCC5ED43B4167666666B4032A419A9999995FD43B4100000000B8032A416766666697D33B4167666666BB032A4133333333CFD23B4133333333F3022A4167666666CED23B41CDCCCCCCEF022A419A99999996D33B4133333333EC022A41CDCCCCCC5ED43B41
-</op>
-</test>
+  <test> <op name="unionArea" arg1="A"> 9709811.15121037 </op> </test>
 </case>
 
 </run>

commit 6f73f02624aaff98a7289f4a481bcfc97bc845df
Author: Daniel Baston <dbaston at gmail.com>
Date:   Tue Feb 2 20:11:58 2021 -0500

    Use TemplateSTRtree in IndexedPointInAreaLocator

diff --git a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
index 364ef34..fc5bb77 100644
--- a/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
+++ b/include/geos/algorithm/locate/IndexedPointInAreaLocator.h
@@ -20,7 +20,7 @@
 #include <geos/geom/LineSegment.h>
 #include <geos/algorithm/locate/PointOnGeometryLocator.h> // inherited
 #include <geos/index/ItemVisitor.h> // inherited
-#include <geos/index/intervalrtree/SortedPackedIntervalRTree.h> // inherited
+#include <geos/index/strtree/TemplateSTRtree.h>
 
 #include <memory>
 #include <vector> // composition
@@ -56,7 +56,7 @@ class IndexedPointInAreaLocator : public PointOnGeometryLocator {
 private:
     class IntervalIndexedGeometry {
     private:
-        index::intervalrtree::SortedPackedIntervalRTree index;
+        index::strtree::TemplateSTRtree<geom::LineSegment*, index::strtree::IntervalTraits> index;
         bool isEmpty;
 
         void init(const geom::Geometry& g);
@@ -68,26 +68,12 @@ private:
     public:
         IntervalIndexedGeometry(const geom::Geometry& g);
 
-        void query(double min, double max, index::ItemVisitor* visitor);
+        template<typename Visitor>
+        void query(double min, double max, Visitor&& f) {
+            index.query(index::strtree::Interval(min, max), f);
+        }
     };
 
-
-    class SegmentVisitor : public index::ItemVisitor {
-    private:
-        algorithm::RayCrossingCounter* counter;
-
-    public:
-        SegmentVisitor(algorithm::RayCrossingCounter* p_counter)
-            :	counter(p_counter)
-        { }
-
-        ~SegmentVisitor() override
-        { }
-
-        void visitItem(void* item) override;
-    };
-
-
     const geom::Geometry& areaGeom;
     std::unique_ptr<IntervalIndexedGeometry> index;
 
diff --git a/src/algorithm/locate/IndexedPointInAreaLocator.cpp b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
index 6e860e3..5a39484 100644
--- a/src/algorithm/locate/IndexedPointInAreaLocator.cpp
+++ b/src/algorithm/locate/IndexedPointInAreaLocator.cpp
@@ -23,7 +23,8 @@
 #include <geos/geom/LinearRing.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/util/LinearComponentExtracter.h>
-#include <geos/index/intervalrtree/SortedPackedIntervalRTree.h>
+#include <geos/index/strtree/Interval.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 #include <geos/util.h>
 #include <geos/algorithm/RayCrossingCounter.h>
 #include <geos/index/ItemVisitor.h>
@@ -63,13 +64,10 @@ IndexedPointInAreaLocator::IntervalIndexedGeometry::init(const geom::Geometry& g
         addLine(line->getCoordinatesRO());
     }
 
-    index = index::intervalrtree::SortedPackedIntervalRTree(segments.size());
-
     for(geom::LineSegment& seg : segments) {
-        index.insert(
-            std::min(seg.p0.y, seg.p1.y),
-            std::max(seg.p0.y, seg.p1.y),
-            &seg);
+        index.insert(index::strtree::Interval(std::min(seg.p0.y, seg.p1.y),
+                                              std::max(seg.p0.y, seg.p1.y)),
+                     &seg);
     }
 }
 
@@ -116,29 +114,13 @@ IndexedPointInAreaLocator::locate(const geom::Coordinate* /*const*/ p)
 
     algorithm::RayCrossingCounter rcc(*p);
 
-    IndexedPointInAreaLocator::SegmentVisitor visitor(&rcc);
-
-    index->query(p->y, p->y, &visitor);
+    index->query(p->y, p->y, [&rcc](const geom::LineSegment* ls) {
+        rcc.countSegment(ls->p0, ls->p1);
+    });
 
     return rcc.getLocation();
 }
 
-void
-IndexedPointInAreaLocator::SegmentVisitor::visitItem(void* item)
-{
-    geom::LineSegment* seg = static_cast<geom::LineSegment*>(item);
-
-    counter->countSegment(seg->p0, seg->p1);
-}
-
-void
-IndexedPointInAreaLocator::IntervalIndexedGeometry::query(double min, double max, index::ItemVisitor* visitor)
-{
-    if (isEmpty)
-        return;
-    index.query(min, max, visitor);
-}
-
 
 } // geos::algorithm::locate
 } // geos::algorithm

commit f79a3ad98cc8bf85dc5d47f441dc6803cda3fda8
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu Jan 21 22:45:23 2021 -0500

    Use template tree in MCIndexSegmentSetMutualIntersector

diff --git a/include/geos/noding/MCIndexSegmentSetMutualIntersector.h b/include/geos/noding/MCIndexSegmentSetMutualIntersector.h
index da3a9c0..2f4b348 100644
--- a/include/geos/noding/MCIndexSegmentSetMutualIntersector.h
+++ b/include/geos/noding/MCIndexSegmentSetMutualIntersector.h
@@ -23,7 +23,7 @@
 #include <geos/noding/SegmentSetMutualIntersector.h> // inherited
 #include <geos/index/chain/MonotoneChainOverlapAction.h> // inherited
 #include <geos/index/chain/MonotoneChain.h> // inherited
-#include <geos/index/strtree/SimpleSTRtree.h> // inherited
+#include <geos/index/strtree/TemplateSTRtree.h> // inherited
 
 namespace geos {
 namespace index {
@@ -58,7 +58,6 @@ public:
 
     MCIndexSegmentSetMutualIntersector()
         : monoChains()
-        , index(new index::strtree::SimpleSTRtree())
         , indexCounter(0)
         , processCounter(0)
         , nOverlaps(0)
@@ -71,7 +70,7 @@ public:
     index::SpatialIndex*
     getIndex()
     {
-        return index.get();
+        return &index;
     }
 
     void setBaseSegments(SegmentString::ConstVect* segStrings) override;
@@ -113,7 +112,7 @@ private:
      * envelope (range) queries efficiently (such as a index::quadtree::Quadtree
      * or index::strtree::STRtree).
      */
-    std::unique_ptr<index::SpatialIndex> index;
+    index::strtree::TemplateSTRtree<index::chain::MonotoneChain*> index;
     int indexCounter;
     int processCounter;
     // statistics
diff --git a/src/noding/MCIndexSegmentSetMutualIntersector.cpp b/src/noding/MCIndexSegmentSetMutualIntersector.cpp
index 0705905..5f18e94 100644
--- a/src/noding/MCIndexSegmentSetMutualIntersector.cpp
+++ b/src/noding/MCIndexSegmentSetMutualIntersector.cpp
@@ -60,15 +60,13 @@ MCIndexSegmentSetMutualIntersector::intersectChains()
 {
     MCIndexSegmentSetMutualIntersector::SegmentOverlapAction overlapAction(*segInt);
 
-    std::vector<void*> overlapChains;
+    std::vector<MonotoneChain*> overlapChains;
     for(auto& queryChain : monoChains) {
         overlapChains.clear();
 
-        index->query(&(queryChain.getEnvelope()), overlapChains);
-
-        for(std::size_t j = 0, nj = overlapChains.size(); j < nj; j++) {
-            MonotoneChain* testChain = (MonotoneChain*)(overlapChains[j]);
+        index.query(queryChain.getEnvelope(), overlapChains);
 
+        for(MonotoneChain* testChain : overlapChains) {
             queryChain.computeOverlaps(testChain, &overlapAction);
             nOverlaps++;
             if(segInt->isDone()) {
@@ -97,7 +95,7 @@ MCIndexSegmentSetMutualIntersector::process(SegmentString::ConstVect* segStrings
 {
     if (!indexBuilt) {
         for (auto& mc: indexChains) {
-            index->insert(&(mc.getEnvelope()), &mc);
+            index.insert(&(mc.getEnvelope()), &mc);
         }
         indexBuilt = true;
     }

commit 04121400d17251c23cbd55dacdea8dd6ae2e2fcd
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu Jan 21 22:45:08 2021 -0500

    Use template tree in MCIndexNoder

diff --git a/include/geos/noding/MCIndexNoder.h b/include/geos/noding/MCIndexNoder.h
index 0b8ef59..f8c76c1 100644
--- a/include/geos/noding/MCIndexNoder.h
+++ b/include/geos/noding/MCIndexNoder.h
@@ -26,7 +26,7 @@
 #include <geos/index/chain/MonotoneChainOverlapAction.h> // for inheritance
 #include <geos/index/chain/MonotoneChain.h>
 #include <geos/noding/SinglePassNoder.h> // for inheritance
-#include <geos/index/strtree/SimpleSTRtree.h> // for composition
+#include <geos/index/strtree/TemplateSTRtree.h> // for composition
 #include <geos/util.h>
 
 #include <vector>
@@ -67,7 +67,7 @@ class GEOS_DLL MCIndexNoder : public SinglePassNoder {
 
 private:
     std::vector<index::chain::MonotoneChain> monoChains;
-    index::strtree::SimpleSTRtree index;
+    index::strtree::TemplateSTRtree<index::chain::MonotoneChain*> index;
     std::vector<SegmentString*>* nodedSegStrings;
     // statistics
     int nOverlaps;

commit 868b33cd901a22e049c60d580667ba51e5381913
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu Jan 21 21:47:07 2021 -0500

    Switch to template tree in IndexedNestedShellTester

diff --git a/src/operation/valid/IndexedNestedShellTester.cpp b/src/operation/valid/IndexedNestedShellTester.cpp
index aa36f1a..9f20215 100644
--- a/src/operation/valid/IndexedNestedShellTester.cpp
+++ b/src/operation/valid/IndexedNestedShellTester.cpp
@@ -15,7 +15,7 @@
 #include <geos/algorithm/PointLocation.h>
 #include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
 #include <geos/geom/Polygon.h>
-#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 #include <geos/operation/valid/IndexedNestedShellTester.h>
 #include <geos/operation/valid/IsValidOp.h>
 
@@ -90,23 +90,21 @@ IndexedNestedShellTester::compute() {
 
     processed = true;
 
-    index::strtree::STRtree tree;
+    index::strtree::TemplateSTRtree<const geom::LinearRing*> tree;
     for (const auto& p : polys) {
-        tree.insert(p->getEnvelopeInternal(), (void*) p->getExteriorRing());
+        tree.insert(p->getExteriorRing());
     }
 
-    std::vector<void*> hits;
+    std::vector<const geom::LinearRing*> hits;
     for (const auto& outerPoly : polys) {
         hits.clear();
 
         PolygonIndexedLocators locs(*outerPoly);
         const geom::LinearRing* outerShell = outerPoly->getExteriorRing();
 
-        tree.query(outerShell->getEnvelopeInternal(), hits);
-
-        for (const auto& hit : hits) {
-            const geom::LinearRing* potentialInnerShell = static_cast<const geom::LinearRing*>(hit);
+        tree.query(*outerShell->getEnvelopeInternal(), hits);
 
+        for (const auto& potentialInnerShell : hits) {
             if (potentialInnerShell == outerShell) {
                 continue;
             }

commit 41e1c326f9bd29ac1232fe88f9a0514fcf57badd
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu Jan 21 18:23:56 2021 -0500

    Switch to templated tree in HoleAssigner

diff --git a/include/geos/operation/polygonize/HoleAssigner.h b/include/geos/operation/polygonize/HoleAssigner.h
index 53776fe..39fd297 100644
--- a/include/geos/operation/polygonize/HoleAssigner.h
+++ b/include/geos/operation/polygonize/HoleAssigner.h
@@ -20,7 +20,7 @@
 #define GEOS_OP_POLYGONIZE_HOLEASSIGNER_H
 
 #include <geos/operation/polygonize/EdgeRing.h>
-#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
 
 #include <vector>
 
@@ -58,7 +58,7 @@ private:
     void buildIndex();
 
     std::vector<EdgeRing*>& m_shells;
-    geos::index::strtree::STRtree m_shellIndex;
+    geos::index::strtree::TemplateSTRtree<EdgeRing*> m_shellIndex;
 };
 }
 }
diff --git a/src/operation/polygonize/HoleAssigner.cpp b/src/operation/polygonize/HoleAssigner.cpp
index 29cba6c..6509d86 100644
--- a/src/operation/polygonize/HoleAssigner.cpp
+++ b/src/operation/polygonize/HoleAssigner.cpp
@@ -58,14 +58,8 @@ HoleAssigner::assignHoleToShell(EdgeRing* holeER)
 
 std::vector<EdgeRing*>
 HoleAssigner::findShells(const geom::Envelope& e) {
-    std::vector<void*> shellsVoid;
-    m_shellIndex.query(&e, shellsVoid);
-
-    // TODO turn AbstractSTRtree::query into a template and remove this
-    std::vector<EdgeRing*> shells{shellsVoid.size()};
-    for (std::size_t i = 0; i < shellsVoid.size(); i++) {
-        shells[i] = static_cast<EdgeRing*>(shellsVoid[i]);
-    }
+    std::vector<EdgeRing*> shells;
+    m_shellIndex.query(e, shells);
 
     return shells;
 }

commit 00fff60636aee7931b5c80c92b84d726e014dd8d
Author: Daniel Baston <dbaston at gmail.com>
Date:   Fri Mar 5 20:17:41 2021 -0500

    Add SpatialIndex benchmark

diff --git a/benchmarks/index/CMakeLists.txt b/benchmarks/index/CMakeLists.txt
index e401c49..c949099 100644
--- a/benchmarks/index/CMakeLists.txt
+++ b/benchmarks/index/CMakeLists.txt
@@ -10,3 +10,14 @@
 ################################################################################
 
 add_subdirectory(chain)
+
+find_package(benchmark QUIET)
+
+IF(benchmark_FOUND)
+    add_executable(perf_spatial_index SpatialIndexPerfTest.cpp)
+    target_include_directories(perf_spatial_index PUBLIC
+            $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
+            $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>)
+    target_link_libraries(perf_spatial_index PRIVATE
+            benchmark::benchmark geos)
+endif()
diff --git a/benchmarks/index/SpatialIndexPerfTest.cpp b/benchmarks/index/SpatialIndexPerfTest.cpp
new file mode 100644
index 0000000..a1c858d
--- /dev/null
+++ b/benchmarks/index/SpatialIndexPerfTest.cpp
@@ -0,0 +1,302 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2021 Daniel Baston
+ *
+ * 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 <random>
+
+#include <benchmark/benchmark.h>
+
+#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/SimpleSTRtree.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
+#include <geos/index/quadtree/Quadtree.h>
+#include <geos/index/intervalrtree/SortedPackedIntervalRTree.h>
+
+using geos::geom::Coordinate;
+using geos::geom::Envelope;
+using geos::index::intervalrtree::SortedPackedIntervalRTree;
+using geos::index::quadtree::Quadtree;
+using geos::index::strtree::STRtree;
+using geos::index::strtree::SimpleSTRtree;
+using geos::index::strtree::TemplateSTRtree;
+using geos::index::strtree::Interval;
+using geos::index::strtree::ItemDistance;
+using geos::index::strtree::ItemBoundable;
+
+using TemplateIntervalTree = TemplateSTRtree<const Interval*, geos::index::strtree::IntervalTraits>;
+
+//////////////////////////
+// Test Data Generation //
+//////////////////////////
+
+static std::vector<Envelope> generate_envelopes(std::default_random_engine & e,
+                                                const Envelope& extent,
+                                                std::size_t n) {
+    std::uniform_real_distribution<> centroid_x(extent.getMinX(), extent.getMaxX());
+    std::uniform_real_distribution<> centroid_y(extent.getMinY(), extent.getMaxY());
+
+    // distributions of width and aspect ratio fit from HydroBASINS level 7 (Africa)
+    std::weibull_distribution<> size_x(1.606, 0.00989);
+    std::lognormal_distribution<> y_rat(-0.027, 0.4884);
+
+    std::vector<Envelope> envelopes(n);
+    for (std::size_t i = 0; i < n; i++) {
+        double cx = centroid_x(e);
+        double cy = centroid_y(e);
+
+        double width = size_x(e) * extent.getWidth();
+        double height = width * y_rat(e);
+
+        envelopes[i] = Envelope(cx - width / 2, cx + width / 2,
+                               cy - height / 2, cy + height / 2);
+
+    }
+
+    return envelopes;
+}
+
+std::vector<Coordinate> generate_uniform_points(std::default_random_engine& eng,
+                                                const Envelope& box,
+                                                std::size_t n) {
+    std::vector<Coordinate> pts(n);
+
+    std::uniform_real_distribution<> qx(box.getMinX(), box.getMaxX());
+    std::uniform_real_distribution<> qy(box.getMinY(), box.getMaxY());
+
+    for (std::size_t i = 0; i < n; i++) {
+        pts[i] = { qx(eng), qy(eng) };
+    }
+
+    return pts;
+}
+
+//////////////
+// Visitors //
+//////////////
+
+class CountingVisitor : public geos::index::ItemVisitor {
+    size_t hits = 0;
+    void visitItem(void* item) override {
+        hits += (item != nullptr);
+    }
+};
+
+struct EnvelopeDistance : public ItemDistance {
+    double distance(const ItemBoundable* a, const ItemBoundable* b) override {
+        Envelope* ea = static_cast<Envelope*>(a->getItem());
+        Envelope* eb = static_cast<Envelope*>(b->getItem());
+
+        return ea->distance(*eb);
+    }
+
+    double operator()(const Envelope* a, const Envelope* b) {
+        return a->distance(*b);
+    }
+};
+
+/////////////////
+// 1D adapters //
+/////////////////
+
+static inline void insert(TemplateIntervalTree & tree, const Interval& i) {
+    tree.insert(i, &i);
+}
+
+static inline void insert(SortedPackedIntervalRTree & tree, const Interval& i) {
+    tree.insert(i.getMin(), i.getMax(), (void*) &i);
+}
+
+static inline void query(TemplateIntervalTree & tree, const Interval& i) {
+    size_t hits = 0;
+    tree.query(i, [&hits](const Interval* item) {
+        hits += (item != nullptr);
+    });
+}
+
+static inline void query(SortedPackedIntervalRTree & tree, const Interval& i) {
+    CountingVisitor cv;
+    tree.query(i.getMin(), i.getMax(), &cv);
+}
+
+/////////////////
+// 2D adapters //
+/////////////////
+
+template<class Tree>
+static inline const void* nearestNeighbour(Tree & tree, const Envelope & e) {
+    EnvelopeDistance dist;
+    return tree.nearestNeighbour(e, &e, dist);
+}
+
+static inline const void* nearestNeighbour(SimpleSTRtree & tree, const Envelope & e) {
+    EnvelopeDistance dist;
+    return tree.nearestNeighbour(&e, &e, &dist);
+}
+
+static inline const void* nearestNeighbour(STRtree & tree, const Envelope & e) {
+    EnvelopeDistance dist;
+    return tree.nearestNeighbour(&e, &e, &dist);
+}
+
+///////////////////
+// 1D benchmarks //
+///////////////////
+
+template<class Tree>
+static void BM_STRtree1DConstruct(benchmark::State& state) {
+    std::default_random_engine eng(12345);
+    Envelope extent(0, 1, 0, 1);
+    auto envelopes = generate_envelopes(eng, extent, 10000);
+    std::vector<Interval> intervals;
+    intervals.reserve(envelopes.size());
+
+    for (const auto& e : envelopes) {
+        intervals.emplace_back(e.getMinY(), e.getMaxY());
+    }
+
+    Interval outside_extent(extent.getMaxY() + 100, extent.getMaxY() + 101);
+
+    for (auto _ : state) {
+        Tree tree(10);
+
+        for (const auto& interval : intervals) {
+            insert(tree, interval);
+        }
+        query(tree, outside_extent);
+    }
+}
+
+template<class Tree>
+static void BM_STRtree1DQuery(benchmark::State& state) {
+    std::default_random_engine eng(12345);
+    Envelope extent(0, 1, 0, 1);
+    auto envelopes = generate_envelopes(eng, extent, 10000);
+    std::vector<Interval> intervals;
+    intervals.reserve(envelopes.size());
+
+    for (const auto& e : envelopes) {
+        intervals.emplace_back(e.getMinY(), e.getMaxY());
+    }
+
+    Tree tree(10);
+
+    for (const auto& interval : intervals) {
+        insert(tree, interval);
+    }
+    Interval outside_extent(extent.getMaxY() + 100, extent.getMaxY() + 101);
+    query(tree, outside_extent); // force construction
+
+    for (auto _ : state) {
+        for (auto& i : intervals) {
+            query(tree, i);
+        }
+    }
+}
+
+///////////////////
+// 2D benchmarks //
+///////////////////
+
+template<class Tree>
+static void BM_STRtree2DConstruct(benchmark::State& state) {
+    std::default_random_engine eng(12345);
+    Envelope extent(0, 1, 0, 1);
+    auto envelopes = generate_envelopes(eng, extent, 10000);
+    Envelope empty_env;
+
+    std::vector<void*> hits;
+
+    for (auto _ : state) {
+        Tree tree;
+        for (auto& e : envelopes) {
+            tree.insert(&e, &e);
+        }
+        tree.query(&empty_env, hits); // query with empty envelope to force construction
+    }
+}
+
+template<class Tree>
+static void BM_STRtree2DQuery(benchmark::State& state) {
+    std::default_random_engine eng(12345);
+    Envelope extent(0, 1, 0, 1);
+    auto envelopes = generate_envelopes(eng, extent, 10000);
+    Envelope empty_env;
+
+    std::vector<void*> hits;
+
+    Tree tree;
+    for (auto& e : envelopes) {
+        tree.insert(&e, &e);
+    }
+    tree.query(&empty_env, hits); // query with empty envelope to force construction
+
+    for (auto _ : state) {
+        hits.clear();
+        for (auto& e : envelopes) {
+            tree.query(&e, hits);
+        }
+    }
+}
+
+template<class Tree>
+static void BM_STRtree2DNearest(benchmark::State& state) {
+    std::default_random_engine eng(12345);
+    Envelope extent(0, 1, 0, 1);
+    auto envelopes = generate_envelopes(eng, extent, 10000);
+    Envelope empty_env;
+
+    Envelope queryEnv = extent;
+    queryEnv.expandBy(0.25 * queryEnv.getWidth(), 0.25 * queryEnv.getHeight());
+    auto queryPoints = generate_uniform_points(eng, queryEnv, 10000);
+
+    std::vector<Envelope> queryEnvelopes(queryPoints.size());
+    for (std::size_t i = 0; i < queryEnvelopes.size(); i++) {
+        queryEnvelopes[i] = Envelope(queryPoints[i]);
+    }
+
+    std::vector<void*> hits;
+
+    Tree tree;
+    for (auto& e : envelopes) {
+        tree.insert(&e, &e);
+    }
+    tree.query(&empty_env, hits); // query with empty envelope to force construction
+
+    for (auto _ : state) {
+        for (auto& e : queryEnvelopes) {
+            nearestNeighbour(tree, e);
+        }
+    }
+}
+
+BENCHMARK_TEMPLATE(BM_STRtree1DConstruct, SortedPackedIntervalRTree);
+BENCHMARK_TEMPLATE(BM_STRtree1DConstruct, TemplateIntervalTree);
+BENCHMARK_TEMPLATE(BM_STRtree1DQuery, SortedPackedIntervalRTree);
+BENCHMARK_TEMPLATE(BM_STRtree1DQuery, TemplateIntervalTree);
+
+BENCHMARK_TEMPLATE(BM_STRtree2DConstruct, Quadtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DConstruct, STRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DConstruct, SimpleSTRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DConstruct, TemplateSTRtree<const Envelope*>);
+
+BENCHMARK_TEMPLATE(BM_STRtree2DNearest, STRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DNearest, SimpleSTRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DNearest, TemplateSTRtree<const Envelope*>);
+
+BENCHMARK_TEMPLATE(BM_STRtree2DQuery, Quadtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DQuery, STRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DQuery, SimpleSTRtree);
+BENCHMARK_TEMPLATE(BM_STRtree2DQuery, TemplateSTRtree<const Envelope*>);
+
+BENCHMARK_MAIN();
+

commit 7cae4e8923984411b26d954453e1bd963a638976
Author: Daniel Baston <dbaston at gmail.com>
Date:   Fri Mar 5 23:13:39 2021 -0500

    Add TemplateSTRtree

diff --git a/include/geos/index/strtree/Interval.h b/include/geos/index/strtree/Interval.h
index 67fccb9..65ff1c4 100644
--- a/include/geos/index/strtree/Interval.h
+++ b/include/geos/index/strtree/Interval.h
@@ -16,6 +16,8 @@
 #define GEOS_INDEX_STRTREE_INTERVAL_H
 
 #include <geos/export.h>
+#include <algorithm>
+#include <cassert>
 
 namespace geos {
 namespace index { // geos::index
@@ -27,11 +29,25 @@ namespace strtree { // geos::index::strtree
 ///
 class GEOS_DLL Interval {
 public:
-    Interval(double newMin, double newMax);
-    double getCentre();
-    Interval* expandToInclude(const Interval* other);
-    bool intersects(const Interval* other) const;
-    bool equals(const Interval* o) const;
+    Interval(double newMin, double newMax) : imin(newMin), imax(newMax) {
+        assert(imin <= imax);
+    }
+
+    double getMin() const { return imin; }
+    double getMax() const { return imax; }
+    double getWidth() const { return imax - imin; }
+    double getCentre() const { return (imin + imax) / 2; }
+    Interval* expandToInclude(const Interval* other) {
+        imax = std::max(imax, other->imax);
+        imin = std::min(imin, other->imin);
+        return this;
+    }
+    bool intersects(const Interval* other) const {
+        return !(other->imin > imax || other->imax < imin);
+    }
+    bool equals(const Interval* other) const {
+        return imin == other->imin && imax == other->imax;
+    }
 private:
     double imin;
     double imax;
diff --git a/include/geos/index/strtree/TemplateSTRNode.h b/include/geos/index/strtree/TemplateSTRNode.h
new file mode 100644
index 0000000..cf3f38d
--- /dev/null
+++ b/include/geos/index/strtree/TemplateSTRNode.h
@@ -0,0 +1,154 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020-2021 Daniel Baston
+ *
+ * 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.
+ *
+ **********************************************************************/
+
+#pragma once
+
+namespace geos {
+namespace index {
+namespace strtree {
+
+template<typename ItemType, typename BoundsTraits>
+class TemplateSTRNode {
+private:
+    using BoundsType = typename BoundsTraits::BoundsType;
+
+    BoundsType bounds;
+
+    union Body {
+        ItemType item;
+        const TemplateSTRNode* childrenEnd;
+
+        explicit Body (ItemType&& p_item) : item(std::forward<ItemType>(p_item)) {}
+
+        explicit Body (const ItemType& p_item) : item(p_item) {}
+
+        explicit Body (const TemplateSTRNode<ItemType, BoundsTraits>* p_childrenEnd) : childrenEnd(p_childrenEnd) {}
+
+        ~Body() = default;
+    } data;
+    const TemplateSTRNode* children;
+
+public:
+    ~TemplateSTRNode() {
+        if (isLeaf()) {
+            data.item.~ItemType();
+        }
+    }
+
+    TemplateSTRNode(ItemType&& p_item, const BoundsType& env) :
+        bounds(env),
+        data(std::forward<ItemType>(p_item)),
+        children(nullptr)
+        {}
+
+    TemplateSTRNode(const ItemType& p_item, const BoundsType& env) :
+            bounds(env),
+            data(p_item),
+            children(nullptr)
+            {}
+
+    TemplateSTRNode(const TemplateSTRNode* begin, const TemplateSTRNode* end) :
+        bounds(boundsFromChildren(begin, end)),
+        data(end),
+        children(begin)
+    {}
+
+    const TemplateSTRNode* beginChildren() const {
+        return children;
+    }
+
+    const TemplateSTRNode* endChildren() const {
+        return data.childrenEnd;
+    }
+
+    bool isDeleted() const {
+        return children == this;
+    }
+
+    bool isLeaf() const {
+        return children == nullptr || children == this;
+    }
+
+    bool isComposite() const {
+        return !isLeaf();
+    }
+
+    bool boundsIntersect(const BoundsType& queryBounds) const {
+        return BoundsTraits::intersects(getBounds(), queryBounds);
+    }
+
+    double getSize() const {
+        return BoundsTraits::size(getBounds());
+    }
+
+    static BoundsType boundsFromChildren(const TemplateSTRNode* from, const TemplateSTRNode* to) {
+        BoundsType bnds = from->getBounds();
+
+        for (auto *child = from + 1; child < to; ++child) {
+            BoundsTraits::expandToInclude(bnds, child->getBounds());
+        }
+
+        return bnds;
+    }
+
+    BoundsType boundsFromChildren() const {
+        return boundsFromChildren(children, data.childrenEnd);
+    }
+
+    const BoundsType& getBounds() const {
+        return bounds;
+    }
+
+    std::size_t getNumNodes() const
+    {
+        if (isLeaf()) {
+            return isDeleted() ? 0 : 1;
+        }
+
+        std::size_t count = 1;
+        for (const auto* child = beginChildren(); child != endChildren(); ++child) {
+            count += child->getNumNodes();
+        }
+
+        return count;
+    }
+
+    std::size_t getNumLeafNodes() const
+    {
+        if (isLeaf()) {
+            return isDeleted() ? 0 : 1;
+        }
+
+        std::size_t count = 0;
+        for (const auto* child = beginChildren(); child != endChildren(); ++child) {
+            count += child->getNumNodes();
+        }
+        return count;
+    }
+
+    const ItemType& getItem() const {
+        assert(!isDeleted());
+        return data.item;
+    }
+
+    void removeItem() {
+        children = this;
+    }
+
+};
+
+}
+}
+}
+
diff --git a/include/geos/index/strtree/TemplateSTRNodePair.h b/include/geos/index/strtree/TemplateSTRNodePair.h
new file mode 100644
index 0000000..e29c408
--- /dev/null
+++ b/include/geos/index/strtree/TemplateSTRNodePair.h
@@ -0,0 +1,69 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020-2021 Daniel Baston
+ *
+ * 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.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/index/strtree/TemplateSTRNode.h>
+
+namespace geos {
+namespace index {
+namespace strtree {
+
+template<typename ItemType, typename BoundsTraits, typename ItemDistance>
+class TemplateSTRNodePair {
+public:
+    using Node = TemplateSTRNode<ItemType, BoundsTraits>;
+
+    TemplateSTRNodePair(const Node &node1, const Node &node2, ItemDistance& id)
+            : m_node1(&node1), m_node2(&node2), m_distance(distance(id)) {}
+
+    bool isLeaves() const {
+        return getFirst().isLeaf() && getSecond().isLeaf();
+    }
+
+    double getDistance() const {
+        return m_distance;
+    }
+
+    std::pair<ItemType, ItemType> getItems() const {
+        assert(isLeaves());
+        return std::make_pair(getFirst().getItem(), getSecond().getItem());
+    }
+
+    const Node &getFirst() const {
+        return *m_node1;
+    }
+
+    const Node &getSecond() const {
+        return *m_node2;
+    }
+
+    double distance(ItemDistance& id) {
+        if (isLeaves()) {
+            return id(getFirst().getItem(), getSecond().getItem());
+        } else {
+            return BoundsTraits::distance(getFirst().getBounds(), getSecond().getBounds());
+        }
+    }
+
+private:
+    const Node* m_node1;
+    const Node* m_node2;
+    double m_distance;
+};
+
+}
+}
+}
+
diff --git a/include/geos/index/strtree/TemplateSTRtree.h b/include/geos/index/strtree/TemplateSTRtree.h
new file mode 100644
index 0000000..4ce189a
--- /dev/null
+++ b/include/geos/index/strtree/TemplateSTRtree.h
@@ -0,0 +1,694 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020-2021 Daniel Baston
+ *
+ * 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.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/geom/Geometry.h>
+#include <geos/index/SpatialIndex.h> // for inheritance
+#include <geos/index/chain/MonotoneChain.h>
+#include <geos/index/ItemVisitor.h>
+#include <geos/util.h>
+
+#include <geos/index/strtree/TemplateSTRNode.h>
+#include <geos/index/strtree/TemplateSTRNodePair.h>
+#include <geos/index/strtree/TemplateSTRtreeDistance.h>
+#include <geos/index/strtree/Interval.h>
+
+#include <vector>
+#include <queue>
+
+namespace geos {
+namespace index {
+namespace strtree {
+
+/**
+ * \brief
+ * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm.
+ * For one- or two-dimensional spatial data.
+ *
+ * The STR packed R-tree is simple to implement and maximizes space
+ * utilization; that is, as many leaves as possible are filled to capacity.
+ * Overlap between nodes is far less than in a basic R-tree. However, once the
+ * tree has been built (explicitly or on the first call to `query`), items may
+ * not be added or removed.
+ *
+ * A user will instantiate `TemplateSTRtree` instead of `TemplateSTRtreeImpl`;
+ * this structure is used so that `TemplateSTRtree` can implement the
+ * requirements of the `SpatialIndex` interface, which is only possible when
+ * `ItemType` is a pointer.
+ *
+ * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial
+ * Databases With Application To GIS. Morgan Kaufmann, San Francisco, 2002.
+ *
+ */
+template<typename ItemType, typename BoundsTraits>
+class TemplateSTRtreeImpl {
+public:
+    using Node = TemplateSTRNode<ItemType, BoundsTraits>;
+    using NodeList = std::vector<Node>;
+    using NodeListIterator = typename NodeList::iterator;
+    using BoundsType = typename BoundsTraits::BoundsType;
+
+    class Iterator {
+    public:
+        using iterator_category = std::forward_iterator_tag;
+        using value_type = ItemType;
+        using difference_type = typename NodeList::const_iterator::difference_type;
+        using pointer = ItemType*;
+        using reference = ItemType&;
+
+        Iterator(typename NodeList::const_iterator&& iter,
+                 typename NodeList::const_iterator&& end) : m_iter(iter), m_end(end) {
+            skipDeleted();
+        }
+
+        const ItemType& operator*() const {
+            return m_iter->getItem();
+        }
+
+        Iterator& operator++() {
+            m_iter++;
+            skipDeleted();
+            return *this;
+        }
+
+        friend bool operator==(const Iterator& a, const Iterator& b) {
+            return a.m_iter == b.m_iter;
+        }
+
+        friend bool operator!=(const Iterator& a, const Iterator& b) {
+            return a.m_iter != b.m_iter;
+        }
+
+    private:
+        void skipDeleted() {
+            while(m_iter != m_end && m_iter->isDeleted()) {
+                m_iter++;
+            }
+        }
+
+        typename NodeList::const_iterator m_iter;
+        typename NodeList::const_iterator m_end;
+    };
+
+    class Items {
+    public:
+        explicit Items(TemplateSTRtreeImpl& tree) : m_tree(tree) {}
+
+        Iterator begin() {
+            return Iterator(m_tree.nodes.cbegin(),
+                            std::next(m_tree.nodes.cbegin(), static_cast<long>(m_tree.numItems)));
+        }
+
+        Iterator end() {
+            return Iterator(std::next(m_tree.nodes.cbegin(), static_cast<long>(m_tree.numItems)),
+                            std::next(m_tree.nodes.cbegin(), static_cast<long>(m_tree.numItems)));
+        }
+    private:
+        TemplateSTRtreeImpl& m_tree;
+    };
+
+    /// \defgroup construct Constructors
+    /// @{
+
+    /**
+     * Constructs a tree with the given maximum number of child nodes that
+     * a node may have.
+     */
+    explicit TemplateSTRtreeImpl(size_t p_nodeCapacity = 10) :
+        root(nullptr),
+        nodeCapacity(p_nodeCapacity),
+        numItems(0)
+        {}
+
+    /**
+     * Constructs a tree with the given maximum number of child nodes that
+     * a node may have, with the expected total number of items in the tree used
+     * to pre-allocate storage.
+     */
+    TemplateSTRtreeImpl(size_t p_nodeCapacity, size_t itemCapacity) :
+        root(nullptr),
+        nodeCapacity(p_nodeCapacity),
+        numItems(0) {
+        auto finalSize = treeSize(itemCapacity);
+        nodes.reserve(finalSize);
+    }
+
+    /// @}
+    /// \defgroup insert Insertion
+    /// @{
+
+    /** Move the given item into the tree */
+    void insert(ItemType&& item) {
+        insert(BoundsTraits::fromItem(item), std::forward<ItemType>(item));
+    }
+
+    /** Insert a copy of the given item into the tree */
+    void insert(const ItemType& item) {
+        insert(BoundsTraits::fromItem(item), item);
+    }
+
+    /** Move the given item into the tree */
+    void insert(const BoundsType& itemEnv, ItemType&& item) {
+        if (!BoundsTraits::isNull(itemEnv)) {
+            createLeafNode(std::forward<ItemType>(item), itemEnv);
+        }
+    }
+
+    /** Insert a copy of the given item into the tree */
+    void insert(const BoundsType& itemEnv, const ItemType& item) {
+        if (!BoundsTraits::isNull(itemEnv)) {
+            createLeafNode(item, itemEnv);
+        }
+    }
+
+    /// @}
+    /// \defgroup NN Nearest-neighbor
+    /// @{
+
+    /** Determine the two closest items in the tree using distance metric `distance`. */
+    template<typename ItemDistance>
+    std::pair<ItemType, ItemType> nearestNeighbour(ItemDistance& distance) {
+        return nearestNeighbour(*this, distance);
+    }
+
+    /** Determine the two closest items in the tree using distance metric `distance`. */
+    template<typename ItemDistance>
+    std::pair<ItemType, ItemType> nearestNeighbour() {
+        ItemDistance id;
+        return nearestNeighbour(*this);
+    }
+
+    /** Determine the two closest items this tree and `other` tree using distance metric `distance`. */
+    template<typename ItemDistance>
+    std::pair<ItemType, ItemType> nearestNeighbour(TemplateSTRtreeImpl<ItemType, BoundsTraits> & other,
+                                                   ItemDistance & distance) {
+        if (!getRoot() || !other.getRoot()) {
+            return { nullptr, nullptr };
+        }
+
+        TemplateSTRtreeDistance<ItemType, BoundsTraits, ItemDistance> td(distance);
+        return td.nearestNeighbour(*root, *other.root);
+    }
+
+    /** Determine the two closest items this tree and `other` tree using distance metric `distance`. */
+    template<typename ItemDistance>
+    std::pair<ItemType, ItemType> nearestNeighbour(TemplateSTRtreeImpl<ItemType, BoundsTraits>& other) {
+        ItemDistance id;
+        return nearestNeighbour(other, id);
+    }
+
+    template<typename ItemDistance>
+    ItemType nearestNeighbour(const BoundsType& env, const ItemType& item, ItemDistance& itemDist) {
+        build();
+
+        if (getRoot() == nullptr) {
+            return nullptr;
+        }
+
+        TemplateSTRNode<ItemType, BoundsTraits> bnd(item, env);
+        TemplateSTRNodePair<ItemType, BoundsTraits, ItemDistance> pair(*getRoot(), bnd, itemDist);
+
+        TemplateSTRtreeDistance<ItemType, BoundsTraits, ItemDistance> td(itemDist);
+        return td.nearestNeighbour(pair).first;
+    }
+
+    template<typename ItemDistance>
+    ItemType nearestNeighbour(const BoundsType& env, const ItemType& item) {
+        ItemDistance id;
+        return nearestNeighbour(env, item, id);
+    }
+
+    /// @}
+    /// \defgroup query Query
+    /// @{
+
+    // Query the tree using the specified visitor. The visitor must be callable
+    // either with a single argument of `const ItemType&` or with the
+    // arguments `(const BoundsType&, const ItemType&).
+    // The visitor need not return a value, but if it does return a value,
+    // false values will be taken as a signal to stop the query.
+    template<typename Visitor>
+    void query(const BoundsType& queryEnv, Visitor &&visitor) {
+        if (!built()) {
+            build();
+        }
+
+        if (root && root->boundsIntersect(queryEnv)) {
+            if (root->isLeaf()) {
+                visitLeaf(visitor, *root);
+            } else {
+                query(queryEnv, *root, visitor);
+            }
+        }
+    }
+
+    // Query the tree and collect items in the provided vector.
+    void query(const BoundsType& queryEnv, std::vector<ItemType>& results) {
+        query(queryEnv, [&results](const ItemType& x) {
+            results.push_back(x);
+        });
+    }
+
+    /**
+     * Returns a depth-first iterator over all items in the tree.
+     */
+    Items items() {
+        build();
+        return Items(*this);
+    }
+
+    /**
+     * Iterate over all items added thus far.  Explicitly does not build
+     * the tree.
+     */
+    template<typename F>
+    void iterate(F&& func) {
+        auto n = built() ? numItems : nodes.size();
+        for (size_t i = 0; i < n; i++) {
+            func(nodes[i].getItem());
+        }
+    }
+
+    /// @}
+    /// \defgroup remove Item removal
+    /// @{
+
+    bool remove(const BoundsType& itemEnv, const ItemType& item) {
+        if (root == nullptr) {
+            return false;
+        }
+
+        if (root->isLeaf()) {
+            if (!root->isDeleted() && root->getItem() == item) {
+                root->removeItem();
+                return true;
+            }
+            return false;
+        }
+
+        return remove(itemEnv, *root, item);
+    }
+
+    /// @}
+    /// \defgroup introspect Introspection
+    /// @{
+
+    /** Determine whether the tree has been built, and no more items may be added. */
+    bool built() const {
+        return root != nullptr;
+    }
+
+    /** Determine whether the tree has been built, and no more items may be added. */
+    const Node* getRoot() {
+        build();
+        return root;
+    }
+
+    /// @}
+
+    /** Build the tree if it has not already been built. */
+    void build() {
+        if (built()) {
+            return;
+        }
+
+        if (nodes.empty()) {
+            return;
+        }
+
+        numItems = nodes.size();
+
+        // compute final size of tree and set it aside in a single
+        // block of memory
+        auto finalSize = treeSize(numItems);
+        nodes.reserve(finalSize);
+
+        // begin and end define a range of nodes needing parents
+        auto begin = nodes.begin();
+        auto end = nodes.end();
+
+        while (std::distance(begin, end) > 1) {
+            createParentNodes(begin, end);
+            begin = end; // parents just added become children in the next round
+            end = nodes.end();
+        }
+
+        assert(finalSize == nodes.size());
+
+        root = &nodes.back();
+    }
+
+protected:
+    NodeList nodes;      //**< a list of all leaf and branch nodes in the tree. */
+    Node* root;          //**< a pointer to the root node, if the tree has been built. */
+    size_t nodeCapacity; //*< maximum number of children of each node */
+    size_t numItems;     //*< total number of items in the tree, if it has been built. */
+
+    // Prevent instantiation of base class.
+    ~TemplateSTRtreeImpl() = default;
+
+    void createLeafNode(ItemType&& item, const BoundsType& env) {
+        nodes.emplace_back(std::forward<ItemType>(item), env);
+    }
+
+    void createLeafNode(const ItemType& item, const BoundsType& env) {
+        nodes.emplace_back(item, env);
+    }
+
+    void createBranchNode(const Node *begin, const Node *end) {
+        assert(nodes.size() < nodes.capacity());
+        nodes.emplace_back(begin, end);
+    }
+
+    // calculate what the tree size will be when it is build. This is simply
+    // a version of createParentNodes that doesn't actually create anything.
+    size_t treeSize(size_t numLeafNodes) {
+        size_t nodesInTree = numLeafNodes;
+
+        size_t nodesWithoutParents = numLeafNodes;
+        while (nodesWithoutParents > 1) {
+            auto numSlices = sliceCount(nodesWithoutParents);
+            auto nodesPerSlice = sliceCapacity(nodesWithoutParents, numSlices);
+
+            size_t parentNodesAdded = 0;
+            for (size_t j = 0; j < numSlices; j++) {
+                auto nodesInSlice = std::min(nodesWithoutParents, nodesPerSlice);
+                nodesWithoutParents -= nodesInSlice;
+
+                parentNodesAdded += static_cast<size_t>(std::ceil(
+                        static_cast<double>(nodesInSlice) / static_cast<double>(nodeCapacity)));
+            }
+
+            nodesInTree += parentNodesAdded;
+            nodesWithoutParents = parentNodesAdded;
+        }
+
+        return nodesInTree;
+    }
+
+    void createParentNodes(const NodeListIterator& begin, const NodeListIterator& end) {
+        // Arrange child nodes in two dimensions.
+        // First, divide them into vertical slices of a given size (left-to-right)
+        // Then create nodes within those slices (bottom-to-top)
+        auto numChildren = static_cast<std::size_t>(std::distance(begin, end));
+        auto numSlices = sliceCount(numChildren);
+        std::size_t nodesPerSlice = sliceCapacity(numChildren, numSlices);
+
+        // We could sort all of the nodes here, but we don't actually need them to be
+        // completely sorted. They need to be sorted enough for each node to end up
+        // in the right vertical slice, but their relative position within the slice
+        // doesn't matter. So we do a partial sort for each slice below instead.
+        sortNodesX(begin, end);
+
+        auto startOfSlice = begin;
+        for (decltype(numSlices) j = 0; j < numSlices; j++) {
+            auto nodesRemaining = static_cast<size_t>(std::distance(startOfSlice, end));
+            auto nodesInSlice = std::min(nodesRemaining, nodesPerSlice);
+            auto endOfSlice = std::next(startOfSlice, static_cast<long>(nodesInSlice));
+
+            // Make sure that every node that should be in this slice ends up somewhere
+            // between startOfSlice and endOfSlice. We don't require any ordering among
+            // nodes between startOfSlice and endOfSlice.
+            //partialSortNodes(startOfSlice, endOfSlice, end);
+
+            addParentNodesFromVerticalSlice(startOfSlice, endOfSlice);
+
+            startOfSlice = endOfSlice;
+        }
+    }
+
+    void addParentNodesFromVerticalSlice(const NodeListIterator& begin, const NodeListIterator& end) {
+        if (BoundsTraits::TwoDimensional::value) {
+            sortNodesY(begin, end);
+        }
+
+        // Arrange the nodes vertically and full up parent nodes sequentially until they're full.
+        // A possible improvement would be to rework this such so that if we have 81 nodes we
+        // put 9 into each parent instead of 10 or 1.
+        auto firstChild = begin;
+        while (firstChild != end) {
+            auto childrenRemaining = static_cast<size_t>(std::distance(firstChild, end));
+            auto childrenForNode = std::min(nodeCapacity, childrenRemaining);
+            auto lastChild = std::next(firstChild, static_cast<long>(childrenForNode));
+
+            //partialSortNodes(firstChild, lastChild, end);
+
+            // Ideally we would be able to store firstChild and lastChild instead of
+            // having to convert them to pointers, but I wasn't sure how to access
+            // the NodeListIterator type from within Node without creating some weird
+            // circular dependency.
+            const Node *ptr_first = &*firstChild;
+            const Node *ptr_end = ptr_first + childrenForNode;
+
+            createBranchNode(ptr_first, ptr_end);
+            firstChild = lastChild;
+        }
+    }
+
+    void sortNodesX(const NodeListIterator& begin, const NodeListIterator& end) {
+        std::sort(begin, end, [](const Node &a, const Node &b) {
+            return BoundsTraits::getX(a.getBounds()) < BoundsTraits::getX(b.getBounds());
+        });
+    }
+
+    void sortNodesY(const NodeListIterator& begin, const NodeListIterator& end) {
+        std::sort(begin, end, [](const Node &a, const Node &b) {
+            return BoundsTraits::getY(a.getBounds()) < BoundsTraits::getY(b.getBounds());
+        });
+    }
+
+    // Helper function to visit an item using a visitor that has no return value.
+    // In this case, we will always return true, indicating that querying should
+    // continue.
+    template<typename Visitor,
+            typename std::enable_if<std::is_void<decltype(std::declval<Visitor>()(std::declval<ItemType>()))>::value, std::nullptr_t>::type = nullptr >
+    bool visitLeaf(Visitor&& visitor, const Node& node)
+    {
+        visitor(node.getItem());
+        return true;
+    }
+
+    // MSVC 2015 does not implement C++11 expression SFINAE and considers this a
+    // redefinition of a previous method
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+    template<typename Visitor,
+             typename std::enable_if<std::is_void<decltype(std::declval<Visitor>()(std::declval<BoundsType>(), std::declval<ItemType>()))>::value, std::nullptr_t>::type = nullptr >
+    bool visitLeaf(Visitor&& visitor, const Node& node)
+    {
+        visitor(node.getBounds(), node.getItem());
+        return true;
+    }
+#endif
+
+    // If the visitor function does return a value, we will use this to indicate
+    // that querying should continue.
+    template<typename Visitor,
+             typename std::enable_if<!std::is_void<decltype(std::declval<Visitor>()(std::declval<ItemType>()))>::value, std::nullptr_t>::type = nullptr>
+    bool visitLeaf(Visitor&& visitor, const Node& node)
+    {
+        return visitor(node.getItem());
+    }
+
+    // MSVC 2015 does not implement C++11 expression SFINAE and considers this a
+    // redefinition of a previous method
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+    template<typename Visitor,
+             typename std::enable_if<!std::is_void<decltype(std::declval<Visitor>()(std::declval<BoundsType>(), std::declval<ItemType>()))>::value, std::nullptr_t>::type = nullptr>
+    bool visitLeaf(Visitor&& visitor, const Node& node)
+    {
+        return visitor(node.getBounds(), node.getItem());
+    }
+#endif
+
+    template<typename Visitor>
+    void query(const BoundsType& queryEnv,
+               const Node& node,
+               Visitor&& visitor) {
+
+        assert(!node.isLeaf());
+
+        for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) {
+            if (child->boundsIntersect(queryEnv)) {
+                if (child->isLeaf() && !child->isDeleted()) {
+                    if (!visitLeaf(visitor, *child)) {
+                        return;
+                    }
+                } else {
+                    query(queryEnv, *child, visitor);
+                }
+            }
+        }
+    }
+
+    bool remove(const BoundsType& queryEnv,
+                const Node& node,
+                const ItemType& item) {
+
+        assert(!node.isLeaf());
+
+        for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) {
+            if (child->boundsIntersect(queryEnv)) {
+                if (child->isLeaf()) {
+                    if (!child->isDeleted() && child->getItem() == item) {
+                        // const cast is ugly, but alternative seems to be to remove all
+                        // const qualifiers in Node and open up mutability everywhere?
+                        auto mutableChild = const_cast<Node*>(child);
+                        mutableChild->removeItem();
+                        return true;
+                    }
+                } else {
+                    bool removed = remove(queryEnv, *child, item);
+                    if (removed) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    size_t sliceCount(size_t numNodes) const {
+        double minLeafCount = std::ceil(static_cast<double>(numNodes) / static_cast<double>(nodeCapacity));
+
+        return static_cast<size_t>(std::ceil(std::sqrt(minLeafCount)));
+    }
+
+    static size_t sliceCapacity(size_t numNodes, size_t numSlices) {
+        return static_cast<size_t>(std::ceil(static_cast<double>(numNodes) / static_cast<double>(numSlices)));
+    }
+};
+
+struct EnvelopeTraits {
+    using BoundsType = geom::Envelope;
+    using TwoDimensional = std::true_type;
+
+    static bool intersects(const BoundsType& a, const BoundsType& b) {
+        return a.intersects(b);
+    }
+
+    static double size(const BoundsType& a) {
+        return a.getArea();
+    }
+
+    static double distance(const BoundsType& a, const BoundsType& b) {
+        return a.distance(b);
+    }
+
+    static BoundsType empty() {
+        return {};
+    }
+
+    template<typename ItemType>
+    static const BoundsType& fromItem(const ItemType& i) {
+        return *(i->getEnvelopeInternal());
+    }
+
+    template<typename ItemType>
+    static const BoundsType& fromItem(ItemType&& i) {
+        return *(i->getEnvelopeInternal());
+    }
+
+    static double getX(const BoundsType& a) {
+        return a.getMinX() + a.getMaxX();
+    }
+
+    static double getY(const BoundsType& a) {
+        return a.getMinY() + a.getMaxY();
+    }
+
+    static void expandToInclude(BoundsType& a, const BoundsType& b) {
+        a.expandToInclude(b);
+    }
+
+    static bool isNull(const BoundsType& a) {
+        return a.isNull();
+    }
+};
+
+struct IntervalTraits {
+    using BoundsType = Interval;
+    using TwoDimensional = std::false_type;
+
+    static bool intersects(const BoundsType& a, const BoundsType& b) {
+        return a.intersects(&b);
+    }
+
+    static double size(const BoundsType& a) {
+        return a.getWidth();
+    }
+
+    static double getX(const BoundsType& a) {
+        return a.getMin() + a.getMax();
+    }
+
+    static double getY(const BoundsType& a) {
+        return a.getMin() + a.getMax();
+    }
+
+    static void expandToInclude(BoundsType& a, const BoundsType& b) {
+        a.expandToInclude(&b);
+    }
+
+    static bool isNull(const BoundsType& a) {
+        (void) a;
+        return false;
+    }
+};
+
+
+template<typename ItemType, typename BoundsTraits = EnvelopeTraits>
+class TemplateSTRtree : public TemplateSTRtreeImpl<ItemType, BoundsTraits> {
+public:
+    using TemplateSTRtreeImpl<ItemType, BoundsTraits>::TemplateSTRtreeImpl;
+};
+
+// When ItemType is a pointer and our bounds are geom::Envelope, adopt
+// the SpatialIndex interface which requires queries via an envelope
+// and items to be representable as void*.
+template<typename ItemType>
+class TemplateSTRtree<ItemType*, EnvelopeTraits> : public TemplateSTRtreeImpl<ItemType*, EnvelopeTraits>, public SpatialIndex {
+public:
+    using TemplateSTRtreeImpl<ItemType*, EnvelopeTraits>::TemplateSTRtreeImpl;
+    using TemplateSTRtreeImpl<ItemType*, EnvelopeTraits>::insert;
+    using TemplateSTRtreeImpl<ItemType*, EnvelopeTraits>::query;
+    using TemplateSTRtreeImpl<ItemType*, EnvelopeTraits>::remove;
+
+    // The SpatialIndex methods only work when we are storing a pointer type.
+    void query(const geom::Envelope* queryEnv, std::vector<void*>& results) override {
+        query(*queryEnv, [&results](const ItemType* x) {
+            results.push_back(const_cast<void*>(static_cast<const void*>(x)));
+        });
+    }
+
+    void query(const geom::Envelope* queryEnv, ItemVisitor& visitor) override {
+        query(*queryEnv, [&visitor](const ItemType* x) {
+            visitor.visitItem(const_cast<void*>(static_cast<const void*>(x)));
+        });
+    }
+
+    bool remove(const geom::Envelope* itemEnv, void* item) override {
+        return remove(*itemEnv, static_cast<ItemType*>(item));
+    }
+
+    void insert(const geom::Envelope* itemEnv, void* item) override {
+        insert(*itemEnv, std::move(static_cast<ItemType*>(item)));
+    }
+};
+
+
+}
+}
+}
diff --git a/include/geos/index/strtree/TemplateSTRtreeDistance.h b/include/geos/index/strtree/TemplateSTRtreeDistance.h
new file mode 100644
index 0000000..7c79360
--- /dev/null
+++ b/include/geos/index/strtree/TemplateSTRtreeDistance.h
@@ -0,0 +1,158 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2020-2021 Daniel Baston
+ *
+ * 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.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/index/strtree/TemplateSTRNode.h>
+#include <geos/index/strtree/TemplateSTRNodePair.h>
+
+#include <queue>
+#include <vector>
+
+namespace geos {
+namespace index {
+namespace strtree {
+
+template<typename ItemType, typename BoundsType, typename ItemDistance>
+class TemplateSTRtreeDistance {
+    using Node = TemplateSTRNode<ItemType, BoundsType>;
+    using NodePair = TemplateSTRNodePair<ItemType, BoundsType, ItemDistance>;
+    using ItemPair = std::pair<ItemType, ItemType>;
+
+    struct PairQueueCompare {
+        bool operator()(const NodePair& a, const NodePair& b) {
+            return a.getDistance() > b.getDistance();
+        }
+    };
+
+    using PairQueue = std::priority_queue<NodePair, std::vector<NodePair>, PairQueueCompare>;
+
+public:
+    explicit TemplateSTRtreeDistance(ItemDistance& id) : m_id(id) {}
+
+    ItemPair nearestNeighbour(const Node& root1, const Node& root2) {
+        NodePair initPair(root1, root2, m_id);
+        return nearestNeighbour(initPair);
+    }
+
+    ItemPair nearestNeighbour(NodePair& initPair) {
+        return nearestNeighbour(initPair, std::numeric_limits<double>::infinity());
+    }
+
+private:
+
+    ItemPair nearestNeighbour(NodePair& initPair, double maxDistance) {
+        double distanceLowerBound = maxDistance;
+        std::unique_ptr<NodePair> minPair;
+
+        PairQueue priQ;
+        priQ.push(initPair);
+
+        while (!priQ.empty() && distanceLowerBound > 0) {
+            NodePair pair = priQ.top();
+            priQ.pop();
+            double currentDistance = pair.getDistance();
+
+            /*
+             * If the distance for the first node in the queue
+             * is >= the current minimum distance, all other nodes
+             * in the queue must also have a greater distance.
+             * So the current minDistance must be the true minimum,
+             * and we are done.
+             */
+            if (minPair && currentDistance >= distanceLowerBound) {
+                break;
+            }
+
+            /*
+             * If the pair members are leaves
+             * then their distance is the exact lower bound.
+             * Update the distanceLowerBound to reflect this
+             * (which must be smaller, due to the test
+             * immediately prior to this).
+             */
+            if (pair.isLeaves()) {
+                distanceLowerBound = currentDistance;
+                if (minPair) {
+                    *minPair = pair;
+                } else {
+                    minPair = detail::make_unique<NodePair>(pair);
+                }
+            } else {
+                /*
+                 * Otherwise, expand one side of the pair,
+                 * (the choice of which side to expand is heuristically determined)
+                 * and insert the new expanded pairs into the queue
+                 */
+                expandToQueue(pair, priQ, distanceLowerBound);
+            }
+        }
+
+        if (!minPair) {
+            throw util::GEOSException("Error computing nearest neighbor");
+        }
+
+        return minPair->getItems();
+    }
+
+    void expandToQueue(const NodePair& pair, PairQueue& priQ, double minDistance) {
+        const Node& node1 = pair.getFirst();
+        const Node& node2 = pair.getSecond();
+
+        bool isComp1 = node1.isComposite();
+        bool isComp2 = node2.isComposite();
+
+        /**
+         * HEURISTIC: If both boundables are composite,
+         * choose the one with largest area to expand.
+         * Otherwise, simply expand whichever is composite.
+         */
+        if (isComp1 && isComp2) {
+            if (node1.getSize() > node2.getSize()) {
+                expand(node1, node2, false, priQ, minDistance);
+                return;
+            } else {
+                expand(node2, node1, true, priQ, minDistance);
+                return;
+            }
+        } else if (isComp1) {
+            expand(node1, node2, false, priQ, minDistance);
+            return;
+        } else if (isComp2) {
+            expand(node2, node1, true, priQ, minDistance);
+            return;
+        }
+
+        throw util::IllegalArgumentException("neither boundable is composite");
+
+    }
+
+    void expand(const Node &nodeComposite, const Node &nodeOther, bool isFlipped, PairQueue& priQ,
+                double minDistance) {
+        for (const auto *child = nodeComposite.beginChildren();
+             child < nodeComposite.endChildren(); ++child) {
+            NodePair sp = isFlipped ? NodePair(nodeOther, *child, m_id) : NodePair(*child, nodeOther, m_id);
+
+            // only add to queue if this pair might contain the closest points
+            if (minDistance == std::numeric_limits<double>::infinity() || sp.getDistance() < minDistance) {
+                priQ.push(sp);
+            }
+        }
+    }
+
+    ItemDistance& m_id;
+};
+}
+}
+}
diff --git a/src/index/strtree/Interval.cpp b/src/index/strtree/Interval.cpp
index b982d6f..4c9833c 100644
--- a/src/index/strtree/Interval.cpp
+++ b/src/index/strtree/Interval.cpp
@@ -26,39 +26,6 @@ namespace geos {
 namespace index { // geos.index
 namespace strtree { // geos.index.strtree
 
-Interval::Interval(double newMin, double newMax)
-{
-    assert(newMin <= newMax);
-    imin = newMin;
-    imax = newMax;
-}
-
-double
-Interval::getCentre()
-{
-    return (imin + imax) / 2;
-}
-
-Interval*
-Interval::expandToInclude(const Interval* other)
-{
-    imax = std::max(imax, other->imax);
-    imin = std::min(imin, other->imin);
-    return this;
-}
-
-bool
-Interval::intersects(const Interval* other) const
-{
-    return !(other->imin > imax || other->imax < imin);
-}
-
-bool
-Interval::equals(const Interval* other) const
-{
-    return imin == other->imin && imax == other->imax;
-}
-
 } // namespace geos.index.strtree
 } // namespace geos.index
 } // namespace geos
diff --git a/tests/unit/index/strtree/TemplateSTRtreeTest.cpp b/tests/unit/index/strtree/TemplateSTRtreeTest.cpp
new file mode 100644
index 0000000..8073a4e
--- /dev/null
+++ b/tests/unit/index/strtree/TemplateSTRtreeTest.cpp
@@ -0,0 +1,388 @@
+#include <tut/tut.hpp>
+// geos
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/Point.h>
+#include <geos/geom/LineSegment.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
+#include <geos/index/ItemVisitor.h>
+#include <geos/io/WKTReader.h>
+
+#include <iostream>
+
+using namespace geos;
+using geos::index::strtree::TemplateSTRtree;
+using geos::geom::Geometry;
+
+namespace tut {
+// dummy data, not used
+struct test_templatestrtree_data {
+    struct Grid {
+        double x0 = 0;
+        double y0 = 0;
+        double dx = 1;
+        double dy = 1;
+        std::size_t nx = 10;
+        std::size_t ny = 10;
+
+        geom::Envelope getEnvelope() const {
+            return {x0, x0+dx*static_cast<double>(nx),
+                    y0, y0+dy*static_cast<double>(ny)};
+        }
+    };
+
+    static std::vector<std::unique_ptr<geom::Point>> pointGrid(const Grid & grid) {
+        std::vector<std::unique_ptr<geom::Point>> ret;
+
+        auto gf = geom::GeometryFactory::create();
+        for (std::size_t i = 0; i < grid.nx; ++i) {
+            for (std::size_t j = 0; j < grid.ny; ++j) {
+                geom::Coordinate c(grid.x0 + grid.dx*static_cast<double>(i),
+                                   grid.y0 + grid.dy*static_cast<double>(j));
+                geom::Point* pt = gf->createPoint(c);
+                ret.emplace_back(pt);
+            }
+        }
+
+        return ret;
+    }
+
+    template<typename TreeItemType, typename VectorItemType>
+    static TemplateSTRtree<TreeItemType> makeTree(const std::vector<VectorItemType> & items) {
+        TemplateSTRtree<TreeItemType> t(10);
+        for (auto& g : items) {
+            t.insert(g.get());
+        }
+        return t;
+    }
+};
+
+using group = test_group<test_templatestrtree_data>;
+using object = group::object;
+group test_templatestrtree_group("geos::index::strtree::TemplateSTRtree");
+
+//
+// Test Cases
+//
+
+template<>
+template<>
+void object::test<1>() {
+    Grid grid;
+    grid.x0 = grid.y0 = 0;
+    grid.dx = grid.dy = 1;
+    grid.nx = grid.ny = 10;
+
+    auto geoms = pointGrid(grid);
+    auto tree = makeTree<const geom::Point*>(geoms);
+
+    // Query by collecting items into a vector
+    geom::Envelope qe(-0.5, 1.5, -0.5, 1.5);
+    std::vector<const geom::Point*> matches;
+    tree.query(qe, matches);
+    ensure(matches.size() == 4);
+}
+
+template<>
+template<>
+void object::test<2>() {
+    Grid grid;
+    grid.x0 = grid.y0 = 0;
+    grid.dx = grid.dy = 1;
+    grid.nx = grid.ny = 10;
+
+    auto geoms = pointGrid(grid);
+    auto tree = makeTree<const geom::Point*>(geoms);
+
+    // Query using visitor
+    class SimpleTestVisitor : public index::ItemVisitor {
+        public:
+            std::size_t count;
+
+            SimpleTestVisitor()
+                : count(0)
+                {}
+
+            void
+            visitItem(void* item) override
+            {
+                auto pt = static_cast<geom::Point*>(item);
+                if (!pt->isEmpty())
+                    count++;
+            }
+    };
+    SimpleTestVisitor vis;
+    geom::Envelope qe(-0.5, 1.5, -0.5, 1.5);
+    tree.query(&qe, vis);
+    ensure(vis.count == 4);
+}
+
+template<>
+template<>
+void object::test<3>
+()
+{
+    Grid grid1, grid2;
+    grid1.x0 = grid1.y0 = 0;
+    grid1.dx = grid1.dy = 1;
+    grid1.nx = grid1.ny = 10;
+
+    grid2.x0 = grid2.y0 = 11;
+    grid2.dx = grid2.dy = 1;
+    grid2.nx = grid2.ny = 10;
+
+    auto geoms1 = pointGrid(grid1);
+    auto geoms2 = pointGrid(grid2);
+
+    auto tree1 = makeTree<const geom::Point*>(geoms1);
+    auto tree2 = makeTree<const geom::Point*>(geoms2);
+
+    struct GeometryDistance {
+        double operator()(const Geometry* a, const Geometry* b) {
+            return a->distance(b);
+        };
+    };
+    auto rslt = tree1.nearestNeighbour<GeometryDistance>(tree2);
+
+    ensure_equals(rslt.first->getX(), 9.0);
+    ensure_equals(rslt.first->getY(), 9.0);
+    ensure_equals(rslt.second->getX(), 11.0);
+    ensure_equals(rslt.second->getY(), 11.0);
+}
+
+template<>
+template<>
+void object::test<4>
+()
+{
+    auto gf = geom::GeometryFactory::create();
+    geos::io::WKTReader wkt(*gf);
+    std::vector<std::unique_ptr<geom::Geometry>> geoms;
+    geoms.emplace_back(wkt.read("LINESTRING(0 0, 10 10)"));
+    geoms.emplace_back(wkt.read("LINESTRING(5 5, 15 15)"));
+    geoms.emplace_back(wkt.read("LINESTRING(10 10, 20 20)"));
+    geoms.emplace_back(wkt.read("LINESTRING(15 15, 25 25)"));
+
+    auto tree = makeTree<Geometry*>(geoms);
+
+    const std::size_t leaf_before = tree.getRoot()->getNumLeafNodes();
+    const std::size_t all_before = tree.getRoot()->getNumNodes();
+    ensure_equals(leaf_before, 4u);
+    ensure_equals(all_before, 5u);
+
+    tree.remove(geoms[3]->getEnvelopeInternal(), geoms[3].get());
+
+    const std::size_t leaf_after = tree.getRoot()->getNumLeafNodes();
+    const std::size_t all_after = tree.getRoot()->getNumNodes();
+    ensure_equals(leaf_after, 3u);
+    ensure_equals(all_after, 4u);
+}
+
+template<>
+template<>
+void object::test<5>
+()
+{
+    Grid grid;
+    grid.x0 = grid.y0 = 0;
+    grid.dx = grid.dy = 1;
+    grid.nx = grid.ny = 20;
+
+    auto geoms = pointGrid(grid);
+
+    // storing integers instead of geometry pointers
+    TemplateSTRtree<size_t> tree;
+    for (std::size_t i = 0; i < geoms.size(); i++) {
+        tree.insert(*geoms[i]->getEnvelopeInternal(), i);
+    }
+
+    // Query into vector
+    std::vector<size_t> hits;
+    geom::Envelope queryEnv(2.5, 4.5, 2.5, 4.5);
+    tree.query(queryEnv, hits);
+    ensure_equals(hits.size(), 4u);
+
+    // Get items in tree order
+    {
+        std::vector<size_t> orderedItems(tree.items().begin(), tree.items().end());
+        ensure_equals(orderedItems.size(), geoms.size());
+    }
+
+    // Remove an item and get items in tree order
+    {
+        auto removed = tree.remove(grid.getEnvelope(), 17);
+        ensure(removed);
+        std::vector<size_t> orderedItems(tree.items().begin(), tree.items().end());
+        ensure_equals(orderedItems.size(), geoms.size() - 1);
+    }
+}
+
+template<>
+template<>
+void object::test<6>()
+{
+    TemplateSTRtree<geom::LineSegment> tree;
+
+    for (double i = 0; i < 100; i += 1.0) {
+        geom::Coordinate p0(i, i);
+        geom::Coordinate p1(i + 1, i + 1);
+
+        geom::LineSegment ls(p0, p1);
+        geom::Envelope e(p0, p1);
+        tree.insert(e, ls);
+    }
+
+    geom::Envelope qe(35.5, 38.5, 35.5, 38.5);
+    std::vector<geom::LineSegment> hits;
+    tree.query(qe, hits);
+
+    ensure_equals(hits.size(), 4u);
+}
+
+template<>
+template<>
+void object::test<7>() {
+    struct FloatBox {
+        FloatBox(double p_xmin, double p_xmax, double p_ymin, double p_ymax) :
+            xmin(static_cast<float>(p_xmin)),
+            xmax(static_cast<float>(p_xmax)),
+            ymin(static_cast<float>(p_ymin)),
+            ymax(static_cast<float>(p_ymax)) {
+
+            if (static_cast<double>(xmax) < p_xmax) {
+                xmax = std::nextafter(xmax, std::numeric_limits<float>::infinity());
+            }
+            if (static_cast<double>(xmin) > p_xmin) {
+                xmin = std::nextafter(xmin, -std::numeric_limits<float>::infinity());
+            }
+            if (static_cast<double>(ymax) < p_ymax) {
+                ymax = std::nextafter(ymax, std::numeric_limits<float>::infinity());
+            }
+            if (static_cast<double>(ymin) > p_ymin) {
+                ymin = std::nextafter(ymin, -std::numeric_limits<float>::infinity());
+            }
+        }
+
+        void expandToInclude(const FloatBox & other) {
+            xmin = std::min(xmin, other.xmin);
+            xmax = std::max(xmax, other.xmax);
+            ymin = std::min(ymin, other.ymin);
+            ymax = std::max(ymax, other.ymax);
+        }
+
+        bool intersects(const FloatBox & other) const {
+            return !(other.xmin > xmax ||
+                     other.xmax < xmin ||
+                     other.ymin > ymax ||
+                     other.ymax < ymin);
+        }
+
+        float xmin;
+        float xmax;
+        float ymin;
+        float ymax;
+    };
+
+    struct BoxTraits {
+        using BoundsType = FloatBox;
+        using TwoDimensional = std::true_type;
+
+        // Quiet incorrect gcc warning about unused local typedef
+        TwoDimensional doNothing() {
+            return {};
+        }
+
+        static bool intersects(const BoundsType & a, const BoundsType & b) {
+            return a.intersects(b);
+        }
+
+        static double getX(const BoundsType& a) {
+            return 0.5 * static_cast<double>(a.xmin + a.xmax);
+        }
+
+        static double getY(const BoundsType& a) {
+            return 0.5 * static_cast<double>(a.ymin + a.ymax);
+        }
+
+        static void expandToInclude(BoundsType& a, const BoundsType& b) {
+            a.expandToInclude(b);
+        }
+
+        static bool isNull(const BoundsType & a) {
+            (void) a;
+            return false;
+        }
+    };
+
+    TemplateSTRtree<geom::LineSegment, BoxTraits> tree;
+
+    for (double i = 0; i < 100; i += 1.0) {
+        geom::Coordinate p0(i, i);
+        geom::Coordinate p1(i + 1, i + 1);
+
+        geom::LineSegment ls(p0, p1);
+        FloatBox e(p0.x, p1.x, p0.y, p1.y);
+        tree.insert(e, ls);
+    }
+
+    FloatBox qe(35.5, 38.5, 35.5, 38.5);
+    std::vector<geom::LineSegment> hits;
+    tree.query(qe, hits);
+
+    ensure_equals(hits.size(), 4u);
+}
+
+// Test visitor short-circuiting
+template<>
+template<>
+void object::test<8>() {
+    Grid grid;
+    grid.x0 = grid.y0 = 0;
+    grid.dx = grid.dy = 1;
+    grid.nx = grid.ny = 10;
+
+    auto geoms = pointGrid(grid);
+    auto tree = makeTree<const geom::Point*>(geoms);
+
+    std::vector<const geom::Point*> matches;
+    auto visitor = [&matches](const geom::Point* pt) {
+        matches.push_back(pt);
+        return matches.size() < 2; // stop the query after we've found two items.
+    };
+
+    // Query by collecting items into a vector
+    geom::Envelope qe(-0.5, 1.5, -0.5, 1.5);
+    tree.query(qe, visitor);
+    ensure(matches.size() == 2);
+}
+
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+// Test bounds-and-item visitor. Method not defined in MSVC 2015.
+template<>
+template<>
+void object::test<9>() {
+    Grid grid;
+    grid.x0 = grid.y0 = 0;
+    grid.dx = grid.dy = 1;
+    grid.nx = grid.ny = 10;
+
+    auto geoms = pointGrid(grid);
+    auto tree = makeTree<const geom::Point*>(geoms);
+
+    // Collect the envelopes instead of the items
+    std::vector<geom::Envelope> matches;
+    auto visitor = [&matches](const geom::Envelope& e, const geom::Point* pt) {
+        (void) pt;
+        matches.push_back(e);
+    };
+
+    geom::Envelope qe(-0.5, 1.5, -0.5, 1.5);
+    tree.query(qe, visitor);
+    ensure(matches.size() == 4);
+}
+#endif
+
+
+} // namespace tut
+

commit e43f780745f34410a56d2ae48677017098340af7
Author: Daniel Baston <dbaston at gmail.com>
Date:   Fri Mar 5 23:13:51 2021 -0500

    Fix warning

diff --git a/benchmarks/algorithm/LineIntersectorPerfTest.cpp b/benchmarks/algorithm/LineIntersectorPerfTest.cpp
index 91c4120..565bdd2 100644
--- a/benchmarks/algorithm/LineIntersectorPerfTest.cpp
+++ b/benchmarks/algorithm/LineIntersectorPerfTest.cpp
@@ -44,7 +44,7 @@ static void BM_Collinear(benchmark::State& state) {
 }
 
 BENCHMARK(BM_PointIntersection);
-//BENCHMARK(BM_Collinear);
+BENCHMARK(BM_Collinear);
 
 BENCHMARK_MAIN();
 

-----------------------------------------------------------------------

Summary of changes:
 benchmarks/algorithm/LineIntersectorPerfTest.cpp   |   2 +-
 benchmarks/index/CMakeLists.txt                    |  11 +
 benchmarks/index/SpatialIndexPerfTest.cpp          | 302 +++++++++
 capi/geos_c.cpp                                    |   4 +-
 capi/geos_ts_c.cpp                                 |  32 +-
 .../algorithm/locate/IndexedPointInAreaLocator.h   |  50 +-
 include/geos/index/strtree/Interval.h              |  26 +-
 include/geos/index/strtree/TemplateSTRNode.h       | 154 +++++
 include/geos/index/strtree/TemplateSTRNodePair.h   |  69 ++
 include/geos/index/strtree/TemplateSTRtree.h       | 694 +++++++++++++++++++++
 .../geos/index/strtree/TemplateSTRtreeDistance.h   | 158 +++++
 include/geos/noding/MCIndexNoder.h                 |   4 +-
 .../noding/MCIndexSegmentSetMutualIntersector.h    |   7 +-
 .../operation/distance/FacetSequenceTreeBuilder.h  |  12 +-
 .../geos/operation/distance/IndexedFacetDistance.h |   2 +-
 include/geos/operation/polygonize/EdgeRing.h       |   5 -
 include/geos/operation/polygonize/HoleAssigner.h   |   4 +-
 .../geos/operation/union/CascadedPolygonUnion.h    |  39 +-
 include/geos/operation/union/UnionStrategy.h       |   3 +
 src/algorithm/locate/IndexedPointInAreaLocator.cpp |  47 +-
 src/index/strtree/Interval.cpp                     |  33 -
 src/noding/MCIndexSegmentSetMutualIntersector.cpp  |  10 +-
 .../distance/FacetSequenceTreeBuilder.cpp          |   4 +-
 src/operation/distance/IndexedFacetDistance.cpp    |  53 +-
 src/operation/polygonize/HoleAssigner.cpp          |  10 +-
 src/operation/union/CascadedPolygonUnion.cpp       | 149 ++---
 src/operation/union/UnaryUnionOp.cpp               |   3 +-
 .../union/UnionStrategy.cpp}                       |  23 +-
 src/operation/valid/IndexedNestedRingTester.cpp    |  26 +-
 src/operation/valid/IndexedNestedRingTester.h      |   7 +-
 src/operation/valid/IndexedNestedShellTester.cpp   |  14 +-
 src/precision/MinimumClearance.cpp                 |  17 +-
 tests/unit/index/strtree/TemplateSTRtreeTest.cpp   | 388 ++++++++++++
 .../distance/IndexedFacetDistanceTest.cpp          |  19 +-
 .../geounion/CascadedPolygonUnionTest.cpp          |   2 +-
 tests/xmltester/tests/issue/issue-geos-837.xml     |   6 +-
 36 files changed, 2031 insertions(+), 358 deletions(-)
 create mode 100644 benchmarks/index/SpatialIndexPerfTest.cpp
 create mode 100644 include/geos/index/strtree/TemplateSTRNode.h
 create mode 100644 include/geos/index/strtree/TemplateSTRNodePair.h
 create mode 100644 include/geos/index/strtree/TemplateSTRtree.h
 create mode 100644 include/geos/index/strtree/TemplateSTRtreeDistance.h
 copy src/{io/Unload.cpp => operation/union/UnionStrategy.cpp} (58%)
 create mode 100644 tests/unit/index/strtree/TemplateSTRtreeTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list