#2 parity with js: mapbox/delaunator#32
diff --git a/README.md b/README.md
index 4eabb27..c747122 100644
--- a/README.md
+++ b/README.md
@@ -48,16 +48,16 @@
 
 ```
 Run on (4 X 2300 MHz CPU s)
-2018-09-23 08:36:08
+2018-09-26 09:28:34
 ------------------------------------------------------------
 Benchmark                     Time           CPU Iterations
 ------------------------------------------------------------
-BM_45K_geojson_nodes         22 ms         22 ms         33
-BM_uniform/2000               1 ms          1 ms        953
-BM_uniform/100000            61 ms         61 ms          9
-BM_uniform/200000           140 ms        140 ms          4
-BM_uniform/500000           409 ms        408 ms          2
-BM_uniform/1000000          998 ms        995 ms          1
+BM_45K_geojson_nodes         24 ms         24 ms         29
+BM_uniform/2000               1 ms          1 ms        887
+BM_uniform/100000            66 ms         66 ms          9
+BM_uniform/200000           158 ms        155 ms          4
+BM_uniform/500000           441 ms        439 ms          2
+BM_uniform/1000000         1062 ms       1058 ms          1
 ```
 
 Library is ~10% faster then JS version for 1M uniform points ([details](https://github.com/delfrrr/delaunator-cpp/pull/8#issuecomment-422690056))
diff --git a/generate-reference-triangles/package-lock.json b/generate-reference-triangles/package-lock.json
index 8b052b6..559cc3a 100644
--- a/generate-reference-triangles/package-lock.json
+++ b/generate-reference-triangles/package-lock.json
@@ -4,9 +4,9 @@
   "lockfileVersion": 1,
   "dependencies": {
     "delaunator": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-2.0.3.tgz",
-      "integrity": "sha512-jHi7rPdAk9AqKFj+AZBchYgckSlVANdY8OMxhPmS24pRDFsJWuNoRyPxvPxOwaS6DWhy4/OOCthiuQmXI3QtKQ=="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-3.0.1.tgz",
+      "integrity": "sha512-UDOXpkVpycXOQyGQajsyz72MfTMJfkWZ7/cfElX08MNtes1TkZVFKow6BxzNba4TU7Y56nnG2Ajr/+wpeS4y8Q=="
     }
   }
 }
diff --git a/generate-reference-triangles/package.json b/generate-reference-triangles/package.json
index 83ec286..679b7fd 100644
--- a/generate-reference-triangles/package.json
+++ b/generate-reference-triangles/package.json
@@ -3,6 +3,6 @@
   "private": true,
   "main": "index.js",
   "dependencies": {
-    "delaunator": "2.0.3"
+    "delaunator": "3.0.1"
   }
 }
diff --git a/include/delaunator.hpp b/include/delaunator.hpp
index 4bfb240..e16e0c3 100644
--- a/include/delaunator.hpp
+++ b/include/delaunator.hpp
@@ -180,6 +180,10 @@
     std::vector<double> const& coords;
     std::vector<std::size_t> triangles;
     std::vector<std::size_t> halfedges;
+    std::vector<std::size_t> hull_prev;
+    std::vector<std::size_t> hull_next;
+    std::vector<std::size_t> hull_tri;
+    std::size_t hull_start;
 
     Delaunator(std::vector<double> const& in_coords);
 
@@ -187,18 +191,12 @@
 
 private:
     std::vector<std::size_t> m_hash;
-    std::vector<DelaunatorPoint> m_hull;
-    std::size_t m_hull_entry;
     double m_center_x;
     double m_center_y;
     std::size_t m_hash_size;
 
-    std::size_t remove_node(std::size_t node);
     std::size_t legalize(std::size_t a);
-    std::size_t insert_node(std::size_t i);
-    std::size_t insert_node(std::size_t i, std::size_t prev);
     std::size_t hash_key(double x, double y);
