diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,15 @@
-0.1.0.0
--------
-* initial release
-
-0.1.0.4
--------
-* removed the upper bounds of the dependencies
-
-0.1.0.5
--------
-* fixed the cabal file
+0.1.0.0
+-------
+* initial release
+
+0.1.0.4
+-------
+* removed the upper bounds of the dependencies
+
+0.1.0.5
+-------
+* fixed the cabal file
+
+0.1.1.0
+-------
+* upgraded the 'CDT' library
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,30 +1,30 @@
-Copyright Stéphane Laurent (c) 2022
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    * Neither the name of Author name here nor the names of other
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+Copyright Stéphane Laurent (c) 2022
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Stéphane Laurent nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE_CDT.txt b/LICENSE_CDT.txt
--- a/LICENSE_CDT.txt
+++ b/LICENSE_CDT.txt
@@ -1,384 +1,384 @@
-The 'hcdt' package as a whole is distributed under BSD-3-Clause.
-
-It includes an open source C++ library: 'CDT'.
-
-Full copy of the license agreement used by this component is included
-below.
-
-
-
-### 'CDT' license
-
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
-    means each individual or legal entity that creates, contributes to
-    the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
-    means the combination of the Contributions of others (if any) used
-    by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
-    means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
-    means Source Code Form to which the initial Contributor has attached
-    the notice in Exhibit A, the Executable Form of such Source Code
-    Form, and Modifications of such Source Code Form, in each case
-    including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
-    means
-
-    (a) that the initial Contributor has attached the notice described
-        in Exhibit B to the Covered Software; or
-
-    (b) that the Covered Software was made available under the terms of
-        version 1.1 or earlier of the License, but not also under the
-        terms of a Secondary License.
-
-1.6. "Executable Form"
-    means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
-    means a work that combines Covered Software with other material, in 
-    a separate file or files, that is not Covered Software.
-
-1.8. "License"
-    means this document.
-
-1.9. "Licensable"
-    means having the right to grant, to the maximum extent possible,
-    whether at the time of the initial grant or subsequently, any and
-    all of the rights conveyed by this License.
-
-1.10. "Modifications"
-    means any of the following:
-
-    (a) any file in Source Code Form that results from an addition to,
-        deletion from, or modification of the contents of Covered
-        Software; or
-
-    (b) any new file in Source Code Form that contains any Covered
-        Software.
-
-1.11. "Patent Claims" of a Contributor
-    means any patent claim(s), including without limitation, method,
-    process, and apparatus claims, in any patent Licensable by such
-    Contributor that would be infringed, but for the grant of the
-    License, by the making, using, selling, offering for sale, having
-    made, import, or transfer of either its Contributions or its
-    Contributor Version.
-
-1.12. "Secondary License"
-    means either the GNU General Public License, Version 2.0, the GNU
-    Lesser General Public License, Version 2.1, the GNU Affero General
-    Public License, Version 3.0, or any later versions of those
-    licenses.
-
-1.13. "Source Code Form"
-    means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
-    means an individual or a legal entity exercising rights under this
-    License. For legal entities, "You" includes any entity that
-    controls, is controlled by, or is under common control with You. For
-    purposes of this definition, "control" means (a) the power, direct
-    or indirect, to cause the direction or management of such entity,
-    whether by contract or otherwise, or (b) ownership of more than
-    fifty percent (50%) of the outstanding shares or beneficial
-    ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
-    Licensable by such Contributor to use, reproduce, make available,
-    modify, display, perform, distribute, and otherwise exploit its
-    Contributions, either on an unmodified basis, with Modifications, or
-    as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
-    for sale, have made, import, and otherwise transfer either its
-    Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
-    or
-
-(b) for infringements caused by: (i) Your and any other third party's
-    modifications of Covered Software, or (ii) the combination of its
-    Contributions with other software (except as part of its Contributor
-    Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
-    its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
-    Form, as described in Section 3.1, and You must inform recipients of
-    the Executable Form how they can obtain a copy of such Source Code
-    Form by reasonable means in a timely manner, at a charge no more
-    than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
-    License, or sublicense it under different terms, provided that the
-    license for the Executable Form does not attempt to limit or alter
-    the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-*                                                                      *
-*  6. Disclaimer of Warranty                                           *
-*  -------------------------                                           *
-*                                                                      *
-*  Covered Software is provided under this License on an "as is"       *
-*  basis, without warranty of any kind, either expressed, implied, or  *
-*  statutory, including, without limitation, warranties that the       *
-*  Covered Software is free of defects, merchantable, fit for a        *
-*  particular purpose or non-infringing. The entire risk as to the     *
-*  quality and performance of the Covered Software is with You.        *
-*  Should any Covered Software prove defective in any respect, You     *
-*  (not any Contributor) assume the cost of any necessary servicing,   *
-*  repair, or correction. This disclaimer of warranty constitutes an   *
-*  essential part of this License. No use of any Covered Software is   *
-*  authorized under this License except under this disclaimer.         *
-*                                                                      *
-************************************************************************
-
-************************************************************************
-*                                                                      *
-*  7. Limitation of Liability                                          *
-*  --------------------------                                          *
-*                                                                      *
-*  Under no circumstances and under no legal theory, whether tort      *
-*  (including negligence), contract, or otherwise, shall any           *
-*  Contributor, or anyone who distributes Covered Software as          *
-*  permitted above, be liable to You for any direct, indirect,         *
-*  special, incidental, or consequential damages of any character      *
-*  including, without limitation, damages for lost profits, loss of    *
-*  goodwill, work stoppage, computer failure or malfunction, or any    *
-*  and all other commercial damages or losses, even if such party      *
-*  shall have been informed of the possibility of such damages. This   *
-*  limitation of liability shall not apply to liability for death or   *
-*  personal injury resulting from such party's negligence to the       *
-*  extent applicable law prohibits such limitation. Some               *
-*  jurisdictions do not allow the exclusion or limitation of           *
-*  incidental or consequential damages, so this exclusion and          *
-*  limitation may not apply to You.                                    *
-*                                                                      *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
-  This Source Code Form is subject to the terms of the Mozilla Public
-  License, v. 2.0. If a copy of the MPL was not distributed with this
-  file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
-  This Source Code Form is "Incompatible With Secondary Licenses", as
-  defined by the Mozilla Public License, v. 2.0.
+The 'hcdt' package as a whole is distributed under BSD-3-Clause.
+
+It includes an open source C++ library: 'CDT'.
+
+Full copy of the license agreement used by this component is included
+below.
+
+
+
+### 'CDT' license
+
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,75 +1,82 @@
-# hcdt
-
-### Delaunay triangulation 
-
-```haskell
-ghci> import Text.Show.Pretty
-ghci> import Geometry.HCDT
-ghci> vertices = [Vertex 0 0, Vertex 0 1, Vertex 1 1, Vertex 1 0]
-ghci> triangulation <- delaunay vertices
-ghci> pPrint triangulation
-Triangulation
-  { _vertices =
-      fromList
-        [ ( 0 , Vertex 0.0 0.0 )
-        , ( 1 , Vertex 0.0 1.0 )
-        , ( 2 , Vertex 1.0 1.0 )
-        , ( 3 , Vertex 1.0 0.0 )
-        ]
-  , _triangles = [ Triangle 1 0 2 , Triangle 2 0 3 ]
-  , _edges = [ Edge 2 3 , Edge 0 3 , Edge 1 2 , Edge 0 2 , Edge 0 1 ]
-  }
-ghci> pPrint $ borderEdges triangulation
-fromList [ Edge 1 0 , Edge 1 2 , Edge 0 3 , Edge 2 3 ]
-```
-
-
-### Constrained Delaunay triangulation 
-
-```haskell
-ghci> import Text.Show.Pretty
-ghci> import Geometry.HCDT
-ghci> vertices = [Vertex 1 1, Vertex 3 1, Vertex 2 2, Vertex 0 0, Vertex 4 0, Vertex 2 5]
-ghci> edges = [Edge 0 1, Edge 0 2, Edge 1 2, Edge 3 4, Edge 3 5, Edge 4 5]
-ghci> triangulation <- cdelaunay vertices edges
-ntriangles: 6
-ghci> pPrint triangulation
-ConstrainedTriangulation
-  { _triangulation =
-      Triangulation
-        { _vertices =
-            fromList
-              [ ( 0 , Vertex 1.0 1.0 )
-              , ( 1 , Vertex 3.0 1.0 )
-              , ( 2 , Vertex 2.0 2.0 )
-              , ( 3 , Vertex 0.0 0.0 )
-              , ( 4 , Vertex 4.0 0.0 )
-              , ( 5 , Vertex 2.0 5.0 )
-              ]
-        , _triangles =
-            [ Triangle 1 0 3
-            , Triangle 2 1 5
-            , Triangle 2 5 0
-            , Triangle 0 5 3
-            , Triangle 1 4 5
-            , Triangle 1 3 4
-            ]
-        , _edges =
-            [ Edge 3 4
-            , Edge 1 4
-            , Edge 3 5
-            , Edge 0 5
-            , Edge 2 5
-            , Edge 0 2
-            , Edge 1 5
-            , Edge 1 2
-            , Edge 1 3
-            , Edge 4 5
-            , Edge 0 3
-            , Edge 0 1
-            ]
-        }
-  , _fixedEdges =
-      [ Edge 0 1 , Edge 0 2 , Edge 4 5 , Edge 1 2 , Edge 3 4 , Edge 3 5 ]
-  }
+# hcdt
+
+<!-- badges: start -->
+[![Stack-lts](https://github.com/stla/hcdt/actions/workflows/Stack-lts.yml/badge.svg)](https://github.com/stla/hcdt/actions/workflows/Stack-lts.yml)
+[![Stack-lts-Mac](https://github.com/stla/hcdt/actions/workflows/Stack-lts-Mac.yml/badge.svg)](https://github.com/stla/hcdt/actions/workflows/Stack-lts-Mac.yml)
+[![Stack-nightly](https://github.com/stla/hcdt/actions/workflows/Stack-nightly.yml/badge.svg)](https://github.com/stla/hcdt/actions/workflows/Stack-nightly.yml)
+<!-- badges: end -->
+
+
+### Delaunay triangulation 
+
+```haskell
+ghci> import Text.Show.Pretty
+ghci> import Geometry.HCDT
+ghci> vertices = [Vertex 0 0, Vertex 0 1, Vertex 1 1, Vertex 1 0]
+ghci> triangulation <- delaunay vertices
+ghci> pPrint triangulation
+Triangulation
+  { _vertices =
+      fromList
+        [ ( 0 , Vertex 0.0 0.0 )
+        , ( 1 , Vertex 0.0 1.0 )
+        , ( 2 , Vertex 1.0 1.0 )
+        , ( 3 , Vertex 1.0 0.0 )
+        ]
+  , _triangles = [ Triangle 1 0 2 , Triangle 2 0 3 ]
+  , _edges = [ Edge 2 3 , Edge 0 3 , Edge 1 2 , Edge 0 2 , Edge 0 1 ]
+  }
+ghci> pPrint $ borderEdges triangulation
+fromList [ Edge 1 0 , Edge 1 2 , Edge 0 3 , Edge 2 3 ]
+```
+
+
+### Constrained Delaunay triangulation 
+
+```haskell
+ghci> import Text.Show.Pretty
+ghci> import Geometry.HCDT
+ghci> vertices = [Vertex 1 1, Vertex 3 1, Vertex 2 2, Vertex 0 0, Vertex 4 0, Vertex 2 5]
+ghci> edges = [Edge 0 1, Edge 0 2, Edge 1 2, Edge 3 4, Edge 3 5, Edge 4 5]
+ghci> triangulation <- cdelaunay vertices edges
+ntriangles: 6
+ghci> pPrint triangulation
+ConstrainedTriangulation
+  { _triangulation =
+      Triangulation
+        { _vertices =
+            fromList
+              [ ( 0 , Vertex 1.0 1.0 )
+              , ( 1 , Vertex 3.0 1.0 )
+              , ( 2 , Vertex 2.0 2.0 )
+              , ( 3 , Vertex 0.0 0.0 )
+              , ( 4 , Vertex 4.0 0.0 )
+              , ( 5 , Vertex 2.0 5.0 )
+              ]
+        , _triangles =
+            [ Triangle 1 0 3
+            , Triangle 2 1 5
+            , Triangle 2 5 0
+            , Triangle 0 5 3
+            , Triangle 1 4 5
+            , Triangle 1 3 4
+            ]
+        , _edges =
+            [ Edge 3 4
+            , Edge 1 4
+            , Edge 3 5
+            , Edge 0 5
+            , Edge 2 5
+            , Edge 0 2
+            , Edge 1 5
+            , Edge 1 2
+            , Edge 1 3
+            , Edge 4 5
+            , Edge 0 3
+            , Edge 0 1
+            ]
+        }
+  , _fixedEdges =
+      [ Edge 0 1 , Edge 0 2 , Edge 4 5 , Edge 1 2 , Edge 3 4 , Edge 3 5 ]
+  }
 ```
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,2 @@
-import Distribution.Simple
-main = defaultMain
+import Distribution.Simple
+main = defaultMain
diff --git a/cpp/CDT.h b/cpp/CDT.h
--- a/cpp/CDT.h
+++ b/cpp/CDT.h
@@ -1,448 +1,448 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Public API
- */
-
-#ifndef CDT_lNrmUayWQaIR5fxnsg9B
-#define CDT_lNrmUayWQaIR5fxnsg9B
-
-#include "CDTUtils.h"
-#include "Triangulation.h"
-
-#include "remove_at.hpp"
-
-#include <algorithm>
-#include <cassert>
-#include <cstdlib>
-#include <iterator>
-#include <memory>
-#include <stack>
-#include <vector>
-
-/// Namespace containing triangulation functionality
-namespace CDT
-{
-
-/** @defgroup API Public API
- *  Contains API for constrained and conforming Delaunay triangulations
- */
-/// @{
-
-/**
- * Type used for storing layer depths for triangles
- * @note LayerDepth should support 60K+ layers, which could be to much or
- * too little for some use cases. Feel free to re-define this typedef.
- */
-typedef unsigned short LayerDepth;
-typedef LayerDepth BoundaryOverlapCount;
-
-/// Triangles by vertex index
-typedef std::vector<TriIndVec> VerticesTriangles;
-
-/** @defgroup helpers Helpers
- *  Helpers for working with CDT::Triangulation.
- */
-/// @{
-
-/**
- * Calculate triangles adjacent to vertices (triangles by vertex index)
- * @param triangles triangulation
- * @param verticesSize total number of vertices to pre-allocate the output
- * @return triangles by vertex index
- */
-CDT_EXPORT VerticesTriangles
-calculateTrianglesByVertex(const TriangleVec& triangles, VertInd verticesSize);
-
-/**
- * Information about removed duplicated vertices.
- *
- * Contains mapping information and removed duplicates indices.
- * @note vertices {0,1,2,3,4} where 0 and 3 are the same will produce mapping
- *       {0,1,2,0,3} (to new vertices {0,1,2,3}) and duplicates {3}
- */
-struct CDT_EXPORT DuplicatesInfo
-{
-    std::vector<std::size_t> mapping;    ///< vertex index mapping
-    std::vector<std::size_t> duplicates; ///< duplicates' indices
-};
-
-/**
- * Find duplicates in given custom point-type range
- * @note duplicates are points with exactly same X and Y coordinates
- * @tparam TVertexIter iterator that dereferences to custom point type
- * @tparam TGetVertexCoordX function object getting x coordinate from vertex.
- * Getter signature: const TVertexIter::value_type& -> T
- * @tparam TGetVertexCoordY function object getting y coordinate from vertex.
- * Getter signature: const TVertexIter::value_type& -> T
- * @param first beginning of the range of vertices
- * @param last end of the range of vertices
- * @param getX getter of X-coordinate
- * @param getY getter of Y-coordinate
- * @returns information about vertex duplicates
- */
-template <
-    typename T,
-    typename TVertexIter,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY>
-DuplicatesInfo FindDuplicates(
-    TVertexIter first,
-    TVertexIter last,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY);
-
-/**
- * Remove duplicates in-place from vector of custom points
- * @tparam TVertex vertex type
- * @tparam TAllocator allocator used by input vector of vertices
- * @param vertices vertices to remove duplicates from
- * @param duplicates information about duplicates
- */
-template <typename TVertex, typename TAllocator>
-void RemoveDuplicates(
-    std::vector<TVertex, TAllocator>& vertices,
-    const std::vector<std::size_t>& duplicates);
-
-/**
- * Remove duplicated points in-place
- *
- * @tparam T type of vertex coordinates (e.g., float, double)
- * @param[in, out] vertices collection of vertices to remove duplicates from
- * @returns information about duplicated vertices that were removed.
- */
-template <typename T>
-CDT_EXPORT DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices);
-
-/**
- * Remap vertex indices in edges (in-place) using given vertex-index mapping.
- * @tparam TEdgeIter iterator that dereferences to custom edge type
- * @tparam TGetEdgeVertexStart function object getting start vertex index
- * from an edge.
- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
- * @tparam TGetEdgeVertexEnd function object getting end vertex index from
- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
- * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from
- * start and end vertices
- * @param first beginning of the range of edges
- * @param last end of the range of edges
- * @param mapping vertex-index mapping
- * @param getStart getter of edge start vertex index
- * @param getEnd getter of edge end vertex index
- * @param makeEdge factory for making edge from vetices
- */
-template <
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd,
-    typename TMakeEdgeFromStartAndEnd>
-CDT_EXPORT void RemapEdges(
-    TEdgeIter first,
-    TEdgeIter last,
-    const std::vector<std::size_t>& mapping,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd,
-    TMakeEdgeFromStartAndEnd makeEdge);
-
-/**
- * Remap vertex indices in edges (in-place) using given vertex-index mapping.
- *
- * @note Mapping can be a result of RemoveDuplicates function
- * @param[in,out] edges collection of edges to remap
- * @param mapping vertex-index mapping
- */
-CDT_EXPORT void
-RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping);
-
-/**
- * Find point duplicates, remove them from vector (in-place) and remap edges
- * (in-place)
- * @note Same as a chained call of CDT::FindDuplicates, CDT::RemoveDuplicates,
- * and CDT::RemapEdges
- * @tparam T type of vertex coordinates (e.g., float, double)
- * @tparam TVertex type of vertex
- * @tparam TGetVertexCoordX function object getting x coordinate from vertex.
- * Getter signature: const TVertexIter::value_type& -> T
- * @tparam TGetVertexCoordY function object getting y coordinate from vertex.
- * Getter signature: const TVertexIter::value_type& -> T
- * @tparam TEdgeIter iterator that dereferences to custom edge type
- * @tparam TGetEdgeVertexStart function object getting start vertex index
- * from an edge.
- * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
- * @tparam TGetEdgeVertexEnd function object getting end vertex index from
- * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
- * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from
- * start and end vertices
- * @param[in, out] vertices vertices to remove duplicates from
- * @param[in, out] edges collection of edges connecting vertices
- * @param getX getter of X-coordinate
- * @param getY getter of Y-coordinate
- * @param edgesFirst beginning of the range of edges
- * @param edgesLast end of the range of edges
- * @param getStart getter of edge start vertex index
- * @param getEnd getter of edge end vertex index
- * @param makeEdge factory for making edge from vetices
- * @returns information about vertex duplicates
- */
-template <
-    typename T,
-    typename TVertex,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY,
-    typename TVertexAllocator,
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd,
-    typename TMakeEdgeFromStartAndEnd>
-DuplicatesInfo RemoveDuplicatesAndRemapEdges(
-    std::vector<TVertex, TVertexAllocator>& vertices,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY,
-    TEdgeIter edgesFirst,
-    TEdgeIter edgesLast,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd,
-    TMakeEdgeFromStartAndEnd makeEdge);
-
-/**
- * Same as a chained call of CDT::RemoveDuplicates + CDT::RemapEdges
- *
- * @tparam T type of vertex coordinates (e.g., float, double)
- * @param[in, out] vertices collection of vertices to remove duplicates from
- * @param[in,out] edges collection of edges to remap
- */
-template <typename T>
-CDT_EXPORT DuplicatesInfo RemoveDuplicatesAndRemapEdges(
-    std::vector<V2d<T> >& vertices,
-    std::vector<Edge>& edges);
-
-/**
- * Extract all edges of triangles
- *
- * @param triangles triangles used to extract edges
- * @return an unordered set of all edges of triangulation
- */
-CDT_EXPORT EdgeUSet extractEdgesFromTriangles(const TriangleVec& triangles);
-
-/*!
- * Converts piece->original_edges mapping to original_edge->pieces
- * @param pieceToOriginals maps pieces to original edges
- * @return mapping of original edges to pieces
- */
-CDT_EXPORT unordered_map<Edge, EdgeVec>
-EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals);
-
-/*!
- * Convert edge-to-pieces mapping into edge-to-split-vertices mapping
- * @tparam T type of vertex coordinates (e.g., float, double)
- * @param edgeToPieces edge-to-pieces mapping
- * @param vertices vertex buffer
- * @return mapping of edge-to-split-points.
- * Split points are sorted from edge's start (v1) to end (v2)
- */
-template <typename T>
-CDT_EXPORT unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(
-    const unordered_map<Edge, EdgeVec>& edgeToPieces,
-    const std::vector<V2d<T> >& vertices);
-
-/// @}
-
-/// @}
-
-} // namespace CDT
-
-//*****************************************************************************
-// Implementations of template functionlity
-//*****************************************************************************
-// hash for CDT::V2d<T>
-#ifdef CDT_CXX11_IS_SUPPORTED
-namespace std
-#else
-namespace boost
-#endif
-{
-template <typename T>
-struct hash<CDT::V2d<T> >
-{
-    size_t operator()(const CDT::V2d<T>& xy) const
-    {
-#ifdef CDT_CXX11_IS_SUPPORTED
-        typedef std::hash<T> Hasher;
-#else
-        typedef boost::hash<T> Hasher;
-#endif
-        return Hasher()(xy.x) ^ Hasher()(xy.y);
-    }
-};
-} // namespace std
-
-namespace CDT
-{
-
-//-----
-// API
-//-----
-template <
-    typename T,
-    typename TVertexIter,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY>
-DuplicatesInfo FindDuplicates(
-    TVertexIter first,
-    TVertexIter last,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY)
-{
-    typedef unordered_map<V2d<T>, std::size_t> PosToIndex;
-    PosToIndex uniqueVerts;
-    const std::size_t verticesSize = std::distance(first, last);
-    DuplicatesInfo di = {
-        std::vector<std::size_t>(verticesSize), std::vector<std::size_t>()};
-    for(std::size_t iIn = 0, iOut = iIn; iIn < verticesSize; ++iIn, ++first)
-    {
-        typename PosToIndex::const_iterator it;
-        bool isUnique;
-        tie(it, isUnique) = uniqueVerts.insert(
-            std::make_pair(V2d<T>::make(getX(*first), getY(*first)), iOut));
-        if(isUnique)
-        {
-            di.mapping[iIn] = iOut++;
-            continue;
-        }
-        di.mapping[iIn] = it->second; // found a duplicate
-        di.duplicates.push_back(iIn);
-    }
-    return di;
-}
-
-template <typename TVertex, typename TAllocator>
-void RemoveDuplicates(
-    std::vector<TVertex, TAllocator>& vertices,
-    const std::vector<std::size_t>& duplicates)
-{
-    vertices.erase(
-        remove_at(
-            vertices.begin(),
-            vertices.end(),
-            duplicates.begin(),
-            duplicates.end()),
-        vertices.end());
-}
-
-template <
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd,
-    typename TMakeEdgeFromStartAndEnd>
-void RemapEdges(
-    TEdgeIter first,
-    const TEdgeIter last,
-    const std::vector<std::size_t>& mapping,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd,
-    TMakeEdgeFromStartAndEnd makeEdge)
-{
-    for(; first != last; ++first)
-    {
-        *first = makeEdge(
-            static_cast<VertInd>(mapping[getStart(*first)]),
-            static_cast<VertInd>(mapping[getEnd(*first)]));
-    }
-}
-
-template <
-    typename T,
-    typename TVertex,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY,
-    typename TVertexAllocator,
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd,
-    typename TMakeEdgeFromStartAndEnd>
-DuplicatesInfo RemoveDuplicatesAndRemapEdges(
-    std::vector<TVertex, TVertexAllocator>& vertices,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY,
-    const TEdgeIter edgesFirst,
-    const TEdgeIter edgesLast,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd,
-    TMakeEdgeFromStartAndEnd makeEdge)
-{
-    const DuplicatesInfo di =
-        FindDuplicates<T>(vertices.begin(), vertices.end(), getX, getY);
-    RemoveDuplicates(vertices, di.duplicates);
-    RemapEdges(edgesFirst, edgesLast, di.mapping, getStart, getEnd, makeEdge);
-    return di;
-}
-
-template <typename T>
-unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(
-    const unordered_map<Edge, EdgeVec>& edgeToPieces,
-    const std::vector<V2d<T> >& vertices)
-{
-    typedef std::pair<VertInd, T> VertCoordPair;
-    struct ComparePred
-    {
-        bool operator()(const VertCoordPair& a, const VertCoordPair& b) const
-        {
-            return a.second < b.second;
-        }
-    } comparePred;
-
-    unordered_map<Edge, std::vector<VertInd> > edgeToSplitVerts;
-    typedef unordered_map<Edge, EdgeVec>::const_iterator It;
-    for(It it = edgeToPieces.begin(); it != edgeToPieces.end(); ++it)
-    {
-        const Edge& e = it->first;
-        const T dX = vertices[e.v2()].x - vertices[e.v1()].x;
-        const T dY = vertices[e.v2()].y - vertices[e.v1()].y;
-        const bool isX = std::abs(dX) >= std::abs(dY); // X-coord longer
-        const bool isAscending =
-            isX ? dX >= 0 : dY >= 0; // Longer coordinate ascends
-        const EdgeVec& pieces = it->second;
-        std::vector<VertCoordPair> splitVerts;
-        // size is:  2[ends] + (pieces - 1)[split vertices] = pieces + 1
-        splitVerts.reserve(pieces.size() + 1);
-        typedef EdgeVec::const_iterator EIt;
-        for(EIt it = pieces.begin(); it != pieces.end(); ++it)
-        {
-            const array<VertInd, 2> vv = {it->v1(), it->v2()};
-            typedef array<VertInd, 2>::const_iterator VIt;
-            for(VIt v = vv.begin(); v != vv.end(); ++v)
-            {
-                const T c = isX ? vertices[*v].x : vertices[*v].y;
-                splitVerts.push_back(std::make_pair(*v, isAscending ? c : -c));
-            }
-        }
-        // sort by longest coordinate
-        std::sort(splitVerts.begin(), splitVerts.end(), comparePred);
-        // remove duplicates
-        splitVerts.erase(
-            std::unique(splitVerts.begin(), splitVerts.end()),
-            splitVerts.end());
-        assert(splitVerts.size() > 2); // 2 end points with split vertices
-        std::pair<Edge, std::vector<VertInd> > val =
-            std::make_pair(e, std::vector<VertInd>());
-        val.second.reserve(splitVerts.size());
-        typedef typename std::vector<VertCoordPair>::const_iterator SEIt;
-        for(SEIt it = splitVerts.begin() + 1; it != splitVerts.end() - 1; ++it)
-        {
-            val.second.push_back(it->first);
-        }
-        edgeToSplitVerts.insert(val);
-    }
-    return edgeToSplitVerts;
-}
-
-} // namespace CDT
-
-#ifndef CDT_USE_AS_COMPILED_LIBRARY
-#include "CDT.hpp"
-#endif
-
-#endif // header-guard
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Public API
+ */
+
+#ifndef CDT_lNrmUayWQaIR5fxnsg9B
+#define CDT_lNrmUayWQaIR5fxnsg9B
+
+#include "CDTUtils.h"
+#include "Triangulation.h"
+
+#include "remove_at.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <iterator>
+#include <memory>
+#include <stack>
+#include <vector>
+
+/// Namespace containing triangulation functionality
+namespace CDT
+{
+
+/** @defgroup API Public API
+ *  Contains API for constrained and conforming Delaunay triangulations
+ */
+/// @{
+
+/**
+ * Type used for storing layer depths for triangles
+ * @note LayerDepth should support 60K+ layers, which could be to much or
+ * too little for some use cases. Feel free to re-define this typedef.
+ */
+typedef unsigned short LayerDepth;
+typedef LayerDepth BoundaryOverlapCount;
+
+/// Triangles by vertex index
+typedef std::vector<TriIndVec> VerticesTriangles;
+
+/** @defgroup helpers Helpers
+ *  Helpers for working with CDT::Triangulation.
+ */
+/// @{
+
+/**
+ * Calculate triangles adjacent to vertices (triangles by vertex index)
+ * @param triangles triangulation
+ * @param verticesSize total number of vertices to pre-allocate the output
+ * @return triangles by vertex index
+ */
+CDT_EXPORT VerticesTriangles
+calculateTrianglesByVertex(const TriangleVec& triangles, VertInd verticesSize);
+
+/**
+ * Information about removed duplicated vertices.
+ *
+ * Contains mapping information and removed duplicates indices.
+ * @note vertices {0,1,2,3,4} where 0 and 3 are the same will produce mapping
+ *       {0,1,2,0,3} (to new vertices {0,1,2,3}) and duplicates {3}
+ */
+struct CDT_EXPORT DuplicatesInfo
+{
+    std::vector<std::size_t> mapping;    ///< vertex index mapping
+    std::vector<std::size_t> duplicates; ///< duplicates' indices
+};
+
+/**
+ * Find duplicates in given custom point-type range
+ * @note duplicates are points with exactly same X and Y coordinates
+ * @tparam TVertexIter iterator that dereferences to custom point type
+ * @tparam TGetVertexCoordX function object getting x coordinate from vertex.
+ * Getter signature: const TVertexIter::value_type& -> T
+ * @tparam TGetVertexCoordY function object getting y coordinate from vertex.
+ * Getter signature: const TVertexIter::value_type& -> T
+ * @param first beginning of the range of vertices
+ * @param last end of the range of vertices
+ * @param getX getter of X-coordinate
+ * @param getY getter of Y-coordinate
+ * @returns information about vertex duplicates
+ */
+template <
+    typename T,
+    typename TVertexIter,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY>
+DuplicatesInfo FindDuplicates(
+    TVertexIter first,
+    TVertexIter last,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY);
+
+/**
+ * Remove duplicates in-place from vector of custom points
+ * @tparam TVertex vertex type
+ * @tparam TAllocator allocator used by input vector of vertices
+ * @param vertices vertices to remove duplicates from
+ * @param duplicates information about duplicates
+ */
+template <typename TVertex, typename TAllocator>
+void RemoveDuplicates(
+    std::vector<TVertex, TAllocator>& vertices,
+    const std::vector<std::size_t>& duplicates);
+
+/**
+ * Remove duplicated points in-place
+ *
+ * @tparam T type of vertex coordinates (e.g., float, double)
+ * @param[in, out] vertices collection of vertices to remove duplicates from
+ * @returns information about duplicated vertices that were removed.
+ */
+template <typename T>
+CDT_EXPORT DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices);
+
+/**
+ * Remap vertex indices in edges (in-place) using given vertex-index mapping.
+ * @tparam TEdgeIter iterator that dereferences to custom edge type
+ * @tparam TGetEdgeVertexStart function object getting start vertex index
+ * from an edge.
+ * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+ * @tparam TGetEdgeVertexEnd function object getting end vertex index from
+ * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+ * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from
+ * start and end vertices
+ * @param first beginning of the range of edges
+ * @param last end of the range of edges
+ * @param mapping vertex-index mapping
+ * @param getStart getter of edge start vertex index
+ * @param getEnd getter of edge end vertex index
+ * @param makeEdge factory for making edge from vetices
+ */
+template <
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd,
+    typename TMakeEdgeFromStartAndEnd>
+CDT_EXPORT void RemapEdges(
+    TEdgeIter first,
+    TEdgeIter last,
+    const std::vector<std::size_t>& mapping,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd,
+    TMakeEdgeFromStartAndEnd makeEdge);
+
+/**
+ * Remap vertex indices in edges (in-place) using given vertex-index mapping.
+ *
+ * @note Mapping can be a result of RemoveDuplicates function
+ * @param[in,out] edges collection of edges to remap
+ * @param mapping vertex-index mapping
+ */
+CDT_EXPORT void
+RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping);
+
+/**
+ * Find point duplicates, remove them from vector (in-place) and remap edges
+ * (in-place)
+ * @note Same as a chained call of CDT::FindDuplicates, CDT::RemoveDuplicates,
+ * and CDT::RemapEdges
+ * @tparam T type of vertex coordinates (e.g., float, double)
+ * @tparam TVertex type of vertex
+ * @tparam TGetVertexCoordX function object getting x coordinate from vertex.
+ * Getter signature: const TVertexIter::value_type& -> T
+ * @tparam TGetVertexCoordY function object getting y coordinate from vertex.
+ * Getter signature: const TVertexIter::value_type& -> T
+ * @tparam TEdgeIter iterator that dereferences to custom edge type
+ * @tparam TGetEdgeVertexStart function object getting start vertex index
+ * from an edge.
+ * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+ * @tparam TGetEdgeVertexEnd function object getting end vertex index from
+ * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+ * @tparam TMakeEdgeFromStartAndEnd function object that makes new edge from
+ * start and end vertices
+ * @param[in, out] vertices vertices to remove duplicates from
+ * @param[in, out] edges collection of edges connecting vertices
+ * @param getX getter of X-coordinate
+ * @param getY getter of Y-coordinate
+ * @param edgesFirst beginning of the range of edges
+ * @param edgesLast end of the range of edges
+ * @param getStart getter of edge start vertex index
+ * @param getEnd getter of edge end vertex index
+ * @param makeEdge factory for making edge from vetices
+ * @returns information about vertex duplicates
+ */
+template <
+    typename T,
+    typename TVertex,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY,
+    typename TVertexAllocator,
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd,
+    typename TMakeEdgeFromStartAndEnd>
+DuplicatesInfo RemoveDuplicatesAndRemapEdges(
+    std::vector<TVertex, TVertexAllocator>& vertices,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY,
+    TEdgeIter edgesFirst,
+    TEdgeIter edgesLast,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd,
+    TMakeEdgeFromStartAndEnd makeEdge);
+
+/**
+ * Same as a chained call of CDT::RemoveDuplicates + CDT::RemapEdges
+ *
+ * @tparam T type of vertex coordinates (e.g., float, double)
+ * @param[in, out] vertices collection of vertices to remove duplicates from
+ * @param[in,out] edges collection of edges to remap
+ */
+template <typename T>
+CDT_EXPORT DuplicatesInfo RemoveDuplicatesAndRemapEdges(
+    std::vector<V2d<T> >& vertices,
+    std::vector<Edge>& edges);
+
+/**
+ * Extract all edges of triangles
+ *
+ * @param triangles triangles used to extract edges
+ * @return an unordered set of all edges of triangulation
+ */
+CDT_EXPORT EdgeUSet extractEdgesFromTriangles(const TriangleVec& triangles);
+
+/*!
+ * Converts piece->original_edges mapping to original_edge->pieces
+ * @param pieceToOriginals maps pieces to original edges
+ * @return mapping of original edges to pieces
+ */
+CDT_EXPORT unordered_map<Edge, EdgeVec>
+EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals);
+
+/*!
+ * Convert edge-to-pieces mapping into edge-to-split-vertices mapping
+ * @tparam T type of vertex coordinates (e.g., float, double)
+ * @param edgeToPieces edge-to-pieces mapping
+ * @param vertices vertex buffer
+ * @return mapping of edge-to-split-points.
+ * Split points are sorted from edge's start (v1) to end (v2)
+ */
+template <typename T>
+CDT_EXPORT unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(
+    const unordered_map<Edge, EdgeVec>& edgeToPieces,
+    const std::vector<V2d<T> >& vertices);
+
+/// @}
+
+/// @}
+
+} // namespace CDT
+
+//*****************************************************************************
+// Implementations of template functionlity
+//*****************************************************************************
+// hash for CDT::V2d<T>
+#ifdef CDT_CXX11_IS_SUPPORTED
+namespace std
+#else
+namespace boost
+#endif
+{
+template <typename T>
+struct hash<CDT::V2d<T> >
+{
+    size_t operator()(const CDT::V2d<T>& xy) const
+    {
+#ifdef CDT_CXX11_IS_SUPPORTED
+        typedef std::hash<T> Hasher;
+#else
+        typedef boost::hash<T> Hasher;
+#endif
+        return Hasher()(xy.x) ^ Hasher()(xy.y);
+    }
+};
+} // namespace std
+
+namespace CDT
+{
+
+//-----
+// API
+//-----
+template <
+    typename T,
+    typename TVertexIter,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY>
+DuplicatesInfo FindDuplicates(
+    TVertexIter first,
+    TVertexIter last,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY)
+{
+    typedef unordered_map<V2d<T>, std::size_t> PosToIndex;
+    PosToIndex uniqueVerts;
+    const std::size_t verticesSize = std::distance(first, last);
+    DuplicatesInfo di = {
+        std::vector<std::size_t>(verticesSize), std::vector<std::size_t>()};
+    for(std::size_t iIn = 0, iOut = iIn; iIn < verticesSize; ++iIn, ++first)
+    {
+        typename PosToIndex::const_iterator it;
+        bool isUnique;
+        tie(it, isUnique) = uniqueVerts.insert(
+            std::make_pair(V2d<T>::make(getX(*first), getY(*first)), iOut));
+        if(isUnique)
+        {
+            di.mapping[iIn] = iOut++;
+            continue;
+        }
+        di.mapping[iIn] = it->second; // found a duplicate
+        di.duplicates.push_back(iIn);
+    }
+    return di;
+}
+
+template <typename TVertex, typename TAllocator>
+void RemoveDuplicates(
+    std::vector<TVertex, TAllocator>& vertices,
+    const std::vector<std::size_t>& duplicates)
+{
+    vertices.erase(
+        remove_at(
+            vertices.begin(),
+            vertices.end(),
+            duplicates.begin(),
+            duplicates.end()),
+        vertices.end());
+}
+
+template <
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd,
+    typename TMakeEdgeFromStartAndEnd>
+void RemapEdges(
+    TEdgeIter first,
+    const TEdgeIter last,
+    const std::vector<std::size_t>& mapping,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd,
+    TMakeEdgeFromStartAndEnd makeEdge)
+{
+    for(; first != last; ++first)
+    {
+        *first = makeEdge(
+            static_cast<VertInd>(mapping[getStart(*first)]),
+            static_cast<VertInd>(mapping[getEnd(*first)]));
+    }
+}
+
+template <
+    typename T,
+    typename TVertex,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY,
+    typename TVertexAllocator,
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd,
+    typename TMakeEdgeFromStartAndEnd>
+DuplicatesInfo RemoveDuplicatesAndRemapEdges(
+    std::vector<TVertex, TVertexAllocator>& vertices,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY,
+    const TEdgeIter edgesFirst,
+    const TEdgeIter edgesLast,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd,
+    TMakeEdgeFromStartAndEnd makeEdge)
+{
+    const DuplicatesInfo di =
+        FindDuplicates<T>(vertices.begin(), vertices.end(), getX, getY);
+    RemoveDuplicates(vertices, di.duplicates);
+    RemapEdges(edgesFirst, edgesLast, di.mapping, getStart, getEnd, makeEdge);
+    return di;
+}
+
+template <typename T>
+unordered_map<Edge, std::vector<VertInd> > EdgeToSplitVertices(
+    const unordered_map<Edge, EdgeVec>& edgeToPieces,
+    const std::vector<V2d<T> >& vertices)
+{
+    typedef std::pair<VertInd, T> VertCoordPair;
+    struct ComparePred
+    {
+        bool operator()(const VertCoordPair& a, const VertCoordPair& b) const
+        {
+            return a.second < b.second;
+        }
+    } comparePred;
+
+    unordered_map<Edge, std::vector<VertInd> > edgeToSplitVerts;
+    typedef unordered_map<Edge, EdgeVec>::const_iterator It;
+    for(It e2pIt = edgeToPieces.begin(); e2pIt != edgeToPieces.end(); ++e2pIt)
+    {
+        const Edge& e = e2pIt->first;
+        const T dX = vertices[e.v2()].x - vertices[e.v1()].x;
+        const T dY = vertices[e.v2()].y - vertices[e.v1()].y;
+        const bool isX = std::abs(dX) >= std::abs(dY); // X-coord longer
+        const bool isAscending =
+            isX ? dX >= 0 : dY >= 0; // Longer coordinate ascends
+        const EdgeVec& pieces = e2pIt->second;
+        std::vector<VertCoordPair> splitVerts;
+        // size is:  2[ends] + (pieces - 1)[split vertices] = pieces + 1
+        splitVerts.reserve(pieces.size() + 1);
+        typedef EdgeVec::const_iterator EIt;
+        for(EIt pieceIt = pieces.begin(); pieceIt != pieces.end(); ++pieceIt)
+        {
+            const array<VertInd, 2> vv = {pieceIt->v1(), pieceIt->v2()};
+            typedef array<VertInd, 2>::const_iterator VIt;
+            for(VIt v = vv.begin(); v != vv.end(); ++v)
+            {
+                const T c = isX ? vertices[*v].x : vertices[*v].y;
+                splitVerts.push_back(std::make_pair(*v, isAscending ? c : -c));
+            }
+        }
+        // sort by longest coordinate
+        std::sort(splitVerts.begin(), splitVerts.end(), comparePred);
+        // remove duplicates
+        splitVerts.erase(
+            std::unique(splitVerts.begin(), splitVerts.end()),
+            splitVerts.end());
+        assert(splitVerts.size() > 2); // 2 end points with split vertices
+        std::pair<Edge, std::vector<VertInd> > val =
+            std::make_pair(e, std::vector<VertInd>());
+        val.second.reserve(splitVerts.size());
+        typedef typename std::vector<VertCoordPair>::const_iterator SEIt;
+        for(SEIt it = splitVerts.begin() + 1; it != splitVerts.end() - 1; ++it)
+        {
+            val.second.push_back(it->first);
+        }
+        edgeToSplitVerts.insert(val);
+    }
+    return edgeToSplitVerts;
+}
+
+} // namespace CDT
+
+#ifndef CDT_USE_AS_COMPILED_LIBRARY
+#include "CDT.hpp"
+#endif
+
+#endif // header-guard
diff --git a/cpp/CDT.hpp b/cpp/CDT.hpp
--- a/cpp/CDT.hpp
+++ b/cpp/CDT.hpp
@@ -1,107 +1,107 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Public API - implementation
- */
-
-#include "CDT.h"
-
-#include <algorithm>
-#include <deque>
-#include <limits>
-#include <stdexcept>
-
-namespace CDT
-{
-
-CDT_INLINE_IF_HEADER_ONLY VerticesTriangles calculateTrianglesByVertex(
-    const TriangleVec& triangles,
-    const VertInd verticesSize)
-{
-    VerticesTriangles vertTris(verticesSize);
-    for(TriInd iT = 0; iT < triangles.size(); ++iT)
-    {
-        const VerticesArr3& vv = triangles[iT].vertices;
-        for(VerticesArr3::const_iterator v = vv.begin(); v != vv.end(); ++v)
-        {
-            vertTris[*v].push_back(iT);
-        }
-    }
-    return vertTris;
-}
-
-template <typename T>
-DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices)
-{
-    const DuplicatesInfo di = FindDuplicates<T>(
-        vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);
-    RemoveDuplicates(vertices, di.duplicates);
-    return di;
-}
-
-CDT_INLINE_IF_HEADER_ONLY void
-RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping)
-{
-    RemapEdges(
-        edges.begin(),
-        edges.end(),
-        mapping,
-        edge_get_v1,
-        edge_get_v2,
-        edge_make);
-}
-
-template <typename T>
-DuplicatesInfo RemoveDuplicatesAndRemapEdges(
-    std::vector<V2d<T> >& vertices,
-    std::vector<Edge>& edges)
-{
-    return RemoveDuplicatesAndRemapEdges<T>(
-        vertices,
-        getX_V2d<T>,
-        getY_V2d<T>,
-        edges.begin(),
-        edges.end(),
-        edge_get_v1,
-        edge_get_v2,
-        edge_make);
-}
-
-CDT_INLINE_IF_HEADER_ONLY EdgeUSet
-extractEdgesFromTriangles(const TriangleVec& triangles)
-{
-    EdgeUSet edges;
-    typedef TriangleVec::const_iterator CIt;
-    for(CIt t = triangles.begin(); t != triangles.end(); ++t)
-    {
-        edges.insert(Edge(VertInd(t->vertices[0]), VertInd(t->vertices[1])));
-        edges.insert(Edge(VertInd(t->vertices[1]), VertInd(t->vertices[2])));
-        edges.insert(Edge(VertInd(t->vertices[2]), VertInd(t->vertices[0])));
-    }
-    return edges;
-}
-
-CDT_INLINE_IF_HEADER_ONLY unordered_map<Edge, EdgeVec>
-EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals)
-{
-    unordered_map<Edge, EdgeVec> originalToPieces;
-    typedef unordered_map<Edge, EdgeVec>::const_iterator Cit;
-    for(Cit ptoIt = pieceToOriginals.begin(); ptoIt != pieceToOriginals.end();
-        ++ptoIt)
-    {
-        const Edge piece = ptoIt->first;
-        const EdgeVec& originals = ptoIt->second;
-        for(EdgeVec::const_iterator origIt = originals.begin();
-            origIt != originals.end();
-            ++origIt)
-        {
-            originalToPieces[*origIt].push_back(piece);
-        }
-    }
-    return originalToPieces;
-}
-
-} // namespace CDT
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Public API - implementation
+ */
+
+#include "CDT.h"
+
+#include <algorithm>
+#include <deque>
+#include <limits>
+#include <stdexcept>
+
+namespace CDT
+{
+
+CDT_INLINE_IF_HEADER_ONLY VerticesTriangles calculateTrianglesByVertex(
+    const TriangleVec& triangles,
+    const VertInd verticesSize)
+{
+    VerticesTriangles vertTris(verticesSize);
+    for(TriInd iT(0); iT < triangles.size(); ++iT)
+    {
+        const VerticesArr3& vv = triangles[iT].vertices;
+        for(VerticesArr3::const_iterator v = vv.begin(); v != vv.end(); ++v)
+        {
+            vertTris[*v].push_back(iT);
+        }
+    }
+    return vertTris;
+}
+
+template <typename T>
+DuplicatesInfo RemoveDuplicates(std::vector<V2d<T> >& vertices)
+{
+    const DuplicatesInfo di = FindDuplicates<T>(
+        vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);
+    RemoveDuplicates(vertices, di.duplicates);
+    return di;
+}
+
+CDT_INLINE_IF_HEADER_ONLY void
+RemapEdges(std::vector<Edge>& edges, const std::vector<std::size_t>& mapping)
+{
+    RemapEdges(
+        edges.begin(),
+        edges.end(),
+        mapping,
+        edge_get_v1,
+        edge_get_v2,
+        edge_make);
+}
+
+template <typename T>
+DuplicatesInfo RemoveDuplicatesAndRemapEdges(
+    std::vector<V2d<T> >& vertices,
+    std::vector<Edge>& edges)
+{
+    return RemoveDuplicatesAndRemapEdges<T>(
+        vertices,
+        getX_V2d<T>,
+        getY_V2d<T>,
+        edges.begin(),
+        edges.end(),
+        edge_get_v1,
+        edge_get_v2,
+        edge_make);
+}
+
+CDT_INLINE_IF_HEADER_ONLY EdgeUSet
+extractEdgesFromTriangles(const TriangleVec& triangles)
+{
+    EdgeUSet edges;
+    typedef TriangleVec::const_iterator CIt;
+    for(CIt t = triangles.begin(); t != triangles.end(); ++t)
+    {
+        edges.insert(Edge(VertInd(t->vertices[0]), VertInd(t->vertices[1])));
+        edges.insert(Edge(VertInd(t->vertices[1]), VertInd(t->vertices[2])));
+        edges.insert(Edge(VertInd(t->vertices[2]), VertInd(t->vertices[0])));
+    }
+    return edges;
+}
+
+CDT_INLINE_IF_HEADER_ONLY unordered_map<Edge, EdgeVec>
+EdgeToPiecesMapping(const unordered_map<Edge, EdgeVec>& pieceToOriginals)
+{
+    unordered_map<Edge, EdgeVec> originalToPieces;
+    typedef unordered_map<Edge, EdgeVec>::const_iterator Cit;
+    for(Cit ptoIt = pieceToOriginals.begin(); ptoIt != pieceToOriginals.end();
+        ++ptoIt)
+    {
+        const Edge piece = ptoIt->first;
+        const EdgeVec& originals = ptoIt->second;
+        for(EdgeVec::const_iterator origIt = originals.begin();
+            origIt != originals.end();
+            ++origIt)
+        {
+            originalToPieces[*origIt].push_back(piece);
+        }
+    }
+    return originalToPieces;
+}
+
+} // namespace CDT
diff --git a/cpp/CDTUtils.h b/cpp/CDTUtils.h
--- a/cpp/CDTUtils.h
+++ b/cpp/CDTUtils.h
@@ -1,475 +1,507 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Utilities and helpers
- */
-
-#ifndef CDT_obwOaxOTdAWcLNTlNnaq
-#define CDT_obwOaxOTdAWcLNTlNnaq
-
-#ifdef CDT_DONT_USE_BOOST_RTREE
-// CDT_DONT_USE_BOOST_RTREE was replaced with CDT_USE_BOOST
-typedef char CDT_DONT_USE_BOOST_RTREE__was__replaced__with__CDT_USE_BOOST[-1];
-#endif
-
-// #define CDT_USE_STRONG_TYPING // strong type checks on indices
-
-// check if c++11 is supported
-#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
-#define CDT_CXX11_IS_SUPPORTED
-#elif !defined(__cplusplus) && !defined(_MSC_VER)
-typedef char couldnt_parse_cxx_standard[-1]; ///< Error: couldn't parse standard
-#endif
-
-// Functions defined outside the class need to be 'inline'
-// if CDT is configured to be used as header-only library:
-// single-definition rule is violated otherwise
-#ifdef CDT_USE_AS_COMPILED_LIBRARY
-#define CDT_INLINE_IF_HEADER_ONLY
-#include "cdt_export.h" // automatically generated by CMake
-#else
-/**
- * Macro for inlining non-template functions when in header-only mode to
- * avoid multiple declaration errors.
- */
-#define CDT_INLINE_IF_HEADER_ONLY inline
-/// Export not needed in header-only mode
-#define CDT_EXPORT
-#endif
-
-#include <cassert>
-#include <cmath>
-#include <limits>
-#include <vector>
-
-#ifdef CDT_USE_BOOST
-#include <boost/container/flat_set.hpp>
-#endif
-
-// use fall-backs for c++11 features
-#ifdef CDT_CXX11_IS_SUPPORTED
-
-#include <array>
-#include <functional>
-#include <random>
-#include <tuple>
-#include <unordered_map>
-#include <unordered_set>
-namespace CDT
-{
-using std::array;
-using std::get;
-using std::make_tuple;
-using std::mt19937;
-using std::tie;
-using std::tuple;
-using std::unordered_map;
-using std::unordered_set;
-} // namespace CDT
-
-#else
-#include <boost/array.hpp>
-#include <boost/functional/hash.hpp>
-#include <boost/random.hpp>
-#include <boost/tuple/tuple.hpp>
-#include <boost/unordered_map.hpp>
-#include <boost/unordered_set.hpp>
-namespace CDT
-{
-using boost::array;
-using boost::get;
-using boost::make_tuple;
-using boost::tie;
-using boost::tuple;
-using boost::unordered_map;
-using boost::unordered_set;
-using boost::random::mt19937;
-} // namespace CDT
-#endif
-
-namespace CDT
-{
-
-/// 2D vector
-template <typename T>
-struct CDT_EXPORT V2d
-{
-    T x; ///< X-coordinate
-    T y; ///< Y-coordinate
-
-    /// Create vector from X and Y coordinates
-    static V2d make(T x, T y);
-};
-
-/// X- coordinate getter for V2d
-template <typename T>
-const T& getX_V2d(const V2d<T>& v)
-{
-    return v.x;
-}
-
-/// Y-coordinate getter for V2d
-template <typename T>
-const T& getY_V2d(const V2d<T>& v)
-{
-    return v.y;
-}
-
-/// If two 2D vectors are exactly equal
-template <typename T>
-bool operator==(const CDT::V2d<T>& lhs, const CDT::V2d<T>& rhs)
-{
-    return lhs.x == rhs.x && lhs.y == rhs.y;
-}
-
-#ifdef CDT_USE_64_BIT_INDEX_TYPE
-typedef unsigned long long IndexSizeType;
-#else
-typedef unsigned int IndexSizeType;
-#endif
-
-#ifndef CDT_USE_STRONG_TYPING
-/// Index in triangle
-typedef unsigned char Index;
-/// Vertex index
-typedef IndexSizeType VertInd;
-/// Triangle index
-typedef IndexSizeType TriInd;
-#else
-/// Index in triangle
-BOOST_STRONG_TYPEDEF(unsigned char, Index);
-/// Vertex index
-BOOST_STRONG_TYPEDEF(IndexSizeType, VertInd);
-/// Triangle index
-BOOST_STRONG_TYPEDEF(IndexSizeType, TriInd);
-#endif
-
-/// Constant representing no valid neighbor for a triangle
-const static TriInd noNeighbor(std::numeric_limits<TriInd>::max());
-/// Constant representing no valid vertex for a triangle
-const static VertInd noVertex(std::numeric_limits<VertInd>::max());
-
-typedef std::vector<TriInd> TriIndVec;  ///< Vector of triangle indices
-typedef array<VertInd, 3> VerticesArr3; ///< array of three vertex indices
-typedef array<TriInd, 3> NeighborsArr3; ///< array of three neighbors
-
-/// 2D bounding box
-template <typename T>
-struct CDT_EXPORT Box2d
-{
-    V2d<T> min; ///< min box corner
-    V2d<T> max; ///< max box corner
-
-    /// Envelop box around a point
-    void envelopPoint(const V2d<T>& p)
-    {
-        envelopPoint(p.x, p.y);
-    }
-    /// Envelop box around a point with given coordinates
-    void envelopPoint(const T x, const T y)
-    {
-        min.x = std::min(x, min.x);
-        max.x = std::max(x, max.x);
-        min.y = std::min(y, min.y);
-        max.y = std::max(y, max.y);
-    }
-};
-
-/// Bounding box of a collection of custom 2D points given coordinate getters
-template <
-    typename T,
-    typename TVertexIter,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY>
-Box2d<T> envelopBox(
-    TVertexIter first,
-    TVertexIter last,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY)
-{
-    const T max = std::numeric_limits<T>::max();
-    Box2d<T> box = {{max, max}, {-max, -max}};
-    for(; first != last; ++first)
-    {
-        box.envelopPoint(getX(*first), getY(*first));
-    }
-    return box;
-}
-
-/// Bounding box of a collection of 2D points
-template <typename T>
-CDT_EXPORT Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices);
-
-/// Edge connecting two vertices: vertex with smaller index is always first
-/// \note: hash Edge is specialized at the bottom
-struct CDT_EXPORT Edge
-{
-    /// Constructor
-    Edge(VertInd iV1, VertInd iV2);
-    /// Equals operator
-    bool operator==(const Edge& other) const;
-    /// Not-equals operator
-    bool operator!=(const Edge& other) const;
-    /// V1 getter
-    VertInd v1() const;
-    /// V2 getter
-    VertInd v2() const;
-    /// Edges' vertices
-    const std::pair<VertInd, VertInd>& verts() const;
-
-private:
-    std::pair<VertInd, VertInd> m_vertices;
-};
-
-/// Get edge first vertex
-inline VertInd edge_get_v1(const Edge& e)
-{
-    return e.v1();
-}
-
-/// Get edge second vertex
-inline VertInd edge_get_v2(const Edge& e)
-{
-    return e.v2();
-}
-
-/// Get edge second vertex
-inline Edge edge_make(VertInd iV1, VertInd iV2)
-{
-    return Edge(iV1, iV2);
-}
-
-typedef std::vector<Edge> EdgeVec;                ///< Vector of edges
-typedef unordered_set<Edge> EdgeUSet;             ///< Hash table of edges
-typedef unordered_set<TriInd> TriIndUSet;         ///< Hash table of triangles
-typedef unordered_map<TriInd, TriInd> TriIndUMap; ///< Triangle hash map
-#ifdef CDT_USE_BOOST
-/// Flat hash table of triangles
-typedef boost::container::flat_set<TriInd> TriIndFlatUSet;
-#endif
-
-/// Triangulation triangle (CCW winding)
-/* Counter-clockwise winding:
-       v3
-       /\
-    n3/  \n2
-     /____\
-   v1  n1  v2                 */
-struct CDT_EXPORT Triangle
-{
-    VerticesArr3 vertices;   ///< triangle's three vertices
-    NeighborsArr3 neighbors; ///< triangle's three neighbors
-
-    /**
-     * Factory method
-     * @note needed for c++03 compatibility (no uniform initialization
-     * available)
-     */
-    static Triangle
-    make(const array<VertInd, 3>& vertices, const array<TriInd, 3>& neighbors)
-    {
-        Triangle t = {vertices, neighbors};
-        return t;
-    }
-};
-
-typedef std::vector<Triangle> TriangleVec; ///< Vector of triangles
-
-/// Advance vertex or neighbor index counter-clockwise
-CDT_EXPORT Index ccw(Index i);
-
-/// Advance vertex or neighbor index clockwise
-CDT_EXPORT Index cw(Index i);
-
-/// Location of point on a triangle
-struct CDT_EXPORT PtTriLocation
-{
-    /// Enum
-    enum Enum
-    {
-        Inside,
-        Outside,
-        OnEdge1,
-        OnEdge2,
-        OnEdge3,
-    };
-};
-
-/// Check if location is classified as on any of three edges
-CDT_EXPORT bool isOnEdge(PtTriLocation::Enum location);
-
-/// Neighbor index from a on-edge location
-/// \note Call only if located on the edge!
-CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location);
-
-/// Relative location of point to a line
-struct CDT_EXPORT PtLineLocation
-{
-    /// Enum
-    enum Enum
-    {
-        Left,
-        Right,
-        OnLine,
-    };
-};
-
-/// Orient p against line v1-v2 2D: robust geometric predicate
-template <typename T>
-CDT_EXPORT T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2);
-
-/// Check if point lies to the left of, to the right of, or on a line
-template <typename T>
-CDT_EXPORT PtLineLocation::Enum locatePointLine(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    T orientationTolerance = T(0));
-
-/// Classify value of orient2d predicate
-template <typename T>
-CDT_EXPORT PtLineLocation::Enum
-classifyOrientation(T orientation, T orientationTolerance = T(0));
-
-/// Check if point a lies inside of, outside of, or on an edge of a triangle
-template <typename T>
-CDT_EXPORT PtTriLocation::Enum locatePointTriangle(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    const V2d<T>& v3);
-
-/// Opposed neighbor index from vertex index
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex);
-
-/// Opposed vertex index from neighbor index
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoVrt(Index neighborIndex);
-
-/// Index of triangle's neighbor opposed to a vertex
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
-opposedTriangleInd(const Triangle& tri, VertInd iVert);
-
-/// Index of triangle's neighbor opposed to an edge
-CDT_INLINE_IF_HEADER_ONLY Index
-opposedTriangleInd(const Triangle& tri, VertInd iVedge1, VertInd iVedge2);
-
-/// Index of triangle's vertex opposed to a triangle
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
-opposedVertexInd(const Triangle& tri, TriInd iTopo);
-
-/// If triangle has a given neighbor return neighbor-index, throw otherwise
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
-neighborInd(const Triangle& tri, TriInd iTnbr);
-
-/// If triangle has a given vertex return vertex-index, throw otherwise
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
-vertexInd(const Triangle& tri, VertInd iV);
-
-/// Given triangle and a vertex find opposed triangle
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd
-opposedTriangle(const Triangle& tri, VertInd iVert);
-
-/// Given two triangles, return vertex of first triangle opposed to the second
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY VertInd
-opposedVertex(const Triangle& tri, TriInd iTopo);
-
-/// Test if point lies in a circumscribed circle of a triangle
-template <typename T>
-CDT_EXPORT bool isInCircumcircle(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    const V2d<T>& v3);
-
-/// Test if two vertices share at least one common triangle
-CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY bool
-verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris);
-
-/// Distance between two 2D points
-template <typename T>
-CDT_EXPORT T distance(const V2d<T>& a, const V2d<T>& b);
-
-/// Squared distance between two 2D points
-template <typename T>
-CDT_EXPORT T distanceSquared(const V2d<T>& a, const V2d<T>& b);
-
-} // namespace CDT
-
-#ifndef CDT_USE_AS_COMPILED_LIBRARY
-#include "CDTUtils.hpp"
-#endif
-
-//*****************************************************************************
-// Specialize hash functions
-//*****************************************************************************
-#ifdef CDT_CXX11_IS_SUPPORTED
-namespace std
-#else
-namespace boost
-#endif
-{
-
-#ifdef CDT_USE_STRONG_TYPING
-
-/// Vertex index hasher
-template <>
-struct hash<CDT::VertInd>
-{
-    /// Hash operator
-    std::size_t operator()(const CDT::VertInd& vi) const
-    {
-        return std::hash<std::size_t>()(vi.t);
-    }
-};
-
-/// Triangle index hasher
-template <>
-struct hash<CDT::TriInd>
-{
-    /// Hash operator
-    std::size_t operator()(const CDT::TriInd& vi) const
-    {
-        return std::hash<std::size_t>()(vi.t);
-    }
-};
-
-#endif // CDT_USE_STRONG_TYPING
-
-/// Edge hasher
-template <>
-struct hash<CDT::Edge>
-{
-    /// Hash operator
-    std::size_t operator()(const CDT::Edge& e) const
-    {
-        return hashEdge(e);
-    }
-
-private:
-    static void hashCombine(std::size_t& seed, const CDT::VertInd& key)
-    {
-#ifdef CDT_CXX11_IS_SUPPORTED
-        typedef std::hash<CDT::VertInd> Hasher;
-#else
-        typedef boost::hash<CDT::VertInd> Hasher;
-#endif
-        seed ^= Hasher()(key) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
-    }
-    static std::size_t hashEdge(const CDT::Edge& e)
-    {
-        const std::pair<CDT::VertInd, CDT::VertInd>& vv = e.verts();
-        std::size_t seed1(0);
-        hashCombine(seed1, vv.first);
-        hashCombine(seed1, vv.second);
-        std::size_t seed2(0);
-        hashCombine(seed2, vv.second);
-        hashCombine(seed2, vv.first);
-        return std::min(seed1, seed2);
-    }
-};
-} // namespace std/boost
-
-#endif // header guard
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Utilities and helpers
+ */
+
+#ifndef CDT_obwOaxOTdAWcLNTlNnaq
+#define CDT_obwOaxOTdAWcLNTlNnaq
+
+#ifdef CDT_DONT_USE_BOOST_RTREE
+// CDT_DONT_USE_BOOST_RTREE was replaced with CDT_USE_BOOST
+typedef char CDT_DONT_USE_BOOST_RTREE__was__replaced__with__CDT_USE_BOOST[-1];
+#endif
+
+// #define CDT_USE_STRONG_TYPING // strong type checks on indices
+
+// check if c++11 is supported
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+#define CDT_CXX11_IS_SUPPORTED
+#elif !defined(__cplusplus) && !defined(_MSC_VER)
+typedef char couldnt_parse_cxx_standard[-1]; ///< Error: couldn't parse standard
+#endif
+
+// Functions defined outside the class need to be 'inline'
+// if CDT is configured to be used as header-only library:
+// single-definition rule is violated otherwise
+#ifdef CDT_USE_AS_COMPILED_LIBRARY
+#define CDT_INLINE_IF_HEADER_ONLY
+#include "cdt_export.h" // automatically generated by CMake
+#else
+/**
+ * Macro for inlining non-template functions when in header-only mode to
+ * avoid multiple declaration errors.
+ */
+#define CDT_INLINE_IF_HEADER_ONLY inline
+/// Export not needed in header-only mode
+#define CDT_EXPORT
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <vector>
+
+#ifdef CDT_USE_STRONG_TYPING
+#include <boost/serialization/strong_typedef.hpp>
+#endif
+
+// use fall-backs for c++11 features
+#ifdef CDT_CXX11_IS_SUPPORTED
+
+#include <array>
+#include <functional>
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+namespace CDT
+{
+using std::array;
+using std::get;
+using std::make_tuple;
+using std::tie;
+using std::tuple;
+using std::unordered_map;
+using std::unordered_set;
+} // namespace CDT
+
+#else
+#include <boost/array.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/unordered_map.hpp>
+#include <boost/unordered_set.hpp>
+namespace CDT
+{
+using boost::array;
+using boost::get;
+using boost::make_tuple;
+using boost::tie;
+using boost::tuple;
+using boost::unordered_map;
+using boost::unordered_set;
+} // namespace CDT
+#endif
+
+namespace CDT
+{
+
+/// 2D vector
+template <typename T>
+struct CDT_EXPORT V2d
+{
+    T x; ///< X-coordinate
+    T y; ///< Y-coordinate
+
+    /// Create vector from X and Y coordinates
+    static V2d make(T x, T y);
+};
+
+/// X- coordinate getter for V2d
+template <typename T>
+const T& getX_V2d(const V2d<T>& v)
+{
+    return v.x;
+}
+
+/// Y-coordinate getter for V2d
+template <typename T>
+const T& getY_V2d(const V2d<T>& v)
+{
+    return v.y;
+}
+
+/// If two 2D vectors are exactly equal
+template <typename T>
+bool operator==(const CDT::V2d<T>& lhs, const CDT::V2d<T>& rhs)
+{
+    return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+#ifdef CDT_USE_64_BIT_INDEX_TYPE
+typedef unsigned long long IndexSizeType;
+#else
+typedef unsigned int IndexSizeType;
+#endif
+
+#ifdef CDT_USE_STRONG_TYPING
+/// Index in triangle
+BOOST_STRONG_TYPEDEF(unsigned char, Index);
+/// Vertex index
+BOOST_STRONG_TYPEDEF(IndexSizeType, VertInd);
+/// Triangle index
+BOOST_STRONG_TYPEDEF(IndexSizeType, TriInd);
+#else
+/// Index in triangle
+typedef unsigned char Index;
+/// Vertex index
+typedef IndexSizeType VertInd;
+/// Triangle index
+typedef IndexSizeType TriInd;
+#endif
+
+/// Constant representing no valid value for index
+const static IndexSizeType
+    invalidIndex(std::numeric_limits<IndexSizeType>::max());
+/// Constant representing no valid neighbor for a triangle
+const static TriInd noNeighbor(invalidIndex);
+/// Constant representing no valid vertex for a triangle
+const static VertInd noVertex(invalidIndex);
+
+typedef std::vector<TriInd> TriIndVec;  ///< Vector of triangle indices
+typedef array<VertInd, 3> VerticesArr3; ///< array of three vertex indices
+typedef array<TriInd, 3> NeighborsArr3; ///< array of three neighbors
+
+/// 2D bounding box
+template <typename T>
+struct CDT_EXPORT Box2d
+{
+    V2d<T> min; ///< min box corner
+    V2d<T> max; ///< max box corner
+
+    /// Envelop box around a point
+    void envelopPoint(const V2d<T>& p)
+    {
+        envelopPoint(p.x, p.y);
+    }
+    /// Envelop box around a point with given coordinates
+    void envelopPoint(const T x, const T y)
+    {
+        min.x = std::min(x, min.x);
+        max.x = std::max(x, max.x);
+        min.y = std::min(y, min.y);
+        max.y = std::max(y, max.y);
+    }
+};
+
+/// Bounding box of a collection of custom 2D points given coordinate getters
+template <
+    typename T,
+    typename TVertexIter,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY>
+Box2d<T> envelopBox(
+    TVertexIter first,
+    TVertexIter last,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY)
+{
+    const T max = std::numeric_limits<T>::max();
+    Box2d<T> box = {{max, max}, {-max, -max}};
+    for(; first != last; ++first)
+    {
+        box.envelopPoint(getX(*first), getY(*first));
+    }
+    return box;
+}
+
+/// Bounding box of a collection of 2D points
+template <typename T>
+CDT_EXPORT Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices);
+
+/// Edge connecting two vertices: vertex with smaller index is always first
+/// \note: hash Edge is specialized at the bottom
+struct CDT_EXPORT Edge
+{
+    /// Constructor
+    Edge(VertInd iV1, VertInd iV2);
+    /// Equals operator
+    bool operator==(const Edge& other) const;
+    /// Not-equals operator
+    bool operator!=(const Edge& other) const;
+    /// V1 getter
+    VertInd v1() const;
+    /// V2 getter
+    VertInd v2() const;
+    /// Edges' vertices
+    const std::pair<VertInd, VertInd>& verts() const;
+
+private:
+    std::pair<VertInd, VertInd> m_vertices;
+};
+
+/// Get edge first vertex
+inline VertInd edge_get_v1(const Edge& e)
+{
+    return e.v1();
+}
+
+/// Get edge second vertex
+inline VertInd edge_get_v2(const Edge& e)
+{
+    return e.v2();
+}
+
+/// Get edge second vertex
+inline Edge edge_make(VertInd iV1, VertInd iV2)
+{
+    return Edge(iV1, iV2);
+}
+
+typedef std::vector<Edge> EdgeVec;                ///< Vector of edges
+typedef unordered_set<Edge> EdgeUSet;             ///< Hash table of edges
+typedef unordered_set<TriInd> TriIndUSet;         ///< Hash table of triangles
+typedef unordered_map<TriInd, TriInd> TriIndUMap; ///< Triangle hash map
+
+/// Triangulation triangle (counter-clockwise winding)
+/*
+ *      v3
+ *      /\
+ *   n3/  \n2
+ *    /____\
+ *  v1  n1  v2
+ */
+struct CDT_EXPORT Triangle
+{
+    VerticesArr3 vertices;   ///< triangle's three vertices
+    NeighborsArr3 neighbors; ///< triangle's three neighbors
+
+    /**
+     * Factory method
+     * @note needed for c++03 compatibility (no uniform initialization
+     * available)
+     */
+    static Triangle
+    make(const array<VertInd, 3>& vertices, const array<TriInd, 3>& neighbors)
+    {
+        Triangle t = {vertices, neighbors};
+        return t;
+    }
+
+    /// Next triangle adjacent to a vertex (clockwise)
+    /// @returns pair of next triangle and the other vertex of a common edge
+    std::pair<TriInd, VertInd> next(const VertInd i) const
+    {
+        assert(vertices[0] == i || vertices[1] == i || vertices[2] == i);
+        if(vertices[0] == i)
+        {
+            return std::make_pair(neighbors[0], vertices[1]);
+        }
+        if(vertices[1] == i)
+        {
+            return std::make_pair(neighbors[1], vertices[2]);
+        }
+        return std::make_pair(neighbors[2], vertices[0]);
+    }
+    /// Previous triangle adjacent to a vertex (counter-clockwise)
+    /// @returns pair of previous triangle and the other vertex of a common edge
+    std::pair<TriInd, VertInd> prev(const VertInd i) const
+    {
+        assert(vertices[0] == i || vertices[1] == i || vertices[2] == i);
+        if(vertices[0] == i)
+            return std::make_pair(neighbors[2], vertices[2]);
+        if(vertices[1] == i)
+            return std::make_pair(neighbors[0], vertices[0]);
+        return std::make_pair(neighbors[1], vertices[1]);
+    }
+
+    bool containsVertex(const VertInd i) const
+    {
+        return std::find(vertices.begin(), vertices.end(), i) != vertices.end();
+    }
+};
+
+typedef std::vector<Triangle> TriangleVec; ///< Vector of triangles
+
+/// Advance vertex or neighbor index counter-clockwise
+CDT_EXPORT Index ccw(Index i);
+
+/// Advance vertex or neighbor index clockwise
+CDT_EXPORT Index cw(Index i);
+
+/// Location of point on a triangle
+struct CDT_EXPORT PtTriLocation
+{
+    /// Enum
+    enum Enum
+    {
+        Inside,
+        Outside,
+        OnEdge1,
+        OnEdge2,
+        OnEdge3,
+    };
+};
+
+/// Check if location is classified as on any of three edges
+CDT_EXPORT bool isOnEdge(PtTriLocation::Enum location);
+
+/// Neighbor index from a on-edge location
+/// \note Call only if located on the edge!
+CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location);
+
+/// Relative location of point to a line
+struct CDT_EXPORT PtLineLocation
+{
+    /// Enum
+    enum Enum
+    {
+        Left,
+        Right,
+        OnLine,
+    };
+};
+
+/// Orient p against line v1-v2 2D: robust geometric predicate
+template <typename T>
+CDT_EXPORT T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2);
+
+/// Check if point lies to the left of, to the right of, or on a line
+template <typename T>
+CDT_EXPORT PtLineLocation::Enum locatePointLine(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    T orientationTolerance = T(0));
+
+/// Classify value of orient2d predicate
+template <typename T>
+CDT_EXPORT PtLineLocation::Enum
+classifyOrientation(T orientation, T orientationTolerance = T(0));
+
+/// Check if point a lies inside of, outside of, or on an edge of a triangle
+template <typename T>
+CDT_EXPORT PtTriLocation::Enum locatePointTriangle(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    const V2d<T>& v3);
+
+/// Opposed neighbor index from vertex index
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex);
+
+/// Opposed vertex index from neighbor index
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoVrt(Index neighborIndex);
+
+/// Index of triangle's neighbor opposed to a vertex
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
+opposedTriangleInd(const VerticesArr3& vv, VertInd iVert);
+
+/// Index of triangle's neighbor opposed to an edge
+CDT_INLINE_IF_HEADER_ONLY Index
+edgeNeighborInd(const VerticesArr3& vv, VertInd iVedge1, VertInd iVedge2);
+
+/// Index of triangle's vertex opposed to a triangle
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
+opposedVertexInd(const NeighborsArr3& nn, TriInd iTopo);
+
+/// If triangle has a given vertex return vertex-index
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index
+vertexInd(const VerticesArr3& vv, VertInd iV);
+
+/// Given triangle and a vertex find opposed triangle
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd
+opposedTriangle(const Triangle& tri, VertInd iVert);
+
+/// Given triangle and an edge find neighbor sharing the edge
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd
+edgeNeighbor(const Triangle& tri, VertInd iVedge1, VertInd iVedge2);
+
+/// Given two triangles, return vertex of first triangle opposed to the second
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY VertInd
+opposedVertex(const Triangle& tri, TriInd iTopo);
+
+/// Test if point lies in a circumscribed circle of a triangle
+template <typename T>
+CDT_EXPORT bool isInCircumcircle(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    const V2d<T>& v3);
+
+/// Test if two vertices share at least one common triangle
+CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY bool
+verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris);
+
+/// Distance between two 2D points
+template <typename T>
+CDT_EXPORT T distance(const V2d<T>& a, const V2d<T>& b);
+
+/// Squared distance between two 2D points
+template <typename T>
+CDT_EXPORT T distanceSquared(const V2d<T>& a, const V2d<T>& b);
+
+/// Check if any of triangle's vertices belongs to a super-triangle
+CDT_INLINE_IF_HEADER_ONLY bool touchesSuperTriangle(const Triangle& t);
+
+} // namespace CDT
+
+#ifndef CDT_USE_AS_COMPILED_LIBRARY
+#include "CDTUtils.hpp"
+#endif
+
+//*****************************************************************************
+// Specialize hash functions
+//*****************************************************************************
+#ifdef CDT_CXX11_IS_SUPPORTED
+namespace std
+#else
+namespace boost
+#endif
+{
+
+#ifdef CDT_USE_STRONG_TYPING
+
+/// Vertex index hasher
+template <>
+struct hash<CDT::VertInd>
+{
+    /// Hash operator
+    std::size_t operator()(const CDT::VertInd& vi) const
+    {
+        return std::hash<std::size_t>()(vi.t);
+    }
+};
+
+/// Triangle index hasher
+template <>
+struct hash<CDT::TriInd>
+{
+    /// Hash operator
+    std::size_t operator()(const CDT::TriInd& vi) const
+    {
+        return std::hash<std::size_t>()(vi.t);
+    }
+};
+
+#endif // CDT_USE_STRONG_TYPING
+
+/// Edge hasher
+template <>
+struct hash<CDT::Edge>
+{
+    /// Hash operator
+    std::size_t operator()(const CDT::Edge& e) const
+    {
+        return hashEdge(e);
+    }
+
+private:
+    static void hashCombine(std::size_t& seed, const CDT::VertInd& key)
+    {
+#ifdef CDT_CXX11_IS_SUPPORTED
+        typedef std::hash<CDT::VertInd> Hasher;
+#else
+        typedef boost::hash<CDT::VertInd> Hasher;
+#endif
+        seed ^= Hasher()(key) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+    }
+    static std::size_t hashEdge(const CDT::Edge& e)
+    {
+        const std::pair<CDT::VertInd, CDT::VertInd>& vv = e.verts();
+        std::size_t seed1(0);
+        hashCombine(seed1, vv.first);
+        hashCombine(seed1, vv.second);
+        std::size_t seed2(0);
+        hashCombine(seed2, vv.second);
+        hashCombine(seed2, vv.first);
+        return std::min(seed1, seed2);
+    }
+};
+} // namespace std/boost
+
+#endif // header guard
diff --git a/cpp/CDTUtils.hpp b/cpp/CDTUtils.hpp
--- a/cpp/CDTUtils.hpp
+++ b/cpp/CDTUtils.hpp
@@ -1,279 +1,308 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Utilities and helpers - implementation
- */
-
-#include "CDTUtils.h"
-
-#include "predicates.h" // robust predicates: orient, in-circle
-
-#include <stdexcept>
-
-namespace CDT
-{
-
-//*****************************************************************************
-// V2d
-//*****************************************************************************
-template <typename T>
-V2d<T> V2d<T>::make(const T x, const T y)
-{
-    V2d<T> out = {x, y};
-    return out;
-}
-
-//*****************************************************************************
-// Box2d
-//*****************************************************************************
-template <typename T>
-Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices)
-{
-    return envelopBox<T>(
-        vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);
-}
-
-//*****************************************************************************
-// Edge
-//*****************************************************************************
-CDT_INLINE_IF_HEADER_ONLY Edge::Edge(VertInd iV1, VertInd iV2)
-    : m_vertices(
-          iV1 < iV2 ? std::make_pair(iV1, iV2) : std::make_pair(iV2, iV1))
-{}
-
-CDT_INLINE_IF_HEADER_ONLY bool Edge::operator==(const Edge& other) const
-{
-    return m_vertices == other.m_vertices;
-}
-
-CDT_INLINE_IF_HEADER_ONLY bool Edge::operator!=(const Edge& other) const
-{
-    return !(this->operator==(other));
-}
-
-CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v1() const
-{
-    return m_vertices.first;
-}
-
-CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v2() const
-{
-    return m_vertices.second;
-}
-
-CDT_INLINE_IF_HEADER_ONLY const std::pair<VertInd, VertInd>& Edge::verts() const
-{
-    return m_vertices;
-}
-
-//*****************************************************************************
-// Utility functions
-//*****************************************************************************
-CDT_INLINE_IF_HEADER_ONLY Index ccw(Index i)
-{
-    return Index((i + 1) % 3);
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index cw(Index i)
-{
-    return Index((i + 2) % 3);
-}
-
-CDT_INLINE_IF_HEADER_ONLY bool isOnEdge(const PtTriLocation::Enum location)
-{
-    return location > PtTriLocation::Outside;
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index edgeNeighbor(const PtTriLocation::Enum location)
-{
-    assert(location >= PtTriLocation::OnEdge1);
-    return static_cast<Index>(location - PtTriLocation::OnEdge1);
-}
-
-template <typename T>
-T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2)
-{
-    return predicates::adaptive::orient2d(v1.x, v1.y, v2.x, v2.y, p.x, p.y);
-}
-
-template <typename T>
-PtLineLocation::Enum locatePointLine(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    const T orientationTolerance)
-{
-    return classifyOrientation(orient2D(p, v1, v2), orientationTolerance);
-}
-
-template <typename T>
-PtLineLocation::Enum
-classifyOrientation(const T orientation, const T orientationTolerance)
-{
-    if(orientation < -orientationTolerance)
-        return PtLineLocation::Right;
-    if(orientation > orientationTolerance)
-        return PtLineLocation::Left;
-    return PtLineLocation::OnLine;
-}
-
-template <typename T>
-PtTriLocation::Enum locatePointTriangle(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    const V2d<T>& v3)
-{
-    using namespace predicates::adaptive;
-    PtTriLocation::Enum result = PtTriLocation::Inside;
-    PtLineLocation::Enum edgeCheck = locatePointLine(p, v1, v2);
-    if(edgeCheck == PtLineLocation::Right)
-        return PtTriLocation::Outside;
-    if(edgeCheck == PtLineLocation::OnLine)
-        result = PtTriLocation::OnEdge1;
-    edgeCheck = locatePointLine(p, v2, v3);
-    if(edgeCheck == PtLineLocation::Right)
-        return PtTriLocation::Outside;
-    if(edgeCheck == PtLineLocation::OnLine)
-        result = PtTriLocation::OnEdge2;
-    edgeCheck = locatePointLine(p, v3, v1);
-    if(edgeCheck == PtLineLocation::Right)
-        return PtTriLocation::Outside;
-    if(edgeCheck == PtLineLocation::OnLine)
-        result = PtTriLocation::OnEdge3;
-    return result;
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index opoNbr(const Index vertIndex)
-{
-    if(vertIndex == Index(0))
-        return Index(1);
-    if(vertIndex == Index(1))
-        return Index(2);
-    if(vertIndex == Index(2))
-        return Index(0);
-    throw std::runtime_error("Invalid vertex index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index opoVrt(const Index neighborIndex)
-{
-    if(neighborIndex == Index(0))
-        return Index(2);
-    if(neighborIndex == Index(1))
-        return Index(0);
-    if(neighborIndex == Index(2))
-        return Index(1);
-    throw std::runtime_error("Invalid neighbor index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index
-opposedTriangleInd(const Triangle& tri, const VertInd iVert)
-{
-    for(Index vi = Index(0); vi < Index(3); ++vi)
-        if(iVert == tri.vertices[vi])
-            return opoNbr(vi);
-    throw std::runtime_error("Could not find opposed triangle index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index opposedTriangleInd(
-    const Triangle& tri,
-    const VertInd iVedge1,
-    const VertInd iVedge2)
-{
-    for(Index vi = Index(0); vi < Index(3); ++vi)
-    {
-        const VertInd iVert = tri.vertices[vi];
-        if(iVert != iVedge1 && iVert != iVedge2)
-            return opoNbr(vi);
-    }
-    throw std::runtime_error("Could not find opposed-to-edge triangle index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index
-opposedVertexInd(const Triangle& tri, const TriInd iTopo)
-{
-    for(Index ni = Index(0); ni < Index(3); ++ni)
-        if(iTopo == tri.neighbors[ni])
-            return opoVrt(ni);
-    throw std::runtime_error("Could not find opposed vertex index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index
-neighborInd(const Triangle& tri, const TriInd iTnbr)
-{
-    for(Index ni = Index(0); ni < Index(3); ++ni)
-        if(iTnbr == tri.neighbors[ni])
-            return ni;
-    throw std::runtime_error("Could not find neighbor triangle index");
-}
-
-CDT_INLINE_IF_HEADER_ONLY Index vertexInd(const Triangle& tri, const VertInd iV)
-{
-    for(Index i = Index(0); i < Index(3); ++i)
-        if(iV == tri.vertices[i])
-            return i;
-    throw std::runtime_error("Could not find vertex index in triangle");
-}
-
-CDT_INLINE_IF_HEADER_ONLY TriInd
-opposedTriangle(const Triangle& tri, const VertInd iVert)
-{
-    return tri.neighbors[opposedTriangleInd(tri, iVert)];
-}
-
-CDT_INLINE_IF_HEADER_ONLY VertInd
-opposedVertex(const Triangle& tri, const TriInd iTopo)
-{
-    return tri.vertices[opposedVertexInd(tri, iTopo)];
-}
-
-template <typename T>
-bool isInCircumcircle(
-    const V2d<T>& p,
-    const V2d<T>& v1,
-    const V2d<T>& v2,
-    const V2d<T>& v3)
-{
-    using namespace predicates::adaptive;
-    return incircle(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, p.x, p.y) > T(0);
-}
-
-CDT_INLINE_IF_HEADER_ONLY
-bool verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris)
-{
-    for(TriIndVec::const_iterator it = aTris.begin(); it != aTris.end(); ++it)
-        if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end())
-            return true;
-    return false;
-}
-
-template <typename T>
-T distanceSquared(const T ax, const T ay, const T bx, const T by)
-{
-    const T dx = bx - ax;
-    const T dy = by - ay;
-    return dx * dx + dy * dy;
-}
-
-template <typename T>
-T distance(const T ax, const T ay, const T bx, const T by)
-{
-    return std::sqrt(distanceSquared(ax, ay, bx, by));
-}
-
-template <typename T>
-T distance(const V2d<T>& a, const V2d<T>& b)
-{
-    return distance(a.x, a.y, b.x, b.y);
-}
-
-template <typename T>
-T distanceSquared(const V2d<T>& a, const V2d<T>& b)
-{
-    return distanceSquared(a.x, a.y, b.x, b.y);
-}
-
-} // namespace CDT
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Utilities and helpers - implementation
+ */
+
+#include "CDTUtils.h"
+
+#include "predicates.h" // robust predicates: orient, in-circle
+
+#include <stdexcept>
+
+namespace CDT
+{
+
+//*****************************************************************************
+// V2d
+//*****************************************************************************
+template <typename T>
+V2d<T> V2d<T>::make(const T x, const T y)
+{
+    V2d<T> out = {x, y};
+    return out;
+}
+
+//*****************************************************************************
+// Box2d
+//*****************************************************************************
+template <typename T>
+Box2d<T> envelopBox(const std::vector<V2d<T> >& vertices)
+{
+    return envelopBox<T>(
+        vertices.begin(), vertices.end(), getX_V2d<T>, getY_V2d<T>);
+}
+
+//*****************************************************************************
+// Edge
+//*****************************************************************************
+CDT_INLINE_IF_HEADER_ONLY Edge::Edge(VertInd iV1, VertInd iV2)
+    : m_vertices(
+          iV1 < iV2 ? std::make_pair(iV1, iV2) : std::make_pair(iV2, iV1))
+{}
+
+CDT_INLINE_IF_HEADER_ONLY bool Edge::operator==(const Edge& other) const
+{
+    return m_vertices == other.m_vertices;
+}
+
+CDT_INLINE_IF_HEADER_ONLY bool Edge::operator!=(const Edge& other) const
+{
+    return !(this->operator==(other));
+}
+
+CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v1() const
+{
+    return m_vertices.first;
+}
+
+CDT_INLINE_IF_HEADER_ONLY VertInd Edge::v2() const
+{
+    return m_vertices.second;
+}
+
+CDT_INLINE_IF_HEADER_ONLY const std::pair<VertInd, VertInd>& Edge::verts() const
+{
+    return m_vertices;
+}
+
+//*****************************************************************************
+// Utility functions
+//*****************************************************************************
+CDT_INLINE_IF_HEADER_ONLY Index ccw(Index i)
+{
+    return Index((i + 1) % 3);
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index cw(Index i)
+{
+    return Index((i + 2) % 3);
+}
+
+CDT_INLINE_IF_HEADER_ONLY bool isOnEdge(const PtTriLocation::Enum location)
+{
+    return location > PtTriLocation::Outside;
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index edgeNeighbor(const PtTriLocation::Enum location)
+{
+    assert(location >= PtTriLocation::OnEdge1);
+    return static_cast<Index>(location - PtTriLocation::OnEdge1);
+}
+
+template <typename T>
+T orient2D(const V2d<T>& p, const V2d<T>& v1, const V2d<T>& v2)
+{
+    return predicates::adaptive::orient2d(v1.x, v1.y, v2.x, v2.y, p.x, p.y);
+}
+
+template <typename T>
+PtLineLocation::Enum locatePointLine(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    const T orientationTolerance)
+{
+    return classifyOrientation(orient2D(p, v1, v2), orientationTolerance);
+}
+
+template <typename T>
+PtLineLocation::Enum
+classifyOrientation(const T orientation, const T orientationTolerance)
+{
+    if(orientation < -orientationTolerance)
+        return PtLineLocation::Right;
+    if(orientation > orientationTolerance)
+        return PtLineLocation::Left;
+    return PtLineLocation::OnLine;
+}
+
+template <typename T>
+PtTriLocation::Enum locatePointTriangle(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    const V2d<T>& v3)
+{
+    using namespace predicates::adaptive;
+    PtTriLocation::Enum result = PtTriLocation::Inside;
+    PtLineLocation::Enum edgeCheck = locatePointLine(p, v1, v2);
+    if(edgeCheck == PtLineLocation::Right)
+        return PtTriLocation::Outside;
+    if(edgeCheck == PtLineLocation::OnLine)
+        result = PtTriLocation::OnEdge1;
+    edgeCheck = locatePointLine(p, v2, v3);
+    if(edgeCheck == PtLineLocation::Right)
+        return PtTriLocation::Outside;
+    if(edgeCheck == PtLineLocation::OnLine)
+        result = PtTriLocation::OnEdge2;
+    edgeCheck = locatePointLine(p, v3, v1);
+    if(edgeCheck == PtLineLocation::Right)
+        return PtTriLocation::Outside;
+    if(edgeCheck == PtLineLocation::OnLine)
+        result = PtTriLocation::OnEdge3;
+    return result;
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index opoNbr(const Index vertIndex)
+{
+    if(vertIndex == Index(0))
+        return Index(1);
+    if(vertIndex == Index(1))
+        return Index(2);
+    if(vertIndex == Index(2))
+        return Index(0);
+    throw std::runtime_error("Invalid vertex index");
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index opoVrt(const Index neighborIndex)
+{
+    if(neighborIndex == Index(0))
+        return Index(2);
+    if(neighborIndex == Index(1))
+        return Index(0);
+    if(neighborIndex == Index(2))
+        return Index(1);
+    throw std::runtime_error("Invalid neighbor index");
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index
+opposedTriangleInd(const VerticesArr3& vv, const VertInd iVert)
+{
+    assert(vv[0] == iVert || vv[1] == iVert || vv[2] == iVert);
+    if(vv[0] == iVert)
+        return Index(1);
+    if(vv[1] == iVert)
+        return Index(2);
+    return Index(0);
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index edgeNeighborInd(
+    const VerticesArr3& vv,
+    const VertInd iVedge1,
+    const VertInd iVedge2)
+{
+    assert(vv[0] == iVedge1 || vv[1] == iVedge1 || vv[2] == iVedge1);
+    assert(vv[0] == iVedge2 || vv[1] == iVedge2 || vv[2] == iVedge2);
+    assert(
+        (vv[0] != iVedge1 && vv[0] != iVedge2) ||
+        (vv[1] != iVedge1 && vv[1] != iVedge2) ||
+        (vv[2] != iVedge1 && vv[2] != iVedge2));
+    /*
+     *      vv[2]
+     *       /\
+     *  n[2]/  \n[1]
+     *     /____\
+     * vv[0] n[0] vv[1]
+     */
+    if(vv[0] == iVedge1)
+    {
+        if(vv[1] == iVedge2)
+            return Index(0);
+        return Index(2);
+    }
+    if(vv[0] == iVedge2)
+    {
+        if(vv[1] == iVedge1)
+            return Index(0);
+        return Index(2);
+    }
+    return Index(1);
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index
+opposedVertexInd(const NeighborsArr3& nn, const TriInd iTopo)
+{
+    assert(nn[0] == iTopo || nn[1] == iTopo || nn[2] == iTopo);
+    if(nn[0] == iTopo)
+        return Index(2);
+    if(nn[1] == iTopo)
+        return Index(0);
+    return Index(1);
+}
+
+CDT_INLINE_IF_HEADER_ONLY Index
+vertexInd(const VerticesArr3& vv, const VertInd iV)
+{
+    assert(vv[0] == iV || vv[1] == iV || vv[2] == iV);
+    if(vv[0] == iV)
+        return Index(0);
+    if(vv[1] == iV)
+        return Index(1);
+    return Index(2);
+}
+
+CDT_INLINE_IF_HEADER_ONLY TriInd
+opposedTriangle(const Triangle& tri, const VertInd iVert)
+{
+    return tri.neighbors[opposedTriangleInd(tri.vertices, iVert)];
+}
+
+CDT_INLINE_IF_HEADER_ONLY VertInd
+opposedVertex(const Triangle& tri, const TriInd iTopo)
+{
+    return tri.vertices[opposedVertexInd(tri.neighbors, iTopo)];
+}
+
+/// Given triangle and an edge find neighbor sharing the edge
+CDT_INLINE_IF_HEADER_ONLY TriInd
+edgeNeighbor(const Triangle& tri, VertInd iVedge1, VertInd iVedge2)
+{
+    return tri.neighbors[edgeNeighborInd(tri.vertices, iVedge1, iVedge2)];
+}
+
+template <typename T>
+bool isInCircumcircle(
+    const V2d<T>& p,
+    const V2d<T>& v1,
+    const V2d<T>& v2,
+    const V2d<T>& v3)
+{
+    using namespace predicates::adaptive;
+    return incircle(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, p.x, p.y) > T(0);
+}
+
+CDT_INLINE_IF_HEADER_ONLY
+bool verticesShareEdge(const TriIndVec& aTris, const TriIndVec& bTris)
+{
+    for(TriIndVec::const_iterator it = aTris.begin(); it != aTris.end(); ++it)
+        if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end())
+            return true;
+    return false;
+}
+
+template <typename T>
+T distanceSquared(const T ax, const T ay, const T bx, const T by)
+{
+    const T dx = bx - ax;
+    const T dy = by - ay;
+    return dx * dx + dy * dy;
+}
+
+template <typename T>
+T distance(const T ax, const T ay, const T bx, const T by)
+{
+    return std::sqrt(distanceSquared(ax, ay, bx, by));
+}
+
+template <typename T>
+T distance(const V2d<T>& a, const V2d<T>& b)
+{
+    return distance(a.x, a.y, b.x, b.y);
+}
+
+template <typename T>
+T distanceSquared(const V2d<T>& a, const V2d<T>& b)
+{
+    return distanceSquared(a.x, a.y, b.x, b.y);
+}
+
+bool touchesSuperTriangle(const Triangle& t)
+{
+    return t.vertices[0] < 3 || t.vertices[1] < 3 || t.vertices[2] < 3;
+}
+
+} // namespace CDT
diff --git a/cpp/KDTree.h b/cpp/KDTree.h
--- a/cpp/KDTree.h
+++ b/cpp/KDTree.h
@@ -1,399 +1,412 @@
-/// This Source Code Form is subject to the terms of the Mozilla Public
-/// License, v. 2.0. If a copy of the MPL was not distributed with this
-/// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-/// Contribution of original implementation:
-/// Andre Fecteau <andre.fecteau1@gmail.com>
-
-#ifndef KDTREE_KDTREE_H
-#define KDTREE_KDTREE_H
-
-#include "CDTUtils.h"
-
-#include <cassert>
-#include <limits>
-
-namespace KDTree
-{
-
-struct NodeSplitDirection
-{
-    enum Enum
-    {
-        X,
-        Y,
-    };
-};
-
-/// Simple tree structure with alternating half splitting nodes
-/// @details Simple tree structure
-///          - Tree to incrementally add points to the structure.
-///          - Get the nearest point to a given input.
-///          - Does not check for duplicates, expect unique points.
-/// @tparam TCoordType type used for storing point coordinate.
-/// @tparam NumVerticesInLeaf The number of points per leaf.
-/// @tparam InitialStackDepth initial size of stack depth for nearest query.
-/// Should be at least 1.
-/// @tparam StackDepthIncrement increment of stack depth for nearest query when
-/// stack depth is reached.
-template <
-    typename TCoordType,
-    size_t NumVerticesInLeaf,
-    size_t InitialStackDepth,
-    size_t StackDepthIncrement>
-class KDTree
-{
-public:
-    typedef TCoordType coord_type;
-    typedef CDT::V2d<coord_type> point_type;
-    typedef CDT::VertInd point_index;
-    typedef std::pair<point_type, point_index> value_type;
-    typedef std::vector<point_index> point_data_vec;
-    typedef point_data_vec::const_iterator pd_cit;
-    typedef CDT::VertInd node_index;
-    typedef CDT::array<node_index, 2> children_type;
-
-    /// Stores kd-tree node data
-    struct Node
-    {
-        children_type children; ///< two children if not leaf; {0,0} if leaf
-        point_data_vec data;    ///< points' data if leaf
-        /// Create empty leaf
-        Node()
-        {
-            setChildren(0, 0);
-            data.reserve(NumVerticesInLeaf);
-        }
-        /// Children setter for convenience
-        void setChildren(const node_index c1, const node_index c2)
-        {
-            children[0] = c1;
-            children[1] = c2;
-        }
-        /// Check if node is a leaf (has no valid children)
-        bool isLeaf() const
-        {
-            return children[0] == children[1];
-        }
-    };
-
-    /// Default constructor
-    KDTree()
-        : m_rootDir(NodeSplitDirection::X)
-        , m_min(point_type::make(
-              -std::numeric_limits<coord_type>::max(),
-              -std::numeric_limits<coord_type>::max()))
-        , m_max(point_type::make(
-              std::numeric_limits<coord_type>::max(),
-              std::numeric_limits<coord_type>::max()))
-        , m_isRootBoxInitialized(false)
-        , m_tasksStack(InitialStackDepth, NearestTask())
-    {
-        m_root = addNewNode();
-    }
-
-    /// Constructor with bounding box known in advance
-    KDTree(const point_type& min, const point_type& max)
-        : m_rootDir(NodeSplitDirection::X)
-        , m_min(min)
-        , m_max(max)
-        , m_isRootBoxInitialized(true)
-        , m_tasksStack(InitialStackDepth, NearestTask())
-    {
-        m_root = addNewNode();
-    }
-
-    /// Insert a point into kd-tree
-    /// @note external point-buffer is used to reduce kd-tree's memory footprint
-    /// @param iPoint index of point in external point-buffer
-    /// @param points external point-buffer
-    void
-    insert(const point_index& iPoint, const std::vector<point_type>& points)
-    {
-        // if point is outside root, extend tree by adding new roots
-        const point_type& pos = points[iPoint];
-        while(!isInsideBox(pos, m_min, m_max))
-        {
-            extendTree(pos);
-        }
-        // now insert the point into the tree
-        node_index node = m_root;
-        point_type min = m_min;
-        point_type max = m_max;
-        NodeSplitDirection::Enum dir = m_rootDir;
-
-        // below: initialized only to suppress warnings
-        NodeSplitDirection::Enum newDir(NodeSplitDirection::X);
-        coord_type mid(0);
-        point_type newMin, newMax;
-        while(true)
-        {
-            if(m_nodes[node].isLeaf())
-            {
-                // add point if capacity is not reached
-                point_data_vec& pd = m_nodes[node].data;
-                if(pd.size() < NumVerticesInLeaf)
-                {
-                    pd.push_back(iPoint);
-                    return;
-                }
-                // initialize bbox first time the root capacity is reached
-                if(!m_isRootBoxInitialized)
-                {
-                    initializeRootBox(points);
-                    min = m_min;
-                    max = m_max;
-                }
-                // split a full leaf node
-                calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
-                const node_index c1 = addNewNode(), c2 = addNewNode();
-                Node& n = m_nodes[node];
-                n.setChildren(c1, c2);
-                point_data_vec& c1data = m_nodes[c1].data;
-                point_data_vec& c2data = m_nodes[c2].data;
-                // move node's points to children
-                for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
-                {
-                    whichChild(points[*it], mid, dir) == 0
-                        ? c1data.push_back(*it)
-                        : c2data.push_back(*it);
-                }
-                n.data = point_data_vec();
-            }
-            else
-            {
-                calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
-            }
-            // add the point to a child
-            const std::size_t iChild = whichChild(points[iPoint], mid, dir);
-            iChild == 0 ? max = newMax : min = newMin;
-            node = m_nodes[node].children[iChild];
-            dir = newDir;
-        }
-    }
-
-    /// Query kd-tree for a nearest neighbor point
-    /// @note external point-buffer is used to reduce kd-tree's memory footprint
-    /// @param point query point position
-    /// @param points external point-buffer
-    value_type nearest(
-        const point_type& point,
-        const std::vector<point_type>& points) const
-    {
-        value_type out;
-        int iTask = -1;
-        coord_type minDistSq = std::numeric_limits<coord_type>::max();
-        m_tasksStack[++iTask] =
-            NearestTask(m_root, m_min, m_max, m_rootDir, minDistSq);
-        while(iTask != -1)
-        {
-            const NearestTask t = m_tasksStack[iTask--];
-            if(t.distSq > minDistSq)
-                continue;
-            const Node& n = m_nodes[t.node];
-            if(n.isLeaf())
-            {
-                for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
-                {
-                    const point_type& p = points[*it];
-                    const coord_type distSq = CDT::distanceSquared(point, p);
-                    if(distSq < minDistSq)
-                    {
-                        minDistSq = distSq;
-                        out.first = p;
-                        out.second = *it;
-                    }
-                }
-            }
-            else
-            {
-                coord_type mid(0);
-                NodeSplitDirection::Enum newDir;
-                point_type newMin, newMax;
-                calcSplitInfo(t.min, t.max, t.dir, mid, newDir, newMin, newMax);
-
-                const coord_type distToMid = t.dir == NodeSplitDirection::X
-                                                 ? (point.x - mid)
-                                                 : (point.y - mid);
-                const coord_type toMidSq = distToMid * distToMid;
-
-                const std::size_t iChild = whichChild(point, mid, t.dir);
-                if(iTask + 2 >= static_cast<int>(m_tasksStack.size()))
-                {
-                    m_tasksStack.resize(
-                        m_tasksStack.size() + StackDepthIncrement);
-                }
-                // node containing point should end up on top of the stack
-                if(iChild == 0)
-                {
-                    m_tasksStack[++iTask] = NearestTask(
-                        n.children[1], newMin, t.max, newDir, toMidSq);
-                    m_tasksStack[++iTask] = NearestTask(
-                        n.children[0], t.min, newMax, newDir, toMidSq);
-                }
-                else
-                {
-                    m_tasksStack[++iTask] = NearestTask(
-                        n.children[0], t.min, newMax, newDir, toMidSq);
-                    m_tasksStack[++iTask] = NearestTask(
-                        n.children[1], newMin, t.max, newDir, toMidSq);
-                }
-            }
-        }
-        return out;
-    }
-
-private:
-    /// Add a new node and return it's index in nodes buffer
-    node_index addNewNode()
-    {
-        const node_index newNodeIndex = static_cast<node_index>(m_nodes.size());
-        m_nodes.push_back(Node());
-        return newNodeIndex;
-    }
-
-    /// Test which child point belongs to after the split
-    /// @returns 0 if first child, 1 if second child
-    std::size_t whichChild(
-        const point_type& point,
-        const coord_type& split,
-        const NodeSplitDirection::Enum dir) const
-    {
-        return static_cast<size_t>(
-            dir == NodeSplitDirection::X ? point.x > split : point.y > split);
-    }
-
-    /// Calculate split location, direction, and children boxes
-    static void calcSplitInfo(
-        const point_type& min,
-        const point_type& max,
-        const NodeSplitDirection::Enum dir,
-        coord_type& midOut,
-        NodeSplitDirection::Enum& newDirOut,
-        point_type& newMinOut,
-        point_type& newMaxOut)
-    {
-        newMaxOut = max;
-        newMinOut = min;
-        switch(dir)
-        {
-        case NodeSplitDirection::X:
-            midOut = (min.x + max.x) / coord_type(2);
-            newDirOut = NodeSplitDirection::Y;
-            newMinOut.x = midOut;
-            newMaxOut.x = midOut;
-            return;
-        case NodeSplitDirection::Y:
-            midOut = (min.y + max.y) / coord_type(2);
-            newDirOut = NodeSplitDirection::X;
-            newMinOut.y = midOut;
-            newMaxOut.y = midOut;
-            return;
-        }
-    }
-
-    /// Test if point is inside a box
-    static bool isInsideBox(
-        const point_type& p,
-        const point_type& min,
-        const point_type& max)
-    {
-        return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y;
-    }
-
-    /// Extend a tree by creating new root with old root and a new node as
-    /// children
-    void extendTree(const point_type& point)
-    {
-        const node_index newRoot = addNewNode();
-        const node_index newLeaf = addNewNode();
-        switch(m_rootDir)
-        {
-        case NodeSplitDirection::X:
-            m_rootDir = NodeSplitDirection::Y;
-            point.y < m_min.y ? m_nodes[newRoot].setChildren(newLeaf, m_root)
-                              : m_nodes[newRoot].setChildren(m_root, newLeaf);
-            if(point.y < m_min.y)
-                m_min.y -= m_max.y - m_min.y;
-            else if(point.y > m_max.y)
-                m_max.y += m_max.y - m_min.y;
-            break;
-        case NodeSplitDirection::Y:
-            m_rootDir = NodeSplitDirection::X;
-            point.x < m_min.x ? m_nodes[newRoot].setChildren(newLeaf, m_root)
-                              : m_nodes[newRoot].setChildren(m_root, newLeaf);
-            if(point.x < m_min.x)
-                m_min.x -= m_max.x - m_min.x;
-            else if(point.x > m_max.x)
-                m_max.x += m_max.x - m_min.x;
-            break;
-        }
-        m_root = newRoot;
-    }
-
-    /// Calculate root's box enclosing all root points
-    void initializeRootBox(const std::vector<point_type>& points)
-    {
-        const point_data_vec& data = m_nodes[m_root].data;
-        m_min = points[data.front()];
-        m_max = m_min;
-        for(pd_cit it = data.begin(); it != data.end(); ++it)
-        {
-            const point_type& p = points[*it];
-            m_min = point_type::make(
-                std::min(m_min.x, p.x), std::min(m_min.y, p.y));
-            m_max = point_type::make(
-                std::max(m_max.x, p.x), std::max(m_max.y, p.y));
-        }
-        // Make sure bounding box does not have a zero size by adding padding:
-        // zero-size bounding box cannot be extended properly
-        const TCoordType padding(1);
-        if(m_min.x == m_max.x)
-        {
-            m_min.x -= padding;
-            m_max.x += padding;
-        }
-        if(m_min.y == m_max.y)
-        {
-            m_min.y -= padding;
-            m_max.y += padding;
-        }
-        m_isRootBoxInitialized = true;
-    }
-
-private:
-    node_index m_root;
-    std::vector<Node> m_nodes;
-    NodeSplitDirection::Enum m_rootDir;
-    point_type m_min;
-    point_type m_max;
-    bool m_isRootBoxInitialized;
-
-    // used for nearest query
-    struct NearestTask
-    {
-        node_index node;
-        point_type min, max;
-        NodeSplitDirection::Enum dir;
-        coord_type distSq;
-        NearestTask()
-        {}
-        NearestTask(
-            const node_index node,
-            const point_type& min,
-            const point_type& max,
-            const NodeSplitDirection::Enum dir,
-            const coord_type distSq)
-            : node(node)
-            , min(min)
-            , max(max)
-            , dir(dir)
-            , distSq(distSq)
-        {}
-    };
-    // allocated in class (not in the 'nearest' method) for better performance
-    mutable std::vector<NearestTask> m_tasksStack;
-};
-
-} // namespace KDTree
-
-#endif // header guard
+/// This Source Code Form is subject to the terms of the Mozilla Public
+/// License, v. 2.0. If a copy of the MPL was not distributed with this
+/// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+/// Contribution of original implementation:
+/// Andre Fecteau <andre.fecteau1@gmail.com>
+
+#ifndef KDTREE_KDTREE_H
+#define KDTREE_KDTREE_H
+
+#include "CDTUtils.h"
+
+#include <cassert>
+#include <limits>
+
+namespace CDT
+{
+namespace KDTree
+{
+
+struct NodeSplitDirection
+{
+    enum Enum
+    {
+        X,
+        Y,
+    };
+};
+
+/// Simple tree structure with alternating half splitting nodes
+/// @details Simple tree structure
+///          - Tree to incrementally add points to the structure.
+///          - Get the nearest point to a given input.
+///          - Does not check for duplicates, expect unique points.
+/// @tparam TCoordType type used for storing point coordinate.
+/// @tparam NumVerticesInLeaf The number of points per leaf.
+/// @tparam InitialStackDepth initial size of stack depth for nearest query.
+/// Should be at least 1.
+/// @tparam StackDepthIncrement increment of stack depth for nearest query when
+/// stack depth is reached.
+template <
+    typename TCoordType,
+    size_t NumVerticesInLeaf,
+    size_t InitialStackDepth,
+    size_t StackDepthIncrement>
+class KDTree
+{
+public:
+    typedef TCoordType coord_type;
+    typedef CDT::V2d<coord_type> point_type;
+    typedef CDT::VertInd point_index;
+    typedef std::pair<point_type, point_index> value_type;
+    typedef std::vector<point_index> point_data_vec;
+    typedef point_data_vec::const_iterator pd_cit;
+    typedef CDT::VertInd node_index;
+    typedef CDT::array<node_index, 2> children_type;
+
+    /// Stores kd-tree node data
+    struct Node
+    {
+        children_type children; ///< two children if not leaf; {0,0} if leaf
+        point_data_vec data;    ///< points' data if leaf
+        /// Create empty leaf
+        Node()
+        {
+            setChildren(node_index(0), node_index(0));
+            data.reserve(NumVerticesInLeaf);
+        }
+        /// Children setter for convenience
+        void setChildren(const node_index c1, const node_index c2)
+        {
+            children[0] = c1;
+            children[1] = c2;
+        }
+        /// Check if node is a leaf (has no valid children)
+        bool isLeaf() const
+        {
+            return children[0] == children[1];
+        }
+    };
+
+    /// Default constructor
+    KDTree()
+        : m_rootDir(NodeSplitDirection::X)
+        , m_min(point_type::make(
+              -std::numeric_limits<coord_type>::max(),
+              -std::numeric_limits<coord_type>::max()))
+        , m_max(point_type::make(
+              std::numeric_limits<coord_type>::max(),
+              std::numeric_limits<coord_type>::max()))
+        , m_size(0)
+        , m_isRootBoxInitialized(false)
+        , m_tasksStack(InitialStackDepth, NearestTask())
+    {
+        m_root = addNewNode();
+    }
+
+    /// Constructor with bounding box known in advance
+    KDTree(const point_type& min, const point_type& max)
+        : m_rootDir(NodeSplitDirection::X)
+        , m_min(min)
+        , m_max(max)
+        , m_size(0)
+        , m_isRootBoxInitialized(true)
+        , m_tasksStack(InitialStackDepth, NearestTask())
+    {
+        m_root = addNewNode();
+    }
+
+    CDT::VertInd size() const
+    {
+        return m_size;
+    }
+
+    /// Insert a point into kd-tree
+    /// @note external point-buffer is used to reduce kd-tree's memory footprint
+    /// @param iPoint index of point in external point-buffer
+    /// @param points external point-buffer
+    void
+    insert(const point_index& iPoint, const std::vector<point_type>& points)
+    {
+        ++m_size;
+        // if point is outside root, extend tree by adding new roots
+        const point_type& pos = points[iPoint];
+        while(!isInsideBox(pos, m_min, m_max))
+        {
+            extendTree(pos);
+        }
+        // now insert the point into the tree
+        node_index node = m_root;
+        point_type min = m_min;
+        point_type max = m_max;
+        NodeSplitDirection::Enum dir = m_rootDir;
+
+        // below: initialized only to suppress warnings
+        NodeSplitDirection::Enum newDir(NodeSplitDirection::X);
+        coord_type mid(0);
+        point_type newMin, newMax;
+        while(true)
+        {
+            if(m_nodes[node].isLeaf())
+            {
+                // add point if capacity is not reached
+                point_data_vec& pd = m_nodes[node].data;
+                if(pd.size() < NumVerticesInLeaf)
+                {
+                    pd.push_back(iPoint);
+                    return;
+                }
+                // initialize bbox first time the root capacity is reached
+                if(!m_isRootBoxInitialized)
+                {
+                    initializeRootBox(points);
+                    min = m_min;
+                    max = m_max;
+                }
+                // split a full leaf node
+                calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
+                const node_index c1 = addNewNode(), c2 = addNewNode();
+                Node& n = m_nodes[node];
+                n.setChildren(c1, c2);
+                point_data_vec& c1data = m_nodes[c1].data;
+                point_data_vec& c2data = m_nodes[c2].data;
+                // move node's points to children
+                for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
+                {
+                    whichChild(points[*it], mid, dir) == 0
+                        ? c1data.push_back(*it)
+                        : c2data.push_back(*it);
+                }
+                n.data = point_data_vec();
+            }
+            else
+            {
+                calcSplitInfo(min, max, dir, mid, newDir, newMin, newMax);
+            }
+            // add the point to a child
+            const std::size_t iChild = whichChild(points[iPoint], mid, dir);
+            iChild == 0 ? max = newMax : min = newMin;
+            node = m_nodes[node].children[iChild];
+            dir = newDir;
+        }
+    }
+
+    /// Query kd-tree for a nearest neighbor point
+    /// @note external point-buffer is used to reduce kd-tree's memory footprint
+    /// @param point query point position
+    /// @param points external point-buffer
+    value_type nearest(
+        const point_type& point,
+        const std::vector<point_type>& points) const
+    {
+        value_type out;
+        int iTask = -1;
+        coord_type minDistSq = std::numeric_limits<coord_type>::max();
+        m_tasksStack[++iTask] =
+            NearestTask(m_root, m_min, m_max, m_rootDir, minDistSq);
+        while(iTask != -1)
+        {
+            const NearestTask t = m_tasksStack[iTask--];
+            if(t.distSq > minDistSq)
+                continue;
+            const Node& n = m_nodes[t.node];
+            if(n.isLeaf())
+            {
+                for(pd_cit it = n.data.begin(); it != n.data.end(); ++it)
+                {
+                    const point_type& p = points[*it];
+                    const coord_type distSq = CDT::distanceSquared(point, p);
+                    if(distSq < minDistSq)
+                    {
+                        minDistSq = distSq;
+                        out.first = p;
+                        out.second = *it;
+                    }
+                }
+            }
+            else
+            {
+                coord_type mid(0);
+                NodeSplitDirection::Enum newDir;
+                point_type newMin, newMax;
+                calcSplitInfo(t.min, t.max, t.dir, mid, newDir, newMin, newMax);
+
+                const coord_type distToMid = t.dir == NodeSplitDirection::X
+                                                 ? (point.x - mid)
+                                                 : (point.y - mid);
+                const coord_type toMidSq = distToMid * distToMid;
+
+                const std::size_t iChild = whichChild(point, mid, t.dir);
+                if(iTask + 2 >= static_cast<int>(m_tasksStack.size()))
+                {
+                    m_tasksStack.resize(
+                        m_tasksStack.size() + StackDepthIncrement);
+                }
+                // node containing point should end up on top of the stack
+                if(iChild == 0)
+                {
+                    m_tasksStack[++iTask] = NearestTask(
+                        n.children[1], newMin, t.max, newDir, toMidSq);
+                    m_tasksStack[++iTask] = NearestTask(
+                        n.children[0], t.min, newMax, newDir, toMidSq);
+                }
+                else
+                {
+                    m_tasksStack[++iTask] = NearestTask(
+                        n.children[0], t.min, newMax, newDir, toMidSq);
+                    m_tasksStack[++iTask] = NearestTask(
+                        n.children[1], newMin, t.max, newDir, toMidSq);
+                }
+            }
+        }
+        return out;
+    }
+
+private:
+    /// Add a new node and return it's index in nodes buffer
+    node_index addNewNode()
+    {
+        const node_index newNodeIndex = static_cast<node_index>(m_nodes.size());
+        m_nodes.push_back(Node());
+        return newNodeIndex;
+    }
+
+    /// Test which child point belongs to after the split
+    /// @returns 0 if first child, 1 if second child
+    std::size_t whichChild(
+        const point_type& point,
+        const coord_type& split,
+        const NodeSplitDirection::Enum dir) const
+    {
+        return static_cast<size_t>(
+            dir == NodeSplitDirection::X ? point.x > split : point.y > split);
+    }
+
+    /// Calculate split location, direction, and children boxes
+    static void calcSplitInfo(
+        const point_type& min,
+        const point_type& max,
+        const NodeSplitDirection::Enum dir,
+        coord_type& midOut,
+        NodeSplitDirection::Enum& newDirOut,
+        point_type& newMinOut,
+        point_type& newMaxOut)
+    {
+        newMaxOut = max;
+        newMinOut = min;
+        switch(dir)
+        {
+        case NodeSplitDirection::X:
+            midOut = (min.x + max.x) / coord_type(2);
+            newDirOut = NodeSplitDirection::Y;
+            newMinOut.x = midOut;
+            newMaxOut.x = midOut;
+            return;
+        case NodeSplitDirection::Y:
+            midOut = (min.y + max.y) / coord_type(2);
+            newDirOut = NodeSplitDirection::X;
+            newMinOut.y = midOut;
+            newMaxOut.y = midOut;
+            return;
+        }
+    }
+
+    /// Test if point is inside a box
+    static bool isInsideBox(
+        const point_type& p,
+        const point_type& min,
+        const point_type& max)
+    {
+        return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y;
+    }
+
+    /// Extend a tree by creating new root with old root and a new node as
+    /// children
+    void extendTree(const point_type& point)
+    {
+        const node_index newRoot = addNewNode();
+        const node_index newLeaf = addNewNode();
+        switch(m_rootDir)
+        {
+        case NodeSplitDirection::X:
+            m_rootDir = NodeSplitDirection::Y;
+            point.y < m_min.y ? m_nodes[newRoot].setChildren(newLeaf, m_root)
+                              : m_nodes[newRoot].setChildren(m_root, newLeaf);
+            if(point.y < m_min.y)
+                m_min.y -= m_max.y - m_min.y;
+            else if(point.y > m_max.y)
+                m_max.y += m_max.y - m_min.y;
+            break;
+        case NodeSplitDirection::Y:
+            m_rootDir = NodeSplitDirection::X;
+            point.x < m_min.x ? m_nodes[newRoot].setChildren(newLeaf, m_root)
+                              : m_nodes[newRoot].setChildren(m_root, newLeaf);
+            if(point.x < m_min.x)
+                m_min.x -= m_max.x - m_min.x;
+            else if(point.x > m_max.x)
+                m_max.x += m_max.x - m_min.x;
+            break;
+        }
+        m_root = newRoot;
+    }
+
+    /// Calculate root's box enclosing all root points
+    void initializeRootBox(const std::vector<point_type>& points)
+    {
+        const point_data_vec& data = m_nodes[m_root].data;
+        m_min = points[data.front()];
+        m_max = m_min;
+        for(pd_cit it = data.begin(); it != data.end(); ++it)
+        {
+            const point_type& p = points[*it];
+            m_min = point_type::make(
+                std::min(m_min.x, p.x), std::min(m_min.y, p.y));
+            m_max = point_type::make(
+                std::max(m_max.x, p.x), std::max(m_max.y, p.y));
+        }
+        // Make sure bounding box does not have a zero size by adding padding:
+        // zero-size bounding box cannot be extended properly
+        const TCoordType padding(1);
+        if(m_min.x == m_max.x)
+        {
+            m_min.x -= padding;
+            m_max.x += padding;
+        }
+        if(m_min.y == m_max.y)
+        {
+            m_min.y -= padding;
+            m_max.y += padding;
+        }
+        m_isRootBoxInitialized = true;
+    }
+
+private:
+    node_index m_root;
+    std::vector<Node> m_nodes;
+    NodeSplitDirection::Enum m_rootDir;
+    point_type m_min;
+    point_type m_max;
+    CDT::VertInd m_size;
+
+    bool m_isRootBoxInitialized;
+
+    // used for nearest query
+    struct NearestTask
+    {
+        node_index node;
+        point_type min, max;
+        NodeSplitDirection::Enum dir;
+        coord_type distSq;
+        NearestTask()
+        {}
+        NearestTask(
+            const node_index node_,
+            const point_type& min_,
+            const point_type& max_,
+            const NodeSplitDirection::Enum dir_,
+            const coord_type distSq_)
+            : node(node_)
+            , min(min_)
+            , max(max_)
+            , dir(dir_)
+            , distSq(distSq_)
+        {}
+    };
+    // allocated in class (not in the 'nearest' method) for better performance
+    mutable std::vector<NearestTask> m_tasksStack;
+};
+
+} // namespace KDTree
+} // namespace CDT
+
+#endif // header guard
diff --git a/cpp/LocatorKDTree.h b/cpp/LocatorKDTree.h
--- a/cpp/LocatorKDTree.h
+++ b/cpp/LocatorKDTree.h
@@ -1,71 +1,81 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Adapter between for KDTree and CDT
- */
-
-#ifndef CDT_POINTKDTREE_H
-#define CDT_POINTKDTREE_H
-
-#include "CDTUtils.h"
-#include "KDTree.h"
-
-namespace CDT
-{
-
-/// KD-tree holding points
-template <
-    typename TCoordType,
-    size_t NumVerticesInLeaf = 32,
-    size_t InitialStackDepth = 32,
-    size_t StackDepthIncrement = 32>
-class LocatorKDTree
-{
-public:
-    /// Initialize KD-tree with points
-    void initialize(const std::vector<V2d<TCoordType> >& points)
-    {
-        typedef V2d<TCoordType> V2d_t;
-        V2d_t min = points.front();
-        V2d_t max = min;
-        typedef typename std::vector<V2d_t>::const_iterator Cit;
-        for(Cit it = points.begin(); it != points.end(); ++it)
-        {
-            min = V2d_t::make(std::min(min.x, it->x), std::min(min.y, it->y));
-            max = V2d_t::make(std::max(max.x, it->x), std::max(max.y, it->y));
-        }
-        m_kdTree = KDTree_t(min, max);
-        for(VertInd i = 0; i < points.size(); ++i)
-        {
-            m_kdTree.insert(i, points);
-        }
-    }
-    /// Add point to KD-tree
-    void addPoint(const VertInd i, const std::vector<V2d<TCoordType> >& points)
-    {
-        m_kdTree.insert(i, points);
-    }
-    /// Find nearest point using R-tree
-    VertInd nearPoint(
-        const V2d<TCoordType>& pos,
-        const std::vector<V2d<TCoordType> >& points) const
-    {
-        return m_kdTree.nearest(pos, points).second;
-    }
-
-private:
-    typedef KDTree::KDTree<
-        TCoordType,
-        NumVerticesInLeaf,
-        InitialStackDepth,
-        StackDepthIncrement>
-        KDTree_t;
-    KDTree_t m_kdTree;
-};
-
-} // namespace CDT
-
-#endif
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Adapter between for KDTree and CDT
+ */
+
+#ifndef CDT_POINTKDTREE_H
+#define CDT_POINTKDTREE_H
+
+#include "CDTUtils.h"
+#include "KDTree.h"
+
+namespace CDT
+{
+
+/// KD-tree holding points
+template <
+    typename TCoordType,
+    size_t NumVerticesInLeaf = 32,
+    size_t InitialStackDepth = 32,
+    size_t StackDepthIncrement = 32>
+class LocatorKDTree
+{
+public:
+    /// Initialize KD-tree with points
+    void initialize(const std::vector<V2d<TCoordType> >& points)
+    {
+        typedef V2d<TCoordType> V2d_t;
+        V2d_t min = points.front();
+        V2d_t max = min;
+        typedef typename std::vector<V2d_t>::const_iterator Cit;
+        for(Cit it = points.begin(); it != points.end(); ++it)
+        {
+            min = V2d_t::make(std::min(min.x, it->x), std::min(min.y, it->y));
+            max = V2d_t::make(std::max(max.x, it->x), std::max(max.y, it->y));
+        }
+        m_kdTree = KDTree_t(min, max);
+        for(VertInd i(0); i < points.size(); ++i)
+        {
+            m_kdTree.insert(i, points);
+        }
+    }
+    /// Add point to KD-tree
+    void addPoint(const VertInd i, const std::vector<V2d<TCoordType> >& points)
+    {
+        m_kdTree.insert(i, points);
+    }
+    /// Find nearest point using R-tree
+    VertInd nearPoint(
+        const V2d<TCoordType>& pos,
+        const std::vector<V2d<TCoordType> >& points) const
+    {
+        return m_kdTree.nearest(pos, points).second;
+    }
+
+    CDT::VertInd size() const
+    {
+        return m_kdTree.size();
+    }
+
+    bool empty() const
+    {
+        return !size();
+    }
+
+private:
+    typedef KDTree::KDTree<
+        TCoordType,
+        NumVerticesInLeaf,
+        InitialStackDepth,
+        StackDepthIncrement>
+        KDTree_t;
+    KDTree_t m_kdTree;
+};
+
+} // namespace CDT
+
+#endif
diff --git a/cpp/Triangulation.h b/cpp/Triangulation.h
--- a/cpp/Triangulation.h
+++ b/cpp/Triangulation.h
@@ -1,617 +1,779 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Triangulation class
- */
-
-#ifndef CDT_vW1vZ0lO8rS4gY4uI4fB
-#define CDT_vW1vZ0lO8rS4gY4uI4fB
-
-#include "CDTUtils.h"
-#include "LocatorKDTree.h"
-
-#include <algorithm>
-#include <cstdlib>
-#include <iterator>
-#include <stack>
-#include <stdexcept>
-#include <utility>
-#include <vector>
-
-/// Namespace containing triangulation functionality
-namespace CDT
-{
-
-/// @addtogroup API
-/// @{
-
-/**
- * Enum of strategies specifying order in which a range of vertices is inserted
- * @note VertexInsertionOrder::Randomized will only randomize order of
- * inserting in triangulation, vertex indices will be preserved as they were
- * specified in the final triangulation
- */
-struct CDT_EXPORT VertexInsertionOrder
-{
-    /**
-     * The Enum itself
-     * @note needed to pre c++11 compilers that don't support 'class enum'
-     */
-    enum Enum
-    {
-        Randomized, ///< vertices will be inserted in random order
-        AsProvided, ///< vertices will be inserted in the same order as provided
-    };
-};
-
-/// Enum of what type of geometry used to embed triangulation into
-struct CDT_EXPORT SuperGeometryType
-{
-    /**
-     * The Enum itself
-     * @note needed to pre c++11 compilers that don't support 'class enum'
-     */
-    enum Enum
-    {
-        SuperTriangle, ///< conventional super-triangle
-        Custom,        ///< user-specified custom geometry (e.g., grid)
-    };
-};
-
-/**
- * Enum of strategies for treating intersecting constraint edges
- */
-struct CDT_EXPORT IntersectingConstraintEdges
-{
-    /**
-     * The Enum itself
-     * @note needed to pre c++11 compilers that don't support 'class enum'
-     */
-    enum Enum
-    {
-        Ignore,  ///< constraint edge intersections are not checked
-        Resolve, ///< constraint edge intersections are resolved
-    };
-};
-
-/**
- * Type used for storing layer depths for triangles
- * @note LayerDepth should support 60K+ layers, which could be to much or
- * too little for some use cases. Feel free to re-define this typedef.
- */
-typedef unsigned short LayerDepth;
-typedef LayerDepth BoundaryOverlapCount;
-
-/// Triangles by vertex index
-typedef std::vector<TriIndVec> VerticesTriangles;
-
-/**
- * @defgroup Triangulation Triangulation Class
- * Class performing triangulations.
- */
-/// @{
-
-/**
- * Data structure representing a 2D constrained Delaunay triangulation
- *
- * @tparam T type of vertex coordinates (e.g., float, double)
- * @tparam TNearPointLocator class providing locating near point for efficiently
- * inserting new points. Provides methods: 'addPoint(vPos, iV)' and
- * 'nearPoint(vPos) -> iV'
- */
-template <typename T, typename TNearPointLocator = LocatorKDTree<T> >
-class CDT_EXPORT Triangulation
-{
-public:
-    typedef std::vector<V2d<T> > V2dVec; ///< Vertices vector
-    V2dVec vertices;                     ///< triangulation's vertices
-    TriangleVec triangles;               ///< triangulation's triangles
-    EdgeUSet fixedEdges; ///< triangulation's constraints (fixed edges)
-    /**
-     * triangles adjacent to each vertex
-     * @note will be reset to empty when super-triangle is removed and
-     * triangulation is finalized. To re-calculate adjacent triangles use
-     * CDT::calculateTrianglesByVertex helper
-     */
-    VerticesTriangles vertTris;
-
-    /** Stores count of overlapping boundaries for a fixed edge. If no entry is
-     * present for an edge: no boundaries overlap.
-     * @note map only has entries for fixed for edges that represent overlapping
-     * boundaries
-     * @note needed for handling depth calculations and hole-removel in case of
-     * overlapping boundaries
-     */
-    unordered_map<Edge, BoundaryOverlapCount> overlapCount;
-
-    /** Stores list of original edges represented by a given fixed edge
-     * @note map only has entries for edges where multiple original fixed edges
-     * overlap or where a fixed edge is a part of original edge created by
-     * conforming Delaunay triangulation vertex insertion
-     */
-    unordered_map<Edge, EdgeVec> pieceToOriginals;
-
-    /*____ API _____*/
-    /// Default constructor
-    Triangulation();
-    /**
-     * Constructor
-     * @param vertexInsertionOrder strategy used for ordering vertex insertions
-     */
-    Triangulation(VertexInsertionOrder::Enum vertexInsertionOrder);
-    /**
-     * Constructor
-     * @param vertexInsertionOrder strategy used for ordering vertex insertions
-     * @param intersectingEdgesStrategy strategy for treating intersecting
-     * constraint edges
-     * @param minDistToConstraintEdge distance within which point is considered
-     * to be lying on a constraint edge. Used when adding constraints to the
-     * triangulation.
-     */
-    Triangulation(
-        VertexInsertionOrder::Enum vertexInsertionOrder,
-        IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
-        T minDistToConstraintEdge);
-    /**
-     * Constructor
-     * @param vertexInsertionOrder strategy used for ordering vertex insertions
-     * @param nearPtLocator class providing locating near point for efficiently
-     * inserting new points
-     * @param intersectingEdgesStrategy strategy for treating intersecting
-     * constraint edges
-     * @param minDistToConstraintEdge distance within which point is considered
-     * to be lying on a constraint edge. Used when adding constraints to the
-     * triangulation.
-     */
-    Triangulation(
-        VertexInsertionOrder::Enum vertexInsertionOrder,
-        const TNearPointLocator& nearPtLocator,
-        IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
-        T minDistToConstraintEdge);
-    /**
-     * Insert custom point-types specified by iterator range and X/Y-getters
-     * @tparam TVertexIter iterator that dereferences to custom point type
-     * @tparam TGetVertexCoordX function object getting x coordinate from
-     * vertex. Getter signature: const TVertexIter::value_type& -> T
-     * @tparam TGetVertexCoordY function object getting y coordinate from
-     * vertex. Getter signature: const TVertexIter::value_type& -> T
-     * @param first beginning of the range of vertices to add
-     * @param last end of the range of vertices to add
-     * @param getX getter of X-coordinate
-     * @param getY getter of Y-coordinate
-     */
-    template <
-        typename TVertexIter,
-        typename TGetVertexCoordX,
-        typename TGetVertexCoordY>
-    void insertVertices(
-        TVertexIter first,
-        TVertexIter last,
-        TGetVertexCoordX getX,
-        TGetVertexCoordY getY);
-    /**
-     * Insert vertices into triangulation
-     * @param vertices vector of vertices to insert
-     */
-    void insertVertices(const std::vector<V2d<T> >& vertices);
-    /**
-     * Insert constraints (custom-type fixed edges) into triangulation
-     * @note Each fixed edge is inserted by deleting the triangles it crosses,
-     * followed by the triangulation of the polygons on each side of the edge.
-     * <b> No new vertices are inserted.</b>
-     * @note If some edge appears more than once in the input this means that
-     * multiple boundaries overlap at the edge and impacts how hole detection
-     * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
-     * <b>Make sure there are no erroneous duplicates.</b>
-     * @tparam TEdgeIter iterator that dereferences to custom edge type
-     * @tparam TGetEdgeVertexStart function object getting start vertex index
-     * from an edge.
-     * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
-     * @tparam TGetEdgeVertexEnd function object getting end vertex index from
-     * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
-     * @param first beginning of the range of edges to add
-     * @param last end of the range of edges to add
-     * @param getStart getter of edge start vertex index
-     * @param getEnd getter of edge end vertex index
-     */
-    template <
-        typename TEdgeIter,
-        typename TGetEdgeVertexStart,
-        typename TGetEdgeVertexEnd>
-    void insertEdges(
-        TEdgeIter first,
-        TEdgeIter last,
-        TGetEdgeVertexStart getStart,
-        TGetEdgeVertexEnd getEnd);
-    /**
-     * Insert constraint edges into triangulation
-     * @note Each fixed edge is inserted by deleting the triangles it crosses,
-     * followed by the triangulation of the polygons on each side of the edge.
-     * <b> No new vertices are inserted.</b>
-     * @note If some edge appears more than once in the input this means that
-     * multiple boundaries overlap at the edge and impacts how hole detection
-     * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
-     * <b>Make sure there are no erroneous duplicates.</b>
-     * @tparam edges constraint edges
-     */
-    void insertEdges(const std::vector<Edge>& edges);
-    /**
-     * Ensure that triangulation conforms to constraints (fixed edges)
-     * @note For each fixed edge that is not present in the triangulation its
-     * midpoint is recursively added until the original edge is represented by a
-     * sequence of its pieces. <b> New vertices are inserted.</b>
-     * @note If some edge appears more than once the input this
-     * means that multiple boundaries overlap at the edge and impacts how hole
-     * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
-     * <b>Make sure there are no erroneous duplicates.</b>
-     * @tparam TEdgeIter iterator that dereferences to custom edge type
-     * @tparam TGetEdgeVertexStart function object getting start vertex index
-     * from an edge.
-     * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
-     * @tparam TGetEdgeVertexEnd function object getting end vertex index from
-     * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
-     * @param first beginning of the range of edges to add
-     * @param last end of the range of edges to add
-     * @param getStart getter of edge start vertex index
-     * @param getEnd getter of edge end vertex index
-     */
-    template <
-        typename TEdgeIter,
-        typename TGetEdgeVertexStart,
-        typename TGetEdgeVertexEnd>
-    void conformToEdges(
-        TEdgeIter first,
-        TEdgeIter last,
-        TGetEdgeVertexStart getStart,
-        TGetEdgeVertexEnd getEnd);
-    /**
-     * Ensure that triangulation conforms to constraints (fixed edges)
-     * @note For each fixed edge that is not present in the triangulation its
-     * midpoint is recursively added until the original edge is represented by a
-     * sequence of its pieces. <b> New vertices are inserted.</b>
-     * @note If some edge appears more than once the input this
-     * means that multiple boundaries overlap at the edge and impacts how hole
-     * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
-     * <b>Make sure there are no erroneous duplicates.</b>
-     * @tparam edges edges to conform to
-     */
-    void conformToEdges(const std::vector<Edge>& edges);
-    /**
-     * Erase triangles adjacent to super triangle
-     *
-     * @note does nothing if custom geometry is used
-     */
-    void eraseSuperTriangle();
-    /// Erase triangles outside of constrained boundary using growing
-    void eraseOuterTriangles();
-    /**
-     * Erase triangles outside of constrained boundary and auto-detected holes
-     *
-     * @note detecting holes relies on layer peeling based on layer depth
-     * @note supports overlapping or touching boundaries
-     */
-    void eraseOuterTrianglesAndHoles();
-    /**
-     * Call this method after directly setting custom super-geometry via
-     * vertices and triangles members
-     */
-    void initializedWithCustomSuperGeometry();
-
-    /**
-     * Check if the triangulation was finalized with `erase...` method and
-     * super-triangle was removed.
-     * @return true if triangulation is finalized, false otherwise
-     */
-    bool isFinalized() const;
-
-    /**
-     * Calculate depth of each triangle in constraint triangulation. Supports
-     * overlapping boundaries.
-     *
-     * Perform depth peeling from super triangle to outermost boundary,
-     * then to next boundary and so on until all triangles are traversed.@n
-     * For example depth is:
-     *  - 0 for triangles outside outermost boundary
-     *  - 1 for triangles inside boundary but outside hole
-     *  - 2 for triangles in hole
-     *  - 3 for triangles in island and so on...
-     * @return vector where element at index i stores depth of i-th triangle
-     */
-    std::vector<LayerDepth> calculateTriangleDepths() const;
-
-    /**
-     * @defgroup Advanced Advanced Triangulation Methods
-     * Advanced methods for manually modifying the triangulation from
-     * outside. Please only use them when you know what you are doing.
-     */
-    /// @{
-
-    /**
-     * Flip an edge between two triangle.
-     * @note Advanced method for manually modifying the triangulation from
-     * outside. Please call it when you know what you are doing.
-     * @param iT first triangle
-     * @param iTopo second triangle
-
-     */
-    void flipEdge(TriInd iT, TriInd iTopo);
-
-    /**
-     * Remove triangles with specified indices.
-     * Adjust internal triangulation state accordingly.
-     * @param removedTriangles indices of triangles to remove
-     */
-    void removeTriangles(const TriIndUSet& removedTriangles);
-    /// @}
-
-private:
-    /*____ Detail __*/
-    void addSuperTriangle(const Box2d<T>& box);
-    void addNewVertex(const V2d<T>& pos, const TriIndVec& tris);
-    void insertVertex(VertInd iVert);
-    void ensureDelaunayByEdgeFlips(
-        const V2d<T>& v,
-        VertInd iVert,
-        std::stack<TriInd>& triStack);
-    /// Flip fixed edges and return a list of flipped fixed edges
-    std::vector<Edge> insertVertex_FlipFixedEdges(VertInd iVert);
-    /**
-     * Insert an edge into constraint Delaunay triangulation
-     * @param edge edge to insert
-     * @param originalEdge original edge inserted edge is part of
-     */
-    void insertEdge(Edge edge, Edge originalEdge);
-    /**
-     * Conform Delaunay triangulation to a fixed edge by recursively inserting
-     * mid point of the edge and then conforming to its halves
-     * @param edge fixed edge to conform to
-     * @param originalEdges original edges that new edge is piece of
-     * @param overlaps count of overlapping boundaries at the edge. Only used
-     * when re-introducing edge with overlaps > 0
-     * @param orientationTolerance tolerance for orient2d predicate,
-     * values [-tolerance,+tolerance] are considered as 0.
-     */
-    void conformToEdge(
-        Edge edge,
-        EdgeVec originalEdges,
-        BoundaryOverlapCount overlaps);
-    tuple<TriInd, VertInd, VertInd> intersectedTriangle(
-        VertInd iA,
-        const std::vector<TriInd>& candidates,
-        const V2d<T>& a,
-        const V2d<T>& b,
-        T orientationTolerance = T(0)) const;
-    /// Returns indices of three resulting triangles
-    std::stack<TriInd> insertPointInTriangle(VertInd v, TriInd iT);
-    /// Returns indices of four resulting triangles
-    std::stack<TriInd> insertPointOnEdge(VertInd v, TriInd iT1, TriInd iT2);
-    array<TriInd, 2> trianglesAt(const V2d<T>& pos) const;
-    array<TriInd, 2> walkingSearchTrianglesAt(const V2d<T>& pos) const;
-    TriInd walkTriangles(VertInd startVertex, const V2d<T>& pos) const;
-    bool isFlipNeeded(
-        const V2d<T>& v,
-        VertInd iV,
-        VertInd iV1,
-        VertInd iV2,
-        VertInd iV3) const;
-    bool
-    isFlipNeeded(const V2d<T>& v, TriInd iT, TriInd iTopo, VertInd iVert) const;
-    void changeNeighbor(TriInd iT, TriInd oldNeighbor, TriInd newNeighbor);
-    void changeNeighbor(
-        TriInd iT,
-        VertInd iVedge1,
-        VertInd iVedge2,
-        TriInd newNeighbor);
-    void addAdjacentTriangle(VertInd iVertex, TriInd iTriangle);
-    void
-    addAdjacentTriangles(VertInd iVertex, TriInd iT1, TriInd iT2, TriInd iT3);
-    void addAdjacentTriangles(
-        VertInd iVertex,
-        TriInd iT1,
-        TriInd iT2,
-        TriInd iT3,
-        TriInd iT4);
-    void removeAdjacentTriangle(VertInd iVertex, TriInd iTriangle);
-    TriInd triangulatePseudopolygon(
-        VertInd ia,
-        VertInd ib,
-        std::vector<VertInd>::const_iterator pointsFirst,
-        std::vector<VertInd>::const_iterator pointsLast);
-    VertInd findDelaunayPoint(
-        VertInd ia,
-        VertInd ib,
-        std::vector<VertInd>::const_iterator pointsFirst,
-        std::vector<VertInd>::const_iterator pointsLast) const;
-    TriInd pseudopolyOuterTriangle(VertInd ia, VertInd ib) const;
-    TriInd addTriangle(const Triangle& t); // note: invalidates iterators!
-    TriInd addTriangle(); // note: invalidates triangle iterators!
-    /**
-     * Remove super-triangle (if used) and triangles with specified indices.
-     * Adjust internal triangulation state accordingly.
-     * @removedTriangles indices of triangles to remove
-     */
-    void finalizeTriangulation(const TriIndUSet& removedTriangles);
-    TriIndUSet growToBoundary(std::stack<TriInd> seeds) const;
-    void fixEdge(const Edge& edge, BoundaryOverlapCount overlaps);
-    void fixEdge(const Edge& edge);
-    void fixEdge(const Edge& edge, const Edge& originalEdge);
-    /**
-     * Flag triangle as dummy
-     * @note Advanced method for manually modifying the triangulation from
-     * outside. Please call it when you know what you are doing.
-     * @param iT index of a triangle to flag
-     */
-    void makeDummy(TriInd iT);
-    /**
-     * Erase all dummy triangles
-     * @note Advanced method for manually modifying the triangulation from
-     * outside. Please call it when you know what you are doing.
-     */
-    void eraseDummies();
-    /**
-     * Depth-peel a layer in triangulation, used when calculating triangle
-     * depths
-     *
-     * It takes starting seed triangles, traverses neighboring triangles, and
-     * assigns given layer depth to the traversed triangles. Traversal is
-     * blocked by constraint edges. Triangles behind constraint edges are
-     * recorded as seeds of next layer and returned from the function.
-     *
-     * @param seeds indices of seed triangles
-     * @param layerDepth current layer's depth to mark triangles with
-     * @param[in, out] triDepths depths of triangles
-     * @return triangles of the deeper layers that are adjacent to the peeled
-     * layer. To be used as seeds when peeling deeper layers.
-     */
-    unordered_map<TriInd, LayerDepth> peelLayer(
-        std::stack<TriInd> seeds,
-        LayerDepth layerDepth,
-        std::vector<LayerDepth>& triDepths) const;
-
-    std::vector<TriInd> m_dummyTris;
-    TNearPointLocator m_nearPtLocator;
-    std::size_t m_nTargetVerts;
-    SuperGeometryType::Enum m_superGeomType;
-    VertexInsertionOrder::Enum m_vertexInsertionOrder;
-    IntersectingConstraintEdges::Enum m_intersectingEdgesStrategy;
-    T m_minDistToConstraintEdge;
-};
-
-/// @}
-/// @}
-
-namespace detail
-{
-
-static mt19937 randGenerator(9001);
-
-template <class RandomIt>
-void random_shuffle(RandomIt first, RandomIt last)
-{
-    typename std::iterator_traits<RandomIt>::difference_type i, n;
-    n = last - first;
-    for(i = n - 1; i > 0; --i)
-    {
-        std::swap(first[i], first[randGenerator() % (i + 1)]);
-    }
-}
-
-} // namespace detail
-
-//-----------------------
-// Triangulation methods
-//-----------------------
-template <typename T, typename TNearPointLocator>
-template <
-    typename TVertexIter,
-    typename TGetVertexCoordX,
-    typename TGetVertexCoordY>
-void Triangulation<T, TNearPointLocator>::insertVertices(
-    const TVertexIter first,
-    const TVertexIter last,
-    TGetVertexCoordX getX,
-    TGetVertexCoordY getY)
-{
-    if(isFinalized())
-    {
-        throw std::runtime_error(
-            "Triangulation was finalized with 'erase...' method. Inserting new "
-            "vertices is not possible");
-    }
-    detail::randGenerator.seed(9001); // ensure deterministic behavior
-    if(vertices.empty())
-    {
-        addSuperTriangle(envelopBox<T>(first, last, getX, getY));
-    }
-
-    const std::size_t nExistingVerts = vertices.size();
-
-    vertices.reserve(nExistingVerts + std::distance(first, last));
-    for(TVertexIter it = first; it != last; ++it)
-        addNewVertex(V2d<T>::make(getX(*it), getY(*it)), TriIndVec());
-
-    switch(m_vertexInsertionOrder)
-    {
-    case VertexInsertionOrder::AsProvided:
-        for(TVertexIter it = first; it != last; ++it)
-            insertVertex(VertInd(nExistingVerts + std::distance(first, it)));
-        break;
-    case VertexInsertionOrder::Randomized:
-        std::vector<VertInd> ii(std::distance(first, last));
-        typedef std::vector<VertInd>::iterator Iter;
-        VertInd value = static_cast<VertInd>(nExistingVerts);
-        for(Iter it = ii.begin(); it != ii.end(); ++it, ++value)
-            *it = value;
-        detail::random_shuffle(ii.begin(), ii.end());
-        for(Iter it = ii.begin(); it != ii.end(); ++it)
-            insertVertex(*it);
-        break;
-    }
-}
-
-template <typename T, typename TNearPointLocator>
-template <
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd>
-void Triangulation<T, TNearPointLocator>::insertEdges(
-    TEdgeIter first,
-    const TEdgeIter last,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd)
-{
-    if(isFinalized())
-    {
-        throw std::runtime_error(
-            "Triangulation was finalized with 'erase...' method. Inserting new "
-            "edges is not possible");
-    }
-    for(; first != last; ++first)
-    {
-        // +3 to account for super-triangle vertices
-        const Edge edge(
-            VertInd(getStart(*first) + m_nTargetVerts),
-            VertInd(getEnd(*first) + m_nTargetVerts));
-        insertEdge(edge, edge);
-    }
-    eraseDummies();
-}
-
-template <typename T, typename TNearPointLocator>
-template <
-    typename TEdgeIter,
-    typename TGetEdgeVertexStart,
-    typename TGetEdgeVertexEnd>
-void Triangulation<T, TNearPointLocator>::conformToEdges(
-    TEdgeIter first,
-    const TEdgeIter last,
-    TGetEdgeVertexStart getStart,
-    TGetEdgeVertexEnd getEnd)
-{
-    if(isFinalized())
-    {
-        throw std::runtime_error(
-            "Triangulation was finalized with 'erase...' method. Conforming to "
-            "new edges is not possible");
-    }
-    for(; first != last; ++first)
-    {
-        // +3 to account for super-triangle vertices
-        const Edge e(
-            VertInd(getStart(*first) + m_nTargetVerts),
-            VertInd(getEnd(*first) + m_nTargetVerts));
-        conformToEdge(e, EdgeVec(1, e), 0);
-    }
-    eraseDummies();
-}
-
-} // namespace CDT
-
-#ifndef CDT_USE_AS_COMPILED_LIBRARY
-#include "Triangulation.hpp"
-#endif
-
-#endif // header-guard
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Triangulation class
+ */
+
+#ifndef CDT_vW1vZ0lO8rS4gY4uI4fB
+#define CDT_vW1vZ0lO8rS4gY4uI4fB
+
+#include "CDTUtils.h"
+#include "LocatorKDTree.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <iterator>
+#include <stack>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+/// Namespace containing triangulation functionality
+namespace CDT
+{
+
+/// @addtogroup API
+/// @{
+
+/**
+ * Enum of strategies specifying order in which a range of vertices is inserted
+ * @note VertexInsertionOrder::Randomized will only randomize order of
+ * inserting in triangulation, vertex indices will be preserved as they were
+ * specified in the final triangulation
+ */
+struct CDT_EXPORT VertexInsertionOrder
+{
+    /**
+     * The Enum itself
+     * @note needed to pre c++11 compilers that don't support 'class enum'
+     */
+    enum Enum
+    {
+        /**
+         * Automatic insertion order optimized for better performance
+         * @details breadth-first traversal of a Kd-tree for initial bulk-load,
+         * randomized for subsequent insertions
+         */
+        Auto,
+        /// insert vertices in same order they are provided
+        AsProvided,
+    };
+};
+
+/// Enum of what type of geometry used to embed triangulation into
+struct CDT_EXPORT SuperGeometryType
+{
+    /**
+     * The Enum itself
+     * @note needed to pre c++11 compilers that don't support 'class enum'
+     */
+    enum Enum
+    {
+        SuperTriangle, ///< conventional super-triangle
+        Custom,        ///< user-specified custom geometry (e.g., grid)
+    };
+};
+
+/**
+ * Enum of strategies for treating intersecting constraint edges
+ */
+struct CDT_EXPORT IntersectingConstraintEdges
+{
+    /**
+     * The Enum itself
+     * @note needed to pre c++11 compilers that don't support 'class enum'
+     */
+    enum Enum
+    {
+        Ignore,  ///< constraint edge intersections are not checked
+        Resolve, ///< constraint edge intersections are resolved
+    };
+};
+
+/**
+ * Type used for storing layer depths for triangles
+ * @note LayerDepth should support 60K+ layers, which could be to much or
+ * too little for some use cases. Feel free to re-define this typedef.
+ */
+typedef unsigned short LayerDepth;
+typedef LayerDepth BoundaryOverlapCount;
+
+/**
+ * @defgroup Triangulation Triangulation Class
+ * Class performing triangulations.
+ */
+/// @{
+
+/**
+ * Data structure representing a 2D constrained Delaunay triangulation
+ *
+ * @tparam T type of vertex coordinates (e.g., float, double)
+ * @tparam TNearPointLocator class providing locating near point for efficiently
+ * inserting new points. Provides methods: 'addPoint(vPos, iV)' and
+ * 'nearPoint(vPos) -> iV'
+ */
+template <typename T, typename TNearPointLocator = LocatorKDTree<T> >
+class CDT_EXPORT Triangulation
+{
+public:
+    typedef std::vector<V2d<T> > V2dVec; ///< Vertices vector
+    V2dVec vertices;                     ///< triangulation's vertices
+    TriangleVec triangles;               ///< triangulation's triangles
+    EdgeUSet fixedEdges; ///< triangulation's constraints (fixed edges)
+
+    /** Stores count of overlapping boundaries for a fixed edge. If no entry is
+     * present for an edge: no boundaries overlap.
+     * @note map only has entries for fixed for edges that represent overlapping
+     * boundaries
+     * @note needed for handling depth calculations and hole-removel in case of
+     * overlapping boundaries
+     */
+    unordered_map<Edge, BoundaryOverlapCount> overlapCount;
+
+    /** Stores list of original edges represented by a given fixed edge
+     * @note map only has entries for edges where multiple original fixed edges
+     * overlap or where a fixed edge is a part of original edge created by
+     * conforming Delaunay triangulation vertex insertion
+     */
+    unordered_map<Edge, EdgeVec> pieceToOriginals;
+
+    /*____ API _____*/
+    /// Default constructor
+    Triangulation();
+    /**
+     * Constructor
+     * @param vertexInsertionOrder strategy used for ordering vertex insertions
+     */
+    explicit Triangulation(VertexInsertionOrder::Enum vertexInsertionOrder);
+    /**
+     * Constructor
+     * @param vertexInsertionOrder strategy used for ordering vertex insertions
+     * @param intersectingEdgesStrategy strategy for treating intersecting
+     * constraint edges
+     * @param minDistToConstraintEdge distance within which point is considered
+     * to be lying on a constraint edge. Used when adding constraints to the
+     * triangulation.
+     */
+    Triangulation(
+        VertexInsertionOrder::Enum vertexInsertionOrder,
+        IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
+        T minDistToConstraintEdge);
+    /**
+     * Constructor
+     * @param vertexInsertionOrder strategy used for ordering vertex insertions
+     * @param nearPtLocator class providing locating near point for efficiently
+     * inserting new points
+     * @param intersectingEdgesStrategy strategy for treating intersecting
+     * constraint edges
+     * @param minDistToConstraintEdge distance within which point is considered
+     * to be lying on a constraint edge. Used when adding constraints to the
+     * triangulation.
+     */
+    Triangulation(
+        VertexInsertionOrder::Enum vertexInsertionOrder,
+        const TNearPointLocator& nearPtLocator,
+        IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
+        T minDistToConstraintEdge);
+    /**
+     * Insert custom point-types specified by iterator range and X/Y-getters
+     * @tparam TVertexIter iterator that dereferences to custom point type
+     * @tparam TGetVertexCoordX function object getting x coordinate from
+     * vertex. Getter signature: const TVertexIter::value_type& -> T
+     * @tparam TGetVertexCoordY function object getting y coordinate from
+     * vertex. Getter signature: const TVertexIter::value_type& -> T
+     * @param first beginning of the range of vertices to add
+     * @param last end of the range of vertices to add
+     * @param getX getter of X-coordinate
+     * @param getY getter of Y-coordinate
+     */
+    template <
+        typename TVertexIter,
+        typename TGetVertexCoordX,
+        typename TGetVertexCoordY>
+    void insertVertices(
+        TVertexIter first,
+        TVertexIter last,
+        TGetVertexCoordX getX,
+        TGetVertexCoordY getY);
+    /**
+     * Insert vertices into triangulation
+     * @param vertices vector of vertices to insert
+     */
+    void insertVertices(const std::vector<V2d<T> >& vertices);
+    /**
+     * Insert constraints (custom-type fixed edges) into triangulation
+     * @note Each fixed edge is inserted by deleting the triangles it crosses,
+     * followed by the triangulation of the polygons on each side of the edge.
+     * <b> No new vertices are inserted.</b>
+     * @note If some edge appears more than once in the input this means that
+     * multiple boundaries overlap at the edge and impacts how hole detection
+     * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
+     * <b>Make sure there are no erroneous duplicates.</b>
+     * @tparam TEdgeIter iterator that dereferences to custom edge type
+     * @tparam TGetEdgeVertexStart function object getting start vertex index
+     * from an edge.
+     * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+     * @tparam TGetEdgeVertexEnd function object getting end vertex index from
+     * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+     * @param first beginning of the range of edges to add
+     * @param last end of the range of edges to add
+     * @param getStart getter of edge start vertex index
+     * @param getEnd getter of edge end vertex index
+     */
+    template <
+        typename TEdgeIter,
+        typename TGetEdgeVertexStart,
+        typename TGetEdgeVertexEnd>
+    void insertEdges(
+        TEdgeIter first,
+        TEdgeIter last,
+        TGetEdgeVertexStart getStart,
+        TGetEdgeVertexEnd getEnd);
+    /**
+     * Insert constraint edges into triangulation
+     * @note Each fixed edge is inserted by deleting the triangles it crosses,
+     * followed by the triangulation of the polygons on each side of the edge.
+     * <b> No new vertices are inserted.</b>
+     * @note If some edge appears more than once in the input this means that
+     * multiple boundaries overlap at the edge and impacts how hole detection
+     * algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
+     * <b>Make sure there are no erroneous duplicates.</b>
+     * @tparam edges constraint edges
+     */
+    void insertEdges(const std::vector<Edge>& edges);
+    /**
+     * Ensure that triangulation conforms to constraints (fixed edges)
+     * @note For each fixed edge that is not present in the triangulation its
+     * midpoint is recursively added until the original edge is represented by a
+     * sequence of its pieces. <b> New vertices are inserted.</b>
+     * @note If some edge appears more than once the input this
+     * means that multiple boundaries overlap at the edge and impacts how hole
+     * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
+     * <b>Make sure there are no erroneous duplicates.</b>
+     * @tparam TEdgeIter iterator that dereferences to custom edge type
+     * @tparam TGetEdgeVertexStart function object getting start vertex index
+     * from an edge.
+     * Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+     * @tparam TGetEdgeVertexEnd function object getting end vertex index from
+     * an edge. Getter signature: const TEdgeIter::value_type& -> CDT::VertInd
+     * @param first beginning of the range of edges to add
+     * @param last end of the range of edges to add
+     * @param getStart getter of edge start vertex index
+     * @param getEnd getter of edge end vertex index
+     */
+    template <
+        typename TEdgeIter,
+        typename TGetEdgeVertexStart,
+        typename TGetEdgeVertexEnd>
+    void conformToEdges(
+        TEdgeIter first,
+        TEdgeIter last,
+        TGetEdgeVertexStart getStart,
+        TGetEdgeVertexEnd getEnd);
+    /**
+     * Ensure that triangulation conforms to constraints (fixed edges)
+     * @note For each fixed edge that is not present in the triangulation its
+     * midpoint is recursively added until the original edge is represented by a
+     * sequence of its pieces. <b> New vertices are inserted.</b>
+     * @note If some edge appears more than once the input this
+     * means that multiple boundaries overlap at the edge and impacts how hole
+     * detection algorithm of Triangulation::eraseOuterTrianglesAndHoles works.
+     * <b>Make sure there are no erroneous duplicates.</b>
+     * @tparam edges edges to conform to
+     */
+    void conformToEdges(const std::vector<Edge>& edges);
+    /**
+     * Erase triangles adjacent to super triangle
+     *
+     * @note does nothing if custom geometry is used
+     */
+    void eraseSuperTriangle();
+    /// Erase triangles outside of constrained boundary using growing
+    void eraseOuterTriangles();
+    /**
+     * Erase triangles outside of constrained boundary and auto-detected holes
+     *
+     * @note detecting holes relies on layer peeling based on layer depth
+     * @note supports overlapping or touching boundaries
+     */
+    void eraseOuterTrianglesAndHoles();
+    /**
+     * Call this method after directly setting custom super-geometry via
+     * vertices and triangles members
+     */
+    void initializedWithCustomSuperGeometry();
+
+    /**
+     * Check if the triangulation was finalized with `erase...` method and
+     * super-triangle was removed.
+     * @return true if triangulation is finalized, false otherwise
+     */
+    bool isFinalized() const;
+
+    /**
+     * Calculate depth of each triangle in constraint triangulation. Supports
+     * overlapping boundaries.
+     *
+     * Perform depth peeling from super triangle to outermost boundary,
+     * then to next boundary and so on until all triangles are traversed.@n
+     * For example depth is:
+     *  - 0 for triangles outside outermost boundary
+     *  - 1 for triangles inside boundary but outside hole
+     *  - 2 for triangles in hole
+     *  - 3 for triangles in island and so on...
+     * @return vector where element at index i stores depth of i-th triangle
+     */
+    std::vector<LayerDepth> calculateTriangleDepths() const;
+
+    /**
+     * @defgroup Advanced Advanced Triangulation Methods
+     * Advanced methods for manually modifying the triangulation from
+     * outside. Please only use them when you know what you are doing.
+     */
+    /// @{
+
+    /**
+     * Flip an edge between two triangle.
+     * @note Advanced method for manually modifying the triangulation from
+     * outside. Please call it when you know what you are doing.
+     * @param iT first triangle
+     * @param iTopo second triangle
+     */
+    void flipEdge(TriInd iT, TriInd iTopo);
+
+    void flipEdge(
+        TriInd iT,
+        TriInd iTopo,
+        VertInd v1,
+        VertInd v2,
+        VertInd v3,
+        VertInd v4,
+        TriInd n1,
+        TriInd n2,
+        TriInd n3,
+        TriInd n4);
+
+    /**
+     * Remove triangles with specified indices.
+     * Adjust internal triangulation state accordingly.
+     * @param removedTriangles indices of triangles to remove
+     */
+    void removeTriangles(const TriIndUSet& removedTriangles);
+
+    /// Access internal vertex adjacent triangles
+    TriIndVec& VertTrisInternal();
+    /// Access internal vertex adjacent triangles
+    const TriIndVec& VertTrisInternal() const;
+    /// @}
+
+private:
+    /*____ Detail __*/
+    void addSuperTriangle(const Box2d<T>& box);
+    void addNewVertex(const V2d<T>& pos, TriInd iT);
+    void insertVertex(VertInd iVert);
+    void insertVertex(VertInd iVert, VertInd walkStart);
+    void ensureDelaunayByEdgeFlips(
+        const V2d<T>& v1,
+        VertInd iV1,
+        std::stack<TriInd>& triStack);
+    /// Flip fixed edges and return a list of flipped fixed edges
+    std::vector<Edge> insertVertex_FlipFixedEdges(VertInd iV1);
+
+    /// State for an iteration of triangulate pseudo-polygon
+    typedef tuple<IndexSizeType, IndexSizeType, TriInd, TriInd, Index>
+        TriangulatePseudopolygonTask;
+
+    /**
+     * Insert an edge into constraint Delaunay triangulation
+     * @param edge edge to insert
+     * @param originalEdge original edge inserted edge is part of
+     * @param[in,out] remaining parts of the edge that still need to
+     * be inserted
+     * @param[in,out] tppIterations stack to be used for storing iterations of
+     * triangulating pseudo-polygon
+     * @note in-out state (@param remaining @param tppIterations) is shared
+     * between different runs for performance gains (reducing memory
+     * allocations)
+     */
+    void insertEdge(
+        Edge edge,
+        Edge originalEdge,
+        EdgeVec& remaining,
+        std::vector<TriangulatePseudopolygonTask>& tppIterations);
+
+    /**
+     * Insert an edge or its part into constraint Delaunay triangulation
+     * @param edge edge to insert
+     * @param originalEdge original edge inserted edge is part of
+     * @param[in,out] remainingStack parts of the edge that still need to
+     * be inserted
+     * @param[in,out] tppIterations stack to be used for storing iterations of
+     * triangulating pseudo-polygon
+     * @note in-out state (@param remaining @param tppIterations) is shared
+     * between different runs for performance gains (reducing memory
+     * allocations)
+     */
+    void insertEdgeIteration(
+        Edge edge,
+        Edge originalEdge,
+        EdgeVec& remaining,
+        std::vector<TriangulatePseudopolygonTask>& tppIterations);
+
+    /// State for iteration of conforming to edge
+    typedef tuple<Edge, EdgeVec, BoundaryOverlapCount> ConformToEdgeTask;
+
+    /**
+     * Conform Delaunay triangulation to a fixed edge by recursively inserting
+     * mid point of the edge and then conforming to its halves
+     * @param edge fixed edge to conform to
+     * @param originals original edges that new edge is piece of
+     * @param overlaps count of overlapping boundaries at the edge. Only used
+     * when re-introducing edge with overlaps > 0
+     * @param[in,out] remaining remaining edge parts to be conformed to
+     * @note in-out state (@param remaining @param reintroduce) is shared
+     * between different runs for performance gains (reducing memory
+     * allocations)
+     */
+    void conformToEdge(
+        Edge edge,
+        EdgeVec originals,
+        BoundaryOverlapCount overlaps,
+        std::vector<ConformToEdgeTask>& remaining);
+
+    /**
+     * Iteration of conform to fixed edge.
+     * @param edge fixed edge to conform to
+     * @param originals original edges that new edge is piece of
+     * @param overlaps count of overlapping boundaries at the edge. Only used
+     * when re-introducing edge with overlaps > 0
+     * @param[in,out] remaining remaining edge parts
+     * @note in-out state (@param remaining @param reintroduce) is shared
+     * between different runs for performance gains (reducing memory
+     * allocations)
+     */
+    void conformToEdgeIteration(
+        Edge edge,
+        const EdgeVec& originals,
+        BoundaryOverlapCount overlaps,
+        std::vector<ConformToEdgeTask>& remaining);
+
+    tuple<TriInd, VertInd, VertInd> intersectedTriangle(
+        VertInd iA,
+        const V2d<T>& a,
+        const V2d<T>& b,
+        T orientationTolerance = T(0)) const;
+    /// Returns indices of three resulting triangles
+    std::stack<TriInd> insertVertexInsideTriangle(VertInd v, TriInd iT);
+    /// Returns indices of four resulting triangles
+    std::stack<TriInd> insertVertexOnEdge(VertInd v, TriInd iT1, TriInd iT2);
+    array<TriInd, 2> trianglesAt(const V2d<T>& pos) const;
+    array<TriInd, 2>
+    walkingSearchTrianglesAt(const V2d<T>& pos, VertInd startVertex) const;
+    TriInd walkTriangles(VertInd startVertex, const V2d<T>& pos) const;
+    /// Given triangle and its vertex find opposite triangle and the other three
+    /// vertices and surrounding neighbors
+    void edgeFlipInfo(
+        TriInd iT,
+        VertInd iV1,
+        TriInd& iTopo,
+        VertInd& iV2,
+        VertInd& iV3,
+        VertInd& iV4,
+        TriInd& n1,
+        TriInd& n2,
+        TriInd& n3,
+        TriInd& n4);
+    bool isFlipNeeded(
+        const V2d<T>& v,
+        VertInd iV1,
+        VertInd iV2,
+        VertInd iV3,
+        VertInd iV4) const;
+    void changeNeighbor(TriInd iT, TriInd oldNeighbor, TriInd newNeighbor);
+    void changeNeighbor(
+        TriInd iT,
+        VertInd iVedge1,
+        VertInd iVedge2,
+        TriInd newNeighbor);
+    void triangulatePseudopolygon(
+        const std::vector<VertInd>& poly,
+        const std::vector<TriInd>& outerTris,
+        TriInd iT,
+        TriInd iN,
+        std::vector<TriangulatePseudopolygonTask>& iterations);
+    void triangulatePseudopolygonIteration(
+        const std::vector<VertInd>& poly,
+        const std::vector<TriInd>& outerTris,
+        std::vector<TriangulatePseudopolygonTask>& iterations);
+    IndexSizeType findDelaunayPoint(
+        const std::vector<VertInd>& poly,
+        IndexSizeType iA,
+        IndexSizeType iB) const;
+    TriInd addTriangle(const Triangle& t); // note: invalidates iterators!
+    TriInd addTriangle(); // note: invalidates triangle iterators!
+    /**
+     * Remove super-triangle (if used) and triangles with specified indices.
+     * Adjust internal triangulation state accordingly.
+     * @removedTriangles indices of triangles to remove
+     */
+    void finalizeTriangulation(const TriIndUSet& removedTriangles);
+    TriIndUSet growToBoundary(std::stack<TriInd> seeds) const;
+    void fixEdge(const Edge& edge);
+    void fixEdge(const Edge& edge, const Edge& originalEdge);
+    /**
+     *  Split existing constraint (fixed) edge
+     * @param edge fixed edge to split
+     * @param iSplitVert index of the vertex to be used as a split vertex
+     */
+    void splitFixedEdge(const Edge& edge, const VertInd iSplitVert);
+    /**
+     * Add a vertex that splits an edge into the triangulation
+     * @param splitVert position of split vertex
+     * @param iT index of a first triangle adjacent to the split edge
+     * @param iTopo index of a second triangle adjacent to the split edge
+     * (opposed to the first triangle)
+     * @return index of a newly added split vertex
+     */
+    VertInd addSplitEdgeVertex(
+        const V2d<T>& splitVert,
+        const TriInd iT,
+        const TriInd iTopo);
+    /**
+     * Split fixed edge and add a split vertex into the triangulation
+     * @param edge fixed edge to split
+     * @param splitVert position of split vertex
+     * @param iT index of a first triangle adjacent to the split edge
+     * @param iTopo index of a second triangle adjacent to the split edge
+     * (opposed to the first triangle)
+     * @return index of a newly added split vertex
+     */
+    VertInd splitFixedEdgeAt(
+        const Edge& edge,
+        const V2d<T>& splitVert,
+        const TriInd iT,
+        const TriInd iTopo);
+    /**
+     * Flag triangle as dummy
+     * @note Advanced method for manually modifying the triangulation from
+     * outside. Please call it when you know what you are doing.
+     * @param iT index of a triangle to flag
+     */
+    void makeDummy(TriInd iT);
+    /**
+     * Erase all dummy triangles
+     * @note Advanced method for manually modifying the triangulation from
+     * outside. Please call it when you know what you are doing.
+     */
+    void eraseDummies();
+    /**
+     * Depth-peel a layer in triangulation, used when calculating triangle
+     * depths
+     *
+     * It takes starting seed triangles, traverses neighboring triangles, and
+     * assigns given layer depth to the traversed triangles. Traversal is
+     * blocked by constraint edges. Triangles behind constraint edges are
+     * recorded as seeds of next layer and returned from the function.
+     *
+     * @param seeds indices of seed triangles
+     * @param layerDepth current layer's depth to mark triangles with
+     * @param[in, out] triDepths depths of triangles
+     * @return triangles of the deeper layers that are adjacent to the peeled
+     * layer. To be used as seeds when peeling deeper layers.
+     */
+    unordered_map<TriInd, LayerDepth> peelLayer(
+        std::stack<TriInd> seeds,
+        LayerDepth layerDepth,
+        std::vector<LayerDepth>& triDepths) const;
+
+    void insertVertices_AsProvided(VertInd superGeomVertCount);
+    void insertVertices_Randomized(VertInd superGeomVertCount);
+    void insertVertices_KDTreeBFS(
+        VertInd superGeomVertCount,
+        V2d<T> boxMin,
+        V2d<T> boxMax);
+    std::pair<TriInd, TriInd> edgeTriangles(VertInd a, VertInd b) const;
+    bool hasEdge(VertInd a, VertInd b) const;
+    void setAdjacentTriangle(const VertInd v, const TriInd t);
+    void pivotVertexTriangleCW(VertInd v);
+    /// Add vertex to nearest-point locator if locator is initialized
+    void tryAddVertexToLocator(const VertInd v);
+    /// Perform lazy initialization of nearest-point locator after the Kd-tree
+    /// BFS bulk load if necessary
+    void tryInitNearestPointLocator();
+
+    std::vector<TriInd> m_dummyTris;
+    TNearPointLocator m_nearPtLocator;
+    std::size_t m_nTargetVerts;
+    SuperGeometryType::Enum m_superGeomType;
+    VertexInsertionOrder::Enum m_vertexInsertionOrder;
+    IntersectingConstraintEdges::Enum m_intersectingEdgesStrategy;
+    T m_minDistToConstraintEdge;
+    TriIndVec m_vertTris; /// one triangle adjacent to each vertex
+};
+
+/// @}
+/// @}
+
+namespace detail
+{
+
+/// SplitMix64  pseudo-random number generator
+struct SplitMix64RandGen
+{
+    typedef unsigned long long uint64;
+    uint64 m_state;
+    explicit SplitMix64RandGen(uint64 state)
+        : m_state(state)
+    {}
+    explicit SplitMix64RandGen()
+        : m_state(0)
+    {}
+    uint64 operator()()
+    {
+        uint64 z = (m_state += 0x9e3779b97f4a7c15);
+        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+        z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+        return z ^ (z >> 31);
+    }
+};
+
+template <class RandomIt>
+void random_shuffle(RandomIt first, RandomIt last)
+{
+    detail::SplitMix64RandGen prng;
+    typename std::iterator_traits<RandomIt>::difference_type i, n;
+    n = last - first;
+    for(i = n - 1; i > 0; --i)
+    {
+        std::swap(first[i], first[prng() % (i + 1)]);
+    }
+}
+
+// backport from c++11
+template <class ForwardIt, class T>
+void iota(ForwardIt first, ForwardIt last, T value)
+{
+    while(first != last)
+    {
+        *first++ = value;
+        ++value;
+    }
+}
+
+} // namespace detail
+
+//-----------------------
+// Triangulation methods
+//-----------------------
+template <typename T, typename TNearPointLocator>
+template <
+    typename TVertexIter,
+    typename TGetVertexCoordX,
+    typename TGetVertexCoordY>
+void Triangulation<T, TNearPointLocator>::insertVertices(
+    const TVertexIter first,
+    const TVertexIter last,
+    TGetVertexCoordX getX,
+    TGetVertexCoordY getY)
+{
+    if(isFinalized())
+    {
+        throw std::runtime_error(
+            "Triangulation was finalized with 'erase...' method. Inserting new "
+            "vertices is not possible");
+    }
+
+    const bool isFirstTime = vertices.empty();
+    const T max = std::numeric_limits<T>::max();
+    Box2d<T> box = {{max, max}, {-max, -max}};
+    if(vertices.empty()) // called first time
+    {
+        box = envelopBox<T>(first, last, getX, getY);
+        addSuperTriangle(box);
+    }
+    tryInitNearestPointLocator();
+
+    const VertInd nExistingVerts = static_cast<VertInd>(vertices.size());
+    const VertInd nVerts =
+        static_cast<VertInd>(nExistingVerts + std::distance(first, last));
+    // optimization, try to pre-allocate tris
+    triangles.reserve(triangles.size() + 2 * nVerts);
+    vertices.reserve(nVerts);
+    m_vertTris.reserve(nVerts);
+    for(TVertexIter it = first; it != last; ++it)
+        addNewVertex(V2d<T>::make(getX(*it), getY(*it)), noNeighbor);
+
+    switch(m_vertexInsertionOrder)
+    {
+    case VertexInsertionOrder::AsProvided:
+        insertVertices_AsProvided(nExistingVerts);
+        break;
+    case VertexInsertionOrder::Auto:
+        isFirstTime ? insertVertices_KDTreeBFS(nExistingVerts, box.min, box.max)
+                    : insertVertices_Randomized(nExistingVerts);
+        break;
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+template <
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd>
+void Triangulation<T, TNearPointLocator>::insertEdges(
+    TEdgeIter first,
+    const TEdgeIter last,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd)
+{
+    // state shared between different runs for performance gains
+    std::vector<TriangulatePseudopolygonTask> tppIterations;
+    EdgeVec remaining;
+    if(isFinalized())
+    {
+        throw std::runtime_error(
+            "Triangulation was finalized with 'erase...' method. Inserting new "
+            "edges is not possible");
+    }
+    for(; first != last; ++first)
+    {
+        // +3 to account for super-triangle vertices
+        const Edge edge(
+            VertInd(getStart(*first) + m_nTargetVerts),
+            VertInd(getEnd(*first) + m_nTargetVerts));
+        insertEdge(edge, edge, remaining, tppIterations);
+    }
+    eraseDummies();
+}
+
+template <typename T, typename TNearPointLocator>
+template <
+    typename TEdgeIter,
+    typename TGetEdgeVertexStart,
+    typename TGetEdgeVertexEnd>
+void Triangulation<T, TNearPointLocator>::conformToEdges(
+    TEdgeIter first,
+    const TEdgeIter last,
+    TGetEdgeVertexStart getStart,
+    TGetEdgeVertexEnd getEnd)
+{
+    if(isFinalized())
+    {
+        throw std::runtime_error(
+            "Triangulation was finalized with 'erase...' method. Conforming to "
+            "new edges is not possible");
+    }
+    tryInitNearestPointLocator();
+    // state shared between different runs for performance gains
+    std::vector<ConformToEdgeTask> remaining;
+    for(; first != last; ++first)
+    {
+        // +3 to account for super-triangle vertices
+        const Edge e(
+            VertInd(getStart(*first) + m_nTargetVerts),
+            VertInd(getEnd(*first) + m_nTargetVerts));
+        conformToEdge(e, EdgeVec(1, e), 0, remaining);
+    }
+    eraseDummies();
+}
+
+} // namespace CDT
+
+#ifndef CDT_USE_AS_COMPILED_LIBRARY
+#include "Triangulation.hpp"
+#endif
+
+#endif // header-guard
diff --git a/cpp/Triangulation.hpp b/cpp/Triangulation.hpp
--- a/cpp/Triangulation.hpp
+++ b/cpp/Triangulation.hpp
@@ -1,1558 +1,2041 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/**
- * @file
- * Triangulation class - implementation
- */
-
-#include "Triangulation.h"
-
-#include <algorithm>
-#include <cassert>
-#include <deque>
-#include <stdexcept>
-
-namespace CDT
-{
-
-typedef std::deque<TriInd> TriDeque;
-
-namespace detail
-{
-
-/// Needed for c++03 compatibility (no uniform initialization available)
-template <typename T>
-array<T, 3> arr3(const T& v0, const T& v1, const T& v2)
-{
-    const array<T, 3> out = {v0, v1, v2};
-    return out;
-}
-
-namespace defaults
-{
-
-const std::size_t nTargetVerts = 0;
-const SuperGeometryType::Enum superGeomType = SuperGeometryType::SuperTriangle;
-const VertexInsertionOrder::Enum vertexInsertionOrder =
-    VertexInsertionOrder::Randomized;
-const IntersectingConstraintEdges::Enum intersectingEdgesStrategy =
-    IntersectingConstraintEdges::Ignore;
-const float minDistToConstraintEdge(0);
-
-} // namespace defaults
-
-} // namespace detail
-
-template <typename T, typename TNearPointLocator>
-Triangulation<T, TNearPointLocator>::Triangulation()
-    : m_nTargetVerts(detail::defaults::nTargetVerts)
-    , m_superGeomType(detail::defaults::superGeomType)
-    , m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder)
-    , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
-    , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
-{}
-
-template <typename T, typename TNearPointLocator>
-Triangulation<T, TNearPointLocator>::Triangulation(
-    const VertexInsertionOrder::Enum vertexInsertionOrder)
-    : m_nTargetVerts(detail::defaults::nTargetVerts)
-    , m_superGeomType(detail::defaults::superGeomType)
-    , m_vertexInsertionOrder(vertexInsertionOrder)
-    , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
-    , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
-{}
-
-template <typename T, typename TNearPointLocator>
-Triangulation<T, TNearPointLocator>::Triangulation(
-    const VertexInsertionOrder::Enum vertexInsertionOrder,
-    const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
-    const T minDistToConstraintEdge)
-    : m_nTargetVerts(detail::defaults::nTargetVerts)
-    , m_superGeomType(detail::defaults::superGeomType)
-    , m_vertexInsertionOrder(vertexInsertionOrder)
-    , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
-    , m_minDistToConstraintEdge(minDistToConstraintEdge)
-{}
-
-template <typename T, typename TNearPointLocator>
-Triangulation<T, TNearPointLocator>::Triangulation(
-    const VertexInsertionOrder::Enum vertexInsertionOrder,
-    const TNearPointLocator& nearPtLocator,
-    const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
-    const T minDistToConstraintEdge)
-    : m_nTargetVerts(detail::defaults::nTargetVerts)
-    , m_nearPtLocator(nearPtLocator)
-    , m_superGeomType(detail::defaults::superGeomType)
-    , m_vertexInsertionOrder(vertexInsertionOrder)
-    , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
-    , m_minDistToConstraintEdge(minDistToConstraintEdge)
-{}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::changeNeighbor(
-    const TriInd iT,
-    const VertInd iVedge1,
-    const VertInd iVedge2,
-    const TriInd newNeighbor)
-{
-    Triangle& t = triangles[iT];
-    t.neighbors[opposedTriangleInd(t, iVedge1, iVedge2)] = newNeighbor;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::eraseDummies()
-{
-    if(m_dummyTris.empty())
-        return;
-    const TriIndUSet dummySet(m_dummyTris.begin(), m_dummyTris.end());
-    TriIndUMap triIndMap;
-    triIndMap[noNeighbor] = noNeighbor;
-    for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
-    {
-        if(dummySet.count(iT))
-            continue;
-        triIndMap[iT] = iTnew;
-        triangles[iTnew] = triangles[iT];
-        iTnew++;
-    }
-    triangles.erase(triangles.end() - dummySet.size(), triangles.end());
-
-    // remap adjacent triangle indices for vertices
-    typedef typename VerticesTriangles::iterator VertTrisIt;
-    for(VertTrisIt vTris = vertTris.begin(); vTris != vertTris.end(); ++vTris)
-    {
-        for(TriIndVec::iterator iT = vTris->begin(); iT != vTris->end(); ++iT)
-            *iT = triIndMap[*iT];
-    }
-    // remap neighbor indices for triangles
-    for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); ++t)
-    {
-        NeighborsArr3& nn = t->neighbors;
-        for(NeighborsArr3::iterator iN = nn.begin(); iN != nn.end(); ++iN)
-            *iN = triIndMap[*iN];
-    }
-    // clear dummy triangles
-    m_dummyTris = std::vector<TriInd>();
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::eraseSuperTriangle()
-{
-    if(m_superGeomType != SuperGeometryType::SuperTriangle)
-        return;
-    // find triangles adjacent to super-triangle's vertices
-    TriIndUSet toErase;
-    toErase.reserve(
-        vertTris[0].size() + vertTris[1].size() + vertTris[2].size());
-    for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT)
-    {
-        Triangle& t = triangles[iT];
-        if(t.vertices[0] < 3 || t.vertices[1] < 3 || t.vertices[2] < 3)
-            toErase.insert(iT);
-    }
-    finalizeTriangulation(toErase);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::eraseOuterTriangles()
-{
-    // make dummy triangles adjacent to super-triangle's vertices
-    const std::stack<TriInd> seed(std::deque<TriInd>(1, vertTris[0].front()));
-    const TriIndUSet toErase = growToBoundary(seed);
-    finalizeTriangulation(toErase);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()
-{
-    const std::vector<LayerDepth> triDepths = calculateTriangleDepths();
-    TriIndUSet toErase;
-    toErase.reserve(triangles.size());
-    for(std::size_t iT = 0; iT != triangles.size(); ++iT)
-    {
-        if(triDepths[iT] % 2 == 0)
-            toErase.insert(static_cast<TriInd>(iT));
-    }
-    finalizeTriangulation(toErase);
-}
-
-/// Remap removing super-triangle: subtract 3 from vertices
-inline Edge RemapNoSuperTriangle(const Edge& e)
-{
-    return Edge(e.v1() - 3, e.v2() - 3);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::removeTriangles(
-    const TriIndUSet& removedTriangles)
-{
-    if(removedTriangles.empty())
-        return;
-    // remove triangles and calculate triangle index mapping
-    TriIndUMap triIndMap;
-    for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
-    {
-        if(removedTriangles.count(iT))
-            continue;
-        triIndMap[iT] = iTnew;
-        triangles[iTnew] = triangles[iT];
-        iTnew++;
-    }
-    triangles.erase(triangles.end() - removedTriangles.size(), triangles.end());
-    // adjust triangles' neighbors
-    vertTris = VerticesTriangles();
-    for(TriInd iT = 0; iT < triangles.size(); ++iT)
-    {
-        Triangle& t = triangles[iT];
-        // update neighbors to account for removed triangles
-        NeighborsArr3& nn = t.neighbors;
-        for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n)
-        {
-            if(removedTriangles.count(*n))
-            {
-                *n = noNeighbor;
-            }
-            else if(*n != noNeighbor)
-            {
-                *n = triIndMap[*n];
-            }
-        }
-    }
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::finalizeTriangulation(
-    const TriIndUSet& removedTriangles)
-{
-    eraseDummies();
-    // remove super-triangle
-    if(m_superGeomType == SuperGeometryType::SuperTriangle)
-    {
-        vertices.erase(vertices.begin(), vertices.begin() + 3);
-        if(removedTriangles.empty())
-            vertTris.erase(vertTris.begin(), vertTris.begin() + 3);
-        // Edge re-mapping
-        { // fixed edges
-            EdgeUSet updatedFixedEdges;
-            typedef CDT::EdgeUSet::const_iterator It;
-            for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e)
-            {
-                updatedFixedEdges.insert(RemapNoSuperTriangle(*e));
-            }
-            fixedEdges = updatedFixedEdges;
-        }
-        { // overlap count
-            unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount;
-            typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator
-                It;
-            for(It it = overlapCount.begin(); it != overlapCount.end(); ++it)
-            {
-                updatedOverlapCount.insert(std::make_pair(
-                    RemapNoSuperTriangle(it->first), it->second));
-            }
-            overlapCount = updatedOverlapCount;
-        }
-        { // split edges mapping
-            unordered_map<Edge, EdgeVec> updatedPieceToOriginals;
-            typedef unordered_map<Edge, EdgeVec>::const_iterator It;
-            for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end();
-                ++it)
-            {
-                EdgeVec ee = it->second;
-                for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end();
-                    ++eeIt)
-                {
-                    *eeIt = RemapNoSuperTriangle(*eeIt);
-                }
-                updatedPieceToOriginals.insert(
-                    std::make_pair(RemapNoSuperTriangle(it->first), ee));
-            }
-            pieceToOriginals = updatedPieceToOriginals;
-        }
-    }
-    // remove other triangles
-    removeTriangles(removedTriangles);
-    // adjust triangle vertices: account for removed super-triangle
-    if(m_superGeomType == SuperGeometryType::SuperTriangle)
-    {
-        for(TriangleVec::iterator t = triangles.begin(); t != triangles.end();
-            ++t)
-        {
-            VerticesArr3& vv = t->vertices;
-            for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v)
-            {
-                *v -= 3;
-            }
-        }
-    }
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::initializedWithCustomSuperGeometry()
-{
-    m_nearPtLocator.initialize(vertices);
-    m_nTargetVerts = vertices.size();
-    m_superGeomType = SuperGeometryType::Custom;
-}
-
-template <typename T, typename TNearPointLocator>
-TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary(
-    std::stack<TriInd> seeds) const
-{
-    TriIndUSet traversed;
-    while(!seeds.empty())
-    {
-        const TriInd iT = seeds.top();
-        seeds.pop();
-        traversed.insert(iT);
-        const Triangle& t = triangles[iT];
-        for(Index i(0); i < Index(3); ++i)
-        {
-            const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
-            if(fixedEdges.count(opEdge))
-                continue;
-            const TriInd iN = t.neighbors[opoNbr(i)];
-            if(iN != noNeighbor && traversed.count(iN) == 0)
-                seeds.push(iN);
-        }
-    }
-    return traversed;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::makeDummy(const TriInd iT)
-{
-    const Triangle& t = triangles[iT];
-
-    typedef VerticesArr3::const_iterator VCit;
-    for(VCit iV = t.vertices.begin(); iV != t.vertices.end(); ++iV)
-        removeAdjacentTriangle(*iV, iT);
-
-    typedef NeighborsArr3::const_iterator NCit;
-    for(NCit iTn = t.neighbors.begin(); iTn != t.neighbors.end(); ++iTn)
-        changeNeighbor(*iTn, iT, noNeighbor);
-
-    m_dummyTris.push_back(iT);
-}
-
-template <typename T, typename TNearPointLocator>
-TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t)
-{
-    if(m_dummyTris.empty())
-    {
-        triangles.push_back(t);
-        return TriInd(triangles.size() - 1);
-    }
-    const TriInd nxtDummy = m_dummyTris.back();
-    m_dummyTris.pop_back();
-    triangles[nxtDummy] = t;
-    return nxtDummy;
-}
-
-template <typename T, typename TNearPointLocator>
-TriInd Triangulation<T, TNearPointLocator>::addTriangle()
-{
-    if(m_dummyTris.empty())
-    {
-        const Triangle dummy = {
-            {noVertex, noVertex, noVertex},
-            {noNeighbor, noNeighbor, noNeighbor}};
-        triangles.push_back(dummy);
-        return TriInd(triangles.size() - 1);
-    }
-    const TriInd nxtDummy = m_dummyTris.back();
-    m_dummyTris.pop_back();
-    return nxtDummy;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::insertEdges(
-    const std::vector<Edge>& edges)
-{
-    insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::conformToEdges(
-    const std::vector<Edge>& edges)
-{
-    conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge)
-{
-    if(!fixedEdges.insert(edge).second)
-    {
-        ++overlapCount[edge]; // if edge is already fixed increment the counter
-    }
-}
-
-namespace detail
-{
-
-// add element to 'to' if not already in 'to'
-template <typename T, typename Allocator1>
-void insert_unique(std::vector<T, Allocator1>& to, const T& elem)
-{
-    if(std::find(to.begin(), to.end(), elem) == to.end())
-    {
-        to.push_back(elem);
-    }
-}
-
-// add elements of 'from' that are not present in 'to' to 'to'
-template <typename T, typename Allocator1, typename Allocator2>
-void insert_unique(
-    std::vector<T, Allocator1>& to,
-    const std::vector<T, Allocator2>& from)
-{
-    typedef typename std::vector<T, Allocator2>::const_iterator Cit;
-    to.reserve(to.size() + from.size());
-    for(Cit cit = from.begin(); cit != from.end(); ++cit)
-    {
-        insert_unique(to, *cit);
-    }
-}
-
-} // namespace detail
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::fixEdge(
-    const Edge& edge,
-    const Edge& originalEdge)
-{
-    fixEdge(edge);
-    if(edge != originalEdge)
-        detail::insert_unique(pieceToOriginals[edge], originalEdge);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::fixEdge(
-    const Edge& edge,
-    const BoundaryOverlapCount overlaps)
-{
-    fixedEdges.insert(edge);
-    overlapCount[edge] = overlaps; // override overlap counter
-}
-
-namespace detail
-{
-
-template <typename T>
-T lerp(const T& a, const T& b, const T t)
-{
-    return (T(1) - t) * a + t * b;
-}
-
-// Precondition: ab and cd intersect normally
-template <typename T>
-V2d<T> intersectionPosition(
-    const V2d<T>& a,
-    const V2d<T>& b,
-    const V2d<T>& c,
-    const V2d<T>& d)
-{
-    using namespace predicates::adaptive;
-    // interpolate point on the shorter segment
-    if(distanceSquared(a, b) < distanceSquared(c, d))
-    {
-        const T a_cd = orient2d(c.x, c.y, d.x, d.y, a.x, a.y);
-        const T b_cd = orient2d(c.x, c.y, d.x, d.y, b.x, b.y);
-        const T t = a_cd / (a_cd - b_cd);
-        return V2d<T>::make(lerp(a.x, b.x, t), lerp(a.y, b.y, t));
-    }
-    else
-    {
-        const T c_ab = orient2d(a.x, a.y, b.x, b.y, c.x, c.y);
-        const T d_ab = orient2d(a.x, a.y, b.x, b.y, d.x, d.y);
-        const T t = c_ab / (c_ab - d_ab);
-        return V2d<T>::make(lerp(c.x, d.x, t), lerp(c.y, d.y, t));
-    }
-}
-
-} // namespace detail
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::insertEdge(
-    const Edge edge,
-    const Edge originalEdge)
-{
-    const VertInd iA = edge.v1();
-    VertInd iB = edge.v2();
-    if(iA == iB) // edge connects a vertex to itself
-        return;
-    const TriIndVec& aTris = vertTris[iA];
-    const TriIndVec& bTris = vertTris[iB];
-    const V2d<T>& a = vertices[iA];
-    const V2d<T>& b = vertices[iB];
-    if(verticesShareEdge(aTris, bTris))
-    {
-        fixEdge(edge, originalEdge);
-        return;
-    }
-
-    const T distanceTolerance =
-        m_minDistToConstraintEdge == T(0)
-            ? T(0)
-            : m_minDistToConstraintEdge * distance(a, b);
-
-    TriInd iT;
-    VertInd iVleft, iVright;
-    tie(iT, iVleft, iVright) =
-        intersectedTriangle(iA, aTris, a, b, distanceTolerance);
-    // if one of the triangle vertices is on the edge, move edge start
-    if(iT == noNeighbor)
-    {
-        const Edge edgePart(iA, iVleft);
-        fixEdge(edgePart, originalEdge);
-        return insertEdge(Edge(iVleft, iB), originalEdge);
-    }
-    std::vector<TriInd> intersected(1, iT);
-    std::vector<VertInd> ptsLeft(1, iVleft);
-    std::vector<VertInd> ptsRight(1, iVright);
-    VertInd iV = iA;
-    Triangle t = triangles[iT];
-    while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==
-          t.vertices.end())
-    {
-        const TriInd iTopo = opposedTriangle(t, iV);
-        const Triangle& tOpo = triangles[iTopo];
-        const VertInd iVopo = opposedVertex(tOpo, iT);
-        const V2d<T> vOpo = vertices[iVopo];
-
-        // Resolve intersection between two constraint edges if needed
-        if(m_intersectingEdgesStrategy ==
-               IntersectingConstraintEdges::Resolve &&
-           fixedEdges.count(Edge(iVleft, iVright)))
-        {
-            const VertInd iNewVert = static_cast<VertInd>(vertices.size());
-
-            // split constraint edge that already exists in triangulation
-            const Edge splitEdge(iVleft, iVright);
-            const Edge half1(iVleft, iNewVert);
-            const Edge half2(iNewVert, iVright);
-            const BoundaryOverlapCount overlaps = overlapCount[splitEdge];
-            // remove the edge that will be split
-            fixedEdges.erase(splitEdge);
-            overlapCount.erase(splitEdge);
-            // add split edge's halves
-            fixEdge(half1, overlaps);
-            fixEdge(half2, overlaps);
-            // maintain piece-to-original mapping
-            EdgeVec newOriginals(1, splitEdge);
-            const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
-                pieceToOriginals.find(splitEdge);
-            if(originalsIt != pieceToOriginals.end())
-            { // edge being split was split before: pass-through originals
-                newOriginals = originalsIt->second;
-                pieceToOriginals.erase(originalsIt);
-            }
-            detail::insert_unique(pieceToOriginals[half1], newOriginals);
-            detail::insert_unique(pieceToOriginals[half2], newOriginals);
-
-            // add a new point at the intersection of two constraint edges
-            const V2d<T> newV = detail::intersectionPosition(
-                vertices[iA],
-                vertices[iB],
-                vertices[iVleft],
-                vertices[iVright]);
-            addNewVertex(newV, TriIndVec());
-            std::stack<TriInd> triStack =
-                insertPointOnEdge(iNewVert, iT, iTopo);
-            ensureDelaunayByEdgeFlips(newV, iNewVert, triStack);
-            // TODO: is it's possible to re-use pseudo-polygons
-            //  for inserting [iA, iNewVert] edge half?
-            insertEdge(Edge(iA, iNewVert), originalEdge);
-            insertEdge(Edge(iNewVert, iB), originalEdge);
-            return;
-        }
-
-        intersected.push_back(iTopo);
-        iT = iTopo;
-        t = triangles[iT];
-
-        const PtLineLocation::Enum loc =
-            locatePointLine(vOpo, a, b, distanceTolerance);
-        if(loc == PtLineLocation::Left)
-        {
-            ptsLeft.push_back(iVopo);
-            iV = iVleft;
-            iVleft = iVopo;
-        }
-        else if(loc == PtLineLocation::Right)
-        {
-            ptsRight.push_back(iVopo);
-            iV = iVright;
-            iVright = iVopo;
-        }
-        else // encountered point on the edge
-            iB = iVopo;
-    }
-    // Remove intersected triangles
-    typedef std::vector<TriInd>::const_iterator TriIndCit;
-    for(TriIndCit it = intersected.begin(); it != intersected.end(); ++it)
-        makeDummy(*it);
-    // Triangulate pseudo-polygons on both sides
-    const TriInd iTleft =
-        triangulatePseudopolygon(iA, iB, ptsLeft.begin(), ptsLeft.end());
-    std::reverse(ptsRight.begin(), ptsRight.end());
-    const TriInd iTright =
-        triangulatePseudopolygon(iB, iA, ptsRight.begin(), ptsRight.end());
-    changeNeighbor(iTleft, noNeighbor, iTright);
-    changeNeighbor(iTright, noNeighbor, iTleft);
-
-    if(iB != edge.v2()) // encountered point on the edge
-    {
-        // fix edge part
-        const Edge edgePart(iA, iB);
-        fixEdge(edgePart, originalEdge);
-        return insertEdge(Edge(iB, edge.v2()), originalEdge);
-    }
-    else
-    {
-        fixEdge(edge, originalEdge);
-    }
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::conformToEdge(
-    const Edge edge,
-    EdgeVec originalEdges,
-    const BoundaryOverlapCount overlaps)
-{
-    const VertInd iA = edge.v1();
-    VertInd iB = edge.v2();
-    if(iA == iB) // edge connects a vertex to itself
-        return;
-    const TriIndVec& aTris = vertTris[iA];
-    const TriIndVec& bTris = vertTris[iB];
-    const V2d<T>& a = vertices[iA];
-    const V2d<T>& b = vertices[iB];
-    if(verticesShareEdge(aTris, bTris))
-    {
-        overlaps > 0 ? fixEdge(edge, overlaps) : fixEdge(edge);
-        // avoid marking edge as a part of itself
-        if(!originalEdges.empty() && edge != originalEdges.front())
-        {
-            detail::insert_unique(pieceToOriginals[edge], originalEdges);
-        }
-        return;
-    }
-
-    const T distanceTolerance =
-        m_minDistToConstraintEdge == T(0)
-            ? T(0)
-            : m_minDistToConstraintEdge * distance(a, b);
-    TriInd iT;
-    VertInd iVleft, iVright;
-    tie(iT, iVleft, iVright) =
-        intersectedTriangle(iA, aTris, a, b, distanceTolerance);
-    // if one of the triangle vertices is on the edge, move edge start
-    if(iT == noNeighbor)
-    {
-        const Edge edgePart(iA, iVleft);
-        overlaps > 0 ? fixEdge(edgePart, overlaps) : fixEdge(edgePart);
-        detail::insert_unique(pieceToOriginals[edgePart], originalEdges);
-        return conformToEdge(Edge(iVleft, iB), originalEdges, overlaps);
-    }
-
-    VertInd iV = iA;
-    Triangle t = triangles[iT];
-    while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==
-          t.vertices.end())
-    {
-        const TriInd iTopo = opposedTriangle(t, iV);
-        const Triangle& tOpo = triangles[iTopo];
-        const VertInd iVopo = opposedVertex(tOpo, iT);
-        const V2d<T> vOpo = vertices[iVopo];
-
-        // Resolve intersection between two constraint edges if needed
-        if(m_intersectingEdgesStrategy ==
-               IntersectingConstraintEdges::Resolve &&
-           fixedEdges.count(Edge(iVleft, iVright)))
-        {
-            const VertInd iNewVert = static_cast<VertInd>(vertices.size());
-
-            // split constraint edge that already exists in triangulation
-            const Edge splitEdge(iVleft, iVright);
-            const Edge half1(iVleft, iNewVert);
-            const Edge half2(iNewVert, iVright);
-            const BoundaryOverlapCount overlaps = overlapCount[splitEdge];
-            // remove the edge that will be split
-            fixedEdges.erase(splitEdge);
-            overlapCount.erase(splitEdge);
-            // add split edge's halves
-            fixEdge(half1, overlaps);
-            fixEdge(half2, overlaps);
-            // maintain piece-to-original mapping
-            EdgeVec newOriginals(1, splitEdge);
-            const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
-                pieceToOriginals.find(splitEdge);
-            if(originalsIt != pieceToOriginals.end())
-            { // edge being split was split before: pass-through originals
-                newOriginals = originalsIt->second;
-                pieceToOriginals.erase(originalsIt);
-            }
-            detail::insert_unique(pieceToOriginals[half1], newOriginals);
-            detail::insert_unique(pieceToOriginals[half2], newOriginals);
-
-            // add a new point at the intersection of two constraint edges
-            const V2d<T> newV = detail::intersectionPosition(
-                vertices[iA],
-                vertices[iB],
-                vertices[iVleft],
-                vertices[iVright]);
-            addNewVertex(newV, TriIndVec());
-            std::stack<TriInd> triStack =
-                insertPointOnEdge(iNewVert, iT, iTopo);
-            ensureDelaunayByEdgeFlips(newV, iNewVert, triStack);
-            conformToEdge(Edge(iA, iNewVert), originalEdges, overlaps);
-            conformToEdge(Edge(iNewVert, iB), originalEdges, overlaps);
-            return;
-        }
-
-        iT = iTopo;
-        t = triangles[iT];
-
-        const PtLineLocation::Enum loc =
-            locatePointLine(vOpo, a, b, distanceTolerance);
-        if(loc == PtLineLocation::Left)
-        {
-            iV = iVleft;
-            iVleft = iVopo;
-        }
-        else if(loc == PtLineLocation::Right)
-        {
-            iV = iVright;
-            iVright = iVopo;
-        }
-        else // encountered point on the edge
-            iB = iVopo;
-    }
-    /**/
-
-    // add mid-point to triangulation
-    const VertInd iMid = static_cast<VertInd>(vertices.size());
-    const V2d<T>& start = vertices[iA];
-    const V2d<T>& end = vertices[iB];
-    addNewVertex(
-        V2d<T>::make((start.x + end.x) / T(2), (start.y + end.y) / T(2)),
-        TriIndVec());
-    const std::vector<Edge> flippedFixedEdges =
-        insertVertex_FlipFixedEdges(iMid);
-
-    conformToEdge(Edge(iA, iMid), originalEdges, overlaps);
-    conformToEdge(Edge(iMid, iB), originalEdges, overlaps);
-    // re-introduce fixed edges that were flipped
-    // and make sure overlap count is preserved
-    for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin();
-        it != flippedFixedEdges.end();
-        ++it)
-    {
-        fixedEdges.erase(*it);
-
-        BoundaryOverlapCount prevOverlaps = 0;
-        const unordered_map<Edge, BoundaryOverlapCount>::const_iterator
-            overlapsIt = overlapCount.find(*it);
-        if(overlapsIt != overlapCount.end())
-        {
-            prevOverlaps = overlapsIt->second;
-            overlapCount.erase(overlapsIt);
-        }
-        // override overlapping boundaries count when re-inserting an edge
-        EdgeVec prevOriginals(1, *it);
-        const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
-            pieceToOriginals.find(*it);
-        if(originalsIt != pieceToOriginals.end())
-        {
-            prevOriginals = originalsIt->second;
-        }
-        conformToEdge(*it, prevOriginals, prevOverlaps);
-    }
-    if(iB != edge.v2())
-        conformToEdge(Edge(iB, edge.v2()), originalEdges, overlaps);
-}
-
-/*!
- * Returns:
- *  - intersected triangle index
- *  - index of point on the left of the line
- *  - index of point on the right of the line
- * If left point is right on the line: no triangle is intersected:
- *  - triangle index is no-neighbor (invalid)
- *  - index of point on the line
- *  - index of point on the right of the line
- */
-template <typename T, typename TNearPointLocator>
-tuple<TriInd, VertInd, VertInd>
-Triangulation<T, TNearPointLocator>::intersectedTriangle(
-    const VertInd iA,
-    const std::vector<TriInd>& candidates,
-    const V2d<T>& a,
-    const V2d<T>& b,
-    const T orientationTolerance) const
-{
-    typedef std::vector<TriInd>::const_iterator TriIndCit;
-    for(TriIndCit it = candidates.begin(); it != candidates.end(); ++it)
-    {
-        const TriInd iT = *it;
-        const Triangle t = triangles[iT];
-        const Index i = vertexInd(t, iA);
-        const VertInd iP2 = t.vertices[ccw(i)];
-        const T orientP2 = orient2D(vertices[iP2], a, b);
-        const PtLineLocation::Enum locP2 = classifyOrientation(orientP2);
-        if(locP2 == PtLineLocation::Right)
-        {
-            const VertInd iP1 = t.vertices[cw(i)];
-            const T orientP1 = orient2D(vertices[iP1], a, b);
-            const PtLineLocation::Enum locP1 = classifyOrientation(orientP1);
-            if(locP1 == PtLineLocation::OnLine)
-            {
-                return make_tuple(noNeighbor, iP1, iP1);
-            }
-            if(locP1 == PtLineLocation::Left)
-            {
-                if(orientationTolerance)
-                {
-                    T closestOrient;
-                    VertInd iClosestP;
-                    if(std::abs(orientP1) <= std::abs(orientP2))
-                    {
-                        closestOrient = orientP1;
-                        iClosestP = iP1;
-                    }
-                    else
-                    {
-                        closestOrient = orientP2;
-                        iClosestP = iP2;
-                    }
-                    if(classifyOrientation(
-                           closestOrient, orientationTolerance) ==
-                       PtLineLocation::OnLine)
-                    {
-                        return make_tuple(noNeighbor, iClosestP, iClosestP);
-                    }
-                }
-                return make_tuple(iT, iP1, iP2);
-            }
-        }
-    }
-    throw std::runtime_error("Could not find vertex triangle intersected by "
-                             "edge. Note: can be caused by duplicate points.");
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box)
-{
-    m_nTargetVerts = 3;
-    m_superGeomType = SuperGeometryType::SuperTriangle;
-
-    const V2d<T> center = {
-        (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2)};
-    const T w = box.max.x - box.min.x;
-    const T h = box.max.y - box.min.y;
-    T r = std::sqrt(w * w + h * h) / T(2); // incircle radius
-    r *= T(1.1);
-    const T R = T(2) * r;                        // excircle radius
-    const T shiftX = R * std::sqrt(T(3)) / T(2); // R * cos(30 deg)
-    const V2d<T> posV1 = {center.x - shiftX, center.y - r};
-    const V2d<T> posV2 = {center.x + shiftX, center.y - r};
-    const V2d<T> posV3 = {center.x, center.y + R};
-    addNewVertex(posV1, TriIndVec(1, TriInd(0)));
-    addNewVertex(posV2, TriIndVec(1, TriInd(0)));
-    addNewVertex(posV3, TriIndVec(1, TriInd(0)));
-    const Triangle superTri = {
-        {VertInd(0), VertInd(1), VertInd(2)},
-        {noNeighbor, noNeighbor, noNeighbor}};
-    addTriangle(superTri);
-    m_nearPtLocator.initialize(vertices);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::addNewVertex(
-    const V2d<T>& pos,
-    const TriIndVec& tris)
-{
-    vertices.push_back(pos);
-    vertTris.push_back(tris);
-}
-
-template <typename T, typename TNearPointLocator>
-std::vector<Edge>
-Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges(
-    const VertInd iVert)
-{
-    std::vector<Edge> flippedFixedEdges;
-
-    const V2d<T>& v = vertices[iVert];
-    array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v);
-    std::stack<TriInd> triStack =
-        trisAt[1] == noNeighbor
-            ? insertPointInTriangle(iVert, trisAt[0])
-            : insertPointOnEdge(iVert, trisAt[0], trisAt[1]);
-    while(!triStack.empty())
-    {
-        const TriInd iT = triStack.top();
-        triStack.pop();
-
-        const Triangle& t = triangles[iT];
-        const TriInd iTopo = opposedTriangle(t, iVert);
-        if(iTopo == noNeighbor)
-            continue;
-
-        /*
-         *                       v3         original edge: (v1, v3)
-         *                      /|\   flip-candidate edge: (v,  v2)
-         *                    /  |  \
-         *                  /    |    \
-         *                /      |      \
-         * new vertex--> v       |       v2
-         *                \      |      /
-         *                  \    |    /
-         *                    \  |  /
-         *                      \|/
-         *                       v1
-         */
-        const Triangle& tOpo = triangles[iTopo];
-        const Index i = opposedVertexInd(tOpo, iT);
-        const VertInd iV2 = tOpo.vertices[i];
-        const VertInd iV1 = tOpo.vertices[cw(i)];
-        const VertInd iV3 = tOpo.vertices[ccw(i)];
-
-        if(isFlipNeeded(v, iVert, iV1, iV2, iV3))
-        {
-            // if flipped edge is fixed, remember it
-            const Edge flippedEdge(iV1, iV3);
-            if(fixedEdges.count(flippedEdge))
-                flippedFixedEdges.push_back(flippedEdge);
-
-            flipEdge(iT, iTopo);
-            triStack.push(iT);
-            triStack.push(iTopo);
-        }
-    }
-
-    m_nearPtLocator.addPoint(iVert, vertices);
-    return flippedFixedEdges;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert)
-{
-    const V2d<T>& v = vertices[iVert];
-    array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v);
-    std::stack<TriInd> triStack =
-        trisAt[1] == noNeighbor
-            ? insertPointInTriangle(iVert, trisAt[0])
-            : insertPointOnEdge(iVert, trisAt[0], trisAt[1]);
-    ensureDelaunayByEdgeFlips(v, iVert, triStack);
-    m_nearPtLocator.addPoint(iVert, vertices);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips(
-    const V2d<T>& v,
-    const VertInd iVert,
-    std::stack<TriInd>& triStack)
-{
-    while(!triStack.empty())
-    {
-        const TriInd iT = triStack.top();
-        triStack.pop();
-
-        const Triangle& t = triangles[iT];
-        const TriInd iTopo = opposedTriangle(t, iVert);
-        if(iTopo == noNeighbor)
-            continue;
-        if(isFlipNeeded(v, iT, iTopo, iVert))
-        {
-            flipEdge(iT, iTopo);
-            triStack.push(iT);
-            triStack.push(iTopo);
-        }
-    }
-}
-
-/*!
- * Handles super-triangle vertices.
- * Super-tri points are not infinitely far and influence the input points
- * Three cases are possible:
- *  1.  If one of the opposed vertices is super-tri: no flip needed
- *  2.  One of the shared vertices is super-tri:
- *      check if on point is same side of line formed by non-super-tri
- * vertices as the non-super-tri shared vertex
- *  3.  None of the vertices are super-tri: normal circumcircle test
- */
-/*
- *                       v3         original edge: (v1, v3)
- *                      /|\   flip-candidate edge: (v,  v2)
- *                    /  |  \
- *                  /    |    \
- *                /      |      \
- * new vertex--> v       |       v2
- *                \      |      /
- *                  \    |    /
- *                    \  |  /
- *                      \|/
- *                       v1
- */
-template <typename T, typename TNearPointLocator>
-bool Triangulation<T, TNearPointLocator>::isFlipNeeded(
-    const V2d<T>& v,
-    const VertInd iV,
-    const VertInd iV1,
-    const VertInd iV2,
-    const VertInd iV3) const
-{
-    const V2d<T>& v1 = vertices[iV1];
-    const V2d<T>& v2 = vertices[iV2];
-    const V2d<T>& v3 = vertices[iV3];
-    if(m_superGeomType == SuperGeometryType::SuperTriangle)
-    {
-        // If flip-candidate edge touches super-triangle in-circumference
-        // test has to be replaced with orient2d test against the line
-        // formed by two non-artificial vertices (that don't belong to
-        // super-triangle)
-        if(iV < 3) // flip-candidate edge touches super-triangle
-        {
-            // does original edge also touch super-triangle?
-            if(iV1 < 3)
-                return locatePointLine(v1, v2, v3) ==
-                       locatePointLine(v, v2, v3);
-            if(iV3 < 3)
-                return locatePointLine(v3, v1, v2) ==
-                       locatePointLine(v, v1, v2);
-            return false; // original edge does not touch super-triangle
-        }
-        if(iV2 < 3) // flip-candidate edge touches super-triangle
-        {
-            // does original edge also touch super-triangle?
-            if(iV1 < 3)
-                return locatePointLine(v1, v, v3) == locatePointLine(v2, v, v3);
-            if(iV3 < 3)
-                return locatePointLine(v3, v1, v) == locatePointLine(v2, v1, v);
-            return false; // original edge does not touch super-triangle
-        }
-        // flip-candidate edge does not touch super-triangle
-        if(iV1 < 3)
-            return locatePointLine(v1, v2, v3) == locatePointLine(v, v2, v3);
-        if(iV3 < 3)
-            return locatePointLine(v3, v1, v2) == locatePointLine(v, v1, v2);
-    }
-    return isInCircumcircle(v, v1, v2, v3);
-}
-
-template <typename T, typename TNearPointLocator>
-bool Triangulation<T, TNearPointLocator>::isFlipNeeded(
-    const V2d<T>& v,
-    const TriInd iT,
-    const TriInd iTopo,
-    const VertInd iV) const
-{
-    /*
-     *                       v3         original edge: (v1, v3)
-     *                      /|\   flip-candidate edge: (v,  v2)
-     *                    /  |  \
-     *                  /    |    \
-     *                /      |      \
-     * new vertex--> v       |       v2
-     *                \      |      /
-     *                  \    |    /
-     *                    \  |  /
-     *                      \|/
-     *                       v1
-     */
-    const Triangle& tOpo = triangles[iTopo];
-    const Index i = opposedVertexInd(tOpo, iT);
-    const VertInd iV2 = tOpo.vertices[i];
-    const VertInd iV1 = tOpo.vertices[cw(i)];
-    const VertInd iV3 = tOpo.vertices[ccw(i)];
-
-    // flip not needed if the original edge is fixed
-    if(fixedEdges.count(Edge(iV1, iV3)))
-        return false;
-
-    return isFlipNeeded(v, iV, iV1, iV2, iV3);
-}
-
-/* Insert point into triangle: split into 3 triangles:
- *  - create 2 new triangles
- *  - re-use old triangle for the 3rd
- *                      v3
- *                    / | \
- *                   /  |  \ <-- original triangle (t)
- *                  /   |   \
- *              n3 /    |    \ n2
- *                /newT2|newT1\
- *               /      v      \
- *              /    __/ \__    \
- *             /  __/       \__  \
- *            / _/      t'     \_ \
- *          v1 ___________________ v2
- *                     n1
- */
-template <typename T, typename TNearPointLocator>
-std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertPointInTriangle(
-    const VertInd v,
-    const TriInd iT)
-{
-    const TriInd iNewT1 = addTriangle();
-    const TriInd iNewT2 = addTriangle();
-
-    Triangle& t = triangles[iT];
-    const array<VertInd, 3> vv = t.vertices;
-    const array<TriInd, 3> nn = t.neighbors;
-    const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];
-    const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];
-    // make two new triangles and convert current triangle to 3rd new
-    // triangle
-    using detail::arr3;
-    triangles[iNewT1] = Triangle::make(arr3(v2, v3, v), arr3(n2, iNewT2, iT));
-    triangles[iNewT2] = Triangle::make(arr3(v3, v1, v), arr3(n3, iT, iNewT1));
-    t = Triangle::make(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));
-    // make and add a new vertex
-    addAdjacentTriangles(v, iT, iNewT1, iNewT2);
-    // adjust lists of adjacent triangles for v1, v2, v3
-    addAdjacentTriangle(v1, iNewT2);
-    addAdjacentTriangle(v2, iNewT1);
-    removeAdjacentTriangle(v3, iT);
-    addAdjacentTriangle(v3, iNewT1);
-    addAdjacentTriangle(v3, iNewT2);
-    // change triangle neighbor's neighbors to new triangles
-    changeNeighbor(n2, iT, iNewT1);
-    changeNeighbor(n3, iT, iNewT2);
-    // return newly added triangles
-    std::stack<TriInd> newTriangles;
-    newTriangles.push(iT);
-    newTriangles.push(iNewT1);
-    newTriangles.push(iNewT2);
-    return newTriangles;
-}
-
-/* Inserting a point on the edge between two triangles
- *    T1 (top)        v1
- *                   /|\
- *              n1 /  |  \ n4
- *               /    |    \
- *             /  T1' | Tnew1\
- *           v2-------v-------v4
- *             \ Tnew2| T2'  /
- *               \    |    /
- *              n2 \  |  / n3
- *                   \|/
- *   T2 (bottom)      v3
- */
-template <typename T, typename TNearPointLocator>
-std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertPointOnEdge(
-    const VertInd v,
-    const TriInd iT1,
-    const TriInd iT2)
-{
-    const TriInd iTnew1 = addTriangle();
-    const TriInd iTnew2 = addTriangle();
-
-    Triangle& t1 = triangles[iT1];
-    Triangle& t2 = triangles[iT2];
-    Index i = opposedVertexInd(t1, iT2);
-    const VertInd v1 = t1.vertices[i];
-    const VertInd v2 = t1.vertices[ccw(i)];
-    const TriInd n1 = t1.neighbors[i];
-    const TriInd n4 = t1.neighbors[cw(i)];
-    i = opposedVertexInd(t2, iT1);
-    const VertInd v3 = t2.vertices[i];
-    const VertInd v4 = t2.vertices[ccw(i)];
-    const TriInd n3 = t2.neighbors[i];
-    const TriInd n2 = t2.neighbors[cw(i)];
-    // add new triangles and change existing ones
-    using detail::arr3;
-    t1 = Triangle::make(arr3(v1, v2, v), arr3(n1, iTnew2, iTnew1));
-    t2 = Triangle::make(arr3(v3, v4, v), arr3(n3, iTnew1, iTnew2));
-    triangles[iTnew1] = Triangle::make(arr3(v1, v, v4), arr3(iT1, iT2, n4));
-    triangles[iTnew2] = Triangle::make(arr3(v3, v, v2), arr3(iT2, iT1, n2));
-    // make and add new vertex
-    addAdjacentTriangles(v, iT1, iTnew2, iT2, iTnew1);
-    // adjust neighboring triangles and vertices
-    changeNeighbor(n4, iT1, iTnew1);
-    changeNeighbor(n2, iT2, iTnew2);
-    addAdjacentTriangle(v1, iTnew1);
-    addAdjacentTriangle(v3, iTnew2);
-    removeAdjacentTriangle(v2, iT2);
-    addAdjacentTriangle(v2, iTnew2);
-    removeAdjacentTriangle(v4, iT1);
-    addAdjacentTriangle(v4, iTnew1);
-    // return newly added triangles
-    std::stack<TriInd> newTriangles;
-    newTriangles.push(iT1);
-    newTriangles.push(iTnew2);
-    newTriangles.push(iT2);
-    newTriangles.push(iTnew1);
-    return newTriangles;
-}
-
-template <typename T, typename TNearPointLocator>
-array<TriInd, 2>
-Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const
-{
-    array<TriInd, 2> out = {noNeighbor, noNeighbor};
-    for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)
-    {
-        const Triangle& t = triangles[i];
-        const V2d<T>& v1 = vertices[t.vertices[0]];
-        const V2d<T>& v2 = vertices[t.vertices[1]];
-        const V2d<T>& v3 = vertices[t.vertices[2]];
-        const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
-        if(loc == PtTriLocation::Outside)
-            continue;
-        out[0] = i;
-        if(isOnEdge(loc))
-            out[1] = t.neighbors[edgeNeighbor(loc)];
-        return out;
-    }
-    throw std::runtime_error("No triangle was found at position");
-}
-
-template <typename T, typename TNearPointLocator>
-TriInd Triangulation<T, TNearPointLocator>::walkTriangles(
-    const VertInd startVertex,
-    const V2d<T>& pos) const
-{
-    // begin walk in search of triangle at pos
-    TriInd currTri = vertTris[startVertex][0];
-#ifdef CDT_USE_BOOST
-    TriIndFlatUSet visited;
-#else
-    TriIndUSet visited;
-#endif
-    bool found = false;
-    while(!found)
-    {
-        const Triangle& t = triangles[currTri];
-        found = true;
-        // stochastic offset to randomize which edge we check first
-        const Index offset(detail::randGenerator() % 3);
-        for(Index i_(0); i_ < Index(3); ++i_)
-        {
-            const Index i((i_ + offset) % 3);
-            const V2d<T>& vStart = vertices[t.vertices[i]];
-            const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];
-            const PtLineLocation::Enum edgeCheck =
-                locatePointLine(pos, vStart, vEnd);
-            if(edgeCheck == PtLineLocation::Right &&
-               t.neighbors[i] != noNeighbor &&
-               visited.insert(t.neighbors[i]).second)
-            {
-                found = false;
-                currTri = t.neighbors[i];
-                break;
-            }
-        }
-    }
-    return currTri;
-}
-
-template <typename T, typename TNearPointLocator>
-array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(
-    const V2d<T>& pos) const
-{
-    array<TriInd, 2> out = {noNeighbor, noNeighbor};
-    // Query  for a vertex close to pos, to start the search
-    const VertInd startVertex = m_nearPtLocator.nearPoint(pos, vertices);
-    const TriInd iT = walkTriangles(startVertex, pos);
-    // Finished walk, locate point in current triangle
-    const Triangle& t = triangles[iT];
-    const V2d<T>& v1 = vertices[t.vertices[0]];
-    const V2d<T>& v2 = vertices[t.vertices[1]];
-    const V2d<T>& v3 = vertices[t.vertices[2]];
-    const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
-    if(loc == PtTriLocation::Outside)
-        throw std::runtime_error("No triangle was found at position");
-    out[0] = iT;
-    if(isOnEdge(loc))
-        out[1] = t.neighbors[edgeNeighbor(loc)];
-    return out;
-}
-
-/* Flip edge between T and Topo:
- *
- *                v4         | - old edge
- *               /|\         ~ - new edge
- *              / | \
- *          n3 /  T' \ n4
- *            /   |   \
- *           /    |    \
- *     T -> v1~~~~~~~~~v3 <- Topo
- *           \    |    /
- *            \   |   /
- *          n1 \Topo'/ n2
- *              \ | /
- *               \|/
- *                v2
- */
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::flipEdge(
-    const TriInd iT,
-    const TriInd iTopo)
-{
-    Triangle& t = triangles[iT];
-    Triangle& tOpo = triangles[iTopo];
-    const array<TriInd, 3>& triNs = t.neighbors;
-    const array<TriInd, 3>& triOpoNs = tOpo.neighbors;
-    const array<VertInd, 3>& triVs = t.vertices;
-    const array<VertInd, 3>& triOpoVs = tOpo.vertices;
-    // find vertices and neighbors
-    Index i = opposedVertexInd(t, iTopo);
-    const VertInd v1 = triVs[i];
-    const VertInd v2 = triVs[ccw(i)];
-    const TriInd n1 = triNs[i];
-    const TriInd n3 = triNs[cw(i)];
-    i = opposedVertexInd(tOpo, iT);
-    const VertInd v3 = triOpoVs[i];
-    const VertInd v4 = triOpoVs[ccw(i)];
-    const TriInd n4 = triOpoNs[i];
-    const TriInd n2 = triOpoNs[cw(i)];
-    // change vertices and neighbors
-    using detail::arr3;
-    t = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
-    tOpo = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1));
-    // adjust neighboring triangles and vertices
-    changeNeighbor(n1, iT, iTopo);
-    changeNeighbor(n4, iTopo, iT);
-    // only adjust adjacent triangles if triangulation is not finalized:
-    // can happen when called from outside on an already finalized triangulation
-    if(!isFinalized())
-    {
-        addAdjacentTriangle(v1, iTopo);
-        addAdjacentTriangle(v3, iT);
-        removeAdjacentTriangle(v2, iT);
-        removeAdjacentTriangle(v4, iTopo);
-    }
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::changeNeighbor(
-    const TriInd iT,
-    const TriInd oldNeighbor,
-    const TriInd newNeighbor)
-{
-    if(iT == noNeighbor)
-        return;
-    Triangle& t = triangles[iT];
-    t.neighbors[neighborInd(t, oldNeighbor)] = newNeighbor;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::addAdjacentTriangle(
-    const VertInd iVertex,
-    const TriInd iTriangle)
-{
-    vertTris[iVertex].push_back(iTriangle);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::addAdjacentTriangles(
-    const VertInd iVertex,
-    const TriInd iT1,
-    const TriInd iT2,
-    const TriInd iT3)
-{
-    TriIndVec& vTris = vertTris[iVertex];
-    vTris.reserve(vTris.size() + 3);
-    vTris.push_back(iT1);
-    vTris.push_back(iT2);
-    vTris.push_back(iT3);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::addAdjacentTriangles(
-    const VertInd iVertex,
-    const TriInd iT1,
-    const TriInd iT2,
-    const TriInd iT3,
-    const TriInd iT4)
-{
-    TriIndVec& vTris = vertTris[iVertex];
-    vTris.reserve(vTris.size() + 4);
-    vTris.push_back(iT1);
-    vTris.push_back(iT2);
-    vTris.push_back(iT3);
-    vTris.push_back(iT4);
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::removeAdjacentTriangle(
-    const VertInd iVertex,
-    const TriInd iTriangle)
-{
-    std::vector<TriInd>& tris = vertTris[iVertex];
-    tris.erase(std::find(tris.begin(), tris.end(), iTriangle));
-}
-
-template <typename T, typename TNearPointLocator>
-TriInd Triangulation<T, TNearPointLocator>::triangulatePseudopolygon(
-    const VertInd ia,
-    const VertInd ib,
-    const std::vector<VertInd>::const_iterator pointsFirst,
-    const std::vector<VertInd>::const_iterator pointsLast)
-{
-    if(pointsFirst == pointsLast)
-        return pseudopolyOuterTriangle(ia, ib);
-    // Find delaunay point
-    const VertInd ic = findDelaunayPoint(ia, ib, pointsFirst, pointsLast);
-    // Find pseudopolygons split by the delaunay point
-    std::vector<VertInd>::const_iterator newLast = pointsFirst;
-    while(*newLast != ic)
-        ++newLast;
-    const std::vector<VertInd>::const_iterator newFirst = newLast + 1;
-    // triangulate splitted pseudo-polygons
-    const TriInd iT2 = triangulatePseudopolygon(ic, ib, newFirst, pointsLast);
-    const TriInd iT1 = triangulatePseudopolygon(ia, ic, pointsFirst, newLast);
-    // add new triangle
-    const Triangle t = {{ia, ib, ic}, {noNeighbor, iT2, iT1}};
-    const TriInd iT = addTriangle(t);
-    // adjust neighboring triangles and vertices
-    if(iT1 != noNeighbor)
-    {
-        if(pointsFirst == newLast)
-            changeNeighbor(iT1, ia, ic, iT);
-        else
-            triangles[iT1].neighbors[0] = iT;
-    }
-    if(iT2 != noNeighbor)
-    {
-        if(newFirst == pointsLast)
-            changeNeighbor(iT2, ic, ib, iT);
-        else
-            triangles[iT2].neighbors[0] = iT;
-    }
-    addAdjacentTriangle(ia, iT);
-    addAdjacentTriangle(ib, iT);
-    addAdjacentTriangle(ic, iT);
-
-    return iT;
-}
-
-template <typename T, typename TNearPointLocator>
-VertInd Triangulation<T, TNearPointLocator>::findDelaunayPoint(
-    const VertInd ia,
-    const VertInd ib,
-    const std::vector<VertInd>::const_iterator pointsFirst,
-    const std::vector<VertInd>::const_iterator pointsLast) const
-{
-    assert(pointsFirst != pointsLast);
-    const V2d<T>& a = vertices[ia];
-    const V2d<T>& b = vertices[ib];
-    VertInd ic = *pointsFirst;
-    V2d<T> c = vertices[ic];
-    typedef std::vector<VertInd>::const_iterator CIt;
-    for(CIt it = pointsFirst + 1; it != pointsLast; ++it)
-    {
-        const V2d<T> v = vertices[*it];
-        if(!isInCircumcircle(v, a, b, c))
-            continue;
-        ic = *it;
-        c = vertices[ic];
-    }
-    return ic;
-}
-
-template <typename T, typename TNearPointLocator>
-TriInd Triangulation<T, TNearPointLocator>::pseudopolyOuterTriangle(
-    const VertInd ia,
-    const VertInd ib) const
-{
-    const std::vector<TriInd>& aTris = vertTris[ia];
-    const std::vector<TriInd>& bTris = vertTris[ib];
-    typedef std::vector<TriInd>::const_iterator TriIndCit;
-    for(TriIndCit it = aTris.begin(); it != aTris.end(); ++it)
-        if(std::find(bTris.begin(), bTris.end(), *it) != bTris.end())
-            return *it;
-    return noNeighbor;
-}
-
-template <typename T, typename TNearPointLocator>
-void Triangulation<T, TNearPointLocator>::insertVertices(
-    const std::vector<V2d<T> >& newVertices)
-{
-    return insertVertices(
-        newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);
-}
-
-template <typename T, typename TNearPointLocator>
-bool Triangulation<T, TNearPointLocator>::isFinalized() const
-{
-    return vertTris.empty() && !vertices.empty();
-}
-
-template <typename T, typename TNearPointLocator>
-unordered_map<TriInd, LayerDepth>
-Triangulation<T, TNearPointLocator>::peelLayer(
-    std::stack<TriInd> seeds,
-    const LayerDepth layerDepth,
-    std::vector<LayerDepth>& triDepths) const
-{
-    unordered_map<TriInd, LayerDepth> behindBoundary;
-    while(!seeds.empty())
-    {
-        const TriInd iT = seeds.top();
-        seeds.pop();
-        triDepths[iT] = layerDepth;
-        behindBoundary.erase(iT);
-        const Triangle& t = triangles[iT];
-        for(Index i(0); i < Index(3); ++i)
-        {
-            const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
-            const TriInd iN = t.neighbors[opoNbr(i)];
-            if(iN == noNeighbor || triDepths[iN] <= layerDepth)
-                continue;
-            if(fixedEdges.count(opEdge))
-            {
-                const unordered_map<Edge, LayerDepth>::const_iterator cit =
-                    overlapCount.find(opEdge);
-                const LayerDepth triDepth = cit == overlapCount.end()
-                                                ? layerDepth + 1
-                                                : layerDepth + cit->second + 1;
-                behindBoundary[iN] = triDepth;
-                continue;
-            }
-            seeds.push(iN);
-        }
-    }
-    return behindBoundary;
-}
-
-template <typename T, typename TNearPointLocator>
-std::vector<LayerDepth>
-Triangulation<T, TNearPointLocator>::calculateTriangleDepths() const
-{
-    std::vector<LayerDepth> triDepths(
-        triangles.size(), std::numeric_limits<LayerDepth>::max());
-    std::stack<TriInd> seeds(TriDeque(1, vertTris[0].front()));
-    LayerDepth layerDepth = 0;
-    LayerDepth deepestSeedDepth = 0;
-
-    unordered_map<LayerDepth, TriIndUSet> seedsByDepth;
-    do
-    {
-        const unordered_map<TriInd, LayerDepth>& newSeeds =
-            peelLayer(seeds, layerDepth, triDepths);
-
-        seedsByDepth.erase(layerDepth);
-        typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;
-        for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)
-        {
-            deepestSeedDepth = std::max(deepestSeedDepth, it->second);
-            seedsByDepth[it->second].insert(it->first);
-        }
-        const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];
-        seeds = std::stack<TriInd>(
-            TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));
-        ++layerDepth;
-    } while(!seeds.empty() || deepestSeedDepth > layerDepth);
-
-    return triDepths;
-}
-
-} // namespace CDT
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * @file
+ * Triangulation class - implementation
+ */
+
+#include "Triangulation.h"
+#include "portable_nth_element.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <deque>
+#include <stdexcept>
+
+namespace CDT
+{
+
+typedef std::deque<TriInd> TriDeque;
+
+namespace detail
+{
+
+/// Needed for c++03 compatibility (no uniform initialization available)
+template <typename T>
+array<T, 3> arr3(const T& v0, const T& v1, const T& v2)
+{
+    const array<T, 3> out = {v0, v1, v2};
+    return out;
+}
+
+namespace defaults
+{
+
+const std::size_t nTargetVerts = 0;
+const SuperGeometryType::Enum superGeomType = SuperGeometryType::SuperTriangle;
+const VertexInsertionOrder::Enum vertexInsertionOrder =
+    VertexInsertionOrder::Auto;
+const IntersectingConstraintEdges::Enum intersectingEdgesStrategy =
+    IntersectingConstraintEdges::Ignore;
+const float minDistToConstraintEdge(0);
+
+} // namespace defaults
+
+} // namespace detail
+
+template <typename T, typename TNearPointLocator>
+Triangulation<T, TNearPointLocator>::Triangulation()
+    : m_nTargetVerts(detail::defaults::nTargetVerts)
+    , m_superGeomType(detail::defaults::superGeomType)
+    , m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder)
+    , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
+    , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
+{}
+
+template <typename T, typename TNearPointLocator>
+Triangulation<T, TNearPointLocator>::Triangulation(
+    const VertexInsertionOrder::Enum vertexInsertionOrder)
+    : m_nTargetVerts(detail::defaults::nTargetVerts)
+    , m_superGeomType(detail::defaults::superGeomType)
+    , m_vertexInsertionOrder(vertexInsertionOrder)
+    , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
+    , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
+{}
+
+template <typename T, typename TNearPointLocator>
+Triangulation<T, TNearPointLocator>::Triangulation(
+    const VertexInsertionOrder::Enum vertexInsertionOrder,
+    const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
+    const T minDistToConstraintEdge)
+    : m_nTargetVerts(detail::defaults::nTargetVerts)
+    , m_superGeomType(detail::defaults::superGeomType)
+    , m_vertexInsertionOrder(vertexInsertionOrder)
+    , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
+    , m_minDistToConstraintEdge(minDistToConstraintEdge)
+{}
+
+template <typename T, typename TNearPointLocator>
+Triangulation<T, TNearPointLocator>::Triangulation(
+    const VertexInsertionOrder::Enum vertexInsertionOrder,
+    const TNearPointLocator& nearPtLocator,
+    const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
+    const T minDistToConstraintEdge)
+    : m_nearPtLocator(nearPtLocator)
+    , m_nTargetVerts(detail::defaults::nTargetVerts)
+    , m_superGeomType(detail::defaults::superGeomType)
+    , m_vertexInsertionOrder(vertexInsertionOrder)
+    , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
+    , m_minDistToConstraintEdge(minDistToConstraintEdge)
+{}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::eraseDummies()
+{
+    if(m_dummyTris.empty())
+        return;
+    const TriIndUSet dummySet(m_dummyTris.begin(), m_dummyTris.end());
+    TriIndUMap triIndMap;
+    triIndMap[noNeighbor] = noNeighbor;
+    for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
+    {
+        if(dummySet.count(iT))
+            continue;
+        triIndMap[iT] = iTnew;
+        triangles[iTnew] = triangles[iT];
+        iTnew++;
+    }
+    triangles.erase(triangles.end() - dummySet.size(), triangles.end());
+
+    // remap adjacent triangle indices for vertices
+    for(TriIndVec::iterator iT = m_vertTris.begin(); iT != m_vertTris.end();
+        ++iT)
+    {
+        *iT = triIndMap[*iT];
+    }
+    // remap neighbor indices for triangles
+    for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); ++t)
+    {
+        NeighborsArr3& nn = t->neighbors;
+        for(NeighborsArr3::iterator iN = nn.begin(); iN != nn.end(); ++iN)
+            *iN = triIndMap[*iN];
+    }
+    // clear dummy triangles
+    m_dummyTris = std::vector<TriInd>();
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::eraseSuperTriangle()
+{
+    if(m_superGeomType != SuperGeometryType::SuperTriangle)
+        return;
+    // find triangles adjacent to super-triangle's vertices
+    TriIndUSet toErase;
+    for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT)
+    {
+        if(touchesSuperTriangle(triangles[iT]))
+            toErase.insert(iT);
+    }
+    finalizeTriangulation(toErase);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::eraseOuterTriangles()
+{
+    // make dummy triangles adjacent to super-triangle's vertices
+    assert(m_vertTris[0] != noNeighbor);
+    const std::stack<TriInd> seed(std::deque<TriInd>(1, m_vertTris[0]));
+    const TriIndUSet toErase = growToBoundary(seed);
+    finalizeTriangulation(toErase);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()
+{
+    const std::vector<LayerDepth> triDepths = calculateTriangleDepths();
+    TriIndUSet toErase;
+    toErase.reserve(triangles.size());
+    for(std::size_t iT = 0; iT != triangles.size(); ++iT)
+    {
+        if(triDepths[iT] % 2 == 0)
+            toErase.insert(static_cast<TriInd>(iT));
+    }
+    finalizeTriangulation(toErase);
+}
+
+/// Remap removing super-triangle: subtract 3 from vertices
+inline Edge RemapNoSuperTriangle(const Edge& e)
+{
+    return Edge(VertInd(e.v1() - 3), VertInd(e.v2() - 3));
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::removeTriangles(
+    const TriIndUSet& removedTriangles)
+{
+    if(removedTriangles.empty())
+        return;
+    // remove triangles and calculate triangle index mapping
+    TriIndUMap triIndMap;
+    for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
+    {
+        if(removedTriangles.count(iT))
+            continue;
+        triIndMap[iT] = iTnew;
+        triangles[iTnew] = triangles[iT];
+        iTnew++;
+    }
+    triangles.erase(triangles.end() - removedTriangles.size(), triangles.end());
+    // adjust triangles' neighbors
+    for(TriInd iT(0); iT < triangles.size(); ++iT)
+    {
+        Triangle& t = triangles[iT];
+        // update neighbors to account for removed triangles
+        NeighborsArr3& nn = t.neighbors;
+        for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n)
+        {
+            if(removedTriangles.count(*n))
+            {
+                *n = noNeighbor;
+            }
+            else if(*n != noNeighbor)
+            {
+                *n = triIndMap[*n];
+            }
+        }
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+TriIndVec& Triangulation<T, TNearPointLocator>::VertTrisInternal()
+{
+    return m_vertTris;
+}
+
+template <typename T, typename TNearPointLocator>
+const TriIndVec& Triangulation<T, TNearPointLocator>::VertTrisInternal() const
+{
+    return m_vertTris;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::finalizeTriangulation(
+    const TriIndUSet& removedTriangles)
+{
+    eraseDummies();
+    m_vertTris = TriIndVec();
+    // remove super-triangle
+    if(m_superGeomType == SuperGeometryType::SuperTriangle)
+    {
+        vertices.erase(vertices.begin(), vertices.begin() + 3);
+        // Edge re-mapping
+        { // fixed edges
+            EdgeUSet updatedFixedEdges;
+            typedef CDT::EdgeUSet::const_iterator It;
+            for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e)
+            {
+                updatedFixedEdges.insert(RemapNoSuperTriangle(*e));
+            }
+            fixedEdges = updatedFixedEdges;
+        }
+        { // overlap count
+            unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount;
+            typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator
+                It;
+            for(It it = overlapCount.begin(); it != overlapCount.end(); ++it)
+            {
+                updatedOverlapCount.insert(std::make_pair(
+                    RemapNoSuperTriangle(it->first), it->second));
+            }
+            overlapCount = updatedOverlapCount;
+        }
+        { // split edges mapping
+            unordered_map<Edge, EdgeVec> updatedPieceToOriginals;
+            typedef unordered_map<Edge, EdgeVec>::const_iterator It;
+            for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end();
+                ++it)
+            {
+                EdgeVec ee = it->second;
+                for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end();
+                    ++eeIt)
+                {
+                    *eeIt = RemapNoSuperTriangle(*eeIt);
+                }
+                updatedPieceToOriginals.insert(
+                    std::make_pair(RemapNoSuperTriangle(it->first), ee));
+            }
+            pieceToOriginals = updatedPieceToOriginals;
+        }
+    }
+    // remove other triangles
+    removeTriangles(removedTriangles);
+    // adjust triangle vertices: account for removed super-triangle
+    if(m_superGeomType == SuperGeometryType::SuperTriangle)
+    {
+        for(TriangleVec::iterator t = triangles.begin(); t != triangles.end();
+            ++t)
+        {
+            VerticesArr3& vv = t->vertices;
+            for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v)
+            {
+                *v -= 3;
+            }
+        }
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::initializedWithCustomSuperGeometry()
+{
+    m_nearPtLocator.initialize(vertices);
+    m_nTargetVerts = vertices.size();
+    m_superGeomType = SuperGeometryType::Custom;
+}
+
+template <typename T, typename TNearPointLocator>
+TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary(
+    std::stack<TriInd> seeds) const
+{
+    TriIndUSet traversed;
+    while(!seeds.empty())
+    {
+        const TriInd iT = seeds.top();
+        seeds.pop();
+        traversed.insert(iT);
+        const Triangle& t = triangles[iT];
+        for(Index i(0); i < Index(3); ++i)
+        {
+            const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
+            if(fixedEdges.count(opEdge))
+                continue;
+            const TriInd iN = t.neighbors[opoNbr(i)];
+            if(iN != noNeighbor && traversed.count(iN) == 0)
+                seeds.push(iN);
+        }
+    }
+    return traversed;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::makeDummy(const TriInd iT)
+{
+    m_dummyTris.push_back(iT);
+}
+
+template <typename T, typename TNearPointLocator>
+TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t)
+{
+    if(m_dummyTris.empty())
+    {
+        triangles.push_back(t);
+        return TriInd(triangles.size() - 1);
+    }
+    const TriInd nxtDummy = m_dummyTris.back();
+    m_dummyTris.pop_back();
+    triangles[nxtDummy] = t;
+    return nxtDummy;
+}
+
+template <typename T, typename TNearPointLocator>
+TriInd Triangulation<T, TNearPointLocator>::addTriangle()
+{
+    if(m_dummyTris.empty())
+    {
+        const Triangle dummy = {
+            {noVertex, noVertex, noVertex},
+            {noNeighbor, noNeighbor, noNeighbor}};
+        triangles.push_back(dummy);
+        return TriInd(triangles.size() - 1);
+    }
+    const TriInd nxtDummy = m_dummyTris.back();
+    m_dummyTris.pop_back();
+    return nxtDummy;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertEdges(
+    const std::vector<Edge>& edges)
+{
+    insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::conformToEdges(
+    const std::vector<Edge>& edges)
+{
+    conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge)
+{
+    if(!fixedEdges.insert(edge).second)
+    {
+        ++overlapCount[edge]; // if edge is already fixed increment the counter
+    }
+}
+
+namespace detail
+{
+
+// add element to 'to' if not already in 'to'
+template <typename T, typename Allocator1>
+void insert_unique(std::vector<T, Allocator1>& to, const T& elem)
+{
+    if(std::find(to.begin(), to.end(), elem) == to.end())
+    {
+        to.push_back(elem);
+    }
+}
+
+// add elements of 'from' that are not present in 'to' to 'to'
+template <typename T, typename Allocator1, typename Allocator2>
+void insert_unique(
+    std::vector<T, Allocator1>& to,
+    const std::vector<T, Allocator2>& from)
+{
+    typedef typename std::vector<T, Allocator2>::const_iterator Cit;
+    to.reserve(to.size() + from.size());
+    for(Cit cit = from.begin(); cit != from.end(); ++cit)
+    {
+        insert_unique(to, *cit);
+    }
+}
+
+} // namespace detail
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::splitFixedEdge(
+    const Edge& edge,
+    const VertInd iSplitVert)
+{
+    // split constraint (fixed) edge that already exists in triangulation
+    const Edge half1(edge.v1(), iSplitVert);
+    const Edge half2(iSplitVert, edge.v2());
+    // remove the edge that and add its halves
+    fixedEdges.erase(edge);
+    fixEdge(half1);
+    fixEdge(half2);
+    // maintain overlaps
+    typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator It;
+    const It overlapIt = overlapCount.find(edge);
+    if(overlapIt != overlapCount.end())
+    {
+        overlapCount[half1] += overlapIt->second;
+        overlapCount[half2] += overlapIt->second;
+        overlapCount.erase(overlapIt);
+    }
+    // maintain piece-to-original mapping
+    EdgeVec newOriginals(1, edge);
+    const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
+        pieceToOriginals.find(edge);
+    if(originalsIt != pieceToOriginals.end())
+    { // edge being split was split before: pass-through originals
+        newOriginals = originalsIt->second;
+        pieceToOriginals.erase(originalsIt);
+    }
+    detail::insert_unique(pieceToOriginals[half1], newOriginals);
+    detail::insert_unique(pieceToOriginals[half2], newOriginals);
+}
+
+template <typename T, typename TNearPointLocator>
+VertInd Triangulation<T, TNearPointLocator>::addSplitEdgeVertex(
+    const V2d<T>& splitVert,
+    const TriInd iT,
+    const TriInd iTopo)
+{
+    // add a new point on the edge that splits an edge in two
+    const VertInd iSplitVert = static_cast<VertInd>(vertices.size());
+    addNewVertex(splitVert, noNeighbor);
+    std::stack<TriInd> triStack = insertVertexOnEdge(iSplitVert, iT, iTopo);
+    tryAddVertexToLocator(iSplitVert);
+    ensureDelaunayByEdgeFlips(splitVert, iSplitVert, triStack);
+    return iSplitVert;
+}
+
+template <typename T, typename TNearPointLocator>
+VertInd Triangulation<T, TNearPointLocator>::splitFixedEdgeAt(
+    const Edge& edge,
+    const V2d<T>& splitVert,
+    const TriInd iT,
+    const TriInd iTopo)
+{
+    const VertInd iSplitVert = addSplitEdgeVertex(splitVert, iT, iTopo);
+    splitFixedEdge(edge, iSplitVert);
+    return iSplitVert;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::fixEdge(
+    const Edge& edge,
+    const Edge& originalEdge)
+{
+    fixEdge(edge);
+    if(edge != originalEdge)
+        detail::insert_unique(pieceToOriginals[edge], originalEdge);
+}
+
+namespace detail
+{
+
+template <typename T>
+T lerp(const T& a, const T& b, const T t)
+{
+    return (T(1) - t) * a + t * b;
+}
+
+// Precondition: ab and cd intersect normally
+template <typename T>
+V2d<T> intersectionPosition(
+    const V2d<T>& a,
+    const V2d<T>& b,
+    const V2d<T>& c,
+    const V2d<T>& d)
+{
+    using namespace predicates::adaptive;
+
+    // note: for better accuracy we interpolate x and y separately
+    // on a segment with the shortest x/y-projection correspondingly
+    const T a_cd = orient2d(c.x, c.y, d.x, d.y, a.x, a.y);
+    const T b_cd = orient2d(c.x, c.y, d.x, d.y, b.x, b.y);
+    const T t_ab = a_cd / (a_cd - b_cd);
+
+    const T c_ab = orient2d(a.x, a.y, b.x, b.y, c.x, c.y);
+    const T d_ab = orient2d(a.x, a.y, b.x, b.y, d.x, d.y);
+    const T t_cd = c_ab / (c_ab - d_ab);
+
+    return V2d<T>::make(
+        std::fabs(a.x - b.x) < std::fabs(c.x - d.x) ? lerp(a.x, b.x, t_ab)
+                                                    : lerp(c.x, d.x, t_cd),
+        std::fabs(a.y - b.y) < std::fabs(c.y - d.y) ? lerp(a.y, b.y, t_ab)
+                                                    : lerp(c.y, d.y, t_cd));
+}
+
+} // namespace detail
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertEdgeIteration(
+    const Edge edge,
+    const Edge originalEdge,
+    EdgeVec& remaining,
+    std::vector<TriangulatePseudopolygonTask>& tppIterations)
+{
+    const VertInd iA = edge.v1();
+    VertInd iB = edge.v2();
+    if(iA == iB) // edge connects a vertex to itself
+        return;
+
+    if(hasEdge(iA, iB))
+    {
+        fixEdge(edge, originalEdge);
+        return;
+    }
+
+    const V2d<T>& a = vertices[iA];
+    const V2d<T>& b = vertices[iB];
+    const T distanceTolerance =
+        m_minDistToConstraintEdge == T(0)
+            ? T(0)
+            : m_minDistToConstraintEdge * distance(a, b);
+
+    TriInd iT;
+    // Note: 'L' is left and 'R' is right of the inserted constraint edge
+    VertInd iVL, iVR;
+    tie(iT, iVL, iVR) = intersectedTriangle(iA, a, b, distanceTolerance);
+    // if one of the triangle vertices is on the edge, move edge start
+    if(iT == noNeighbor)
+    {
+        const Edge edgePart(iA, iVL);
+        fixEdge(edgePart, originalEdge);
+        remaining.push_back(Edge(iVL, iB));
+        return;
+    }
+    Triangle t = triangles[iT];
+    std::vector<TriInd> intersected(1, iT);
+    std::vector<VertInd> polyL, polyR;
+    std::vector<TriInd> outerTrisL, outerTrisR;
+    polyL.reserve(2);
+    polyL.push_back(iA);
+    polyL.push_back(iVL);
+    outerTrisL.push_back(edgeNeighbor(t, iA, iVL));
+    polyR.reserve(2);
+    polyR.push_back(iA);
+    polyR.push_back(iVR);
+    outerTrisR.push_back(edgeNeighbor(t, iA, iVR));
+    VertInd iV = iA;
+    IndexSizeType nChainedHangingEdgesL = 0;
+    IndexSizeType nChainedHangingEdgesR = 0;
+
+    while(!t.containsVertex(iB))
+    {
+        const TriInd iTopo = opposedTriangle(t, iV);
+        const Triangle& tOpo = triangles[iTopo];
+        const VertInd iVopo = opposedVertex(tOpo, iT);
+
+        // Resolve intersection between two constraint edges if needed
+        if(m_intersectingEdgesStrategy ==
+               IntersectingConstraintEdges::Resolve &&
+           fixedEdges.count(Edge(iVL, iVR)))
+        {
+            // split edge at the intersection of two constraint edges
+            const V2d<T> newV = detail::intersectionPosition(
+                vertices[iA], vertices[iB], vertices[iVL], vertices[iVR]);
+            const VertInd iNewVert =
+                splitFixedEdgeAt(Edge(iVL, iVR), newV, iT, iTopo);
+            // TODO: is it's possible to re-use pseudo-polygons
+            //  for inserting [iA, iNewVert] edge half?
+            remaining.push_back(Edge(iA, iNewVert));
+            remaining.push_back(Edge(iNewVert, iB));
+            return;
+        }
+
+        const PtLineLocation::Enum loc =
+            locatePointLine(vertices[iVopo], a, b, distanceTolerance);
+        if(loc == PtLineLocation::Left)
+        {
+            // hanging edge check
+            // previous entry of the vertex in poly if edge is hanging
+            const IndexSizeType prev =
+                (polyL.size() - 2) - 2 * nChainedHangingEdgesL;
+            if(iVopo == polyL[prev])
+            { // hanging edge
+                ++nChainedHangingEdgesL;
+                outerTrisL[prev] = noNeighbor;
+                outerTrisL.push_back(noNeighbor);
+            }
+            else
+            { // normal case
+                nChainedHangingEdgesL = 0;
+                outerTrisL.push_back(edgeNeighbor(tOpo, polyL.back(), iVopo));
+            }
+            polyL.push_back(iVopo);
+            iV = iVL;
+            iVL = iVopo;
+        }
+        else if(loc == PtLineLocation::Right)
+        {
+            // hanging edge check
+            // previous entry of the vertex in poly if edge is hanging
+            const IndexSizeType prev =
+                (polyR.size() - 2) - 2 * nChainedHangingEdgesR;
+            if(iVopo == polyR[prev])
+            { // hanging edge
+                ++nChainedHangingEdgesR;
+                outerTrisR[prev] = noNeighbor;
+                outerTrisR.push_back(noNeighbor);
+            }
+            else
+            { // normal case
+                nChainedHangingEdgesR = 0;
+                outerTrisR.push_back(edgeNeighbor(tOpo, polyR.back(), iVopo));
+            }
+            polyR.push_back(iVopo);
+            iV = iVR;
+            iVR = iVopo;
+        }
+        else // encountered point on the edge
+            iB = iVopo;
+
+        intersected.push_back(iTopo);
+        iT = iTopo;
+        t = triangles[iT];
+    }
+    outerTrisL.push_back(edgeNeighbor(t, polyL.back(), iB));
+    outerTrisR.push_back(edgeNeighbor(t, polyR.back(), iB));
+    polyL.push_back(iB);
+    polyR.push_back(iB);
+
+    assert(!intersected.empty());
+    // make sure start/end vertices have a valid adjacent triangle
+    // that is not intersected by an edge
+    if(m_vertTris[iA] == intersected.front())
+        pivotVertexTriangleCW(iA);
+    if(m_vertTris[iB] == intersected.back())
+        pivotVertexTriangleCW(iB);
+    // Remove intersected triangles
+    typedef std::vector<TriInd>::const_iterator TriIndCit;
+    for(TriIndCit it = intersected.begin(); it != intersected.end(); ++it)
+        makeDummy(*it);
+    { // Triangulate pseudo-polygons on both sides
+        std::reverse(polyR.begin(), polyR.end());
+        std::reverse(outerTrisR.begin(), outerTrisR.end());
+        const TriInd iTL = addTriangle();
+        const TriInd iTR = addTriangle();
+        triangulatePseudopolygon(polyL, outerTrisL, iTL, iTR, tppIterations);
+        triangulatePseudopolygon(polyR, outerTrisR, iTR, iTL, tppIterations);
+    }
+
+    if(iB != edge.v2()) // encountered point on the edge
+    {
+        // fix edge part
+        const Edge edgePart(iA, iB);
+        fixEdge(edgePart, originalEdge);
+        remaining.push_back(Edge(iB, edge.v2()));
+        return;
+    }
+    else
+    {
+        fixEdge(edge, originalEdge);
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertEdge(
+    Edge edge,
+    const Edge originalEdge,
+    EdgeVec& remaining,
+    std::vector<TriangulatePseudopolygonTask>& tppIterations)
+{
+    // use iteration over recursion to avoid stack overflows
+    remaining.clear();
+    remaining.push_back(edge);
+    while(!remaining.empty())
+    {
+        edge = remaining.back();
+        remaining.pop_back();
+        insertEdgeIteration(edge, originalEdge, remaining, tppIterations);
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::conformToEdgeIteration(
+    Edge edge,
+    const EdgeVec& originals,
+    BoundaryOverlapCount overlaps,
+    std::vector<ConformToEdgeTask>& remaining)
+{
+    const VertInd iA = edge.v1();
+    VertInd iB = edge.v2();
+    if(iA == iB) // edge connects a vertex to itself
+        return;
+
+    if(hasEdge(iA, iB))
+    {
+        fixEdge(edge);
+        if(overlaps > 0)
+            overlapCount[edge] = overlaps;
+        // avoid marking edge as a part of itself
+        if(!originals.empty() && edge != originals.front())
+        {
+            detail::insert_unique(pieceToOriginals[edge], originals);
+        }
+        return;
+    }
+
+    const V2d<T>& a = vertices[iA];
+    const V2d<T>& b = vertices[iB];
+    const T distanceTolerance =
+        m_minDistToConstraintEdge == T(0)
+            ? T(0)
+            : m_minDistToConstraintEdge * distance(a, b);
+    TriInd iT;
+    VertInd iVleft, iVright;
+    tie(iT, iVleft, iVright) = intersectedTriangle(iA, a, b, distanceTolerance);
+    // if one of the triangle vertices is on the edge, move edge start
+    if(iT == noNeighbor)
+    {
+        const Edge edgePart(iA, iVleft);
+        fixEdge(edgePart);
+        if(overlaps > 0)
+            overlapCount[edgePart] = overlaps;
+        detail::insert_unique(pieceToOriginals[edgePart], originals);
+#ifdef CDT_CXX11_IS_SUPPORTED
+        remaining.emplace_back(Edge(iVleft, iB), originals, overlaps);
+#else
+        remaining.push_back(make_tuple(Edge(iVleft, iB), originals, overlaps));
+#endif
+        return;
+    }
+
+    VertInd iV = iA;
+    Triangle t = triangles[iT];
+    while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==
+          t.vertices.end())
+    {
+        const TriInd iTopo = opposedTriangle(t, iV);
+        const Triangle& tOpo = triangles[iTopo];
+        const VertInd iVopo = opposedVertex(tOpo, iT);
+        const V2d<T> vOpo = vertices[iVopo];
+
+        // Resolve intersection between two constraint edges if needed
+        if(m_intersectingEdgesStrategy ==
+               IntersectingConstraintEdges::Resolve &&
+           fixedEdges.count(Edge(iVleft, iVright)))
+        {
+            // split edge at the intersection of two constraint edges
+            const V2d<T> newV = detail::intersectionPosition(
+                vertices[iA],
+                vertices[iB],
+                vertices[iVleft],
+                vertices[iVright]);
+            const VertInd iNewVert =
+                splitFixedEdgeAt(Edge(iVleft, iVright), newV, iT, iTopo);
+#ifdef CDT_CXX11_IS_SUPPORTED
+            remaining.emplace_back(Edge(iNewVert, iB), originals, overlaps);
+            remaining.emplace_back(Edge(iA, iNewVert), originals, overlaps);
+#else
+            remaining.push_back(
+                make_tuple(Edge(iNewVert, iB), originals, overlaps));
+            remaining.push_back(
+                make_tuple(Edge(iA, iNewVert), originals, overlaps));
+#endif
+            return;
+        }
+
+        iT = iTopo;
+        t = triangles[iT];
+
+        const PtLineLocation::Enum loc =
+            locatePointLine(vOpo, a, b, distanceTolerance);
+        if(loc == PtLineLocation::Left)
+        {
+            iV = iVleft;
+            iVleft = iVopo;
+        }
+        else if(loc == PtLineLocation::Right)
+        {
+            iV = iVright;
+            iVright = iVopo;
+        }
+        else // encountered point on the edge
+            iB = iVopo;
+    }
+
+    // encountered one or more points on the edge: add remaining edge part
+    if(iB != edge.v2())
+    {
+#ifdef CDT_CXX11_IS_SUPPORTED
+        remaining.emplace_back(Edge(iB, edge.v2()), originals, overlaps);
+#else
+        remaining.push_back(
+            make_tuple(Edge(iB, edge.v2()), originals, overlaps));
+#endif
+    }
+
+    // add mid-point to triangulation
+    const VertInd iMid = static_cast<VertInd>(vertices.size());
+    const V2d<T>& start = vertices[iA];
+    const V2d<T>& end = vertices[iB];
+    addNewVertex(
+        V2d<T>::make((start.x + end.x) / T(2), (start.y + end.y) / T(2)),
+        noNeighbor);
+    const std::vector<Edge> flippedFixedEdges =
+        insertVertex_FlipFixedEdges(iMid);
+
+#ifdef CDT_CXX11_IS_SUPPORTED
+    remaining.emplace_back(Edge(iMid, iB), originals, overlaps);
+    remaining.emplace_back(Edge(iA, iMid), originals, overlaps);
+#else
+    remaining.push_back(make_tuple(Edge(iMid, iB), originals, overlaps));
+    remaining.push_back(make_tuple(Edge(iA, iMid), originals, overlaps));
+#endif
+
+    // re-introduce fixed edges that were flipped
+    // and make sure overlap count is preserved
+    for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin();
+        it != flippedFixedEdges.end();
+        ++it)
+    {
+        const Edge& flippedFixedEdge = *it;
+        fixedEdges.erase(flippedFixedEdge);
+
+        BoundaryOverlapCount prevOverlaps = 0;
+        const unordered_map<Edge, BoundaryOverlapCount>::const_iterator
+            overlapsIt = overlapCount.find(flippedFixedEdge);
+        if(overlapsIt != overlapCount.end())
+        {
+            prevOverlaps = overlapsIt->second;
+            overlapCount.erase(overlapsIt);
+        }
+        // override overlapping boundaries count when re-inserting an edge
+        EdgeVec prevOriginals(1, flippedFixedEdge);
+        const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
+            pieceToOriginals.find(flippedFixedEdge);
+        if(originalsIt != pieceToOriginals.end())
+        {
+            prevOriginals = originalsIt->second;
+        }
+#ifdef CDT_CXX11_IS_SUPPORTED
+        remaining.emplace_back(flippedFixedEdge, prevOriginals, prevOverlaps);
+#else
+        remaining.push_back(
+            make_tuple(flippedFixedEdge, prevOriginals, prevOverlaps));
+#endif
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::conformToEdge(
+    Edge edge,
+    EdgeVec originals,
+    BoundaryOverlapCount overlaps,
+    std::vector<ConformToEdgeTask>& remaining)
+{
+    // use iteration over recursion to avoid stack overflows
+    remaining.clear();
+#ifdef CDT_CXX11_IS_SUPPORTED
+    remaining.emplace_back(edge, originals, overlaps);
+#else
+    remaining.push_back(make_tuple(edge, originals, overlaps));
+#endif
+    while(!remaining.empty())
+    {
+        tie(edge, originals, overlaps) = remaining.back();
+        remaining.pop_back();
+        conformToEdgeIteration(edge, originals, overlaps, remaining);
+    }
+}
+
+/*!
+ * Returns:
+ *  - intersected triangle index
+ *  - index of point on the left of the line
+ *  - index of point on the right of the line
+ * If left point is right on the line: no triangle is intersected:
+ *  - triangle index is no-neighbor (invalid)
+ *  - index of point on the line
+ *  - index of point on the right of the line
+ */
+template <typename T, typename TNearPointLocator>
+tuple<TriInd, VertInd, VertInd>
+Triangulation<T, TNearPointLocator>::intersectedTriangle(
+    const VertInd iA,
+    const V2d<T>& a,
+    const V2d<T>& b,
+    const T orientationTolerance) const
+{
+    const TriInd startTri = m_vertTris[iA];
+    TriInd iT = startTri;
+    do
+    {
+        const Triangle t = triangles[iT];
+        const Index i = vertexInd(t.vertices, iA);
+        const VertInd iP2 = t.vertices[ccw(i)];
+        const T orientP2 = orient2D(vertices[iP2], a, b);
+        const PtLineLocation::Enum locP2 = classifyOrientation(orientP2);
+        if(locP2 == PtLineLocation::Right)
+        {
+            const VertInd iP1 = t.vertices[cw(i)];
+            const T orientP1 = orient2D(vertices[iP1], a, b);
+            const PtLineLocation::Enum locP1 = classifyOrientation(orientP1);
+            if(locP1 == PtLineLocation::OnLine)
+            {
+                return make_tuple(noNeighbor, iP1, iP1);
+            }
+            if(locP1 == PtLineLocation::Left)
+            {
+                if(orientationTolerance)
+                {
+                    T closestOrient;
+                    VertInd iClosestP;
+                    if(std::abs(orientP1) <= std::abs(orientP2))
+                    {
+                        closestOrient = orientP1;
+                        iClosestP = iP1;
+                    }
+                    else
+                    {
+                        closestOrient = orientP2;
+                        iClosestP = iP2;
+                    }
+                    if(classifyOrientation(
+                           closestOrient, orientationTolerance) ==
+                       PtLineLocation::OnLine)
+                    {
+                        return make_tuple(noNeighbor, iClosestP, iClosestP);
+                    }
+                }
+                return make_tuple(iT, iP1, iP2);
+            }
+        }
+        iT = t.next(iA).first;
+    } while(iT != startTri);
+    throw std::runtime_error("Could not find vertex triangle intersected by "
+                             "edge. Note: can be caused by duplicate points.");
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box)
+{
+    m_nTargetVerts = 3;
+    m_superGeomType = SuperGeometryType::SuperTriangle;
+
+    const V2d<T> center = {
+        (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2)};
+    const T w = box.max.x - box.min.x;
+    const T h = box.max.y - box.min.y;
+    T r = std::sqrt(w * w + h * h) / T(2); // incircle radius
+    r *= T(1.1);
+    const T R = T(2) * r;                        // excircle radius
+    const T shiftX = R * std::sqrt(T(3)) / T(2); // R * cos(30 deg)
+    const V2d<T> posV1 = {center.x - shiftX, center.y - r};
+    const V2d<T> posV2 = {center.x + shiftX, center.y - r};
+    const V2d<T> posV3 = {center.x, center.y + R};
+    addNewVertex(posV1, TriInd(0));
+    addNewVertex(posV2, TriInd(0));
+    addNewVertex(posV3, TriInd(0));
+    const Triangle superTri = {
+        {VertInd(0), VertInd(1), VertInd(2)},
+        {noNeighbor, noNeighbor, noNeighbor}};
+    addTriangle(superTri);
+    if(m_vertexInsertionOrder != VertexInsertionOrder::Auto)
+    {
+        m_nearPtLocator.initialize(vertices);
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::addNewVertex(
+    const V2d<T>& pos,
+    const TriInd iT)
+{
+    vertices.push_back(pos);
+    m_vertTris.push_back(iT);
+}
+
+template <typename T, typename TNearPointLocator>
+std::vector<Edge>
+Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges(
+    const VertInd iV1)
+{
+    std::vector<Edge> flippedFixedEdges;
+
+    const V2d<T>& v1 = vertices[iV1];
+    const VertInd startVertex = m_nearPtLocator.nearPoint(v1, vertices);
+    array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v1, startVertex);
+    std::stack<TriInd> triStack =
+        trisAt[1] == noNeighbor ? insertVertexInsideTriangle(iV1, trisAt[0])
+                                : insertVertexOnEdge(iV1, trisAt[0], trisAt[1]);
+
+    TriInd iTopo, n1, n2, n3, n4;
+    VertInd iV2, iV3, iV4;
+    while(!triStack.empty())
+    {
+        const TriInd iT = triStack.top();
+        triStack.pop();
+
+        edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
+        if(iTopo != noNeighbor && isFlipNeeded(v1, iV1, iV2, iV3, iV4))
+        {
+            // if flipped edge is fixed, remember it
+            const Edge flippedEdge(iV2, iV4);
+            if(!fixedEdges.empty() &&
+               fixedEdges.find(flippedEdge) != fixedEdges.end())
+            {
+                flippedFixedEdges.push_back(flippedEdge);
+            }
+
+            flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
+            triStack.push(iT);
+            triStack.push(iTopo);
+        }
+    }
+
+    tryAddVertexToLocator(iV1);
+    return flippedFixedEdges;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertex(
+    const VertInd iVert,
+    const VertInd walkStart)
+{
+    const V2d<T>& v = vertices[iVert];
+    const array<TriInd, 2> trisAt = walkingSearchTrianglesAt(v, walkStart);
+    std::stack<TriInd> triStack =
+        trisAt[1] == noNeighbor
+            ? insertVertexInsideTriangle(iVert, trisAt[0])
+            : insertVertexOnEdge(iVert, trisAt[0], trisAt[1]);
+    ensureDelaunayByEdgeFlips(v, iVert, triStack);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert)
+{
+    const V2d<T>& v = vertices[iVert];
+    const VertInd walkStart = m_nearPtLocator.nearPoint(v, vertices);
+    insertVertex(iVert, walkStart);
+    tryAddVertexToLocator(iVert);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips(
+    const V2d<T>& v1,
+    const VertInd iV1,
+    std::stack<TriInd>& triStack)
+{
+    TriInd iTopo, n1, n2, n3, n4;
+    VertInd iV2, iV3, iV4;
+    while(!triStack.empty())
+    {
+        const TriInd iT = triStack.top();
+        triStack.pop();
+
+        edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
+        if(iTopo != noNeighbor && isFlipNeeded(v1, iV1, iV2, iV3, iV4))
+        {
+            flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
+            triStack.push(iT);
+            triStack.push(iTopo);
+        }
+    }
+}
+
+/*
+ *                       v4         original edge: (v1, v3)
+ *                      /|\   flip-candidate edge: (v,  v2)
+ *                    /  |  \
+ *              n3  /    |    \  n4
+ *                /      |      \
+ * new vertex--> v1    T | Topo  v3
+ *                \      |      /
+ *              n1  \    |    /  n2
+ *                    \  |  /
+ *                      \|/
+ *                       v2
+ */
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::edgeFlipInfo(
+    const TriInd iT,
+    const VertInd iV1,
+    TriInd& iTopo,
+    VertInd& iV2,
+    VertInd& iV3,
+    VertInd& iV4,
+    TriInd& n1,
+    TriInd& n2,
+    TriInd& n3,
+    TriInd& n4)
+{
+    /*     v[2]
+           / \
+      n[2]/   \n[1]
+         /_____\
+    v[0]  n[0]  v[1]  */
+    const Triangle& t = triangles[iT];
+    if(t.vertices[0] == iV1)
+    {
+        iV2 = t.vertices[1];
+        iV4 = t.vertices[2];
+        n1 = t.neighbors[0];
+        n3 = t.neighbors[2];
+        iTopo = t.neighbors[1];
+    }
+    else if(t.vertices[1] == iV1)
+    {
+        iV2 = t.vertices[2];
+        iV4 = t.vertices[0];
+        n1 = t.neighbors[1];
+        n3 = t.neighbors[0];
+        iTopo = t.neighbors[2];
+    }
+    else
+    {
+        iV2 = t.vertices[0];
+        iV4 = t.vertices[1];
+        n1 = t.neighbors[2];
+        n3 = t.neighbors[1];
+        iTopo = t.neighbors[0];
+    }
+    if(iTopo == noNeighbor)
+        return;
+    const Triangle& tOpo = triangles[iTopo];
+    if(tOpo.neighbors[0] == iT)
+    {
+        iV3 = tOpo.vertices[2];
+        n2 = tOpo.neighbors[1];
+        n4 = tOpo.neighbors[2];
+    }
+    else if(tOpo.neighbors[1] == iT)
+    {
+        iV3 = tOpo.vertices[0];
+        n2 = tOpo.neighbors[2];
+        n4 = tOpo.neighbors[0];
+    }
+    else
+    {
+        iV3 = tOpo.vertices[1];
+        n2 = tOpo.neighbors[0];
+        n4 = tOpo.neighbors[1];
+    }
+}
+
+/*!
+ * Handles super-triangle vertices.
+ * Super-tri points are not infinitely far and influence the input points
+ * Three cases are possible:
+ *  1.  If one of the opposed vertices is super-tri: no flip needed
+ *  2.  One of the shared vertices is super-tri:
+ *      check if on point is same side of line formed by non-super-tri
+ * vertices as the non-super-tri shared vertex
+ *  3.  None of the vertices are super-tri: normal circumcircle test
+ */
+/*
+ *                       v4         original edge: (v2, v4)
+ *                      /|\   flip-candidate edge: (v1, v3)
+ *                    /  |  \
+ *                  /    |    \
+ *                /      |      \
+ * new vertex--> v1      |       v3
+ *                \      |      /
+ *                  \    |    /
+ *                    \  |  /
+ *                      \|/
+ *                       v2
+ */
+template <typename T, typename TNearPointLocator>
+bool Triangulation<T, TNearPointLocator>::isFlipNeeded(
+    const V2d<T>& v,
+    const VertInd iV1,
+    const VertInd iV2,
+    const VertInd iV3,
+    const VertInd iV4) const
+{
+    if(fixedEdges.count(Edge(iV2, iV4)))
+        return false; // flip not needed if the original edge is fixed
+    const V2d<T>& v2 = vertices[iV2];
+    const V2d<T>& v3 = vertices[iV3];
+    const V2d<T>& v4 = vertices[iV4];
+    if(m_superGeomType == SuperGeometryType::SuperTriangle)
+    {
+        // If flip-candidate edge touches super-triangle in-circumference
+        // test has to be replaced with orient2d test against the line
+        // formed by two non-artificial vertices (that don't belong to
+        // super-triangle)
+        if(iV1 < 3) // flip-candidate edge touches super-triangle
+        {
+            // does original edge also touch super-triangle?
+            if(iV2 < 3)
+                return locatePointLine(v2, v3, v4) ==
+                       locatePointLine(v, v3, v4);
+            if(iV4 < 3)
+                return locatePointLine(v4, v2, v3) ==
+                       locatePointLine(v, v2, v3);
+            return false; // original edge does not touch super-triangle
+        }
+        if(iV3 < 3) // flip-candidate edge touches super-triangle
+        {
+            // does original edge also touch super-triangle?
+            if(iV2 < 3)
+                return locatePointLine(v2, v, v4) == locatePointLine(v3, v, v4);
+            if(iV4 < 3)
+                return locatePointLine(v4, v2, v) == locatePointLine(v3, v2, v);
+            return false; // original edge does not touch super-triangle
+        }
+        // flip-candidate edge does not touch super-triangle
+        if(iV2 < 3)
+            return locatePointLine(v2, v3, v4) == locatePointLine(v, v3, v4);
+        if(iV4 < 3)
+            return locatePointLine(v4, v2, v3) == locatePointLine(v, v2, v3);
+    }
+    return isInCircumcircle(v, v2, v3, v4);
+}
+
+/* Flip edge between T and Topo:
+ *
+ *                v4         | - old edge
+ *               /|\         ~ - new edge
+ *              / | \
+ *          n3 /  T' \ n4
+ *            /   |   \
+ *           /    |    \
+ *     T -> v1 ~~~~~~~~ v3 <- Topo
+ *           \    |    /
+ *            \   |   /
+ *          n1 \Topo'/ n2
+ *              \ | /
+ *               \|/
+ *                v2
+ */
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::flipEdge(
+    const TriInd iT,
+    const TriInd iTopo,
+    const VertInd v1,
+    const VertInd v2,
+    const VertInd v3,
+    const VertInd v4,
+    const TriInd n1,
+    const TriInd n2,
+    const TriInd n3,
+    const TriInd n4)
+{
+    // change vertices and neighbors
+    using detail::arr3;
+    triangles[iT] = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
+    triangles[iTopo] = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1));
+    // adjust neighboring triangles and vertices
+    changeNeighbor(n1, iT, iTopo);
+    changeNeighbor(n4, iTopo, iT);
+    // only adjust adjacent triangles if triangulation is not finalized:
+    // can happen when called from outside on an already finalized
+    // triangulation
+    if(!isFinalized())
+    {
+        setAdjacentTriangle(v4, iT);
+        setAdjacentTriangle(v2, iTopo);
+    }
+}
+
+/* Insert point into triangle: split into 3 triangles:
+ *  - create 2 new triangles
+ *  - re-use old triangle for the 3rd
+ *                      v3
+ *                    / | \
+ *                   /  |  \ <-- original triangle (t)
+ *                  /   |   \
+ *              n3 /    |    \ n2
+ *                /newT2|newT1\
+ *               /      v      \
+ *              /    __/ \__    \
+ *             /  __/       \__  \
+ *            / _/      t'     \_ \
+ *          v1 ___________________ v2
+ *                     n1
+ */
+template <typename T, typename TNearPointLocator>
+std::stack<TriInd>
+Triangulation<T, TNearPointLocator>::insertVertexInsideTriangle(
+    VertInd v,
+    TriInd iT)
+{
+    const TriInd iNewT1 = addTriangle();
+    const TriInd iNewT2 = addTriangle();
+
+    Triangle& t = triangles[iT];
+    const array<VertInd, 3> vv = t.vertices;
+    const array<TriInd, 3> nn = t.neighbors;
+    const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];
+    const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];
+    // make two new triangles and convert current triangle to 3rd new
+    // triangle
+    using detail::arr3;
+    triangles[iNewT1] = Triangle::make(arr3(v2, v3, v), arr3(n2, iNewT2, iT));
+    triangles[iNewT2] = Triangle::make(arr3(v3, v1, v), arr3(n3, iT, iNewT1));
+    t = Triangle::make(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));
+    // adjust adjacent triangles
+    setAdjacentTriangle(v, iT);
+    setAdjacentTriangle(v3, iNewT1);
+    // change triangle neighbor's neighbors to new triangles
+    changeNeighbor(n2, iT, iNewT1);
+    changeNeighbor(n3, iT, iNewT2);
+    // return newly added triangles
+    std::stack<TriInd> newTriangles;
+    newTriangles.push(iT);
+    newTriangles.push(iNewT1);
+    newTriangles.push(iNewT2);
+    return newTriangles;
+}
+
+/* Inserting a point on the edge between two triangles
+ *    T1 (top)        v1
+ *                   /|\
+ *              n1 /  |  \ n4
+ *               /    |    \
+ *             /  T1' | Tnew1\
+ *           v2-------v-------v4
+ *             \  T2' | Tnew2/
+ *               \    |    /
+ *              n2 \  |  / n3
+ *                   \|/
+ *   T2 (bottom)      v3
+ */
+template <typename T, typename TNearPointLocator>
+std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertVertexOnEdge(
+    VertInd v,
+    TriInd iT1,
+    TriInd iT2)
+{
+    const TriInd iTnew1 = addTriangle();
+    const TriInd iTnew2 = addTriangle();
+
+    Triangle& t1 = triangles[iT1];
+    Triangle& t2 = triangles[iT2];
+    Index i = opposedVertexInd(t1.neighbors, iT2);
+    const VertInd v1 = t1.vertices[i];
+    const VertInd v2 = t1.vertices[ccw(i)];
+    const TriInd n1 = t1.neighbors[i];
+    const TriInd n4 = t1.neighbors[cw(i)];
+    i = opposedVertexInd(t2.neighbors, iT1);
+    const VertInd v3 = t2.vertices[i];
+    const VertInd v4 = t2.vertices[ccw(i)];
+    const TriInd n3 = t2.neighbors[i];
+    const TriInd n2 = t2.neighbors[cw(i)];
+    // add new triangles and change existing ones
+    using detail::arr3;
+    t1 = Triangle::make(arr3(v, v1, v2), arr3(iTnew1, n1, iT2));
+    t2 = Triangle::make(arr3(v, v2, v3), arr3(iT1, n2, iTnew2));
+    triangles[iTnew1] = Triangle::make(arr3(v, v4, v1), arr3(iTnew2, n4, iT1));
+    triangles[iTnew2] = Triangle::make(arr3(v, v3, v4), arr3(iT2, n3, iTnew1));
+    // adjust adjacent triangles
+    setAdjacentTriangle(v, iT1);
+    setAdjacentTriangle(v4, iTnew1);
+    // adjust neighboring triangles and vertices
+    changeNeighbor(n4, iT1, iTnew1);
+    changeNeighbor(n3, iT2, iTnew2);
+    // return newly added triangles
+    std::stack<TriInd> newTriangles;
+    newTriangles.push(iT1);
+    newTriangles.push(iTnew2);
+    newTriangles.push(iT2);
+    newTriangles.push(iTnew1);
+    return newTriangles;
+}
+
+template <typename T, typename TNearPointLocator>
+array<TriInd, 2>
+Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const
+{
+    array<TriInd, 2> out = {noNeighbor, noNeighbor};
+    for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)
+    {
+        const Triangle& t = triangles[i];
+        const V2d<T>& v1 = vertices[t.vertices[0]];
+        const V2d<T>& v2 = vertices[t.vertices[1]];
+        const V2d<T>& v3 = vertices[t.vertices[2]];
+        const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
+        if(loc == PtTriLocation::Outside)
+            continue;
+        out[0] = i;
+        if(isOnEdge(loc))
+            out[1] = t.neighbors[edgeNeighbor(loc)];
+        return out;
+    }
+    throw std::runtime_error("No triangle was found at position");
+}
+
+template <typename T, typename TNearPointLocator>
+TriInd Triangulation<T, TNearPointLocator>::walkTriangles(
+    const VertInd startVertex,
+    const V2d<T>& pos) const
+{
+    // begin walk in search of triangle at pos
+    TriInd currTri = m_vertTris[startVertex];
+    bool found = false;
+    detail::SplitMix64RandGen prng;
+    while(!found)
+    {
+        const Triangle& t = triangles[currTri];
+        found = true;
+        // stochastic offset to randomize which edge we check first
+        const Index offset(prng() % 3);
+        for(Index i_(0); i_ < Index(3); ++i_)
+        {
+            const Index i((i_ + offset) % 3);
+            const V2d<T>& vStart = vertices[t.vertices[i]];
+            const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];
+            const PtLineLocation::Enum edgeCheck =
+                locatePointLine(pos, vStart, vEnd);
+            const TriInd iN = t.neighbors[i];
+            if(edgeCheck == PtLineLocation::Right && iN != noNeighbor)
+            {
+                found = false;
+                currTri = t.neighbors[i];
+                break;
+            }
+        }
+    }
+    return currTri;
+}
+
+template <typename T, typename TNearPointLocator>
+array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(
+    const V2d<T>& pos,
+    const VertInd startVertex) const
+{
+    array<TriInd, 2> out = {noNeighbor, noNeighbor};
+    const TriInd iT = walkTriangles(startVertex, pos);
+    // Finished walk, locate point in current triangle
+    const Triangle& t = triangles[iT];
+    const V2d<T>& v1 = vertices[t.vertices[0]];
+    const V2d<T>& v2 = vertices[t.vertices[1]];
+    const V2d<T>& v3 = vertices[t.vertices[2]];
+    const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
+    if(loc == PtTriLocation::Outside)
+        throw std::runtime_error("No triangle was found at position");
+    out[0] = iT;
+    if(isOnEdge(loc))
+        out[1] = t.neighbors[edgeNeighbor(loc)];
+    return out;
+}
+
+/* Flip edge between T and Topo:
+ *
+ *                v4         | - old edge
+ *               /|\         ~ - new edge
+ *              / | \
+ *          n3 /  T' \ n4
+ *            /   |   \
+ *           /    |    \
+ *     T -> v1~~~~~~~~~v3 <- Topo
+ *           \    |    /
+ *            \   |   /
+ *          n1 \Topo'/ n2
+ *              \ | /
+ *               \|/
+ *                v2
+ */
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::flipEdge(
+    const TriInd iT,
+    const TriInd iTopo)
+{
+    Triangle& t = triangles[iT];
+    Triangle& tOpo = triangles[iTopo];
+    const array<TriInd, 3>& triNs = t.neighbors;
+    const array<TriInd, 3>& triOpoNs = tOpo.neighbors;
+    const array<VertInd, 3>& triVs = t.vertices;
+    const array<VertInd, 3>& triOpoVs = tOpo.vertices;
+    // find vertices and neighbors
+    Index i = opposedVertexInd(t.neighbors, iTopo);
+    const VertInd v1 = triVs[i];
+    const VertInd v2 = triVs[ccw(i)];
+    const TriInd n1 = triNs[i];
+    const TriInd n3 = triNs[cw(i)];
+    i = opposedVertexInd(tOpo.neighbors, iT);
+    const VertInd v3 = triOpoVs[i];
+    const VertInd v4 = triOpoVs[ccw(i)];
+    const TriInd n4 = triOpoNs[i];
+    const TriInd n2 = triOpoNs[cw(i)];
+    // change vertices and neighbors
+    using detail::arr3;
+    t = Triangle::make(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
+    tOpo = Triangle::make(arr3(v2, v3, v1), arr3(n2, iT, n1));
+    // adjust neighboring triangles and vertices
+    changeNeighbor(n1, iT, iTopo);
+    changeNeighbor(n4, iTopo, iT);
+    // only adjust adjacent triangles if triangulation is not finalized:
+    // can happen when called from outside on an already finalized
+    // triangulation
+    if(!isFinalized())
+    {
+        setAdjacentTriangle(v4, iT);
+        setAdjacentTriangle(v2, iTopo);
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::changeNeighbor(
+    const TriInd iT,
+    const TriInd oldNeighbor,
+    const TriInd newNeighbor)
+{
+    if(iT == noNeighbor)
+        return;
+    NeighborsArr3& nn = triangles[iT].neighbors;
+    assert(
+        nn[0] == oldNeighbor || nn[1] == oldNeighbor || nn[2] == oldNeighbor);
+    if(nn[0] == oldNeighbor)
+        nn[0] = newNeighbor;
+    else if(nn[1] == oldNeighbor)
+        nn[1] = newNeighbor;
+    else
+        nn[2] = newNeighbor;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::changeNeighbor(
+    const TriInd iT,
+    const VertInd iVedge1,
+    const VertInd iVedge2,
+    const TriInd newNeighbor)
+{
+    assert(iT != noNeighbor);
+    Triangle& t = triangles[iT];
+    t.neighbors[edgeNeighborInd(t.vertices, iVedge1, iVedge2)] = newNeighbor;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::triangulatePseudopolygon(
+    const std::vector<VertInd>& poly,
+    const std::vector<TriInd>& outerTris,
+    const TriInd iT,
+    const TriInd iN,
+    std::vector<TriangulatePseudopolygonTask>& iterations)
+{
+    assert(poly.size() > 2);
+
+    // note: needed for proper linking with outer triangles
+    // during pseudo-polygon triangulation, vertex triangle
+    // will be set back, see asserts at the end
+    for(std::size_t i = 1; i < outerTris.size(); ++i)
+        if(outerTris[i] == noNeighbor)
+            m_vertTris[poly[i]] = noNeighbor;
+
+    // note: uses interation instead of recursion to avoid stack overflows
+    iterations.clear();
+    iterations.push_back(make_tuple(
+        IndexSizeType(0),
+        static_cast<IndexSizeType>(poly.size() - 1),
+        iT,
+        iN,
+        Index(0)));
+    while(!iterations.empty())
+    {
+        triangulatePseudopolygonIteration(poly, outerTris, iterations);
+    }
+
+    // make sure adjacent triangles were restored
+    for(std::size_t i = 0; i < poly.size(); ++i)
+        assert(m_vertTris[poly[i]] != noNeighbor);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::triangulatePseudopolygonIteration(
+    const std::vector<VertInd>& poly,
+    const std::vector<TriInd>& outerTris,
+    std::vector<TriangulatePseudopolygonTask>& iterations)
+{
+    IndexSizeType iA, iB;
+    TriInd iT, iParent;
+    Index iInParent;
+    assert(!iterations.empty());
+    tie(iA, iB, iT, iParent, iInParent) = iterations.back();
+    iterations.pop_back();
+    assert(iB - iA > 1 && iT != noNeighbor && iParent != noNeighbor);
+    Triangle& t = triangles[iT];
+    // find Delaunay point
+    const IndexSizeType iC = findDelaunayPoint(poly, iA, iB);
+
+    const VertInd a = poly[iA];
+    const VertInd b = poly[iB];
+    const VertInd c = poly[iC];
+    // split pseudo-polygon in two parts and triangulate them
+    //
+    // note: first part needs to be pushed on stack last
+    // in order to be processed first
+    //
+    // second part: points after the Delaunay point
+    if(iB - iC > 1)
+    {
+        const TriInd iNext = addTriangle();
+        iterations.push_back(make_tuple(iC, iB, iNext, iT, Index(1)));
+    }
+    else // pseudo-poly is reduced to a single outer edge
+    {
+        const TriInd outerTri = outerTris[iC];
+        if(outerTri != noNeighbor)
+        {
+            assert(outerTri != iT);
+            t.neighbors[1] = outerTri;
+            changeNeighbor(outerTri, c, b, iT);
+        }
+    }
+    // first part: points before the Delaunay point
+    if(iC - iA > 1)
+    { // add next triangle and add another iteration
+        const TriInd iNext = addTriangle();
+        iterations.push_back(make_tuple(iA, iC, iNext, iT, Index(2)));
+    }
+    else
+    { // pseudo-poly is reduced to a single outer edge
+        const TriInd outerTri =
+            outerTris[iA] != noNeighbor ? outerTris[iA] : m_vertTris[c];
+        if(outerTri != noNeighbor)
+        {
+            assert(outerTri != iT);
+            t.neighbors[2] = outerTri;
+            changeNeighbor(outerTri, c, a, iT);
+        }
+    }
+    // Finalize triangle
+    // note: only when triangle is finalized to we add it as a neighbor to
+    // parent to maintain triangulation topology consistency
+    triangles[iParent].neighbors[iInParent] = iT;
+    t.neighbors[0] = iParent;
+    t.vertices = detail::arr3(a, b, c);
+    setAdjacentTriangle(a, iT);
+    setAdjacentTriangle(c, iT);
+}
+
+template <typename T, typename TNearPointLocator>
+IndexSizeType Triangulation<T, TNearPointLocator>::findDelaunayPoint(
+    const std::vector<VertInd>& poly,
+    const IndexSizeType iA,
+    const IndexSizeType iB) const
+{
+    assert(iB - iA > 1);
+    const V2d<T>& a = vertices[poly[iA]];
+    const V2d<T>& b = vertices[poly[iB]];
+    IndexSizeType out = iA + 1;
+    const V2d<T>* c = &vertices[poly[out]]; // caching for better performance
+    for(IndexSizeType i = iA + 1; i < iB; ++i)
+    {
+        const V2d<T>& v = vertices[poly[i]];
+        if(isInCircumcircle(v, a, b, *c))
+        {
+            out = i;
+            c = &v;
+        }
+    }
+    assert(out > iA && out < iB); // point is between ends
+    return out;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertices(
+    const std::vector<V2d<T> >& newVertices)
+{
+    return insertVertices(
+        newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);
+}
+
+template <typename T, typename TNearPointLocator>
+bool Triangulation<T, TNearPointLocator>::isFinalized() const
+{
+    return m_vertTris.empty() && !vertices.empty();
+}
+
+template <typename T, typename TNearPointLocator>
+unordered_map<TriInd, LayerDepth>
+Triangulation<T, TNearPointLocator>::peelLayer(
+    std::stack<TriInd> seeds,
+    const LayerDepth layerDepth,
+    std::vector<LayerDepth>& triDepths) const
+{
+    unordered_map<TriInd, LayerDepth> behindBoundary;
+    while(!seeds.empty())
+    {
+        const TriInd iT = seeds.top();
+        seeds.pop();
+        triDepths[iT] = std::min(triDepths[iT], layerDepth);
+        behindBoundary.erase(iT);
+        const Triangle& t = triangles[iT];
+        for(Index i(0); i < Index(3); ++i)
+        {
+            const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
+            const TriInd iN = t.neighbors[opoNbr(i)];
+            if(iN == noNeighbor || triDepths[iN] <= layerDepth)
+                continue;
+            if(fixedEdges.count(opEdge))
+            {
+                const unordered_map<Edge, LayerDepth>::const_iterator cit =
+                    overlapCount.find(opEdge);
+                const LayerDepth triDepth = cit == overlapCount.end()
+                                                ? layerDepth + 1
+                                                : layerDepth + cit->second + 1;
+                behindBoundary[iN] = triDepth;
+                continue;
+            }
+            seeds.push(iN);
+        }
+    }
+    return behindBoundary;
+}
+
+template <typename T, typename TNearPointLocator>
+std::vector<LayerDepth>
+Triangulation<T, TNearPointLocator>::calculateTriangleDepths() const
+{
+    std::vector<LayerDepth> triDepths(
+        triangles.size(), std::numeric_limits<LayerDepth>::max());
+    std::stack<TriInd> seeds(TriDeque(1, m_vertTris[0]));
+    LayerDepth layerDepth = 0;
+    LayerDepth deepestSeedDepth = 0;
+
+    unordered_map<LayerDepth, TriIndUSet> seedsByDepth;
+    do
+    {
+        const unordered_map<TriInd, LayerDepth>& newSeeds =
+            peelLayer(seeds, layerDepth, triDepths);
+
+        seedsByDepth.erase(layerDepth);
+        typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;
+        for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)
+        {
+            deepestSeedDepth = std::max(deepestSeedDepth, it->second);
+            seedsByDepth[it->second].insert(it->first);
+        }
+        const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];
+        seeds = std::stack<TriInd>(
+            TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));
+        ++layerDepth;
+    } while(!seeds.empty() || deepestSeedDepth > layerDepth);
+
+    return triDepths;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertices_AsProvided(
+    VertInd superGeomVertCount)
+{
+    for(VertInd iV = superGeomVertCount; iV < vertices.size(); ++iV)
+    {
+        insertVertex(iV);
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertices_Randomized(
+    VertInd superGeomVertCount)
+{
+    std::size_t vertexCount = vertices.size() - superGeomVertCount;
+    std::vector<VertInd> ii(vertexCount);
+    detail::iota(ii.begin(), ii.end(), superGeomVertCount);
+    detail::random_shuffle(ii.begin(), ii.end());
+    for(std::vector<VertInd>::iterator it = ii.begin(); it != ii.end(); ++it)
+    {
+        insertVertex(*it);
+    }
+}
+
+namespace detail
+{
+
+// log2 implementation backwards compatible with pre c++11
+template <typename T>
+inline double log2_bc(T x)
+{
+#ifdef CDT_CXX11_IS_SUPPORTED
+    return std::log2(x);
+#else
+    static double log2_constant = std::log(2.0);
+    return std::log(static_cast<double>(x)) / log2_constant;
+#endif
+}
+
+/// Since KD-tree bulk load builds a balanced tree the maximum length of a
+/// queue can be pre-calculated: it is calculated as size of a completely
+/// filled tree layer plus the number of the nodes on a completely filled
+/// layer that have two children.
+inline std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount)
+{
+    const int filledLayerPow2 =
+        static_cast<int>(std::floor(log2_bc(vertexCount)) - 1);
+    const std::size_t nodesInFilledTree =
+        static_cast<std::size_t>(std::pow(2., filledLayerPow2 + 1) - 1);
+    const std::size_t nodesInLastFilledLayer =
+        static_cast<std::size_t>(std::pow(2., filledLayerPow2));
+    const std::size_t nodesInLastLayer = vertexCount - nodesInFilledTree;
+    return nodesInLastLayer >= nodesInLastFilledLayer
+               ? nodesInLastFilledLayer + nodesInLastLayer -
+                     nodesInLastFilledLayer
+               : nodesInLastFilledLayer;
+}
+
+template <typename T>
+class FixedCapacityQueue
+{
+public:
+    FixedCapacityQueue(const std::size_t capacity)
+        : m_vec(capacity)
+        , m_front(m_vec.begin())
+        , m_back(m_vec.begin())
+        , m_size(0)
+    {}
+    bool empty() const
+    {
+        return m_size == 0;
+    }
+    const T& front() const
+    {
+        return *m_front;
+    }
+    void pop()
+    {
+        assert(m_size > 0);
+        ++m_front;
+        if(m_front == m_vec.end())
+            m_front = m_vec.begin();
+        --m_size;
+    }
+    void push(const T& t)
+    {
+        assert(m_size < m_vec.size());
+        *m_back = t;
+        ++m_back;
+        if(m_back == m_vec.end())
+            m_back = m_vec.begin();
+        ++m_size;
+    }
+#ifdef CDT_CXX11_IS_SUPPORTED
+    void push(const T&& t)
+    {
+        assert(m_size < m_vec.size());
+        *m_back = t;
+        ++m_back;
+        if(m_back == m_vec.end())
+            m_back = m_vec.begin();
+        ++m_size;
+    }
+#endif
+private:
+    std::vector<T> m_vec;
+    typename std::vector<T>::iterator m_front;
+    typename std::vector<T>::iterator m_back;
+    std::size_t m_size;
+};
+
+template <typename T>
+struct less_than_x
+{
+    less_than_x(const std::vector<V2d<T> >& vertices)
+        : vertices(vertices)
+    {}
+    bool operator()(const VertInd a, const VertInd b) const
+    {
+        return vertices[a].x < vertices[b].x;
+    }
+    const std::vector<V2d<T> >& vertices;
+};
+
+template <typename T>
+struct less_than_y
+{
+    less_than_y(const std::vector<V2d<T> >& vertices)
+        : vertices(vertices)
+    {}
+    bool operator()(const VertInd a, const VertInd b) const
+    {
+        return vertices[a].y < vertices[b].y;
+    }
+    const std::vector<V2d<T> >& vertices;
+};
+
+} // namespace detail
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::insertVertices_KDTreeBFS(
+    VertInd superGeomVertCount,
+    V2d<T> boxMin,
+    V2d<T> boxMax)
+{
+    // calculate original indices
+    const VertInd vertexCount = vertices.size() - superGeomVertCount;
+    if(vertexCount <= 0)
+        return;
+    std::vector<VertInd> ii(vertexCount);
+    detail::iota(ii.begin(), ii.end(), superGeomVertCount);
+
+    typedef std::vector<VertInd>::iterator It;
+    detail::FixedCapacityQueue<tuple<It, It, V2d<T>, V2d<T>, VertInd> > queue(
+        detail::maxQueueLengthBFSKDTree(vertexCount));
+    queue.push(make_tuple(ii.begin(), ii.end(), boxMin, boxMax, VertInd(0)));
+
+    It first, last;
+    V2d<T> newBoxMin, newBoxMax;
+    VertInd parent, mid;
+
+    const detail::less_than_x<T> cmpX(vertices);
+    const detail::less_than_y<T> cmpY(vertices);
+
+    while(!queue.empty())
+    {
+        tie(first, last, boxMin, boxMax, parent) = queue.front();
+        queue.pop();
+        assert(first != last);
+
+        const std::ptrdiff_t len = std::distance(first, last);
+        if(len == 1)
+        {
+            insertVertex(*first, parent);
+            continue;
+        }
+        const It midIt = first + len / 2;
+        if(boxMax.x - boxMin.x >= boxMax.y - boxMin.y)
+        {
+            detail::portable_nth_element(first, midIt, last, cmpX);
+            mid = *midIt;
+            const T split = vertices[mid].x;
+            newBoxMin.x = split;
+            newBoxMin.y = boxMin.y;
+            newBoxMax.x = split;
+            newBoxMax.y = boxMax.y;
+        }
+        else
+        {
+            detail::portable_nth_element(first, midIt, last, cmpY);
+            mid = *midIt;
+            const T split = vertices[mid].y;
+            newBoxMin.x = boxMin.x;
+            newBoxMin.y = split;
+            newBoxMax.x = boxMax.x;
+            newBoxMax.y = split;
+        }
+        insertVertex(mid, parent);
+        if(first != midIt)
+        {
+            queue.push(make_tuple(first, midIt, boxMin, newBoxMax, mid));
+        }
+        if(midIt + 1 != last)
+        {
+            queue.push(make_tuple(midIt + 1, last, newBoxMin, boxMax, mid));
+        }
+    }
+}
+
+template <typename T, typename TNearPointLocator>
+std::pair<TriInd, TriInd> Triangulation<T, TNearPointLocator>::edgeTriangles(
+    const VertInd a,
+    const VertInd b) const
+{
+    const TriInd triStart = m_vertTris[a];
+    assert(triStart != noNeighbor);
+    TriInd iT = triStart, iTNext = triStart;
+    VertInd iV = noVertex;
+    do
+    {
+        const Triangle& t = triangles[iT];
+        tie(iTNext, iV) = t.next(a);
+        assert(iTNext != noNeighbor);
+        if(iV == b)
+        {
+            return std::make_pair(iT, iTNext);
+        }
+        iT = iTNext;
+    } while(iT != triStart);
+    return std::make_pair(invalidIndex, invalidIndex);
+}
+
+template <typename T, typename TNearPointLocator>
+bool Triangulation<T, TNearPointLocator>::hasEdge(
+    const VertInd a,
+    const VertInd b) const
+{
+    return edgeTriangles(a, b).first != invalidIndex;
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::setAdjacentTriangle(
+    const VertInd v,
+    const TriInd t)
+{
+    assert(t != noNeighbor);
+    m_vertTris[v] = t;
+    assert(
+        triangles[t].vertices[0] == v || triangles[t].vertices[1] == v ||
+        triangles[t].vertices[2] == v);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::pivotVertexTriangleCW(const VertInd v)
+{
+    assert(m_vertTris[v] != noNeighbor);
+    m_vertTris[v] = triangles[m_vertTris[v]].next(v).first;
+    assert(m_vertTris[v] != noNeighbor);
+    assert(
+        triangles[m_vertTris[v]].vertices[0] == v ||
+        triangles[m_vertTris[v]].vertices[1] == v ||
+        triangles[m_vertTris[v]].vertices[2] == v);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::tryAddVertexToLocator(const VertInd v)
+{
+    if(!m_nearPtLocator.empty()) // only if locator is initialized already
+        m_nearPtLocator.addPoint(v, vertices);
+}
+
+template <typename T, typename TNearPointLocator>
+void Triangulation<T, TNearPointLocator>::tryInitNearestPointLocator()
+{
+    if(!vertices.empty() && m_nearPtLocator.empty())
+    {
+        m_nearPtLocator.initialize(vertices);
+    }
+}
+
+} // namespace CDT
diff --git a/cpp/hcdt.cpp b/cpp/hcdt.cpp
--- a/cpp/hcdt.cpp
+++ b/cpp/hcdt.cpp
@@ -1,133 +1,133 @@
-#include <cstdlib> // to use malloc
-#include <stdio.h> // to use printf
-#include "hcdt.hpp"
-#include "CDT.h"
-
-extern "C"
-{
-
-TriangulationT* delaunay(VertexT* points, size_t npoints){
-  CDT::Triangulation<double> cdt(CDT::VertexInsertionOrder::AsProvided);
-  // insert vertices
-  std::vector<CDT::V2d<double>> vertices(npoints);
-  for(size_t k = 0; k < npoints; ++k) {
-    const VertexT v = points[k];
-    vertices[k] = CDT::V2d<double>::make(v.x, v.y);
-  }
-  cdt.insertVertices(vertices);
-  cdt.eraseSuperTriangle();
-  //// output
-  // triangles
-  const CDT::TriangleVec triangles = cdt.triangles;
-  const size_t ntriangles = triangles.size();
-  TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));
-  for(size_t k = 0; k < ntriangles; ++k){
-    const CDT::VerticesArr3 trgl = triangles[k].vertices;
-    out_triangles[k].i1 = trgl[0];
-    out_triangles[k].i2 = trgl[1];
-    out_triangles[k].i3 = trgl[2];
-  }
-  // all edges
-  CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);
-  const size_t nedges = allEdges.size();
-  EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));
-  std::unordered_set<CDT::Edge> :: iterator it;
-  size_t k = 0;
-  for(it = allEdges.begin(); it != allEdges.end(); it++){
-    const CDT::Edge edge = *it;
-    out_alledges[k].i = CDT::edge_get_v1(edge);
-    out_alledges[k].j = CDT::edge_get_v2(edge);
-    k++;
-  }
-  /* output mesh */
-  TriangulationT* out = (TriangulationT*)malloc(sizeof(TriangulationT));
-  out->vertices = points;
-  out->nvertices = npoints;
-  out->triangles = out_triangles;
-  out->ntriangles = ntriangles;
-  out->edges = out_alledges;
-  out->nedges = nedges;
-  return out;
-}
-
-CTriangulationT* cdelaunay(VertexT* points, size_t npoints, EdgeT* fedges, size_t nfedges){
-  CDT::Triangulation<double> cdt(
-    CDT::VertexInsertionOrder::AsProvided,
-    CDT::IntersectingConstraintEdges::Resolve,
-    0.0
-  );
-  // insert vertices
-  std::vector<CDT::V2d<double>> vertices(npoints);
-  for(size_t k = 0; k < npoints; ++k) {
-    const VertexT v = points[k];
-    vertices[k] = CDT::V2d<double>::make(v.x, v.y);
-  }
-  // insert edges
-  std::vector<CDT::Edge> Edges;
-  Edges.reserve(nfedges);
-  for (size_t k = 0; k < nfedges; ++k) {
-    const EdgeT e = fedges[k];
-    Edges.push_back(CDT::Edge(e.i, e.j));
-  }
-  cdt.insertVertices(vertices);
-  cdt.insertEdges(Edges);
-  cdt.eraseOuterTrianglesAndHoles();
-  //// output
-  // vertices
-  const std::vector<CDT::V2d<double>> cdt_vertices = cdt.vertices;
-  const size_t nvertices = cdt_vertices.size();
-  VertexT* out_vertices = (VertexT*)malloc(nvertices * sizeof(VertexT));
-  for(size_t k = 0; k < nvertices; ++k){
-    const CDT::V2d<double> v = cdt_vertices[k];
-    out_vertices[k].x = v.x;
-    out_vertices[k].y = v.y;
-  }
-  // triangles
-  const CDT::TriangleVec triangles = cdt.triangles;
-  const size_t ntriangles = triangles.size();
-  //printf("ntriangles: %u\n", ntriangles);
-  TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));
-  for(size_t k = 0; k < ntriangles; ++k){
-    const CDT::VerticesArr3 trgl = triangles[k].vertices;
-    out_triangles[k].i1 = trgl[0];
-    out_triangles[k].i2 = trgl[1];
-    out_triangles[k].i3 = trgl[2];
-  }
-  // all edges
-  CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);
-  const size_t nedges = allEdges.size();
-  EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));
-  std::unordered_set<CDT::Edge> :: iterator it;
-  size_t k = 0;
-  for(it = allEdges.begin(); it != allEdges.end(); it++){
-    const CDT::Edge edge = *it;
-    out_alledges[k].i = CDT::edge_get_v1(edge);
-    out_alledges[k].j = CDT::edge_get_v2(edge);
-    k++;
-  }
-  // fixed edges
-  CDT::EdgeUSet fixedEdges = cdt.fixedEdges;
-  const size_t nfixededges = fixedEdges.size();
-  EdgeT* out_fixededges = (EdgeT*)malloc(nfixededges * sizeof(EdgeT));
-  std::unordered_set<CDT::Edge> :: iterator itedge;
-  size_t l = 0;
-  for(itedge = fixedEdges.begin(); itedge != fixedEdges.end(); itedge++){
-    const CDT::Edge edge = *itedge;
-    out_fixededges[l].i = CDT::edge_get_v1(edge);
-    out_fixededges[l].j = CDT::edge_get_v2(edge);
-    l++;
-  }
-  /* output mesh */
-  CTriangulationT* out = (CTriangulationT*)malloc(sizeof(CTriangulationT));
-  out->vertices = out_vertices;
-  out->nvertices = nvertices;
-  out->triangles = out_triangles;
-  out->ntriangles = ntriangles;
-  out->edges = out_alledges;
-  out->nedges = nedges;
-  out->fixededges = out_fixededges;
-  out->nfixededges = nfixededges;
-  return out;
-}
-
+#include <cstdlib> // to use malloc
+#include <stdio.h> // to use printf
+#include "hcdt.hpp"
+#include "CDT.h"
+
+extern "C"
+{
+
+TriangulationT* delaunay(VertexT* points, size_t npoints){
+  CDT::Triangulation<double> cdt(CDT::VertexInsertionOrder::AsProvided);
+  // insert vertices
+  std::vector<CDT::V2d<double>> vertices(npoints);
+  for(size_t k = 0; k < npoints; ++k) {
+    const VertexT v = points[k];
+    vertices[k] = CDT::V2d<double>::make(v.x, v.y);
+  }
+  cdt.insertVertices(vertices);
+  cdt.eraseSuperTriangle();
+  //// output
+  // triangles
+  const CDT::TriangleVec triangles = cdt.triangles;
+  const size_t ntriangles = triangles.size();
+  TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));
+  for(size_t k = 0; k < ntriangles; ++k){
+    const CDT::VerticesArr3 trgl = triangles[k].vertices;
+    out_triangles[k].i1 = trgl[0];
+    out_triangles[k].i2 = trgl[1];
+    out_triangles[k].i3 = trgl[2];
+  }
+  // all edges
+  CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);
+  const size_t nedges = allEdges.size();
+  EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));
+  std::unordered_set<CDT::Edge> :: iterator it;
+  size_t k = 0;
+  for(it = allEdges.begin(); it != allEdges.end(); it++){
+    const CDT::Edge edge = *it;
+    out_alledges[k].i = CDT::edge_get_v1(edge);
+    out_alledges[k].j = CDT::edge_get_v2(edge);
+    k++;
+  }
+  /* output mesh */
+  TriangulationT* out = (TriangulationT*)malloc(sizeof(TriangulationT));
+  out->vertices = points;
+  out->nvertices = npoints;
+  out->triangles = out_triangles;
+  out->ntriangles = ntriangles;
+  out->edges = out_alledges;
+  out->nedges = nedges;
+  return out;
+}
+
+CTriangulationT* cdelaunay(VertexT* points, size_t npoints, EdgeT* fedges, size_t nfedges){
+  CDT::Triangulation<double> cdt(
+    CDT::VertexInsertionOrder::AsProvided,
+    CDT::IntersectingConstraintEdges::Resolve,
+    0.0
+  );
+  // insert vertices
+  std::vector<CDT::V2d<double>> vertices(npoints);
+  for(size_t k = 0; k < npoints; ++k) {
+    const VertexT v = points[k];
+    vertices[k] = CDT::V2d<double>::make(v.x, v.y);
+  }
+  // insert edges
+  std::vector<CDT::Edge> Edges;
+  Edges.reserve(nfedges);
+  for (size_t k = 0; k < nfedges; ++k) {
+    const EdgeT e = fedges[k];
+    Edges.push_back(CDT::Edge(e.i, e.j));
+  }
+  cdt.insertVertices(vertices);
+  cdt.insertEdges(Edges);
+  cdt.eraseOuterTrianglesAndHoles();
+  //// output
+  // vertices
+  const std::vector<CDT::V2d<double>> cdt_vertices = cdt.vertices;
+  const size_t nvertices = cdt_vertices.size();
+  VertexT* out_vertices = (VertexT*)malloc(nvertices * sizeof(VertexT));
+  for(size_t k = 0; k < nvertices; ++k){
+    const CDT::V2d<double> v = cdt_vertices[k];
+    out_vertices[k].x = v.x;
+    out_vertices[k].y = v.y;
+  }
+  // triangles
+  const CDT::TriangleVec triangles = cdt.triangles;
+  const size_t ntriangles = triangles.size();
+  //printf("ntriangles: %u\n", ntriangles);
+  TriangleT* out_triangles = (TriangleT*)malloc(ntriangles * sizeof(TriangleT));
+  for(size_t k = 0; k < ntriangles; ++k){
+    const CDT::VerticesArr3 trgl = triangles[k].vertices;
+    out_triangles[k].i1 = trgl[0];
+    out_triangles[k].i2 = trgl[1];
+    out_triangles[k].i3 = trgl[2];
+  }
+  // all edges
+  CDT::EdgeUSet allEdges = CDT::extractEdgesFromTriangles(triangles);
+  const size_t nedges = allEdges.size();
+  EdgeT* out_alledges = (EdgeT*)malloc(nedges * sizeof(EdgeT));
+  std::unordered_set<CDT::Edge> :: iterator it;
+  size_t k = 0;
+  for(it = allEdges.begin(); it != allEdges.end(); it++){
+    const CDT::Edge edge = *it;
+    out_alledges[k].i = CDT::edge_get_v1(edge);
+    out_alledges[k].j = CDT::edge_get_v2(edge);
+    k++;
+  }
+  // fixed edges
+  CDT::EdgeUSet fixedEdges = cdt.fixedEdges;
+  const size_t nfixededges = fixedEdges.size();
+  EdgeT* out_fixededges = (EdgeT*)malloc(nfixededges * sizeof(EdgeT));
+  std::unordered_set<CDT::Edge> :: iterator itedge;
+  size_t l = 0;
+  for(itedge = fixedEdges.begin(); itedge != fixedEdges.end(); itedge++){
+    const CDT::Edge edge = *itedge;
+    out_fixededges[l].i = CDT::edge_get_v1(edge);
+    out_fixededges[l].j = CDT::edge_get_v2(edge);
+    l++;
+  }
+  /* output mesh */
+  CTriangulationT* out = (CTriangulationT*)malloc(sizeof(CTriangulationT));
+  out->vertices = out_vertices;
+  out->nvertices = nvertices;
+  out->triangles = out_triangles;
+  out->ntriangles = ntriangles;
+  out->edges = out_alledges;
+  out->nedges = nedges;
+  out->fixededges = out_fixededges;
+  out->nfixededges = nfixededges;
+  return out;
+}
+
 }
diff --git a/cpp/hcdt.hpp b/cpp/hcdt.hpp
--- a/cpp/hcdt.hpp
+++ b/cpp/hcdt.hpp
@@ -1,35 +1,35 @@
-typedef struct Vertex {
-  double x;
-  double y;
-} VertexT;
-
-typedef struct Edge {
-  unsigned i;
-  unsigned j;
-} EdgeT;
-
-typedef struct Triangle {
-  unsigned i1;
-  unsigned i2;
-  unsigned i3;
-} TriangleT;
-
-typedef struct Triangulation {
-  VertexT*   vertices;
-  size_t     nvertices;
-  TriangleT* triangles;
-  size_t     ntriangles;
-  EdgeT*     edges;
-  size_t     nedges;
-} TriangulationT;
-
-typedef struct CTriangulation {
-  VertexT*   vertices;
-  size_t     nvertices;
-  TriangleT* triangles;
-  size_t     ntriangles;
-  EdgeT*     edges;
-  size_t     nedges;
-  EdgeT*     fixededges;
-  size_t     nfixededges;
+typedef struct Vertex {
+  double x;
+  double y;
+} VertexT;
+
+typedef struct Edge {
+  unsigned i;
+  unsigned j;
+} EdgeT;
+
+typedef struct Triangle {
+  unsigned i1;
+  unsigned i2;
+  unsigned i3;
+} TriangleT;
+
+typedef struct Triangulation {
+  VertexT*   vertices;
+  size_t     nvertices;
+  TriangleT* triangles;
+  size_t     ntriangles;
+  EdgeT*     edges;
+  size_t     nedges;
+} TriangulationT;
+
+typedef struct CTriangulation {
+  VertexT*   vertices;
+  size_t     nvertices;
+  TriangleT* triangles;
+  size_t     ntriangles;
+  EdgeT*     edges;
+  size_t     nedges;
+  EdgeT*     fixededges;
+  size_t     nfixededges;
 } CTriangulationT;
diff --git a/cpp/predicates.h b/cpp/predicates.h
--- a/cpp/predicates.h
+++ b/cpp/predicates.h
@@ -1,939 +1,915 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                                 *
- * Copyright (c) 2019, William C. Lenthe                                           *
- * All rights reserved.                                                            *
- *                                                                                 *
- * Redistribution and use in source and binary forms, with or without              *
- * modification, are permitted provided that the following conditions are met:     *
- *                                                                                 *
- * 1. Redistributions of source code must retain the above copyright notice, this  *
- *    list of conditions and the following disclaimer.                             *
- *                                                                                 *
- * 2. Redistributions in binary form must reproduce the above copyright notice,    *
- *    this list of conditions and the following disclaimer in the documentation    *
- *    and/or other materials provided with the distribution.                       *
- *                                                                                 *
- * 3. Neither the name of the copyright holder nor the names of its                *
- *    contributors may be used to endorse or promote products derived from         *
- *    this software without specific prior written permission.                     *
- *                                                                                 *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"     *
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       *
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  *
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE    *
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL      *
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR      *
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   *
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   *
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            *
- *                                                                                 *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#ifndef PREDICATES_H_INCLUDED
-#define PREDICATES_H_INCLUDED
-
-//@reference: https://www.cs.cmu.edu/~quake/robust.html
-
-namespace  predicates {
-	//@brief: geometric predicates using arbitrary precision arithmetic 
-	//@note : these are provided primarily for illustrative purposes and adaptive routines should be preferred
-	namespace exact {
-		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
-		//@param ax: X-coordinate of a
-		//@param ay: Y-coordinate of a
-		//@param bx: X-coordinate of b
-		//@param by: Y-coordinate of b
-		//@param cx: X-coordinate of c
-		//@param cy: Y-coordinate of c
-		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
-		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
-		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);
-
-		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
-		//@param pa: pointer to a as {x, y}
-		//@param pb: pointer to b as {x, y}
-		//@param pc: pointer to c as {x, y}
-		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
-		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
-		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);
-
-		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
-		//@param ax: X-coordinate of a
-		//@param ay: Y-coordinate of a
-		//@param bx: X-coordinate of b
-		//@param by: Y-coordinate of b
-		//@param cx: X-coordinate of c
-		//@param cy: Y-coordinate of c
-		//@param dx: X-coordinate of d
-		//@param dy: Y-coordinate of d
-		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);
-
-		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
-		//@param pa: pointer to a as {x, y}
-		//@param pb: pointer to b as {x, y}
-		//@param pc: pointer to c as {x, y}
-		//@param pc: pointer to d as {x, y}
-		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
-
-		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
-		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
-		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
-		
-		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@param pe: pointer to e as {x, y, z}
-		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);
-	}
-
-	//@brief: geometric predicates using normal floating point arithmetic but falling back to arbitrary precision when needed
-	//@note : these should have the same accuracy but are significantly faster when determinants are large
-	namespace adaptive {
-		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
-		//@param ax: X-coordinate of a
-		//@param ay: Y-coordinate of a
-		//@param bx: X-coordinate of b
-		//@param by: Y-coordinate of b
-		//@param cx: X-coordinate of c
-		//@param cy: Y-coordinate of c
-		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
-		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
-		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);
-
-		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
-		//@param pa: pointer to a as {x, y}
-		//@param pb: pointer to b as {x, y}
-		//@param pc: pointer to c as {x, y}
-		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
-		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
-		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);
-
-		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
-		//@param ax: X-coordinate of a
-		//@param ay: Y-coordinate of a
-		//@param bx: X-coordinate of b
-		//@param by: Y-coordinate of b
-		//@param cx: X-coordinate of c
-		//@param cy: Y-coordinate of c
-		//@param dx: X-coordinate of d
-		//@param dy: Y-coordinate of d
-		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);
-
-		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
-		//@param pa: pointer to a as {x, y}
-		//@param pb: pointer to b as {x, y}
-		//@param pc: pointer to c as {x, y}
-		//@param pc: pointer to d as {x, y}
-		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
-
-		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
-		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
-		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
-
-		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@param pe: pointer to e as {x, y, z}
-		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);
-	}
-}
-
-#include <cmath>//abs, fma
-#include <limits>
-#include <utility>//pair
-#include <numeric>//accumulate
-#include <algorithm>//transform, copy_n, merge
-#include <functional>//negate
-
-// a macro based static assert for pre c++11
-#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) typedef char message[(condition) ? 1 : -1]
-
-// check if c++11 is supported
-#if !defined(__cplusplus) && !defined(_MSC_VER)
-	PREDICATES_PORTABLE_STATIC_ASSERT(false, couldnt_parse_cxx_standard)
-#endif
-#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
-	#define PREDICATES_CXX11_IS_SUPPORTED
-#endif
-
-// choose to use c++11 features or their backports
-#ifdef PREDICATES_CXX11_IS_SUPPORTED
-#include <array>
-#include <type_traits>// is_same, enable_if
-#undef PREDICATES_PORTABLE_STATIC_ASSERT
-#define PREDICATES_TOKEN_TO_STRING1(x)  #x
-#define PREDICATES_TOKEN_TO_STRING(x)  PREDICATES_TOKEN_TO_STRING1(x)
-#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) static_assert(condition, PREDICATES_TOKEN_TO_STRING(message))
-namespace  predicates {
-namespace stdx {
-	using std::array;
-	using std::copy_n;
-}
-#else
-namespace  predicates {
-namespace stdx {
-	// array
-	template<typename T, size_t N>
-	class array {
-		T buff[N];
-	public:
-		T& operator[](const size_t& i) { return buff[i]; }
-		const T& operator[](const size_t& i) const { return buff[i]; }
-
-		T       * data() { return buff; }
-		T const * data() const { return buff; }
-
-		T       *  begin() { return buff; }
-		T const * cbegin() const { return buff; }
-	};
-	// copy_n
-	template< class InputIt, class Size, class OutputIt>
-	OutputIt copy_n(InputIt first, Size count, OutputIt result)
-	{
-		if (count > 0) {
-			*result++ = *first;
-			for (Size i = 1; i < count; ++i) {
-				*result++ = *++first;
-			}
-		}
-		return result;
-	}
-}
-#endif // PREDICATES_CXX11_IS_SUPPORTED
-
-namespace detail {
-	template<typename T> class ExpansionBase;
-
-	//@brief: class to exactly represent the result of a sequence of arithmetic operations as an sequence of values that sum to the result
-	template<typename T, size_t N>
-	class Expansion : private ExpansionBase<T>, public stdx::array<T, N> {
-		private:
-		public:
-			size_t m_size;
-			template <typename S> friend class ExpansionBase;//access for base class
-			template <typename S, size_t M> friend class Expansion;//access for expansions of different size
-
-			Expansion() : m_size(0) {}
-			template <size_t M> Expansion& operator=(const Expansion<T, M>& e) {
-				PREDICATES_PORTABLE_STATIC_ASSERT(M <= N, cannot_assign_a_larger_expansion_to_a_smaller_expansion);
-				stdx::copy_n(e.cbegin(), e.size(), stdx::array<T, N>::begin());
-				m_size = e.size();
-				return *this;
-			}
-
-			//vector like convenience functions
-			size_t size() const {return m_size;}
-			bool empty() const {return 0 == m_size;}
-			void push_back(const T v) {stdx::array<T, N>::operator[](m_size++) = v;}
-
-		public:
-			//estimates of expansion value
-			T mostSignificant() const {return empty() ? T(0) : stdx::array<T, N>::operator[](m_size - 1);}
-			T estimate() const {return std::accumulate(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), T(0));}
-
-			template <size_t M> Expansion<T, N+M> operator+(const Expansion<T, M>& f) const {
-				Expansion<T, N+M> h;
-				h.m_size = ExpansionBase<T>::ExpansionSum(this->data(), this->size(), f.data(), f.size(), h.data());
-				return h;
-			}
-
-			void negate() {std::transform(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), stdx::array<T, N>::begin(), std::negate<T>());}
-			Expansion operator-() const {Expansion e = *this; e.negate(); return e;}
-			template <size_t M> Expansion<T, N+M> operator-(const Expansion<T, M>& f) const {return operator+(-f);}
-
-			Expansion<T, 2*N> operator*(const T b) const {
-				Expansion<T, 2*N> h;
-				h.m_size = ExpansionBase<T>::ScaleExpansion(this->data(), this->size(), b, h.data());
-				return h;
-			}
-	};
-
-	//std::fma is faster than dekker's product when the processor instruction is available
-	#ifdef FP_FAST_FMAF
-		static const bool fp_fast_fmaf = true;
-	#else
-		static const bool fp_fast_fmaf = false;
-	#endif
-
-	#ifdef FP_FAST_FMA
-		static const bool fp_fast_fma = true;
-	#else
-		static const bool fp_fast_fma = false;
-	#endif
-
-	#ifdef FP_FAST_FMAL
-		static const bool fp_fast_fmal = true;
-	#else
-		static const bool fp_fast_fmal = false;
-	#endif
-
-	#ifdef PREDICATES_CXX11_IS_SUPPORTED
-	template <typename T> struct use_fma {static const bool value = (std::is_same<T, float>::value       && fp_fast_fmaf) ||
-	                                                                (std::is_same<T, double>::value      && fp_fast_fma)  ||
-	                                                                (std::is_same<T, long double>::value && fp_fast_fmal);};
-	#endif
-
-	//@brief  : helper function to sort by absolute value
-	//@param a: lhs item to compare
-	//@param b: rhs item to compare
-	//@return : true if |a| < |b|
-	//@note   : defined since lambda functions aren't allow in c++03
-	template <typename T> bool absLess(const T& a, const T& b) {return std::abs(a) < std::abs(b);}
-
-	template<typename T>
-	class ExpansionBase {
-		private:
-			static const T Splitter;
-
-			PREDICATES_PORTABLE_STATIC_ASSERT(std::numeric_limits<T>::is_iec559, Requires_IEC_559_IEEE_754_floating_point_type);
-			PREDICATES_PORTABLE_STATIC_ASSERT(2 == std::numeric_limits<T>::radix, Requires_base_2_floating_point_type);
-
-			//combine result + roundoff error into expansion
-			static inline Expansion<T, 2> MakeExpansion(const T value, const T tail) {
-				Expansion<T, 2> e;
-				if(T(0) != tail) e.push_back(tail);
-				if(T(0) != value) e.push_back(value);
-				return e;
-			}
-
-		protected:
-			//add 2 expansions
-			static size_t ExpansionSum(T const * const e, const size_t n, T const * const f, const size_t m, T * const h) {
-				std::merge(e, e + n, f, f + m, h, absLess<T>);
-				if(m == 0) return n;
-				if(n == 0) return m;
-				size_t hIndex = 0;
-				T Q = h[0];
-				T Qnew = h[1] + Q;
-				T hh = FastPlusTail(h[1], Q, Qnew);
-				Q = Qnew;
-				if(T(0) != hh) h[hIndex++] = hh;
-				for(size_t g = 2; g != n + m; ++g) {
-					Qnew = Q + h[g];
-					hh = PlusTail(Q, h[g], Qnew);
-					Q = Qnew;
-					if(T(0) != hh) h[hIndex++] = hh;
-				}
-				if(T(0) != Q) h[hIndex++] = Q;
-				return hIndex;
-			}
-
-			//scale an expansion by a constant
-			static size_t ScaleExpansion(T const * const e, const size_t n, const T b, T * const h) {
-				if(n == 0 || T(0) == b) return 0;
-				size_t hIndex = 0;
-				T Q = e[0] * b;
-				const std::pair<T, T> bSplit = Split(b);
-				T hh = MultTailPreSplit(e[0], b, bSplit, Q);
-				if(T(0) != hh) h[hIndex++] = hh;
-				for(size_t eIndex = 1; eIndex < n; ++eIndex) {
-					T Ti = e[eIndex] * b;
-					T ti = MultTailPreSplit(e[eIndex], b, bSplit, Ti);
-					T Qi = Q + ti;
-					hh = PlusTail(Q, ti, Qi);
-					if(T(0) != hh) h[hIndex++] = hh;
-					Q = Ti + Qi;
-					hh = FastPlusTail(Ti, Qi, Q);
-					if(T(0) != hh) h[hIndex++] = hh;
-				}
-				if(T(0) != Q) h[hIndex++] = Q;
-				return hIndex;
-			}
-		
-		public:
-			//roundoff error of x = a + b
-			static inline T PlusTail(const T a, const T b, const T x) {
-				const T bVirtual = x - a;
-				const T aVirtual = x - bVirtual;
-				const T bRoundoff = b - bVirtual;
-				const T aRoundoff = a - aVirtual;
-				return aRoundoff + bRoundoff;
-			}
-
-			//roundoff error of x = a + b if |a| > |b|
-			static inline T FastPlusTail(const T a, const T b, const T x) {
-				const T bVirtual = x - a;
-				return b - bVirtual;
-			}
-
-			//roundoff error of x = a - b
-			static inline T MinusTail(const T a, const T b, const T x) {
-				const T bVirtual = a - x;
-				const T aVirtual = x + bVirtual;
-				const T bRoundoff = bVirtual - b;
-				const T aRoundoff = a - aVirtual;
-				return aRoundoff + bRoundoff;
-			}
-
-			//split a into 2 nonoverlapping values
-			static inline std::pair<T, T> Split(const T a) {
-				const T c = a * Splitter;
-				const T aBig = c - a;
-				const T aHi = c - aBig;
-				return std::pair<T, T>(aHi, a - aHi);
-			}
-
-			//roundoff error of x = a * b via dekkers product
-			static inline T DekkersProduct(const T /*a*/, const std::pair<T, T> aSplit, const T /*b*/, const std::pair<T, T> bSplit, const T p) {
-				T y = p - T(aSplit.first * bSplit.first);
-				y -= T(aSplit.second * bSplit.first);
-				y -= T(aSplit.first * bSplit.second);
-				return T(aSplit.second * bSplit.second) - y;
-			}
-
-			//roundoff error of x = a * b
-#ifdef PREDICATES_CXX11_IS_SUPPORTED
-			template <typename S = T> static typename std::enable_if< use_fma<S>::value, S>::type MultTail(const T a, const T b, const T p) {return std::fma(a, b, -p);}
-			template <typename S = T> static typename std::enable_if<!use_fma<S>::value, S>::type MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);}
-
-			template <typename S = T> static typename std::enable_if< use_fma<S>::value, S>::type MultTailPreSplit(const T a, const T b, const std::pair<T, T> /*bSplit*/, const T p) {return std::fma(a, b, -p);}
-			template <typename S = T> static typename std::enable_if<!use_fma<S>::value, S>::type MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);}
-#else
-			static T MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);}
-			static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);}
-#endif
-			//expand a + b
-			static inline Expansion<T, 2> Plus(const T a, const T b) {
-				const T x = a + b;
-				return MakeExpansion(x, PlusTail(a, b, x));
-			}
-
-			//expand a - b
-			static inline Expansion<T, 2> Minus(const T a, const T b) {return Plus(a, -b);}
-
-			//expand a * b
-			static inline Expansion<T, 2> Mult(const T a, const T b) {
-				const T x = a * b;
-				return MakeExpansion(x, MultTail(a, b, x));
-			}
-
-			//expand the determinant of {{ax, ay}, {bx, by}} (unrolled Mult(ax, by) - Mult(ay, bx))
-			static inline Expansion<T, 4> TwoTwoDiff(const T ax, const T by, const T ay, const T bx) {
-				const T axby1 = ax * by;
-				const T axby0 = MultTail(ax, by, axby1);
-				const T bxay1 = bx * ay;
-				const T bxay0 = MultTail(bx, ay, bxay1);
-				const T _i0 = axby0 - bxay0;
-				const T x0 = MinusTail(axby0, bxay0, _i0);
-				const T _j = axby1 + _i0;
-				const T _0 = PlusTail(axby1, _i0, _j);
-				const T _i1 = _0 - bxay1;
-				const T x1 = MinusTail(_0, bxay1, _i1);
-				const T x3 = _j + _i1;
-				const T x2 = PlusTail(_j, _i1, x3);
-				Expansion<T, 4> e;
-				if(T(0) != x0) e.push_back(x0);
-				if(T(0) != x1) e.push_back(x1);
-				if(T(0) != x2) e.push_back(x2);
-				if(T(0) != x3) e.push_back(x3);
-				return e;
-			}
-
-			//TwoTwoDiff checking for zeros to avoid extra splitting
-			static inline Expansion<T, 4> TwoTwoDiffZeroCheck(const T ax, const T by, const T ay, const T bx) {
-				Expansion<T, 4> e;
-				if(T(0) == ax && T(0) == ay) return e;
-				else if(T(0) == ax) e = Mult(ay, bx);
-				else if(T(0) == ay) e = Mult(ax, by);
-				else e = TwoTwoDiff(ax, by, ay, bx);
-				return e;
-			}
-
-			//(a * b) * c checking for zeros
-			static inline Expansion<T, 4> ThreeProd(const T a, const T b, const T c) {return (T(0) == a || T(0) == b || T(0) == c) ? Expansion<T, 4>() : Mult(a, b) * c;}
-	};
-
-	template <typename T> const T ExpansionBase<T>::Splitter = static_cast<T>(
-#ifdef PREDICATES_CXX11_IS_SUPPORTED
-		std::exp2((std::numeric_limits<T>::digits + std::numeric_limits<T>::digits%2)/2 + 1)
-#else
-		std::ldexp(T(1), (std::numeric_limits<T>::digits + std::numeric_limits<T>::digits%2)/2 + 1)
-#endif
-	);
-}
-
-	namespace exact {
-		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy)
-		{
-			const detail::Expansion<T, 4> aterms = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, ax, cy);
-			const detail::Expansion<T, 4> bterms = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, bx, ay);
-			const detail::Expansion<T, 4> cterms = detail::ExpansionBase<T>::TwoTwoDiff(cx, ay, cx, by);
-			const detail::Expansion<T, 12> w = aterms + bterms + cterms;
-			return w.mostSignificant();
-		}
-
-		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {
-			return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);
-		}
-
-		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, bx, ay);
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, cx, by);
-			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cx, dy, dx, cy);
-			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dx, ay, ax, dy);
-			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(ax, cy, cx, ay);
-			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bx, dy, dx, by);
-
-			const detail::Expansion<T, 12> abc = ab + bc - ac;
-			const detail::Expansion<T, 12> bcd = bc + cd - bd;
-			const detail::Expansion<T, 12> cda = cd + da + ac;
-			const detail::Expansion<T, 12> dab = da + ab + bd;
-
-			const detail::Expansion<T, 96> adet = bcd * ax *  ax + bcd * ay *  ay;
-			const detail::Expansion<T, 96> bdet = cda * bx * -bx + cda * by * -by;
-			const detail::Expansion<T, 96> cdet = dab * cx *  cx + dab * cy *  cy;
-			const detail::Expansion<T, 96> ddet = abc * dx * -dx + abc * dy * -dy;
-
-			const detail::Expansion<T, 384> deter = (adet + bdet) + (cdet + ddet);
-			return deter.mostSignificant();
-		}
-
-		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
-			return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);
-		}
-
-		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
-		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
-		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);
-			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);
-			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);
-			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);
-			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);
-
-			const detail::Expansion<T, 12> abc = ab + bc - ac;
-			const detail::Expansion<T, 12> bcd = bc + cd - bd;
-			const detail::Expansion<T, 12> cda = cd + da + ac;
-			const detail::Expansion<T, 12> dab = da + ab + bd;
-
-			const detail::Expansion<T, 24> adet = bcd *  pa[2];
-			const detail::Expansion<T, 24> bdet = cda * -pb[2];
-			const detail::Expansion<T, 24> cdet = dab *  pc[2];
-			const detail::Expansion<T, 24> ddet = abc * -pd[2];
-
-			const detail::Expansion<T, 96> deter = (adet + bdet) + (cdet + ddet);
-			return deter.mostSignificant();
-		}
-
-		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@param pe: pointer to e as {x, y, z}
-		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);
-			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);
-			const detail::Expansion<T, 4> de = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pe[1], pe[0], pd[1]);
-			const detail::Expansion<T, 4> ea = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pa[1], pa[0], pe[1]);
-			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);
-			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);
-			const detail::Expansion<T, 4> ce = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pe[1], pe[0], pc[1]);
-			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);
-			const detail::Expansion<T, 4> eb = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pb[1], pb[0], pe[1]);
-
-			const detail::Expansion<T, 24> abc = bc * pa[2] + ac * -pb[2] + ab * pc[2];
-			const detail::Expansion<T, 24> bcd = cd * pb[2] + bd * -pc[2] + bc * pd[2];
-			const detail::Expansion<T, 24> cde = de * pc[2] + ce * -pd[2] + cd * pe[2];
-			const detail::Expansion<T, 24> dea = ea * pd[2] + da * -pe[2] + de * pa[2];
-			const detail::Expansion<T, 24> eab = ab * pe[2] + eb * -pa[2] + ea * pb[2];
-			const detail::Expansion<T, 24> abd = bd * pa[2] + da *  pb[2] + ab * pd[2];
-			const detail::Expansion<T, 24> bce = ce * pb[2] + eb *  pc[2] + bc * pe[2];
-			const detail::Expansion<T, 24> cda = da * pc[2] + ac *  pd[2] + cd * pa[2];
-			const detail::Expansion<T, 24> deb = eb * pd[2] + bd *  pe[2] + de * pb[2];
-			const detail::Expansion<T, 24> eac = ac * pe[2] + ce *  pa[2] + ea * pc[2];
-
-			const detail::Expansion<T, 96> bcde = (cde + bce) - (deb + bcd);
-			const detail::Expansion<T, 96> cdea = (dea + cda) - (eac + cde);
-			const detail::Expansion<T, 96> deab = (eab + deb) - (abd + dea);
-			const detail::Expansion<T, 96> eabc = (abc + eac) - (bce + eab);
-			const detail::Expansion<T, 96> abcd = (bcd + abd) - (cda + abc);
-
-			const detail::Expansion<T, 1152> adet = bcde * pa[0] * pa[0] + bcde * pa[1] * pa[1] + bcde * pa[2] * pa[2];
-			const detail::Expansion<T, 1152> bdet = cdea * pb[0] * pb[0] + cdea * pb[1] * pb[1] + cdea * pb[2] * pb[2];
-			const detail::Expansion<T, 1152> cdet = deab * pc[0] * pc[0] + deab * pc[1] * pc[1] + deab * pc[2] * pc[2];
-			const detail::Expansion<T, 1152> ddet = eabc * pd[0] * pd[0] + eabc * pd[1] * pd[1] + eabc * pd[2] * pd[2];
-			const detail::Expansion<T, 1152> edet = abcd * pe[0] * pe[0] + abcd * pe[1] * pe[1] + abcd * pe[2] * pe[2];
-
-			const detail::Expansion<T, 5760> deter = (adet + bdet) + ((cdet + ddet) + edet);
-			return deter.mostSignificant();
-		}
-	}
-
-	template <typename T>
-	const T& Epsilon()
-	{
-		static const T epsilon = static_cast<T>(
-#ifdef PREDICATES_CXX11_IS_SUPPORTED
-			std::exp2(-std::numeric_limits<T>::digits)
-#else
-			std::ldexp(T(1), -std::numeric_limits<T>::digits)
-#endif
-		);
-		return epsilon;
-	}
-
-	template <typename T>
-	class Constants {
-		public:
-			static const T epsilon, resulterrbound;
-			static const T ccwerrboundA, ccwerrboundB, ccwerrboundC;
-			static const T o3derrboundA, o3derrboundB, o3derrboundC;
-			static const T iccerrboundA, iccerrboundB, iccerrboundC;
-			static const T isperrboundA, isperrboundB, isperrboundC;
-	};
-
-	template <typename T> const T Constants<T>::epsilon = Epsilon<T>();
-	template <typename T> const T Constants<T>::resulterrbound = (T( 3) + T(   8) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::ccwerrboundA   = (T( 3) + T(  16) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::ccwerrboundB   = (T( 2) + T(  12) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::ccwerrboundC   = (T( 9) + T(  64) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
-	template <typename T> const T Constants<T>::o3derrboundA   = (T( 7) + T(  56) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::o3derrboundB   = (T( 3) + T(  28) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::o3derrboundC   = (T(26) + T( 288) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
-	template <typename T> const T Constants<T>::iccerrboundA   = (T(10) + T(  96) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::iccerrboundB   = (T( 4) + T(  48) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::iccerrboundC   = (T(44) + T( 576) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
-	template <typename T> const T Constants<T>::isperrboundA   = (T(16) + T( 224) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::isperrboundB   = (T( 5) + T(  72) * Epsilon<T>()) * Epsilon<T>();
-	template <typename T> const T Constants<T>::isperrboundC   = (T(71) + T(1408) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
-
-	namespace adaptive {
-		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy) {
-			const T acx = ax - cx;
-			const T bcx = bx - cx;
-			const T acy = ay - cy;
-			const T bcy = by - cy;
-			const T detleft = acx * bcy;
-			const T detright = acy * bcx;
-			T det = detleft - detright;
-			if((detleft < 0) != (detright < 0)) return det;
-			if(T(0) == detleft || T(0) == detright) return det;
-
-			const T detsum = std::abs(detleft + detright);
-			T errbound = Constants<T>::ccwerrboundA * detsum;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const detail::Expansion<T, 4> B = detail::ExpansionBase<T>::TwoTwoDiff(acx, bcy, acy, bcx);
-			det = B.estimate();
-			errbound = Constants<T>::ccwerrboundB * detsum;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const T acxtail = detail::ExpansionBase<T>::MinusTail(ax, cx, acx);
-			const T bcxtail = detail::ExpansionBase<T>::MinusTail(bx, cx, bcx);
-			const T acytail = detail::ExpansionBase<T>::MinusTail(ay, cy, acy);
-			const T bcytail = detail::ExpansionBase<T>::MinusTail(by, cy, bcy);
-			if(T(0) == acxtail && T(0) == bcxtail && T(0) == acytail && T(0) == bcytail) return det;
-
-			errbound = Constants<T>::ccwerrboundC * detsum + Constants<T>::resulterrbound * std::abs(det);
-			det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const detail::Expansion<T, 16> D = ((B + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcy, acytail, bcx)) + detail::ExpansionBase<T>::TwoTwoDiff(acx, bcytail, acy, bcxtail)) + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcytail, acytail, bcxtail);
-			return D.mostSignificant();
-		}
-
-		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {
-			return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);
-		}
-
-		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {
-			const T adx = ax - dx;
-			const T bdx = bx - dx;
-			const T cdx = cx - dx;
-			const T ady = ay - dy;
-			const T bdy = by - dy;
-			const T cdy = cy - dy;
-			const T bdxcdy = bdx * cdy;
-			const T cdxbdy = cdx * bdy;
-			const T cdxady = cdx * ady;
-			const T adxcdy = adx * cdy;
-			const T adxbdy = adx * bdy;
-			const T bdxady = bdx * ady;
-			const T alift = adx * adx + ady * ady;
-			const T blift = bdx * bdx + bdy * bdy;
-			const T clift = cdx * cdx + cdy * cdy;
-			T det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
-			const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * alift
-			                  + (std::abs(cdxady) + std::abs(adxcdy)) * blift
-			                  + (std::abs(adxbdy) + std::abs(bdxady)) * clift;
-			T errbound = Constants<T>::iccerrboundA * permanent;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);
-			const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);
-			const detail::Expansion<T, 32> adet = bc * adx * adx + bc * ady * ady;
-			const detail::Expansion<T, 32> bdet = ca * bdx * bdx + ca * bdy * bdy;
-			const detail::Expansion<T, 32> cdet = ab * cdx * cdx + ab * cdy * cdy;
-			const detail::Expansion<T, 96> fin1 = adet + bdet + cdet;
-			det = fin1.estimate();
-			errbound = Constants<T>::iccerrboundB * permanent;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const T adxtail = detail::ExpansionBase<T>::MinusTail(ax, dx, adx);
-			const T adytail = detail::ExpansionBase<T>::MinusTail(ay, dy, ady);
-			const T bdxtail = detail::ExpansionBase<T>::MinusTail(bx, dx, bdx);
-			const T bdytail = detail::ExpansionBase<T>::MinusTail(by, dy, bdy);
-			const T cdxtail = detail::ExpansionBase<T>::MinusTail(cx, dx, cdx);
-			const T cdytail = detail::ExpansionBase<T>::MinusTail(cy, dy, cdy);
-			if(T(0) == adxtail && T(0) == bdxtail && T(0) == cdxtail && T(0) == adytail && T(0) == bdytail && T(0) == cdytail) return det;
-
-			errbound = Constants<T>::iccerrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
-			det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail))
-			    +   (bdx * cdy - bdy * cdx) *  (adx * adxtail + ady * adytail) * T(2))
-			    +  ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail))
-			    +   (cdx * ady - cdy * adx) *  (bdx * bdxtail + bdy * bdytail) * T(2))
-			    +  ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail))
-			    +   (adx * bdy - ady * bdx) *  (cdx * cdxtail + cdy * cdytail) * T(2));
-			if(std::abs(det) >= std::abs(errbound)) return det;
-			return exact::incircle(ax, ay, bx, by, cx, cy, dx, dy);
-		}
-
-		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
-			return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);
-		}
-
-		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
-		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
-		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
-			const T adx = pa[0] - pd[0];
-			const T bdx = pb[0] - pd[0];
-			const T cdx = pc[0] - pd[0];
-			const T ady = pa[1] - pd[1];
-			const T bdy = pb[1] - pd[1];
-			const T cdy = pc[1] - pd[1];
-			const T adz = pa[2] - pd[2];
-			const T bdz = pb[2] - pd[2];
-			const T cdz = pc[2] - pd[2];
-			const T bdxcdy = bdx * cdy;
-			const T cdxbdy = cdx * bdy;
-			const T cdxady = cdx * ady;
-			const T adxcdy = adx * cdy;
-			const T adxbdy = adx * bdy;
-			const T bdxady = bdx * ady;
-			T det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
-			const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * std::abs(adz) + (std::abs(cdxady) + std::abs(adxcdy)) * std::abs(bdz) + (std::abs(adxbdy) + std::abs(bdxady)) * std::abs(cdz);
-			T errbound = Constants<T>::o3derrboundA * permanent;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);
-			const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);
-			const detail::Expansion<T, 24> fin1 = (bc * adz + ca * bdz) + ab * cdz;
-			det = fin1.estimate();
-			errbound = Constants<T>::o3derrboundB * permanent;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const T adxtail = detail::ExpansionBase<T>::MinusTail(pa[0], pd[0], adx);
-			const T bdxtail = detail::ExpansionBase<T>::MinusTail(pb[0], pd[0], bdx);
-			const T cdxtail = detail::ExpansionBase<T>::MinusTail(pc[0], pd[0], cdx);
-			const T adytail = detail::ExpansionBase<T>::MinusTail(pa[1], pd[1], ady);
-			const T bdytail = detail::ExpansionBase<T>::MinusTail(pb[1], pd[1], bdy);
-			const T cdytail = detail::ExpansionBase<T>::MinusTail(pc[1], pd[1], cdy);
-			const T adztail = detail::ExpansionBase<T>::MinusTail(pa[2], pd[2], adz);
-			const T bdztail = detail::ExpansionBase<T>::MinusTail(pb[2], pd[2], bdz);
-			const T cdztail = detail::ExpansionBase<T>::MinusTail(pc[2], pd[2], cdz);
-			if(T(0) == adxtail && T(0) == adytail && T(0) == adztail &&
-			   T(0) == bdxtail && T(0) == bdytail && T(0) == bdztail &&
-			   T(0) == cdxtail && T(0) == cdytail && T(0) == cdztail) return det;
-
-			errbound = Constants<T>::o3derrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
-			det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + adztail * (bdx * cdy - bdy * cdx))
-			    +  (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + bdztail * (cdx * ady - cdy * adx))
-			    +  (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + cdztail * (adx * bdy - ady * bdx));
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const detail::Expansion<T, 8> bct = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdxtail, cdy, bdytail, cdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdytail, bdx, cdxtail, bdy);
-			const detail::Expansion<T, 8> cat = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdxtail, ady, cdytail, adx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adytail, cdx, adxtail, cdy);
-			const detail::Expansion<T, 8> abt = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adxtail, bdy, adytail, bdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdytail, adx, bdxtail, ady);
-			const detail::Expansion<T, 192> fin2 = fin1 + bct * adz + cat * bdz + abt * cdz + bc * adztail + ca * bdztail + ab * cdztail
-			                                     + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdz) + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdztail)
-			                                     + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdz) + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdztail)
-			                                     + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adz) + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adztail)
-			                                     + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdz) + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdztail)
-			                                     + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdz) + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdztail)
-			                                     + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adz) + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adztail)
-			                                     + bct * adztail + cat * bdztail + abt * cdztail;
-			return fin2.mostSignificant();
-		}
-
-		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
-		//@param pa: pointer to a as {x, y, z}
-		//@param pb: pointer to b as {x, y, z}
-		//@param pc: pointer to c as {x, y, z}
-		//@param pd: pointer to d as {x, y, z}
-		//@param pe: pointer to e as {x, y, z}
-		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
-		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
-		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {
-			T permanent;
-			const T aex = pa[0] - pe[0];
-			const T bex = pb[0] - pe[0];
-			const T cex = pc[0] - pe[0];
-			const T dex = pd[0] - pe[0];
-			const T aey = pa[1] - pe[1];
-			const T bey = pb[1] - pe[1];
-			const T cey = pc[1] - pe[1];
-			const T dey = pd[1] - pe[1];
-			const T aez = pa[2] - pe[2];
-			const T bez = pb[2] - pe[2];
-			const T cez = pc[2] - pe[2];
-			const T dez = pd[2] - pe[2];
-			{
-				const T aexbey = aex * bey;
-				const T bexaey = bex * aey;
-				const T bexcey = bex * cey;
-				const T cexbey = cex * bey;
-				const T cexdey = cex * dey;
-				const T dexcey = dex * cey;
-				const T dexaey = dex * aey;
-				const T aexdey = aex * dey;
-				const T aexcey = aex * cey;
-				const T cexaey = cex * aey;
-				const T bexdey = bex * dey;
-				const T dexbey = dex * bey;
-				const T ab = aexbey - bexaey;
-				const T bc = bexcey - cexbey;
-				const T cd = cexdey - dexcey;
-				const T da = dexaey - aexdey;
-				const T ac = aexcey - cexaey;
-				const T bd = bexdey - dexbey;
-				const T abc = aez * bc - bez * ac + cez * ab;
-				const T bcd = bez * cd - cez * bd + dez * bc;
-				const T cda = cez * da + dez * ac + aez * cd;
-				const T dab = dez * ab + aez * bd + bez * da;
-				const T alift = aex * aex + aey * aey + aez * aez;
-				const T blift = bex * bex + bey * bey + bez * bez;
-				const T clift = cex * cex + cey * cey + cez * cez;
-				const T dlift = dex * dex + dey * dey + dez * dez;
-				const T det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
-				const T aezplus = std::abs(aez);
-				const T bezplus = std::abs(bez);
-				const T cezplus = std::abs(cez);
-				const T dezplus = std::abs(dez);
-				const T aexbeyplus = std::abs(aexbey);
-				const T bexaeyplus = std::abs(bexaey);
-				const T bexceyplus = std::abs(bexcey);
-				const T cexbeyplus = std::abs(cexbey);
-				const T cexdeyplus = std::abs(cexdey);
-				const T dexceyplus = std::abs(dexcey);
-				const T dexaeyplus = std::abs(dexaey);
-				const T aexdeyplus = std::abs(aexdey);
-				const T aexceyplus = std::abs(aexcey);
-				const T cexaeyplus = std::abs(cexaey);
-				const T bexdeyplus = std::abs(bexdey);
-				const T dexbeyplus = std::abs(dexbey);
-				permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + (bexceyplus + cexbeyplus) * dezplus) * alift
-				          + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + (cexdeyplus + dexceyplus) * aezplus) * blift
-				          + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + (dexaeyplus + aexdeyplus) * bezplus) * clift
-				          + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + (aexbeyplus + bexaeyplus) * cezplus) * dlift;
-				const T errbound = Constants<T>::isperrboundA * permanent;
-				if(std::abs(det) >= std::abs(errbound)) return det;
-			}
-
-			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(aex, bey, bex, aey);
-			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bex, cey, cex, bey);
-			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cex, dey, dex, cey);
-			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dex, aey, aex, dey);
-			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(aex, cey, cex, aey);
-			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bex, dey, dex, bey);
-			const detail::Expansion<T, 24> temp24a = bc * dez + (cd * bez + bd * -cez);
-			const detail::Expansion<T, 24> temp24b = cd * aez + (da * cez + ac *  dez);
-			const detail::Expansion<T, 24> temp24c = da * bez + (ab * dez + bd *  aez);
-			const detail::Expansion<T, 24> temp24d = ab * cez + (bc * aez + ac * -bez);
-			const detail::Expansion<T, 288> adet = temp24a * aex * -aex + temp24a * aey * -aey + temp24a * aez * -aez;
-			const detail::Expansion<T, 288> bdet = temp24b * bex *  bex + temp24b * bey *  bey + temp24b * bez *  bez;
-			const detail::Expansion<T, 288> cdet = temp24c * cex * -cex + temp24c * cey * -cey + temp24c * cez * -cez;
-			const detail::Expansion<T, 288> ddet = temp24d * dex *  dex + temp24d * dey *  dey + temp24d * dez *  dez;
-			const detail::Expansion<T, 1152> fin1 = (adet + bdet) + (cdet + ddet);
-			T det = fin1.estimate();
-			T errbound = Constants<T>::isperrboundB * permanent;
-			if(std::abs(det) >= std::abs(errbound)) return det;
-
-			const T aextail = detail::ExpansionBase<T>::MinusTail(pa[0], pe[0], aex);
-			const T aeytail = detail::ExpansionBase<T>::MinusTail(pa[1], pe[1], aey);
-			const T aeztail = detail::ExpansionBase<T>::MinusTail(pa[2], pe[2], aez);
-			const T bextail = detail::ExpansionBase<T>::MinusTail(pb[0], pe[0], bex);
-			const T beytail = detail::ExpansionBase<T>::MinusTail(pb[1], pe[1], bey);
-			const T beztail = detail::ExpansionBase<T>::MinusTail(pb[2], pe[2], bez);
-			const T cextail = detail::ExpansionBase<T>::MinusTail(pc[0], pe[0], cex);
-			const T ceytail = detail::ExpansionBase<T>::MinusTail(pc[1], pe[1], cey);
-			const T ceztail = detail::ExpansionBase<T>::MinusTail(pc[2], pe[2], cez);
-			const T dextail = detail::ExpansionBase<T>::MinusTail(pd[0], pe[0], dex);
-			const T deytail = detail::ExpansionBase<T>::MinusTail(pd[1], pe[1], dey);
-			const T deztail = detail::ExpansionBase<T>::MinusTail(pd[2], pe[2], dez);
-			if (T(0) == aextail && T(0) == aeytail && T(0) == aeztail &&
-			    T(0) == bextail && T(0) == beytail && T(0) == beztail &&
-			    T(0) == cextail && T(0) == ceytail && T(0) == ceztail &&
-			    T(0) == dextail && T(0) == deytail && T(0) == deztail) return det;
-
-			errbound = Constants<T>::isperrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
-			const T abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail);
-			const T bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail);
-			const T cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail);
-			const T daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail);
-			const T aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail);
-			const T bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail);
-			const T ab3 = ab.mostSignificant();
-			const T bc3 = bc.mostSignificant();
-			const T cd3 = cd.mostSignificant();
-			const T da3 = da.mostSignificant();
-			const T ac3 = ac.mostSignificant();
-			const T bd3 = bd.mostSignificant();
-			det += ( ( (bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + (ceztail * da3 + deztail * ac3 + aeztail * cd3))
-			         + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)) )
-			       - ( (aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + (beztail * cd3 - ceztail * bd3 + deztail * bc3))
-			         + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + (deztail * ab3 + aeztail * bd3 + beztail * da3)) ) )
-			    + T(2) * ( ( (bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3)
-			               + (dex * dextail + dey * deytail + dez * deztail) * (aez * bc3 - bez * ac3 + cez * ab3))
-			             - ( (aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3)
-			               + (cex * cextail + cey * ceytail + cez * ceztail) * (dez * ab3 + aez * bd3 + bez * da3)));
-			if(std::abs(det) >= std::abs(errbound)) return det;
-			return exact::insphere(pa, pb, pc, pd, pe);
-		}
-	}
-}
-
-#endif
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *                                                                                 *
+ * Copyright (c) 2019, William C. Lenthe                                           *
+ * All rights reserved.                                                            *
+ *                                                                                 *
+ * Redistribution and use in source and binary forms, with or without              *
+ * modification, are permitted provided that the following conditions are met:     *
+ *                                                                                 *
+ * 1. Redistributions of source code must retain the above copyright notice, this  *
+ *    list of conditions and the following disclaimer.                             *
+ *                                                                                 *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,    *
+ *    this list of conditions and the following disclaimer in the documentation    *
+ *    and/or other materials provided with the distribution.                       *
+ *                                                                                 *
+ * 3. Neither the name of the copyright holder nor the names of its                *
+ *    contributors may be used to endorse or promote products derived from         *
+ *    this software without specific prior written permission.                     *
+ *                                                                                 *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"     *
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       *
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  *
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE    *
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL      *
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR      *
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   *
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   *
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            *
+ *                                                                                 *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef PREDICATES_H_INCLUDED
+#define PREDICATES_H_INCLUDED
+
+//@reference: https://www.cs.cmu.edu/~quake/robust.html
+
+namespace  predicates {
+	//@brief: geometric predicates using arbitrary precision arithmetic 
+	//@note : these are provided primarily for illustrative purposes and adaptive routines should be preferred
+	namespace exact {
+		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
+		//@param ax: X-coordinate of a
+		//@param ay: Y-coordinate of a
+		//@param bx: X-coordinate of b
+		//@param by: Y-coordinate of b
+		//@param cx: X-coordinate of c
+		//@param cy: Y-coordinate of c
+		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
+		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
+		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);
+
+		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
+		//@param pa: pointer to a as {x, y}
+		//@param pb: pointer to b as {x, y}
+		//@param pc: pointer to c as {x, y}
+		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
+		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
+		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);
+
+		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
+		//@param ax: X-coordinate of a
+		//@param ay: Y-coordinate of a
+		//@param bx: X-coordinate of b
+		//@param by: Y-coordinate of b
+		//@param cx: X-coordinate of c
+		//@param cy: Y-coordinate of c
+		//@param dx: X-coordinate of d
+		//@param dy: Y-coordinate of d
+		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);
+
+		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
+		//@param pa: pointer to a as {x, y}
+		//@param pb: pointer to b as {x, y}
+		//@param pc: pointer to c as {x, y}
+		//@param pc: pointer to d as {x, y}
+		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
+
+		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
+		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
+		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
+		
+		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@param pe: pointer to e as {x, y, z}
+		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);
+	}
+
+	//@brief: geometric predicates using normal floating point arithmetic but falling back to arbitrary precision when needed
+	//@note : these should have the same accuracy but are significantly faster when determinants are large
+	namespace adaptive {
+		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
+		//@param ax: X-coordinate of a
+		//@param ay: Y-coordinate of a
+		//@param bx: X-coordinate of b
+		//@param by: Y-coordinate of b
+		//@param cx: X-coordinate of c
+		//@param cy: Y-coordinate of c
+		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
+		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
+		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy);
+
+		//@brief   : determine if the 2d point c is above, on, or below the line defined by a and b
+		//@param pa: pointer to a as {x, y}
+		//@param pb: pointer to b as {x, y}
+		//@param pc: pointer to c as {x, y}
+		//@return  : determinant of {{ax - cx, ay - cy}, {bx - cx, by - cy}}
+		//@note    : positive, 0, negative result for c above, on, or below the line defined by a -> b
+		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc);
+
+		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
+		//@param ax: X-coordinate of a
+		//@param ay: Y-coordinate of a
+		//@param bx: X-coordinate of b
+		//@param by: Y-coordinate of b
+		//@param cx: X-coordinate of c
+		//@param cy: Y-coordinate of c
+		//@param dx: X-coordinate of d
+		//@param dy: Y-coordinate of d
+		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy);
+
+		//@brief   : determine if the 2d point d is inside, on, or outside the circle defined by a, b, and c
+		//@param pa: pointer to a as {x, y}
+		//@param pb: pointer to b as {x, y}
+		//@param pc: pointer to c as {x, y}
+		//@param pc: pointer to d as {x, y}
+		//@return  : determinant of {{ax - dx, ay - dy, (ax - dx)^2 + (ay - dy)^2}, {bx - dx, by - dy, (bx - dx)^2 + (by - dy)^2}, {cx - dx, cy - dy, (cx - dx)^2 + (cy - dy)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
+
+		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
+		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
+		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd);
+
+		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@param pe: pointer to e as {x, y, z}
+		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe);
+	}
+}
+
+#include <cmath>//abs, fma
+#include <limits>
+#include <utility>//pair
+#include <numeric>//accumulate
+#include <algorithm>//transform, copy_n, merge
+#include <functional>//negate
+
+// a macro based static assert for pre c++11
+#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) typedef char message[(condition) ? 1 : -1]
+
+// check if c++11 is supported
+#if !defined(__cplusplus) && !defined(_MSC_VER)
+	PREDICATES_PORTABLE_STATIC_ASSERT(false, couldnt_parse_cxx_standard)
+#endif
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+	#define PREDICATES_CXX11_IS_SUPPORTED
+#endif
+
+// choose to use c++11 features or their backports
+#ifdef PREDICATES_CXX11_IS_SUPPORTED
+#include <array>
+#include <type_traits>// is_same, enable_if
+#undef PREDICATES_PORTABLE_STATIC_ASSERT
+#define PREDICATES_TOKEN_TO_STRING1(x)  #x
+#define PREDICATES_TOKEN_TO_STRING(x)  PREDICATES_TOKEN_TO_STRING1(x)
+#define PREDICATES_PORTABLE_STATIC_ASSERT(condition, message) static_assert(condition, PREDICATES_TOKEN_TO_STRING(message))
+namespace  predicates {
+namespace stdx {
+	using std::array;
+	using std::copy_n;
+}
+#else
+namespace  predicates {
+namespace stdx {
+	// array
+	template<typename T, size_t N>
+	class array {
+		T buff[N];
+	public:
+		T& operator[](const size_t& i) { return buff[i]; }
+		const T& operator[](const size_t& i) const { return buff[i]; }
+
+		T       * data() { return buff; }
+		T const * data() const { return buff; }
+
+		T       *  begin() { return buff; }
+		T const * cbegin() const { return buff; }
+	};
+	// copy_n
+	template< class InputIt, class Size, class OutputIt>
+	OutputIt copy_n(InputIt first, Size count, OutputIt result)
+	{
+		if (count > 0) {
+			*result++ = *first;
+			for (Size i = 1; i < count; ++i) {
+				*result++ = *++first;
+			}
+		}
+		return result;
+	}
+}
+#endif // PREDICATES_CXX11_IS_SUPPORTED
+
+namespace detail {
+	template<typename T> class ExpansionBase;
+
+	//@brief: class to exactly represent the result of a sequence of arithmetic operations as an sequence of values that sum to the result
+	template<typename T, size_t N>
+	class Expansion : private ExpansionBase<T>, public stdx::array<T, N> {
+		private:
+		public:
+			size_t m_size;
+			template <typename S> friend class ExpansionBase;//access for base class
+			template <typename S, size_t M> friend class Expansion;//access for expansions of different size
+
+			Expansion() : m_size(0) {}
+			template <size_t M> Expansion& operator=(const Expansion<T, M>& e) {
+				PREDICATES_PORTABLE_STATIC_ASSERT(M <= N, cannot_assign_a_larger_expansion_to_a_smaller_expansion);
+				stdx::copy_n(e.cbegin(), e.size(), stdx::array<T, N>::begin());
+				m_size = e.size();
+				return *this;
+			}
+
+			//vector like convenience functions
+			size_t size() const {return m_size;}
+			bool empty() const {return 0 == m_size;}
+			void push_back(const T v) {stdx::array<T, N>::operator[](m_size++) = v;}
+
+		public:
+			//estimates of expansion value
+			T mostSignificant() const {return empty() ? T(0) : stdx::array<T, N>::operator[](m_size - 1);}
+			T estimate() const {return std::accumulate(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), T(0));}
+
+			template <size_t M> Expansion<T, N+M> operator+(const Expansion<T, M>& f) const {
+				Expansion<T, N+M> h;
+				h.m_size = ExpansionBase<T>::ExpansionSum(this->data(), this->size(), f.data(), f.size(), h.data());
+				return h;
+			}
+
+			void negate() {std::transform(stdx::array<T, N>::cbegin(), stdx::array<T, N>::cbegin() + size(), stdx::array<T, N>::begin(), std::negate<T>());}
+			Expansion operator-() const {Expansion e = *this; e.negate(); return e;}
+			template <size_t M> Expansion<T, N+M> operator-(const Expansion<T, M>& f) const {return operator+(-f);}
+
+			Expansion<T, 2*N> operator*(const T b) const {
+				Expansion<T, 2*N> h;
+				h.m_size = ExpansionBase<T>::ScaleExpansion(this->data(), this->size(), b, h.data());
+				return h;
+			}
+	};
+
+// See: https://stackoverflow.com/a/40765925/1597714
+// Standard defines: https://en.cppreference.com/w/cpp/numeric/math/fma
+#if defined(__FMA__) || defined(__FMA4__) || defined(__AVX2__) || (defined(FP_FAST_FMA) && defined(FP_FAST_FMAF))
+#define PREDICATES_FAST_FMA 1
+#endif
+
+	//@brief  : helper function to sort by absolute value
+	//@param a: lhs item to compare
+	//@param b: rhs item to compare
+	//@return : true if |a| < |b|
+	//@note   : defined since lambda functions aren't allow in c++03
+	template <typename T> bool absLess(const T& a, const T& b) {return std::abs(a) < std::abs(b);}
+
+	template<typename T>
+	class ExpansionBase {
+		private:
+			static const T Splitter;
+
+			PREDICATES_PORTABLE_STATIC_ASSERT(std::numeric_limits<T>::is_iec559, Requires_IEC_559_IEEE_754_floating_point_type);
+			PREDICATES_PORTABLE_STATIC_ASSERT(2 == std::numeric_limits<T>::radix, Requires_base_2_floating_point_type);
+
+			//combine result + roundoff error into expansion
+			static inline Expansion<T, 2> MakeExpansion(const T value, const T tail) {
+				Expansion<T, 2> e;
+				if(T(0) != tail) e.push_back(tail);
+				if(T(0) != value) e.push_back(value);
+				return e;
+			}
+
+		protected:
+			//add 2 expansions
+			static size_t ExpansionSum(T const * const e, const size_t n, T const * const f, const size_t m, T * const h) {
+				std::merge(e, e + n, f, f + m, h, absLess<T>);
+				if(m == 0) return n;
+				if(n == 0) return m;
+				size_t hIndex = 0;
+				T Q = h[0];
+				T Qnew = h[1] + Q;
+				T hh = FastPlusTail(h[1], Q, Qnew);
+				Q = Qnew;
+				if(T(0) != hh) h[hIndex++] = hh;
+				for(size_t g = 2; g != n + m; ++g) {
+					Qnew = Q + h[g];
+					hh = PlusTail(Q, h[g], Qnew);
+					Q = Qnew;
+					if(T(0) != hh) h[hIndex++] = hh;
+				}
+				if(T(0) != Q) h[hIndex++] = Q;
+				return hIndex;
+			}
+
+			//scale an expansion by a constant
+			static size_t ScaleExpansion(T const * const e, const size_t n, const T b, T * const h) {
+				if(n == 0 || T(0) == b) return 0;
+				size_t hIndex = 0;
+				T Q = e[0] * b;
+				const std::pair<T, T> bSplit = Split(b);
+				T hh = MultTailPreSplit(e[0], b, bSplit, Q);
+				if(T(0) != hh) h[hIndex++] = hh;
+				for(size_t eIndex = 1; eIndex < n; ++eIndex) {
+					T Ti = e[eIndex] * b;
+					T ti = MultTailPreSplit(e[eIndex], b, bSplit, Ti);
+					T Qi = Q + ti;
+					hh = PlusTail(Q, ti, Qi);
+					if(T(0) != hh) h[hIndex++] = hh;
+					Q = Ti + Qi;
+					hh = FastPlusTail(Ti, Qi, Q);
+					if(T(0) != hh) h[hIndex++] = hh;
+				}
+				if(T(0) != Q) h[hIndex++] = Q;
+				return hIndex;
+			}
+		
+		public:
+			//roundoff error of x = a + b
+			static inline T PlusTail(const T a, const T b, const T x) {
+				const T bVirtual = x - a;
+				const T aVirtual = x - bVirtual;
+				const T bRoundoff = b - bVirtual;
+				const T aRoundoff = a - aVirtual;
+				return aRoundoff + bRoundoff;
+			}
+
+			//roundoff error of x = a + b if |a| > |b|
+			static inline T FastPlusTail(const T a, const T b, const T x) {
+				const T bVirtual = x - a;
+				return b - bVirtual;
+			}
+
+			//roundoff error of x = a - b
+			static inline T MinusTail(const T a, const T b, const T x) {
+				const T bVirtual = a - x;
+				const T aVirtual = x + bVirtual;
+				const T bRoundoff = bVirtual - b;
+				const T aRoundoff = a - aVirtual;
+				return aRoundoff + bRoundoff;
+			}
+
+			//split a into 2 nonoverlapping values
+			static inline std::pair<T, T> Split(const T a) {
+				const T c = a * Splitter;
+				const T aBig = c - a;
+				const T aHi = c - aBig;
+				return std::pair<T, T>(aHi, a - aHi);
+			}
+
+			//roundoff error of x = a * b via dekkers product
+			static inline T DekkersProduct(const T /*a*/, const std::pair<T, T> aSplit, const T /*b*/, const std::pair<T, T> bSplit, const T p) {
+				T y = p - T(aSplit.first * bSplit.first);
+				y -= T(aSplit.second * bSplit.first);
+				y -= T(aSplit.first * bSplit.second);
+				return T(aSplit.second * bSplit.second) - y;
+			}
+
+			//roundoff error of x = a * b
+#if defined(PREDICATES_CXX11_IS_SUPPORTED) && defined(PREDICATES_FAST_FMA)
+			static T MultTail(const T a, const T b, const T p) {return std::fma(a, b, -p);}
+			static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> /*bSplit*/, const T p) {return std::fma(a, b, -p);}
+#else
+			static T MultTail(const T a, const T b, const T p) {return DekkersProduct(a, Split(a), b, Split(b), p);}
+			static T MultTailPreSplit(const T a, const T b, const std::pair<T, T> bSplit, const T p) {return DekkersProduct(a, Split(a), b, bSplit, p);}
+#endif
+			//expand a + b
+			static inline Expansion<T, 2> Plus(const T a, const T b) {
+				const T x = a + b;
+				return MakeExpansion(x, PlusTail(a, b, x));
+			}
+
+			//expand a - b
+			static inline Expansion<T, 2> Minus(const T a, const T b) {return Plus(a, -b);}
+
+			//expand a * b
+			static inline Expansion<T, 2> Mult(const T a, const T b) {
+				const T x = a * b;
+				return MakeExpansion(x, MultTail(a, b, x));
+			}
+
+			//expand the determinant of {{ax, ay}, {bx, by}} (unrolled Mult(ax, by) - Mult(ay, bx))
+			static inline Expansion<T, 4> TwoTwoDiff(const T ax, const T by, const T ay, const T bx) {
+				const T axby1 = ax * by;
+				const T axby0 = MultTail(ax, by, axby1);
+				const T bxay1 = bx * ay;
+				const T bxay0 = MultTail(bx, ay, bxay1);
+				const T _i0 = axby0 - bxay0;
+				const T x0 = MinusTail(axby0, bxay0, _i0);
+				const T _j = axby1 + _i0;
+				const T _0 = PlusTail(axby1, _i0, _j);
+				const T _i1 = _0 - bxay1;
+				const T x1 = MinusTail(_0, bxay1, _i1);
+				const T x3 = _j + _i1;
+				const T x2 = PlusTail(_j, _i1, x3);
+				Expansion<T, 4> e;
+				if(T(0) != x0) e.push_back(x0);
+				if(T(0) != x1) e.push_back(x1);
+				if(T(0) != x2) e.push_back(x2);
+				if(T(0) != x3) e.push_back(x3);
+				return e;
+			}
+
+			//TwoTwoDiff checking for zeros to avoid extra splitting
+			static inline Expansion<T, 4> TwoTwoDiffZeroCheck(const T ax, const T by, const T ay, const T bx) {
+				Expansion<T, 4> e;
+				if(T(0) == ax && T(0) == ay) return e;
+				else if(T(0) == ax) e = Mult(ay, bx);
+				else if(T(0) == ay) e = Mult(ax, by);
+				else e = TwoTwoDiff(ax, by, ay, bx);
+				return e;
+			}
+
+			//(a * b) * c checking for zeros
+			static inline Expansion<T, 4> ThreeProd(const T a, const T b, const T c) {return (T(0) == a || T(0) == b || T(0) == c) ? Expansion<T, 4>() : Mult(a, b) * c;}
+	};
+
+	template <typename T> const T ExpansionBase<T>::Splitter =
+#ifdef PREDICATES_CXX11_IS_SUPPORTED
+            static_cast<T>(std::exp2(std::numeric_limits<T>::digits/2 + 1));
+#else
+            static_cast<T>(std::ldexp(T(1), std::numeric_limits<T>::digits/2 + 1));
+#endif
+}
+
+	namespace exact {
+		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy)
+		{
+			const detail::Expansion<T, 4> aterms = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, ax, cy);
+			const detail::Expansion<T, 4> bterms = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, bx, ay);
+			const detail::Expansion<T, 4> cterms = detail::ExpansionBase<T>::TwoTwoDiff(cx, ay, cx, by);
+			const detail::Expansion<T, 12> w = aterms + bterms + cterms;
+			return w.mostSignificant();
+		}
+
+		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {
+			return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);
+		}
+
+		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(ax, by, bx, ay);
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bx, cy, cx, by);
+			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cx, dy, dx, cy);
+			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dx, ay, ax, dy);
+			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(ax, cy, cx, ay);
+			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bx, dy, dx, by);
+
+			const detail::Expansion<T, 12> abc = ab + bc - ac;
+			const detail::Expansion<T, 12> bcd = bc + cd - bd;
+			const detail::Expansion<T, 12> cda = cd + da + ac;
+			const detail::Expansion<T, 12> dab = da + ab + bd;
+
+			const detail::Expansion<T, 96> adet = bcd * ax *  ax + bcd * ay *  ay;
+			const detail::Expansion<T, 96> bdet = cda * bx * -bx + cda * by * -by;
+			const detail::Expansion<T, 96> cdet = dab * cx *  cx + dab * cy *  cy;
+			const detail::Expansion<T, 96> ddet = abc * dx * -dx + abc * dy * -dy;
+
+			const detail::Expansion<T, 384> deter = (adet + bdet) + (cdet + ddet);
+			return deter.mostSignificant();
+		}
+
+		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
+			return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);
+		}
+
+		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
+		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
+		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);
+			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);
+			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);
+			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);
+			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);
+
+			const detail::Expansion<T, 12> abc = ab + bc - ac;
+			const detail::Expansion<T, 12> bcd = bc + cd - bd;
+			const detail::Expansion<T, 12> cda = cd + da + ac;
+			const detail::Expansion<T, 12> dab = da + ab + bd;
+
+			const detail::Expansion<T, 24> adet = bcd *  pa[2];
+			const detail::Expansion<T, 24> bdet = cda * -pb[2];
+			const detail::Expansion<T, 24> cdet = dab *  pc[2];
+			const detail::Expansion<T, 24> ddet = abc * -pd[2];
+
+			const detail::Expansion<T, 96> deter = (adet + bdet) + (cdet + ddet);
+			return deter.mostSignificant();
+		}
+
+		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@param pe: pointer to e as {x, y, z}
+		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pb[1], pb[0], pa[1]);
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pc[1], pc[0], pb[1]);
+			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pd[1], pd[0], pc[1]);
+			const detail::Expansion<T, 4> de = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pe[1], pe[0], pd[1]);
+			const detail::Expansion<T, 4> ea = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pa[1], pa[0], pe[1]);
+			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(pa[0], pc[1], pc[0], pa[1]);
+			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(pb[0], pd[1], pd[0], pb[1]);
+			const detail::Expansion<T, 4> ce = detail::ExpansionBase<T>::TwoTwoDiff(pc[0], pe[1], pe[0], pc[1]);
+			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(pd[0], pa[1], pa[0], pd[1]);
+			const detail::Expansion<T, 4> eb = detail::ExpansionBase<T>::TwoTwoDiff(pe[0], pb[1], pb[0], pe[1]);
+
+			const detail::Expansion<T, 24> abc = bc * pa[2] + ac * -pb[2] + ab * pc[2];
+			const detail::Expansion<T, 24> bcd = cd * pb[2] + bd * -pc[2] + bc * pd[2];
+			const detail::Expansion<T, 24> cde = de * pc[2] + ce * -pd[2] + cd * pe[2];
+			const detail::Expansion<T, 24> dea = ea * pd[2] + da * -pe[2] + de * pa[2];
+			const detail::Expansion<T, 24> eab = ab * pe[2] + eb * -pa[2] + ea * pb[2];
+			const detail::Expansion<T, 24> abd = bd * pa[2] + da *  pb[2] + ab * pd[2];
+			const detail::Expansion<T, 24> bce = ce * pb[2] + eb *  pc[2] + bc * pe[2];
+			const detail::Expansion<T, 24> cda = da * pc[2] + ac *  pd[2] + cd * pa[2];
+			const detail::Expansion<T, 24> deb = eb * pd[2] + bd *  pe[2] + de * pb[2];
+			const detail::Expansion<T, 24> eac = ac * pe[2] + ce *  pa[2] + ea * pc[2];
+
+			const detail::Expansion<T, 96> bcde = (cde + bce) - (deb + bcd);
+			const detail::Expansion<T, 96> cdea = (dea + cda) - (eac + cde);
+			const detail::Expansion<T, 96> deab = (eab + deb) - (abd + dea);
+			const detail::Expansion<T, 96> eabc = (abc + eac) - (bce + eab);
+			const detail::Expansion<T, 96> abcd = (bcd + abd) - (cda + abc);
+
+			const detail::Expansion<T, 1152> adet = bcde * pa[0] * pa[0] + bcde * pa[1] * pa[1] + bcde * pa[2] * pa[2];
+			const detail::Expansion<T, 1152> bdet = cdea * pb[0] * pb[0] + cdea * pb[1] * pb[1] + cdea * pb[2] * pb[2];
+			const detail::Expansion<T, 1152> cdet = deab * pc[0] * pc[0] + deab * pc[1] * pc[1] + deab * pc[2] * pc[2];
+			const detail::Expansion<T, 1152> ddet = eabc * pd[0] * pd[0] + eabc * pd[1] * pd[1] + eabc * pd[2] * pd[2];
+			const detail::Expansion<T, 1152> edet = abcd * pe[0] * pe[0] + abcd * pe[1] * pe[1] + abcd * pe[2] * pe[2];
+
+			const detail::Expansion<T, 5760> deter = (adet + bdet) + ((cdet + ddet) + edet);
+			return deter.mostSignificant();
+		}
+	}
+
+	template <typename T>
+	const T& Epsilon()
+	{
+		static const T epsilon =
+#ifdef PREDICATES_CXX11_IS_SUPPORTED
+			static_cast<T>(std::exp2(-std::numeric_limits<T>::digits));
+#else
+			static_cast<T>(std::ldexp(T(1), -std::numeric_limits<T>::digits));
+#endif
+		return epsilon;
+	}
+
+	template <typename T>
+	class Constants {
+		public:
+			static const T epsilon, resulterrbound;
+			static const T ccwerrboundA, ccwerrboundB, ccwerrboundC;
+			static const T o3derrboundA, o3derrboundB, o3derrboundC;
+			static const T iccerrboundA, iccerrboundB, iccerrboundC;
+			static const T isperrboundA, isperrboundB, isperrboundC;
+	};
+
+	template <typename T> const T Constants<T>::epsilon = Epsilon<T>();
+	template <typename T> const T Constants<T>::resulterrbound = (T( 3) + T(   8) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::ccwerrboundA   = (T( 3) + T(  16) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::ccwerrboundB   = (T( 2) + T(  12) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::ccwerrboundC   = (T( 9) + T(  64) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
+	template <typename T> const T Constants<T>::o3derrboundA   = (T( 7) + T(  56) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::o3derrboundB   = (T( 3) + T(  28) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::o3derrboundC   = (T(26) + T( 288) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
+	template <typename T> const T Constants<T>::iccerrboundA   = (T(10) + T(  96) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::iccerrboundB   = (T( 4) + T(  48) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::iccerrboundC   = (T(44) + T( 576) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
+	template <typename T> const T Constants<T>::isperrboundA   = (T(16) + T( 224) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::isperrboundB   = (T( 5) + T(  72) * Epsilon<T>()) * Epsilon<T>();
+	template <typename T> const T Constants<T>::isperrboundC   = (T(71) + T(1408) * Epsilon<T>()) * Epsilon<T>() * Epsilon<T>();
+
+	namespace adaptive {
+		template <typename T> T orient2d(T const ax, T const ay, T const bx, T const by, T const cx, T const cy) {
+			const T acx = ax - cx;
+			const T bcx = bx - cx;
+			const T acy = ay - cy;
+			const T bcy = by - cy;
+			const T detleft = acx * bcy;
+			const T detright = acy * bcx;
+			T det = detleft - detright;
+			if((detleft < 0) != (detright < 0)) return det;
+			if(T(0) == detleft || T(0) == detright) return det;
+
+			const T detsum = std::abs(detleft + detright);
+			T errbound = Constants<T>::ccwerrboundA * detsum;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const detail::Expansion<T, 4> B = detail::ExpansionBase<T>::TwoTwoDiff(acx, bcy, acy, bcx);
+			det = B.estimate();
+			errbound = Constants<T>::ccwerrboundB * detsum;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const T acxtail = detail::ExpansionBase<T>::MinusTail(ax, cx, acx);
+			const T bcxtail = detail::ExpansionBase<T>::MinusTail(bx, cx, bcx);
+			const T acytail = detail::ExpansionBase<T>::MinusTail(ay, cy, acy);
+			const T bcytail = detail::ExpansionBase<T>::MinusTail(by, cy, bcy);
+			if(T(0) == acxtail && T(0) == bcxtail && T(0) == acytail && T(0) == bcytail) return det;
+
+			errbound = Constants<T>::ccwerrboundC * detsum + Constants<T>::resulterrbound * std::abs(det);
+			det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const detail::Expansion<T, 16> D = ((B + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcy, acytail, bcx)) + detail::ExpansionBase<T>::TwoTwoDiff(acx, bcytail, acy, bcxtail)) + detail::ExpansionBase<T>::TwoTwoDiff(acxtail, bcytail, acytail, bcxtail);
+			return D.mostSignificant();
+		}
+
+		template <typename T> T orient2d(T const*const pa, T const*const pb, T const*const pc) {
+			return orient2d(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1]);
+		}
+
+		template <typename T> T incircle(T const ax, T const ay, T const bx, T const by, T const cx, T const cy, T const dx, T const dy) {
+			const T adx = ax - dx;
+			const T bdx = bx - dx;
+			const T cdx = cx - dx;
+			const T ady = ay - dy;
+			const T bdy = by - dy;
+			const T cdy = cy - dy;
+			const T bdxcdy = bdx * cdy;
+			const T cdxbdy = cdx * bdy;
+			const T cdxady = cdx * ady;
+			const T adxcdy = adx * cdy;
+			const T adxbdy = adx * bdy;
+			const T bdxady = bdx * ady;
+			const T alift = adx * adx + ady * ady;
+			const T blift = bdx * bdx + bdy * bdy;
+			const T clift = cdx * cdx + cdy * cdy;
+			T det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
+			const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * alift
+			                  + (std::abs(cdxady) + std::abs(adxcdy)) * blift
+			                  + (std::abs(adxbdy) + std::abs(bdxady)) * clift;
+			T errbound = Constants<T>::iccerrboundA * permanent;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);
+			const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);
+			const detail::Expansion<T, 32> adet = bc * adx * adx + bc * ady * ady;
+			const detail::Expansion<T, 32> bdet = ca * bdx * bdx + ca * bdy * bdy;
+			const detail::Expansion<T, 32> cdet = ab * cdx * cdx + ab * cdy * cdy;
+			const detail::Expansion<T, 96> fin1 = adet + bdet + cdet;
+			det = fin1.estimate();
+			errbound = Constants<T>::iccerrboundB * permanent;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const T adxtail = detail::ExpansionBase<T>::MinusTail(ax, dx, adx);
+			const T adytail = detail::ExpansionBase<T>::MinusTail(ay, dy, ady);
+			const T bdxtail = detail::ExpansionBase<T>::MinusTail(bx, dx, bdx);
+			const T bdytail = detail::ExpansionBase<T>::MinusTail(by, dy, bdy);
+			const T cdxtail = detail::ExpansionBase<T>::MinusTail(cx, dx, cdx);
+			const T cdytail = detail::ExpansionBase<T>::MinusTail(cy, dy, cdy);
+			if(T(0) == adxtail && T(0) == bdxtail && T(0) == cdxtail && T(0) == adytail && T(0) == bdytail && T(0) == cdytail) return det;
+
+			errbound = Constants<T>::iccerrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
+			det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail))
+			    +   (bdx * cdy - bdy * cdx) *  (adx * adxtail + ady * adytail) * T(2))
+			    +  ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail))
+			    +   (cdx * ady - cdy * adx) *  (bdx * bdxtail + bdy * bdytail) * T(2))
+			    +  ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail))
+			    +   (adx * bdy - ady * bdx) *  (cdx * cdxtail + cdy * cdytail) * T(2));
+			if(std::abs(det) >= std::abs(errbound)) return det;
+			return exact::incircle(ax, ay, bx, by, cx, cy, dx, dy);
+		}
+
+		template <typename T> T incircle(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
+			return incircle(pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]);
+		}
+
+		//@brief   : determine if the 3d point d is above, on, or below the plane defined by a, b, and c
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@return  : determinant of {{ax - dx, ay - dy, az - dz}, {bx - dx, by - dy, bz - dz}, {cx - dx, cy - dy, cz - dz}}
+		//@note    : positive, 0, negative result for c above, on, or below the plane defined by a, b, and c
+		template <typename T> T orient3d(T const*const pa, T const*const pb, T const*const pc, T const*const pd) {
+			const T adx = pa[0] - pd[0];
+			const T bdx = pb[0] - pd[0];
+			const T cdx = pc[0] - pd[0];
+			const T ady = pa[1] - pd[1];
+			const T bdy = pb[1] - pd[1];
+			const T cdy = pc[1] - pd[1];
+			const T adz = pa[2] - pd[2];
+			const T bdz = pb[2] - pd[2];
+			const T cdz = pc[2] - pd[2];
+			const T bdxcdy = bdx * cdy;
+			const T cdxbdy = cdx * bdy;
+			const T cdxady = cdx * ady;
+			const T adxcdy = adx * cdy;
+			const T adxbdy = adx * bdy;
+			const T bdxady = bdx * ady;
+			T det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady);
+			const T permanent = (std::abs(bdxcdy) + std::abs(cdxbdy)) * std::abs(adz) + (std::abs(cdxady) + std::abs(adxcdy)) * std::abs(bdz) + (std::abs(adxbdy) + std::abs(bdxady)) * std::abs(cdz);
+			T errbound = Constants<T>::o3derrboundA * permanent;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bdx, cdy, cdx, bdy);
+			const detail::Expansion<T, 4> ca = detail::ExpansionBase<T>::TwoTwoDiff(cdx, ady, adx, cdy);
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(adx, bdy, bdx, ady);
+			const detail::Expansion<T, 24> fin1 = (bc * adz + ca * bdz) + ab * cdz;
+			det = fin1.estimate();
+			errbound = Constants<T>::o3derrboundB * permanent;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const T adxtail = detail::ExpansionBase<T>::MinusTail(pa[0], pd[0], adx);
+			const T bdxtail = detail::ExpansionBase<T>::MinusTail(pb[0], pd[0], bdx);
+			const T cdxtail = detail::ExpansionBase<T>::MinusTail(pc[0], pd[0], cdx);
+			const T adytail = detail::ExpansionBase<T>::MinusTail(pa[1], pd[1], ady);
+			const T bdytail = detail::ExpansionBase<T>::MinusTail(pb[1], pd[1], bdy);
+			const T cdytail = detail::ExpansionBase<T>::MinusTail(pc[1], pd[1], cdy);
+			const T adztail = detail::ExpansionBase<T>::MinusTail(pa[2], pd[2], adz);
+			const T bdztail = detail::ExpansionBase<T>::MinusTail(pb[2], pd[2], bdz);
+			const T cdztail = detail::ExpansionBase<T>::MinusTail(pc[2], pd[2], cdz);
+			if(T(0) == adxtail && T(0) == adytail && T(0) == adztail &&
+			   T(0) == bdxtail && T(0) == bdytail && T(0) == bdztail &&
+			   T(0) == cdxtail && T(0) == cdytail && T(0) == cdztail) return det;
+
+			errbound = Constants<T>::o3derrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
+			det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + adztail * (bdx * cdy - bdy * cdx))
+			    +  (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + bdztail * (cdx * ady - cdy * adx))
+			    +  (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + cdztail * (adx * bdy - ady * bdx));
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const detail::Expansion<T, 8> bct = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdxtail, cdy, bdytail, cdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdytail, bdx, cdxtail, bdy);
+			const detail::Expansion<T, 8> cat = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(cdxtail, ady, cdytail, adx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adytail, cdx, adxtail, cdy);
+			const detail::Expansion<T, 8> abt = detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(adxtail, bdy, adytail, bdx) + detail::ExpansionBase<T>::TwoTwoDiffZeroCheck(bdytail, adx, bdxtail, ady);
+			const detail::Expansion<T, 192> fin2 = fin1 + bct * adz + cat * bdz + abt * cdz + bc * adztail + ca * bdztail + ab * cdztail
+			                                     + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdz) + detail::ExpansionBase<T>::ThreeProd( adxtail, bdytail, cdztail)
+			                                     + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdz) + detail::ExpansionBase<T>::ThreeProd(-adxtail, cdytail, bdztail)
+			                                     + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adz) + detail::ExpansionBase<T>::ThreeProd( bdxtail, cdytail, adztail)
+			                                     + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdz) + detail::ExpansionBase<T>::ThreeProd(-bdxtail, adytail, cdztail)
+			                                     + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdz) + detail::ExpansionBase<T>::ThreeProd( cdxtail, adytail, bdztail)
+			                                     + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adz) + detail::ExpansionBase<T>::ThreeProd(-cdxtail, bdytail, adztail)
+			                                     + bct * adztail + cat * bdztail + abt * cdztail;
+			return fin2.mostSignificant();
+		}
+
+		//@brief   : determine if the 3d point e is inside, on, or outside the sphere defined by a, b, c, and d
+		//@param pa: pointer to a as {x, y, z}
+		//@param pb: pointer to b as {x, y, z}
+		//@param pc: pointer to c as {x, y, z}
+		//@param pd: pointer to d as {x, y, z}
+		//@param pe: pointer to e as {x, y, z}
+		//@return  : determinant of {{ax - ex, ay - ey, az - ez, (ax - ex)^2 + (ay - ey)^2 + (az - ez)^2}, {bx - ex, by - ey, bz - ez, (bx - ex)^2 + (by - ey)^2 + (bz - ez)^2}, {cx - ex, cy - ey, cz - ez, (cx - ex)^2 + (cy - ey)^2 + (cz - ez)^2}, {dx - ex, dy - ey, dz - ez, (dx - ex)^2 + (dy - ey)^2 + (dz - ez)^2}}
+		//@note    : positive, 0, negative result for d inside, on, or outside the circle defined by a, b, and c
+		template <typename T> T insphere(T const*const pa, T const*const pb, T const*const pc, T const*const pd, T const*const pe) {
+			T permanent;
+			const T aex = pa[0] - pe[0];
+			const T bex = pb[0] - pe[0];
+			const T cex = pc[0] - pe[0];
+			const T dex = pd[0] - pe[0];
+			const T aey = pa[1] - pe[1];
+			const T bey = pb[1] - pe[1];
+			const T cey = pc[1] - pe[1];
+			const T dey = pd[1] - pe[1];
+			const T aez = pa[2] - pe[2];
+			const T bez = pb[2] - pe[2];
+			const T cez = pc[2] - pe[2];
+			const T dez = pd[2] - pe[2];
+			{
+				const T aexbey = aex * bey;
+				const T bexaey = bex * aey;
+				const T bexcey = bex * cey;
+				const T cexbey = cex * bey;
+				const T cexdey = cex * dey;
+				const T dexcey = dex * cey;
+				const T dexaey = dex * aey;
+				const T aexdey = aex * dey;
+				const T aexcey = aex * cey;
+				const T cexaey = cex * aey;
+				const T bexdey = bex * dey;
+				const T dexbey = dex * bey;
+				const T ab = aexbey - bexaey;
+				const T bc = bexcey - cexbey;
+				const T cd = cexdey - dexcey;
+				const T da = dexaey - aexdey;
+				const T ac = aexcey - cexaey;
+				const T bd = bexdey - dexbey;
+				const T abc = aez * bc - bez * ac + cez * ab;
+				const T bcd = bez * cd - cez * bd + dez * bc;
+				const T cda = cez * da + dez * ac + aez * cd;
+				const T dab = dez * ab + aez * bd + bez * da;
+				const T alift = aex * aex + aey * aey + aez * aez;
+				const T blift = bex * bex + bey * bey + bez * bez;
+				const T clift = cex * cex + cey * cey + cez * cez;
+				const T dlift = dex * dex + dey * dey + dez * dez;
+				const T det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+				const T aezplus = std::abs(aez);
+				const T bezplus = std::abs(bez);
+				const T cezplus = std::abs(cez);
+				const T dezplus = std::abs(dez);
+				const T aexbeyplus = std::abs(aexbey);
+				const T bexaeyplus = std::abs(bexaey);
+				const T bexceyplus = std::abs(bexcey);
+				const T cexbeyplus = std::abs(cexbey);
+				const T cexdeyplus = std::abs(cexdey);
+				const T dexceyplus = std::abs(dexcey);
+				const T dexaeyplus = std::abs(dexaey);
+				const T aexdeyplus = std::abs(aexdey);
+				const T aexceyplus = std::abs(aexcey);
+				const T cexaeyplus = std::abs(cexaey);
+				const T bexdeyplus = std::abs(bexdey);
+				const T dexbeyplus = std::abs(dexbey);
+				permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + (bexceyplus + cexbeyplus) * dezplus) * alift
+				          + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + (cexdeyplus + dexceyplus) * aezplus) * blift
+				          + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + (dexaeyplus + aexdeyplus) * bezplus) * clift
+				          + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + (aexbeyplus + bexaeyplus) * cezplus) * dlift;
+				const T errbound = Constants<T>::isperrboundA * permanent;
+				if(std::abs(det) >= std::abs(errbound)) return det;
+			}
+
+			const detail::Expansion<T, 4> ab = detail::ExpansionBase<T>::TwoTwoDiff(aex, bey, bex, aey);
+			const detail::Expansion<T, 4> bc = detail::ExpansionBase<T>::TwoTwoDiff(bex, cey, cex, bey);
+			const detail::Expansion<T, 4> cd = detail::ExpansionBase<T>::TwoTwoDiff(cex, dey, dex, cey);
+			const detail::Expansion<T, 4> da = detail::ExpansionBase<T>::TwoTwoDiff(dex, aey, aex, dey);
+			const detail::Expansion<T, 4> ac = detail::ExpansionBase<T>::TwoTwoDiff(aex, cey, cex, aey);
+			const detail::Expansion<T, 4> bd = detail::ExpansionBase<T>::TwoTwoDiff(bex, dey, dex, bey);
+			const detail::Expansion<T, 24> temp24a = bc * dez + (cd * bez + bd * -cez);
+			const detail::Expansion<T, 24> temp24b = cd * aez + (da * cez + ac *  dez);
+			const detail::Expansion<T, 24> temp24c = da * bez + (ab * dez + bd *  aez);
+			const detail::Expansion<T, 24> temp24d = ab * cez + (bc * aez + ac * -bez);
+			const detail::Expansion<T, 288> adet = temp24a * aex * -aex + temp24a * aey * -aey + temp24a * aez * -aez;
+			const detail::Expansion<T, 288> bdet = temp24b * bex *  bex + temp24b * bey *  bey + temp24b * bez *  bez;
+			const detail::Expansion<T, 288> cdet = temp24c * cex * -cex + temp24c * cey * -cey + temp24c * cez * -cez;
+			const detail::Expansion<T, 288> ddet = temp24d * dex *  dex + temp24d * dey *  dey + temp24d * dez *  dez;
+			const detail::Expansion<T, 1152> fin1 = (adet + bdet) + (cdet + ddet);
+			T det = fin1.estimate();
+			T errbound = Constants<T>::isperrboundB * permanent;
+			if(std::abs(det) >= std::abs(errbound)) return det;
+
+			const T aextail = detail::ExpansionBase<T>::MinusTail(pa[0], pe[0], aex);
+			const T aeytail = detail::ExpansionBase<T>::MinusTail(pa[1], pe[1], aey);
+			const T aeztail = detail::ExpansionBase<T>::MinusTail(pa[2], pe[2], aez);
+			const T bextail = detail::ExpansionBase<T>::MinusTail(pb[0], pe[0], bex);
+			const T beytail = detail::ExpansionBase<T>::MinusTail(pb[1], pe[1], bey);
+			const T beztail = detail::ExpansionBase<T>::MinusTail(pb[2], pe[2], bez);
+			const T cextail = detail::ExpansionBase<T>::MinusTail(pc[0], pe[0], cex);
+			const T ceytail = detail::ExpansionBase<T>::MinusTail(pc[1], pe[1], cey);
+			const T ceztail = detail::ExpansionBase<T>::MinusTail(pc[2], pe[2], cez);
+			const T dextail = detail::ExpansionBase<T>::MinusTail(pd[0], pe[0], dex);
+			const T deytail = detail::ExpansionBase<T>::MinusTail(pd[1], pe[1], dey);
+			const T deztail = detail::ExpansionBase<T>::MinusTail(pd[2], pe[2], dez);
+			if (T(0) == aextail && T(0) == aeytail && T(0) == aeztail &&
+			    T(0) == bextail && T(0) == beytail && T(0) == beztail &&
+			    T(0) == cextail && T(0) == ceytail && T(0) == ceztail &&
+			    T(0) == dextail && T(0) == deytail && T(0) == deztail) return det;
+
+			errbound = Constants<T>::isperrboundC * permanent + Constants<T>::resulterrbound * std::abs(det);
+			const T abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail);
+			const T bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail);
+			const T cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail);
+			const T daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail);
+			const T aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail);
+			const T bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail);
+			const T ab3 = ab.mostSignificant();
+			const T bc3 = bc.mostSignificant();
+			const T cd3 = cd.mostSignificant();
+			const T da3 = da.mostSignificant();
+			const T ac3 = ac.mostSignificant();
+			const T bd3 = bd.mostSignificant();
+			det += ( ( (bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+			         + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)) )
+			       - ( (aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+			         + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + (deztail * ab3 + aeztail * bd3 + beztail * da3)) ) )
+			    + T(2) * ( ( (bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3)
+			               + (dex * dextail + dey * deytail + dez * deztail) * (aez * bc3 - bez * ac3 + cez * ab3))
+			             - ( (aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3)
+			               + (cex * cextail + cey * ceytail + cez * ceztail) * (dez * ab3 + aez * bd3 + bez * da3)));
+			if(std::abs(det) >= std::abs(errbound)) return det;
+			return exact::insphere(pa, pb, pc, pd, pe);
+		}
+	}
+}
+
+#endif
diff --git a/hcdt.cabal b/hcdt.cabal
--- a/hcdt.cabal
+++ b/hcdt.cabal
@@ -1,53 +1,65 @@
-cabal-version:      2.2
-name:               hcdt
-version:            0.1.0.5
-license:            BSD-3-Clause
-license-file:       LICENSE
-copyright:          2022 Stéphane Laurent
-maintainer:         laurent_step@outlook.fr
-author:             Stéphane Laurent
-homepage:           https://github.com/githubuser/hcdt#readme
-synopsis:           2d Delaunay triangulation
-description:
-    This library performs the constrained or unconstrained 2d Delaunay triangulation.
-    .
-    It uses the C++ library 'CDT'.
-    .
-    For examples, look the README file.
-
-category:           Geometry
-build-type:         Simple
-extra-source-files:
-    README.md
-    CHANGELOG.md
-    LICENSE_CDT.txt
-
-source-repository head
-    type:     git
-    location: https://github.com/githubuser/hcdt
-
-library
-    exposed-modules:  Geometry.HCDT
-    cxx-options:      -fPIC -std=c++11
-    c-sources:        cpp/hcdt.cpp
-    hs-source-dirs:   src
-    other-modules:
-        Geometry.HCDT.Types
-        Geometry.HCDT.Triangulation
-        Geometry.HCDT.Delaunay
-        Geometry.HCDT.Utils
-
-    default-language: Haskell2010
-    other-extensions: CPP ForeignFunctionInterface
-    extra-libraries:  stdc++
-    include-dirs:     cpp
-    install-includes:
-        cpp/CDT.h cpp/CDT.hpp cpp/CDTUtils.h cpp/CDTUtils.hpp cpp/hcdt.hpp
-        cpp/KDTree.h cpp/LocatorKDTree.h cpp/predicates.h cpp/remove_at.hpp
-        cpp/Triangulation.h cpp/Triangulation.hpp
-
-    ghc-options:      -Wall -optcxx-std=c++11
-    build-depends:
-        base >=4.7 && <5,
-        containers >=0.6.4.1,
-        indexed-traversable >=0.1.2
+cabal-version:       2.2
+name:                hcdt
+version:             0.1.1.0
+synopsis:            2d Delaunay triangulation
+description:         
+    This library performs the constrained or unconstrained 2d Delaunay triangulation.
+    .
+    It uses the C++ library 'CDT'.
+    .
+    For examples, look the README file.
+homepage:            https://github.com/stla/hcdt#readme
+license:             BSD-3-Clause
+license-file:        LICENSE
+author:              Stéphane Laurent
+maintainer:          laurent_step@outlook.fr
+copyright:           2022 Stéphane Laurent
+category:            Geometry
+build-type:          Simple
+extra-source-files:  README.md
+                     CHANGELOG.md
+                     LICENSE_CDT.txt
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Geometry.HCDT
+  other-modules:       Geometry.HCDT.Types
+                     , Geometry.HCDT.Triangulation
+                     , Geometry.HCDT.Delaunay
+                     , Geometry.HCDT.Utils
+  build-depends:       base >= 4.7 && < 5
+                     , containers >= 0.6.4.1
+                     , indexed-traversable >= 0.1.2
+                     , hashable >= 1.3.5.0
+                     , witherable >= 0.4
+  if impl(ghc >= 9.4)
+    build-depends:     system-cxx-std-lib == 1.0
+  elif os(darwin) || os(freebsd)
+    extra-libraries:   c++11
+  else
+    extra-libraries:   stdc++
+  include-dirs:        cpp
+  C-sources:           cpp/hcdt.cpp
+  install-includes:    cpp/CDT.h
+                     , cpp/CDT.hpp
+                     , cpp/CDTUtils.h
+                     , cpp/CDTUtils.hpp
+                     , cpp/hcdt.hpp
+                     , cpp/KDTree.h
+                     , cpp/LocatorKDTree.h
+                     , cpp/predicates.h
+                     , cpp/remove_at.hpp
+                     , cpp/Triangulation.h
+                     , cpp/Triangulation.hpp
+  ghc-options:         -Wall 
+                       -optcxx-std=c++11
+  if os(darwin) || os(freebsd)
+    ghc-options:       -optcxx-stdlib=libc++
+  other-extensions:    CPP
+                     , ForeignFunctionInterface
+                     , InstanceSigs
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/stla/hcdt
diff --git a/src/Geometry/HCDT.hs b/src/Geometry/HCDT.hs
--- a/src/Geometry/HCDT.hs
+++ b/src/Geometry/HCDT.hs
@@ -1,6 +1,6 @@
-module Geometry.HCDT
-  (module X)
-  where
-import           Geometry.HCDT.Delaunay as X
-import           Geometry.HCDT.Types    as X
+module Geometry.HCDT
+  (module X)
+  where
+import           Geometry.HCDT.Delaunay as X
+import           Geometry.HCDT.Types    as X
 import           Geometry.HCDT.Utils    as X
diff --git a/src/Geometry/HCDT/Delaunay.hs b/src/Geometry/HCDT/Delaunay.hs
--- a/src/Geometry/HCDT/Delaunay.hs
+++ b/src/Geometry/HCDT/Delaunay.hs
@@ -1,42 +1,42 @@
-module Geometry.HCDT.Delaunay
-  ( delaunay,
-    cdelaunay
-  )
-where
-
-import           Foreign.Marshal.Alloc       (free, mallocBytes)
-import           Foreign.Marshal.Array       (pokeArray)
-import           Foreign.Storable            (peek, sizeOf)
-import           Geometry.HCDT.Triangulation
-import           Geometry.HCDT.Types         (Triangulation, Vertex, ConstrainedTriangulation, Edge)
-
--- | Unconstrained 2d Delaunay triangulation.
-delaunay :: [Vertex] -> IO Triangulation
-delaunay vertices = do
-  let nvertices = length vertices
-  verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))
-  cvertices <- mapM vertexToCVertex vertices
-  pokeArray verticesPtr cvertices
-  ctriangulationPtr <- c_delaunay verticesPtr (fromIntegral nvertices)
-  ctriangulation <- peek ctriangulationPtr 
-  free verticesPtr
-  free ctriangulationPtr
-  cTriangulationToTriangulation ctriangulation
-
--- | Constrained 2d Delaunay triangulation.
-cdelaunay :: [Vertex] -> [Edge] -> IO ConstrainedTriangulation
-cdelaunay vertices edges = do
-  let nvertices = length vertices
-  verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))
-  cvertices <- mapM vertexToCVertex vertices
-  pokeArray verticesPtr cvertices
-  let nedges = length edges
-  edgesPtr <- mallocBytes (nedges * sizeOf (undefined :: CEdge))
-  cedges <- mapM edgeToCEdge edges
-  pokeArray edgesPtr cedges
-  cctriangulationPtr <- c_cdelaunay verticesPtr (fromIntegral nvertices) edgesPtr (fromIntegral nedges)
-  cctriangulation <- peek cctriangulationPtr 
-  free verticesPtr
-  free edgesPtr
-  free cctriangulationPtr
-  cCTriangulationToConstrainedTriangulation cctriangulation
+module Geometry.HCDT.Delaunay
+  ( delaunay,
+    cdelaunay
+  )
+where
+
+import           Foreign.Marshal.Alloc       (free, mallocBytes)
+import           Foreign.Marshal.Array       (pokeArray)
+import           Foreign.Storable            (peek, sizeOf)
+import           Geometry.HCDT.Triangulation
+import           Geometry.HCDT.Types         (Triangulation, Vertex, ConstrainedTriangulation, Edge)
+
+-- | Unconstrained 2d Delaunay triangulation.
+delaunay :: [Vertex] -> IO Triangulation
+delaunay vertices = do
+  let nvertices = length vertices
+  verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))
+  cvertices <- mapM vertexToCVertex vertices
+  pokeArray verticesPtr cvertices
+  ctriangulationPtr <- c_delaunay verticesPtr (fromIntegral nvertices)
+  ctriangulation <- peek ctriangulationPtr 
+  free verticesPtr
+  free ctriangulationPtr
+  cTriangulationToTriangulation ctriangulation
+
+-- | Constrained 2d Delaunay triangulation.
+cdelaunay :: [Vertex] -> [Edge] -> IO ConstrainedTriangulation
+cdelaunay vertices edges = do
+  let nvertices = length vertices
+  verticesPtr <- mallocBytes (nvertices * sizeOf (undefined :: CVertex))
+  cvertices <- mapM vertexToCVertex vertices
+  pokeArray verticesPtr cvertices
+  let nedges = length edges
+  edgesPtr <- mallocBytes (nedges * sizeOf (undefined :: CEdge))
+  cedges <- mapM edgeToCEdge edges
+  pokeArray edgesPtr cedges
+  cctriangulationPtr <- c_cdelaunay verticesPtr (fromIntegral nvertices) edgesPtr (fromIntegral nedges)
+  cctriangulation <- peek cctriangulationPtr 
+  free verticesPtr
+  free edgesPtr
+  free cctriangulationPtr
+  cCTriangulationToConstrainedTriangulation cctriangulation
diff --git a/src/Geometry/HCDT/Triangulation.hs b/src/Geometry/HCDT/Triangulation.hs
--- a/src/Geometry/HCDT/Triangulation.hs
+++ b/src/Geometry/HCDT/Triangulation.hs
@@ -1,277 +1,277 @@
-{-# LINE 1 "Triangulation.hsc" #-}
-{-# LANGUAGE ForeignFunctionInterface #-}
-{-# LANGUAGE CPP #-}
-module Geometry.HCDT.Triangulation
-( cTriangulationToTriangulation
-, cCTriangulationToConstrainedTriangulation
-, vertexToCVertex
-, edgeToCEdge
-, c_delaunay 
-, c_cdelaunay
-, CTriangulation (..)
-, CVertex (..)
-, CCTriangulation (..)
-, CEdge (..) )
-  where
-import           Geometry.HCDT.Types
-import           Foreign
-import           Foreign.C.Types
-import qualified Data.IntMap.Strict as IM
-
-
-
-data CVertex = CVertex {
-    __x :: CDouble
-  , __y :: CDouble
-}
-
-instance Storable CVertex where
-    sizeOf    __ = (16)
-{-# LINE 29 "Triangulation.hsc" #-}
-    alignment __ = 8
-{-# LINE 30 "Triangulation.hsc" #-}
-    peek ptr = do
-      x'  <- (`peekByteOff` 0) ptr
-{-# LINE 32 "Triangulation.hsc" #-}
-      y'  <- (`peekByteOff` 8) ptr
-{-# LINE 33 "Triangulation.hsc" #-}
-      return CVertex { __x = x', __y = y' }
-    poke ptr (CVertex r1 r2)
-      = do
-        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
-{-# LINE 37 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
-{-# LINE 38 "Triangulation.hsc" #-}
-
-cVertexToVertex :: CVertex -> IO Vertex
-cVertexToVertex cvertex = do
-  let x = realToFrac $ __x cvertex
-  let y = realToFrac $ __y cvertex
-  return $ Vertex x y
-
-vertexToCVertex :: Vertex -> IO CVertex
-vertexToCVertex (Vertex x y) = do
-  return $ CVertex { __x = realToFrac x, __y = realToFrac y }
-
-data CEdge = CEdge {
-    __i :: CUInt
-  , __j :: CUInt
-}
-
-instance Storable CEdge where
-    sizeOf    __ = (8)
-{-# LINE 56 "Triangulation.hsc" #-}
-    alignment __ = 4
-{-# LINE 57 "Triangulation.hsc" #-}
-    peek ptr = do
-      i'  <- (`peekByteOff` 0) ptr
-{-# LINE 59 "Triangulation.hsc" #-}
-      j'  <- (`peekByteOff` 4) ptr
-{-# LINE 60 "Triangulation.hsc" #-}
-      return CEdge { __i = i'
-                   , __j = j' }
-    poke ptr (CEdge r1 r2)
-      = do
-        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
-{-# LINE 65 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2
-{-# LINE 66 "Triangulation.hsc" #-}
-
-cEdgeToEdge :: CEdge -> IO Edge
-cEdgeToEdge cedge = do
-  let i = fromIntegral $ __i cedge
-  let j = fromIntegral $ __j cedge
-  return $ Edge i j
-
-edgeToCEdge :: Edge -> IO CEdge
-edgeToCEdge (Edge i j) = do
-  return $ CEdge { __i = fromIntegral i, __j = fromIntegral j }
-
-data CTriangle = CTriangle {
-    __i1 :: CUInt
-  , __i2 :: CUInt
-  , __i3 :: CUInt
-}
-
-instance Storable CTriangle where
-    sizeOf    __ = (12)
-{-# LINE 85 "Triangulation.hsc" #-}
-    alignment __ = 4
-{-# LINE 86 "Triangulation.hsc" #-}
-    peek ptr = do
-      i1'  <- (`peekByteOff` 0) ptr
-{-# LINE 88 "Triangulation.hsc" #-}
-      i2'  <- (`peekByteOff` 4) ptr
-{-# LINE 89 "Triangulation.hsc" #-}
-      i3'  <- (`peekByteOff` 8) ptr
-{-# LINE 90 "Triangulation.hsc" #-}
-      return CTriangle { __i1 = i1'
-                       , __i2 = i2'
-                       , __i3 = i3' }
-    poke ptr (CTriangle r1 r2 r3)
-      = do
-        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
-{-# LINE 96 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2
-{-# LINE 97 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r3
-{-# LINE 98 "Triangulation.hsc" #-}
-
-cTriangleToTriangle :: CTriangle -> IO Triangle
-cTriangleToTriangle ctriangle = do
-  let i1 = fromIntegral $ __i1 ctriangle
-  let i2 = fromIntegral $ __i2 ctriangle
-  let i3 = fromIntegral $ __i3 ctriangle
-  return $ Triangle i1 i2 i3
-
-data CTriangulation = CTriangulation {
-    __vertices   :: Ptr CVertex
-  , __nvertices  :: CSize
-  , __triangles  :: Ptr CTriangle
-  , __ntriangles :: CSize
-  , __edges      :: Ptr CEdge
-  , __nedges     :: CSize
-}
-
-instance Storable CTriangulation where
-    sizeOf    __ = (48)
-{-# LINE 117 "Triangulation.hsc" #-}
-    alignment __ = 8
-{-# LINE 118 "Triangulation.hsc" #-}
-    peek ptr = do
-      vs  <- (`peekByteOff` 0) ptr
-{-# LINE 120 "Triangulation.hsc" #-}
-      nvs <- (`peekByteOff` 8) ptr
-{-# LINE 121 "Triangulation.hsc" #-}
-      ts  <- (`peekByteOff` 16) ptr
-{-# LINE 122 "Triangulation.hsc" #-}
-      nts <- (`peekByteOff` 24) ptr
-{-# LINE 123 "Triangulation.hsc" #-}
-      es  <- (`peekByteOff` 32) ptr
-{-# LINE 124 "Triangulation.hsc" #-}
-      nes <- (`peekByteOff` 40) ptr
-{-# LINE 125 "Triangulation.hsc" #-}
-      return CTriangulation { __vertices   = vs
-                            , __nvertices  = nvs
-                            , __triangles  = ts
-                            , __ntriangles = nts
-                            , __edges      = es
-                            , __nedges     = nes }
-    poke ptr (CTriangulation r1 r2 r3 r4 r5 r6)
-      = do
-        (\hsc_ptr -> pokeByteOff hsc_ptr 0)   ptr r1
-{-# LINE 134 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 8)  ptr r2
-{-# LINE 135 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 16)  ptr r3
-{-# LINE 136 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
-{-# LINE 137 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 32)      ptr r5
-{-# LINE 138 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 40)     ptr r6
-{-# LINE 139 "Triangulation.hsc" #-}
-
-data CCTriangulation = CCTriangulation {
-    __vertices'    :: Ptr CVertex
-  , __nvertices'   :: CSize
-  , __triangles'   :: Ptr CTriangle
-  , __ntriangles'  :: CSize
-  , __edges'       :: Ptr CEdge
-  , __nedges'      :: CSize
-  , __fixededges'  :: Ptr CEdge
-  , __nfixededges' :: CSize
-}
-
-instance Storable CCTriangulation where
-    sizeOf    __ = (64)
-{-# LINE 153 "Triangulation.hsc" #-}
-    alignment __ = 8
-{-# LINE 154 "Triangulation.hsc" #-}
-    peek ptr = do
-      vs   <- (`peekByteOff` 0) ptr
-{-# LINE 156 "Triangulation.hsc" #-}
-      nvs  <- (`peekByteOff` 8) ptr
-{-# LINE 157 "Triangulation.hsc" #-}
-      ts   <- (`peekByteOff` 16) ptr
-{-# LINE 158 "Triangulation.hsc" #-}
-      nts  <- (`peekByteOff` 24) ptr
-{-# LINE 159 "Triangulation.hsc" #-}
-      es   <- (`peekByteOff` 32) ptr
-{-# LINE 160 "Triangulation.hsc" #-}
-      nes  <- (`peekByteOff` 40) ptr
-{-# LINE 161 "Triangulation.hsc" #-}
-      fes  <- (`peekByteOff` 48) ptr
-{-# LINE 162 "Triangulation.hsc" #-}
-      nfes <- (`peekByteOff` 56) ptr
-{-# LINE 163 "Triangulation.hsc" #-}
-      return CCTriangulation { __vertices'    = vs
-                            , __nvertices'   = nvs
-                            , __triangles'   = ts
-                            , __ntriangles'  = nts
-                            , __edges'       = es
-                            , __nedges'      = nes 
-                            , __fixededges'  = fes
-                            , __nfixededges' = nfes }
-    poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8)
-      = do
-        (\hsc_ptr -> pokeByteOff hsc_ptr 0)    ptr r1
-{-# LINE 174 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 8)   ptr r2
-{-# LINE 175 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 16)   ptr r3
-{-# LINE 176 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 24)  ptr r4
-{-# LINE 177 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 32)       ptr r5
-{-# LINE 178 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 40)      ptr r6
-{-# LINE 179 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 48)  ptr r7
-{-# LINE 180 "Triangulation.hsc" #-}
-        (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8
-{-# LINE 181 "Triangulation.hsc" #-}
-
-cTriangulationToTriangulation :: CTriangulation -> IO Triangulation
-cTriangulationToTriangulation ctriangulation = do
-  let nvertices  = fromIntegral $ __nvertices ctriangulation
-      ntriangles = fromIntegral $ __ntriangles ctriangulation
-      nedges     = fromIntegral $ __nedges ctriangulation
-  vertices  <- peekArray nvertices  (__vertices ctriangulation)
-  triangles <- peekArray ntriangles (__triangles ctriangulation)
-  edges     <- peekArray nedges     (__edges ctriangulation)
-  vertices'  <- mapM cVertexToVertex vertices
-  triangles' <- mapM cTriangleToTriangle triangles
-  edges'     <- mapM cEdgeToEdge edges
-  return $ Triangulation { _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
-                         , _triangles = triangles'
-                         , _edges     = edges' }
-
-cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation
-cCTriangulationToConstrainedTriangulation cctriangulation = do
-  let nvertices  = fromIntegral $ __nvertices' cctriangulation
-      ntriangles = fromIntegral $ __ntriangles' cctriangulation
-      nedges     = fromIntegral $ __nedges' cctriangulation
-      nfedges    = fromIntegral $ __nfixededges' cctriangulation
-  vertices  <- peekArray nvertices  (__vertices' cctriangulation)
-  triangles <- peekArray ntriangles (__triangles' cctriangulation)
-  edges     <- peekArray nedges     (__edges' cctriangulation)
-  fedges    <- peekArray nfedges    (__fixededges' cctriangulation)
-  vertices'  <- mapM cVertexToVertex vertices
-  triangles' <- mapM cTriangleToTriangle triangles
-  edges'     <- mapM cEdgeToEdge edges
-  fedges'    <- mapM cEdgeToEdge fedges
-  let triangulation = Triangulation { 
-                          _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
-                        , _triangles = triangles'
-                        , _edges     = edges' 
-                      }
-  return ConstrainedTriangulation { 
-                          _triangulation = triangulation
-                        , _fixedEdges    = fedges' }
-
-foreign import ccall unsafe "delaunay" c_delaunay
-  :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation)
-
-foreign import ccall unsafe "cdelaunay" c_cdelaunay
-  :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)
+{-# LINE 1 "Triangulation.hsc" #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE CPP #-}
+module Geometry.HCDT.Triangulation
+( cTriangulationToTriangulation
+, cCTriangulationToConstrainedTriangulation
+, vertexToCVertex
+, edgeToCEdge
+, c_delaunay 
+, c_cdelaunay
+, CTriangulation (..)
+, CVertex (..)
+, CCTriangulation (..)
+, CEdge (..) )
+  where
+import           Geometry.HCDT.Types
+import           Foreign
+import           Foreign.C.Types
+import qualified Data.IntMap.Strict as IM
+
+
+
+data CVertex = CVertex {
+    __x :: CDouble
+  , __y :: CDouble
+}
+
+instance Storable CVertex where
+    sizeOf    __ = (16)
+{-# LINE 29 "Triangulation.hsc" #-}
+    alignment __ = 8
+{-# LINE 30 "Triangulation.hsc" #-}
+    peek ptr = do
+      x'  <- (`peekByteOff` 0) ptr
+{-# LINE 32 "Triangulation.hsc" #-}
+      y'  <- (`peekByteOff` 8) ptr
+{-# LINE 33 "Triangulation.hsc" #-}
+      return CVertex { __x = x', __y = y' }
+    poke ptr (CVertex r1 r2)
+      = do
+        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 37 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 38 "Triangulation.hsc" #-}
+
+cVertexToVertex :: CVertex -> IO Vertex
+cVertexToVertex cvertex = do
+  let x = realToFrac $ __x cvertex
+  let y = realToFrac $ __y cvertex
+  return $ Vertex x y
+
+vertexToCVertex :: Vertex -> IO CVertex
+vertexToCVertex (Vertex x y) = do
+  return $ CVertex { __x = realToFrac x, __y = realToFrac y }
+
+data CEdge = CEdge {
+    __i :: CUInt
+  , __j :: CUInt
+}
+
+instance Storable CEdge where
+    sizeOf    __ = (8)
+{-# LINE 56 "Triangulation.hsc" #-}
+    alignment __ = 4
+{-# LINE 57 "Triangulation.hsc" #-}
+    peek ptr = do
+      i'  <- (`peekByteOff` 0) ptr
+{-# LINE 59 "Triangulation.hsc" #-}
+      j'  <- (`peekByteOff` 4) ptr
+{-# LINE 60 "Triangulation.hsc" #-}
+      return CEdge { __i = i'
+                   , __j = j' }
+    poke ptr (CEdge r1 r2)
+      = do
+        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 65 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2
+{-# LINE 66 "Triangulation.hsc" #-}
+
+cEdgeToEdge :: CEdge -> IO Edge
+cEdgeToEdge cedge = do
+  let i = fromIntegral $ __i cedge
+  let j = fromIntegral $ __j cedge
+  return $ Edge i j
+
+edgeToCEdge :: Edge -> IO CEdge
+edgeToCEdge (Edge i j) = do
+  return $ CEdge { __i = fromIntegral i, __j = fromIntegral j }
+
+data CTriangle = CTriangle {
+    __i1 :: CUInt
+  , __i2 :: CUInt
+  , __i3 :: CUInt
+}
+
+instance Storable CTriangle where
+    sizeOf    __ = (12)
+{-# LINE 85 "Triangulation.hsc" #-}
+    alignment __ = 4
+{-# LINE 86 "Triangulation.hsc" #-}
+    peek ptr = do
+      i1'  <- (`peekByteOff` 0) ptr
+{-# LINE 88 "Triangulation.hsc" #-}
+      i2'  <- (`peekByteOff` 4) ptr
+{-# LINE 89 "Triangulation.hsc" #-}
+      i3'  <- (`peekByteOff` 8) ptr
+{-# LINE 90 "Triangulation.hsc" #-}
+      return CTriangle { __i1 = i1'
+                       , __i2 = i2'
+                       , __i3 = i3' }
+    poke ptr (CTriangle r1 r2 r3)
+      = do
+        (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 96 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 4) ptr r2
+{-# LINE 97 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r3
+{-# LINE 98 "Triangulation.hsc" #-}
+
+cTriangleToTriangle :: CTriangle -> IO Triangle
+cTriangleToTriangle ctriangle = do
+  let i1 = fromIntegral $ __i1 ctriangle
+  let i2 = fromIntegral $ __i2 ctriangle
+  let i3 = fromIntegral $ __i3 ctriangle
+  return $ Triangle i1 i2 i3
+
+data CTriangulation = CTriangulation {
+    __vertices   :: Ptr CVertex
+  , __nvertices  :: CSize
+  , __triangles  :: Ptr CTriangle
+  , __ntriangles :: CSize
+  , __edges      :: Ptr CEdge
+  , __nedges     :: CSize
+}
+
+instance Storable CTriangulation where
+    sizeOf    __ = (48)
+{-# LINE 117 "Triangulation.hsc" #-}
+    alignment __ = 8
+{-# LINE 118 "Triangulation.hsc" #-}
+    peek ptr = do
+      vs  <- (`peekByteOff` 0) ptr
+{-# LINE 120 "Triangulation.hsc" #-}
+      nvs <- (`peekByteOff` 8) ptr
+{-# LINE 121 "Triangulation.hsc" #-}
+      ts  <- (`peekByteOff` 16) ptr
+{-# LINE 122 "Triangulation.hsc" #-}
+      nts <- (`peekByteOff` 24) ptr
+{-# LINE 123 "Triangulation.hsc" #-}
+      es  <- (`peekByteOff` 32) ptr
+{-# LINE 124 "Triangulation.hsc" #-}
+      nes <- (`peekByteOff` 40) ptr
+{-# LINE 125 "Triangulation.hsc" #-}
+      return CTriangulation { __vertices   = vs
+                            , __nvertices  = nvs
+                            , __triangles  = ts
+                            , __ntriangles = nts
+                            , __edges      = es
+                            , __nedges     = nes }
+    poke ptr (CTriangulation r1 r2 r3 r4 r5 r6)
+      = do
+        (\hsc_ptr -> pokeByteOff hsc_ptr 0)   ptr r1
+{-# LINE 134 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 8)  ptr r2
+{-# LINE 135 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 16)  ptr r3
+{-# LINE 136 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 137 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 32)      ptr r5
+{-# LINE 138 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 40)     ptr r6
+{-# LINE 139 "Triangulation.hsc" #-}
+
+data CCTriangulation = CCTriangulation {
+    __vertices'    :: Ptr CVertex
+  , __nvertices'   :: CSize
+  , __triangles'   :: Ptr CTriangle
+  , __ntriangles'  :: CSize
+  , __edges'       :: Ptr CEdge
+  , __nedges'      :: CSize
+  , __fixededges'  :: Ptr CEdge
+  , __nfixededges' :: CSize
+}
+
+instance Storable CCTriangulation where
+    sizeOf    __ = (64)
+{-# LINE 153 "Triangulation.hsc" #-}
+    alignment __ = 8
+{-# LINE 154 "Triangulation.hsc" #-}
+    peek ptr = do
+      vs   <- (`peekByteOff` 0) ptr
+{-# LINE 156 "Triangulation.hsc" #-}
+      nvs  <- (`peekByteOff` 8) ptr
+{-# LINE 157 "Triangulation.hsc" #-}
+      ts   <- (`peekByteOff` 16) ptr
+{-# LINE 158 "Triangulation.hsc" #-}
+      nts  <- (`peekByteOff` 24) ptr
+{-# LINE 159 "Triangulation.hsc" #-}
+      es   <- (`peekByteOff` 32) ptr
+{-# LINE 160 "Triangulation.hsc" #-}
+      nes  <- (`peekByteOff` 40) ptr
+{-# LINE 161 "Triangulation.hsc" #-}
+      fes  <- (`peekByteOff` 48) ptr
+{-# LINE 162 "Triangulation.hsc" #-}
+      nfes <- (`peekByteOff` 56) ptr
+{-# LINE 163 "Triangulation.hsc" #-}
+      return CCTriangulation { __vertices'    = vs
+                            , __nvertices'   = nvs
+                            , __triangles'   = ts
+                            , __ntriangles'  = nts
+                            , __edges'       = es
+                            , __nedges'      = nes 
+                            , __fixededges'  = fes
+                            , __nfixededges' = nfes }
+    poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8)
+      = do
+        (\hsc_ptr -> pokeByteOff hsc_ptr 0)    ptr r1
+{-# LINE 174 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 8)   ptr r2
+{-# LINE 175 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 16)   ptr r3
+{-# LINE 176 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 24)  ptr r4
+{-# LINE 177 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 32)       ptr r5
+{-# LINE 178 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 40)      ptr r6
+{-# LINE 179 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 48)  ptr r7
+{-# LINE 180 "Triangulation.hsc" #-}
+        (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8
+{-# LINE 181 "Triangulation.hsc" #-}
+
+cTriangulationToTriangulation :: CTriangulation -> IO Triangulation
+cTriangulationToTriangulation ctriangulation = do
+  let nvertices  = fromIntegral $ __nvertices ctriangulation
+      ntriangles = fromIntegral $ __ntriangles ctriangulation
+      nedges     = fromIntegral $ __nedges ctriangulation
+  vertices  <- peekArray nvertices  (__vertices ctriangulation)
+  triangles <- peekArray ntriangles (__triangles ctriangulation)
+  edges     <- peekArray nedges     (__edges ctriangulation)
+  vertices'  <- mapM cVertexToVertex vertices
+  triangles' <- mapM cTriangleToTriangle triangles
+  edges'     <- mapM cEdgeToEdge edges
+  return $ Triangulation { _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
+                         , _triangles = triangles'
+                         , _edges     = edges' }
+
+cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation
+cCTriangulationToConstrainedTriangulation cctriangulation = do
+  let nvertices  = fromIntegral $ __nvertices' cctriangulation
+      ntriangles = fromIntegral $ __ntriangles' cctriangulation
+      nedges     = fromIntegral $ __nedges' cctriangulation
+      nfedges    = fromIntegral $ __nfixededges' cctriangulation
+  vertices  <- peekArray nvertices  (__vertices' cctriangulation)
+  triangles <- peekArray ntriangles (__triangles' cctriangulation)
+  edges     <- peekArray nedges     (__edges' cctriangulation)
+  fedges    <- peekArray nfedges    (__fixededges' cctriangulation)
+  vertices'  <- mapM cVertexToVertex vertices
+  triangles' <- mapM cTriangleToTriangle triangles
+  edges'     <- mapM cEdgeToEdge edges
+  fedges'    <- mapM cEdgeToEdge fedges
+  let triangulation = Triangulation { 
+                          _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
+                        , _triangles = triangles'
+                        , _edges     = edges' 
+                      }
+  return ConstrainedTriangulation { 
+                          _triangulation = triangulation
+                        , _fixedEdges    = fedges' }
+
+foreign import ccall unsafe "delaunay" c_delaunay
+  :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation)
+
+foreign import ccall unsafe "cdelaunay" c_cdelaunay
+  :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)
diff --git a/src/Geometry/HCDT/Triangulation.hsc b/src/Geometry/HCDT/Triangulation.hsc
new file mode 100644
--- /dev/null
+++ b/src/Geometry/HCDT/Triangulation.hsc
@@ -0,0 +1,224 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE CPP #-}
+module Geometry.HCDT.Triangulation
+( cTriangulationToTriangulation
+, cCTriangulationToConstrainedTriangulation
+, vertexToCVertex
+, edgeToCEdge
+, c_delaunay 
+, c_cdelaunay
+, CTriangulation (..)
+, CVertex (..)
+, CCTriangulation (..)
+, CEdge (..) )
+  where
+import           Geometry.HCDT.Types
+import           Foreign
+import           Foreign.C.Types
+import qualified Data.IntMap.Strict as IM
+
+#include "hcdt.hpp"
+
+data CVertex = CVertex {
+    __x :: CDouble
+  , __y :: CDouble
+}
+
+instance Storable CVertex where
+    sizeOf    __ = #{size VertexT}
+    alignment __ = #{alignment VertexT}
+    peek ptr = do
+      x'  <- #{peek VertexT, x} ptr
+      y'  <- #{peek VertexT, y} ptr
+      return CVertex { __x = x', __y = y' }
+    poke ptr (CVertex r1 r2)
+      = do
+        #{poke VertexT, x} ptr r1
+        #{poke VertexT, y} ptr r2
+
+cVertexToVertex :: CVertex -> IO Vertex
+cVertexToVertex cvertex = do
+  let x = realToFrac $ __x cvertex
+  let y = realToFrac $ __y cvertex
+  return $ Vertex x y
+
+vertexToCVertex :: Vertex -> IO CVertex
+vertexToCVertex (Vertex x y) = do
+  return $ CVertex { __x = realToFrac x, __y = realToFrac y }
+
+data CEdge = CEdge {
+    __i :: CUInt
+  , __j :: CUInt
+}
+
+instance Storable CEdge where
+    sizeOf    __ = #{size EdgeT}
+    alignment __ = #{alignment EdgeT}
+    peek ptr = do
+      i'  <- #{peek EdgeT, i} ptr
+      j'  <- #{peek EdgeT, j} ptr
+      return CEdge { __i = i'
+                   , __j = j' }
+    poke ptr (CEdge r1 r2)
+      = do
+        #{poke EdgeT, i} ptr r1
+        #{poke EdgeT, j} ptr r2
+
+cEdgeToEdge :: CEdge -> IO Edge
+cEdgeToEdge cedge = do
+  let i = fromIntegral $ __i cedge
+  let j = fromIntegral $ __j cedge
+  return $ Edge i j
+
+edgeToCEdge :: Edge -> IO CEdge
+edgeToCEdge (Edge i j) = do
+  return $ CEdge { __i = fromIntegral i, __j = fromIntegral j }
+
+data CTriangle = CTriangle {
+    __i1 :: CUInt
+  , __i2 :: CUInt
+  , __i3 :: CUInt
+}
+
+instance Storable CTriangle where
+    sizeOf    __ = #{size TriangleT}
+    alignment __ = #{alignment TriangleT}
+    peek ptr = do
+      i1'  <- #{peek TriangleT, i1} ptr
+      i2'  <- #{peek TriangleT, i2} ptr
+      i3'  <- #{peek TriangleT, i3} ptr
+      return CTriangle { __i1 = i1'
+                       , __i2 = i2'
+                       , __i3 = i3' }
+    poke ptr (CTriangle r1 r2 r3)
+      = do
+        #{poke TriangleT, i1} ptr r1
+        #{poke TriangleT, i2} ptr r2
+        #{poke TriangleT, i3} ptr r3
+
+cTriangleToTriangle :: CTriangle -> IO Triangle
+cTriangleToTriangle ctriangle = do
+  let i1 = fromIntegral $ __i1 ctriangle
+  let i2 = fromIntegral $ __i2 ctriangle
+  let i3 = fromIntegral $ __i3 ctriangle
+  return $ Triangle i1 i2 i3
+
+data CTriangulation = CTriangulation {
+    __vertices   :: Ptr CVertex
+  , __nvertices  :: CSize
+  , __triangles  :: Ptr CTriangle
+  , __ntriangles :: CSize
+  , __edges      :: Ptr CEdge
+  , __nedges     :: CSize
+}
+
+instance Storable CTriangulation where
+    sizeOf    __ = #{size TriangulationT}
+    alignment __ = #{alignment TriangulationT}
+    peek ptr = do
+      vs  <- #{peek TriangulationT, vertices} ptr
+      nvs <- #{peek TriangulationT, nvertices} ptr
+      ts  <- #{peek TriangulationT, triangles} ptr
+      nts <- #{peek TriangulationT, ntriangles} ptr
+      es  <- #{peek TriangulationT, edges} ptr
+      nes <- #{peek TriangulationT, nedges} ptr
+      return CTriangulation { __vertices   = vs
+                            , __nvertices  = nvs
+                            , __triangles  = ts
+                            , __ntriangles = nts
+                            , __edges      = es
+                            , __nedges     = nes }
+    poke ptr (CTriangulation r1 r2 r3 r4 r5 r6)
+      = do
+        #{poke TriangulationT, vertices}   ptr r1
+        #{poke TriangulationT, nvertices}  ptr r2
+        #{poke TriangulationT, triangles}  ptr r3
+        #{poke TriangulationT, ntriangles} ptr r4
+        #{poke TriangulationT, edges}      ptr r5
+        #{poke TriangulationT, nedges}     ptr r6
+
+data CCTriangulation = CCTriangulation {
+    __vertices'    :: Ptr CVertex
+  , __nvertices'   :: CSize
+  , __triangles'   :: Ptr CTriangle
+  , __ntriangles'  :: CSize
+  , __edges'       :: Ptr CEdge
+  , __nedges'      :: CSize
+  , __fixededges'  :: Ptr CEdge
+  , __nfixededges' :: CSize
+}
+
+instance Storable CCTriangulation where
+    sizeOf    __ = #{size CTriangulationT}
+    alignment __ = #{alignment CTriangulationT}
+    peek ptr = do
+      vs   <- #{peek CTriangulationT, vertices} ptr
+      nvs  <- #{peek CTriangulationT, nvertices} ptr
+      ts   <- #{peek CTriangulationT, triangles} ptr
+      nts  <- #{peek CTriangulationT, ntriangles} ptr
+      es   <- #{peek CTriangulationT, edges} ptr
+      nes  <- #{peek CTriangulationT, nedges} ptr
+      fes  <- #{peek CTriangulationT, fixededges} ptr
+      nfes <- #{peek CTriangulationT, nfixededges} ptr
+      return CCTriangulation { __vertices'    = vs
+                            , __nvertices'   = nvs
+                            , __triangles'   = ts
+                            , __ntriangles'  = nts
+                            , __edges'       = es
+                            , __nedges'      = nes 
+                            , __fixededges'  = fes
+                            , __nfixededges' = nfes }
+    poke ptr (CCTriangulation r1 r2 r3 r4 r5 r6 r7 r8)
+      = do
+        #{poke CTriangulationT, vertices}    ptr r1
+        #{poke CTriangulationT, nvertices}   ptr r2
+        #{poke CTriangulationT, triangles}   ptr r3
+        #{poke CTriangulationT, ntriangles}  ptr r4
+        #{poke CTriangulationT, edges}       ptr r5
+        #{poke CTriangulationT, nedges}      ptr r6
+        #{poke CTriangulationT, fixededges}  ptr r7
+        #{poke CTriangulationT, nfixededges} ptr r8
+
+cTriangulationToTriangulation :: CTriangulation -> IO Triangulation
+cTriangulationToTriangulation ctriangulation = do
+  let nvertices  = fromIntegral $ __nvertices ctriangulation
+      ntriangles = fromIntegral $ __ntriangles ctriangulation
+      nedges     = fromIntegral $ __nedges ctriangulation
+  vertices  <- peekArray nvertices  (__vertices ctriangulation)
+  triangles <- peekArray ntriangles (__triangles ctriangulation)
+  edges     <- peekArray nedges     (__edges ctriangulation)
+  vertices'  <- mapM cVertexToVertex vertices
+  triangles' <- mapM cTriangleToTriangle triangles
+  edges'     <- mapM cEdgeToEdge edges
+  return $ Triangulation { _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
+                         , _triangles = triangles'
+                         , _edges     = edges' }
+
+cCTriangulationToConstrainedTriangulation :: CCTriangulation -> IO ConstrainedTriangulation
+cCTriangulationToConstrainedTriangulation cctriangulation = do
+  let nvertices  = fromIntegral $ __nvertices' cctriangulation
+      ntriangles = fromIntegral $ __ntriangles' cctriangulation
+      nedges     = fromIntegral $ __nedges' cctriangulation
+      nfedges    = fromIntegral $ __nfixededges' cctriangulation
+  vertices  <- peekArray nvertices  (__vertices' cctriangulation)
+  triangles <- peekArray ntriangles (__triangles' cctriangulation)
+  edges     <- peekArray nedges     (__edges' cctriangulation)
+  fedges    <- peekArray nfedges    (__fixededges' cctriangulation)
+  vertices'  <- mapM cVertexToVertex vertices
+  triangles' <- mapM cTriangleToTriangle triangles
+  edges'     <- mapM cEdgeToEdge edges
+  fedges'    <- mapM cEdgeToEdge fedges
+  let triangulation = Triangulation { 
+                          _vertices  = IM.fromAscList (zip [0 .. nvertices-1] vertices')
+                        , _triangles = triangles'
+                        , _edges     = edges' 
+                      }
+  return ConstrainedTriangulation { 
+                          _triangulation = triangulation
+                        , _fixedEdges    = fedges' }
+
+foreign import ccall unsafe "delaunay" c_delaunay
+  :: Ptr CVertex -> CSize -> IO (Ptr CTriangulation)
+
+foreign import ccall unsafe "cdelaunay" c_cdelaunay
+  :: Ptr CVertex -> CSize -> Ptr CEdge -> CSize -> IO (Ptr CCTriangulation)
diff --git a/src/Geometry/HCDT/Types.hs b/src/Geometry/HCDT/Types.hs
--- a/src/Geometry/HCDT/Types.hs
+++ b/src/Geometry/HCDT/Types.hs
@@ -1,28 +1,35 @@
-module Geometry.HCDT.Types
-  where
-import           Data.IntMap.Strict (IntMap)
-import           Data.List          (sort)
-
-data Triangle = Triangle Int Int Int 
-  deriving (Show, Read)
-instance Eq Triangle where
-    Triangle i j k == Triangle i' j' k' = sort [i, j, k] == sort [i', j', k']
-
-data Edge = Edge Int Int
-  deriving (Show, Read)
-instance Eq Edge where
-    Edge i j == Edge i' j' = (i == i' && j == j') || (i == j' && j == i')
-
-data Vertex = Vertex Double Double
-  deriving (Show, Eq)
-
-data Triangulation = Triangulation {
-    _vertices  :: IntMap Vertex
-  , _triangles :: [Triangle]
-  , _edges     :: [Edge]
-} deriving Show
-
-data ConstrainedTriangulation = ConstrainedTriangulation {
-    _triangulation :: Triangulation
-  , _fixedEdges    :: [Edge]
+{-# LANGUAGE InstanceSigs #-}
+
+module Geometry.HCDT.Types
+  where
+import           Data.Hashable      (Hashable, hashWithSalt)
+import           Data.IntMap.Strict (IntMap)
+import           Data.List          (sort)
+
+data Triangle = Triangle Int Int Int 
+  deriving (Show, Read)
+instance Eq Triangle where
+    (==) :: Triangle -> Triangle -> Bool
+    Triangle i j k == Triangle i' j' k' = sort [i, j, k] == sort [i', j', k']
+
+data Edge = Edge Int Int
+  deriving (Show, Read)
+instance Eq Edge where
+    (==) :: Edge -> Edge -> Bool
+    Edge i j == Edge i' j' = (i == i' && j == j') || (i == j' && j == i')
+instance Hashable Edge where
+  hashWithSalt s (Edge m1 m2) = hashWithSalt s (min m1 m2) `hashWithSalt` (max m1 m2)
+
+data Vertex = Vertex Double Double
+  deriving (Show, Eq)
+
+data Triangulation = Triangulation {
+    _vertices  :: IntMap Vertex
+  , _triangles :: [Triangle]
+  , _edges     :: [Edge]
+} deriving Show
+
+data ConstrainedTriangulation = ConstrainedTriangulation {
+    _triangulation :: Triangulation
+  , _fixedEdges    :: [Edge]
 } deriving Show
diff --git a/src/Geometry/HCDT/Utils.hs b/src/Geometry/HCDT/Utils.hs
--- a/src/Geometry/HCDT/Utils.hs
+++ b/src/Geometry/HCDT/Utils.hs
@@ -1,47 +1,49 @@
-module Geometry.HCDT.Utils
-  ( borderEdges,
-    partitionEdges
-  )
-where
-    
-import           Geometry.HCDT.Types 
-import           Data.Sequence as DS     (Seq, fromList, (><), filter, drop) 
-import           Data.Maybe              (isNothing, fromJust)
-import           Data.Foldable           (find)
-import           Data.Foldable.WithIndex (ifind)
-
-triangleEdges :: Triangle -> Seq Edge
-triangleEdges (Triangle i j k) = fromList [Edge i j, Edge j k, Edge i k]
-
--- isUnique :: Eq a => Seq a -> a -> Bool
--- isUnique xs x = length (DS.filter (== x) xs) == 1
-
-allEdges :: Triangulation -> Seq Edge
-allEdges triangulation = 
-  let triplets = map triangleEdges (_triangles triangulation) in
-    foldl1 (><) triplets
-
--- borderEdges :: Triangulation -> Seq Edge
--- borderEdges triangulation = DS.filter (isUnique edges) edges
---   where
---     edges = allEdges triangulation
-
-isUnique :: Eq a => Seq a -> a -> Bool
-isUnique xs x = isNothing y 
-  where
-    (i, _) = fromJust $ ifind (\_ x' -> x' == x) xs
-    y = find (== x) (DS.drop i xs)
-
--- | Exterior edges of a Delaunay triangulation.
-borderEdges :: Triangulation -> Seq Edge
-borderEdges triangulation = DS.filter (isUnique edges) edges
-  where
-    edges = allEdges triangulation
-
--- | Exterior and interior edges of a Delaunay triangulation.
-partitionEdges :: Triangulation -> (Seq Edge, Seq Edge)
-partitionEdges triangulation = (exEdges, inEdges)
-  where
-    edges = allEdges triangulation
-    exEdges = DS.filter (isUnique edges) edges
-    inEdges = DS.filter (not . isUnique edges) edges
+module Geometry.HCDT.Utils
+  ( borderEdges,
+    partitionEdges
+  )
+where
+    
+import Geometry.HCDT.Types
+    ( Triangulation(_triangles), Edge(..), Triangle(..) ) 
+import           Data.Sequence as DS     (Seq, fromList, (><), filter, deleteAt) 
+import           Data.Maybe              (isNothing, fromJust)
+import           Data.Foldable           (find)
+import           Data.Foldable.WithIndex (ifind)
+import           Witherable              (hashNub)
+
+triangleEdges :: Triangle -> Seq Edge
+triangleEdges (Triangle i j k) = fromList [Edge i j, Edge j k, Edge i k]
+
+-- isUnique :: Eq a => Seq a -> a -> Bool
+-- isUnique xs x = length (DS.filter (== x) xs) == 1
+
+allEdges :: Triangulation -> Seq Edge
+allEdges triangulation = 
+  let triplets = map triangleEdges (_triangles triangulation) in
+    foldl1 (><) triplets
+
+-- borderEdges :: Triangulation -> Seq Edge
+-- borderEdges triangulation = DS.filter (isUnique edges) edges
+--   where
+--     edges = allEdges triangulation
+
+isUnique :: Eq a => Seq a -> a -> Bool
+isUnique xs x = isNothing y 
+  where
+    (i, _) = fromJust $ ifind (\_ x' -> x' == x) xs
+    y = find (== x) (DS.deleteAt i xs)
+
+-- | Exterior edges of a Delaunay triangulation.
+borderEdges :: Triangulation -> Seq Edge
+borderEdges triangulation = DS.filter (isUnique edges) edges
+  where
+    edges = allEdges triangulation
+
+-- | Exterior and interior edges of a Delaunay triangulation.
+partitionEdges :: Triangulation -> (Seq Edge, Seq Edge)
+partitionEdges triangulation = (exEdges, inEdges)
+  where
+    edges = allEdges triangulation
+    exEdges = DS.filter (isUnique edges) edges
+    inEdges = hashNub $ DS.filter (not . isUnique edges) edges