-    void hash_edge(std::size_t e);
     std::size_t add_triangle(
         std::size_t i0,
         std::size_t i1,
@@ -213,9 +211,11 @@
     : coords(in_coords),
       triangles(),
       halfedges(),
+      hull_prev(),
+      hull_next(),
+      hull_tri(),
+      hull_start(),
       m_hash(),
-      m_hull(),
-      m_hull_entry(),
       m_center_x(),
       m_center_y(),
       m_hash_size() {
@@ -309,27 +309,29 @@
 
     // initialize a hash table for storing edges of the advancing convex hull
     m_hash_size = static_cast<std::size_t>(std::llround(std::ceil(std::sqrt(n))));
-    m_hash.reserve(m_hash_size);
-    for (std::size_t i = 0; i < m_hash_size; i++) {
-        m_hash.push_back(INVALID_INDEX);
-    }
+    m_hash.resize(m_hash_size);
+    std::fill(m_hash.begin(), m_hash.end(), INVALID_INDEX);
 
-    // initialize a circular doubly-linked list that will hold an advancing convex hull
-    // doubly-linked list is stored as plain vector
-    // vertices are linked via ineces of next/prev vertice
-    m_hull.reserve(coords.size());
-    std::size_t e = insert_node(i0);
-    m_hull_entry = e;
-    hash_edge(e);
-    m_hull[e].t = 0;
+    // initialize arrays for tracking the edges of the advancing convex hull
+    hull_prev.resize(n);
+    hull_next.resize(n);
+    hull_tri.resize(n);
 
-    e = insert_node(i1, e);
-    hash_edge(e);
-    m_hull[e].t = 1;
+    hull_start = i0;
 
-    e = insert_node(i2, e);
-    hash_edge(e);
-    m_hull[e].t = 2;
+    size_t hull_size = 3;
+
+    hull_next[i0] = hull_prev[i2] = i1;
+    hull_next[i1] = hull_prev[i0] = i2;
+    hull_next[i2] = hull_prev[i1] = i0;
+
+    hull_tri[i0] = 0;
+    hull_tri[i1] = 1;
+    hull_tri[i2] = 2;
+
+    m_hash[hash_key(i0x, i0y)] = i0;
+    m_hash[hash_key(i1x, i1y)] = i1;
+    m_hash[hash_key(i2x, i2y)] = i2;
 
     std::size_t max_triangles = n < 3 ? 1 : 2 * n - 5;
     triangles.reserve(max_triangles * 3);
@@ -354,108 +356,89 @@
             check_pts_equal(x, y, i2x, i2y)) continue;
 
         // find a visible edge on the convex hull using edge hash
-        const std::size_t start_key = hash_key(x, y);
-        std::size_t key = start_key;
-        std::size_t start = INVALID_INDEX;
-        do {
-            start = m_hash[key];
-            key = (key + 1) % m_hash_size;
-        } while (
-            (start == INVALID_INDEX || m_hull[start].removed) &&
-            (key != start_key));
+        std::size_t start = 0;
 
-        start = m_hull[start].prev;
-        e = start;
-        while (!orient(
-            x, y, //
-            m_hull[e].x,
-            m_hull[e].y,              //
-            m_hull[m_hull[e].next].x, //
-            m_hull[m_hull[e].next].y) //
-        ) {
-            e = m_hull[e].next;
+        size_t key = hash_key(x, y);
+        for (size_t j = 0; j < m_hash_size; j++) {
+            start = m_hash[(key + j) % m_hash_size];
+            if (start != INVALID_INDEX && start != hull_next[start]) break;
+        }
 
+        start = hull_prev[start];
+        size_t e = start;
+        size_t q;
+
+        while (q = hull_next[e], !orient(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1])) { //TODO: does it works in a same way as in JS
+            e = q;
             if (e == start) {
-                e = INVALID_INDEX; //TODO: will it work correct?
+                e = INVALID_INDEX;
                 break;
             }
         }
 
-        // likely a near-duplicate point; skip it
-        if (e == INVALID_INDEX) continue;
-
-        const bool walk_back = e == start;
+        if (e == INVALID_INDEX) continue; // likely a near-duplicate point; skip it
 
         // add the first triangle from the point
         std::size_t t = add_triangle(
-            m_hull[e].i,
+            e,
             i,
-            m_hull[m_hull[e].next].i,
+            hull_next[e],
             INVALID_INDEX,
             INVALID_INDEX,
-            m_hull[e].t);
+            hull_tri[e]);
 
-        m_hull[e].t = t; // keep track of boundary triangles on the hull
-        e = insert_node(i, e);
-
-        // recursively flip triangles from the point until they satisfy the Delaunay condition
-        m_hull[e].t = legalize(t + 2);
+        hull_tri[i] = legalize(t + 2);
+        hull_tri[e] = t;
+        hull_size++;
 
         // walk forward through the hull, adding more triangles and flipping recursively
-        std::size_t q = m_hull[e].next;
-        while (orient(
-            x, y, //
-            m_hull[q].x,
-            m_hull[q].y, //
-            m_hull[m_hull[q].next].x,
-            m_hull[m_hull[q].next].y //
-            )) {
-            t = add_triangle(
-                m_hull[q].i, i, m_hull[m_hull[q].next].i, m_hull[m_hull[q].prev].t, INVALID_INDEX, m_hull[q].t);
-            m_hull[m_hull[q].prev].t = legalize(t + 2);
-            m_hull_entry = remove_node(q);
-            q = m_hull[q].next;
+        std::size_t next = hull_next[e];
+        while (
+            q = hull_next[next],
+            orient(x, y, coords[2 * next], coords[2 * next + 1], coords[2 * q], coords[2 * q + 1])) {
+            t = add_triangle(next, i, q, hull_tri[i], INVALID_INDEX, hull_tri[next]);
+            hull_tri[i] = legalize(t + 2);
+            hull_next[next] = next; // mark as removed
+            hull_size--;
+            next = q;
         }
-        if (walk_back) {
-            // walk backward from the other side, adding more triangles and flipping
-            q = m_hull[e].prev;
-            while (orient(
-                x, y, //
-                m_hull[m_hull[q].prev].x,
-                m_hull[m_hull[q].prev].y, //
-                m_hull[q].x,
-                m_hull[q].y //
-                )) {
-                t = add_triangle(
-                    m_hull[m_hull[q].prev].i, i, m_hull[q].i, INVALID_INDEX, m_hull[q].t, m_hull[m_hull[q].prev].t);
+
+        // walk backward from the other side, adding more triangles and flipping
+        if (e == start) {
+            while (
+                q = hull_prev[e],
+                orient(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1])) {
+                t = add_triangle(q, i, e, INVALID_INDEX, hull_tri[e], hull_tri[q]);
                 legalize(t + 2);
-                m_hull[m_hull[q].prev].t = t;
-                m_hull_entry = remove_node(q);
-                q = m_hull[q].prev;
+                hull_tri[q] = t;
+                hull_next[e] = e; // mark as removed
+                hull_size--;
+                e = q;
             }
         }
-        hash_edge(e);
-        hash_edge(m_hull[e].prev);
+
+        // update the hull indices
+        hull_prev[i] = e;
+        hull_start = e;
+        hull_prev[next] = i;
+        hull_next[e] = i;
+        hull_next[i] = next;
+
+        m_hash[hash_key(x, y)] = i;
+        m_hash[hash_key(coords[2 * e], coords[2 * e + 1])] = e;
     }
 }
 
 double Delaunator::get_hull_area() {
     std::vector<double> hull_area;
-    size_t e = m_hull_entry;
+    size_t e = hull_start;
     do {
-        hull_area.push_back((m_hull[e].x - m_hull[m_hull[e].prev].x) * (m_hull[e].y + m_hull[m_hull[e].prev].y));
-        e = m_hull[e].next;
-    } while (e != m_hull_entry);
+        hull_area.push_back((coords[2 * e] - coords[2 * hull_prev[e]]) * (coords[2 * e + 1] + coords[2 * hull_prev[e] + 1]));
+        e = hull_next[e];
+    } while (e != hull_start);
     return sum(hull_area);
 }
 
-std::size_t Delaunator::remove_node(std::size_t node) {
-    m_hull[m_hull[node].prev].next = m_hull[node].next;
-    m_hull[m_hull[node].next].prev = m_hull[node].prev;
-    m_hull[node].removed = true;
-    return m_hull[node].prev;
-}
-
 std::size_t Delaunator::legalize(std::size_t a) {
     const std::size_t b = halfedges[a];
 
@@ -508,14 +491,14 @@
 
         // edge swapped on the other side of the hull (rare); fix the halfedge reference
         if (hbl == INVALID_INDEX) {
-            std::size_t e = m_hull_entry;
+            std::size_t e = hull_start;
             do {
-                if (m_hull[e].t == bl) {
-                    m_hull[e].t = a;
+                if (hull_tri[e] == bl) {
+                    hull_tri[e] = a;
                     break;
                 }
-                e = m_hull[e].next;
-            } while (e != m_hull_entry);
+                e = hull_next[e];
+            } while (e != hull_start);
         }
         link(a, hbl);
         link(b, halfedges[ar]);
@@ -529,30 +512,6 @@
     return ar;
 }
 
-std::size_t Delaunator::insert_node(std::size_t i) {
-    std::size_t node = m_hull.size();
-    DelaunatorPoint p = {
-        i,
-        coords[2 * i],
-        coords[2 * i + 1],
-        0,
-        node,
-        node,
-        false
-    };
-    m_hull.push_back(p);
-    return node;
-}
-
-std::size_t Delaunator::insert_node(std::size_t i, std::size_t prev) {
-    std::size_t node = insert_node(i);
-    m_hull[node].next = m_hull[prev].next;
-    m_hull[node].prev = prev;
-    m_hull[m_hull[node].next].prev = node;
-    m_hull[prev].next = node;
-    return node;
-}
-
 std::size_t Delaunator::hash_key(double x, double y) {
     const double dx = x - m_center_x;
     const double dy = y - m_center_y;
@@ -561,10 +520,6 @@
            m_hash_size;
 }
 
-void Delaunator::hash_edge(std::size_t e) {
-    m_hash[hash_key(m_hull[e].x, m_hull[e].y)] = e;
-}
-
 std::size_t Delaunator::add_triangle(
     std::size_t i0,
     std::size_t i1,