diff --git a/src/Matrix4x4T.hh b/src/Matrix4x4T.hh
index 1e6d92af1e4c3f82447f553b7afdafe0b6eb499e..80907d5215a429784cf63519f4cc0a7873bc4983 100644
--- a/src/Matrix4x4T.hh
+++ b/src/Matrix4x4T.hh
@@ -28,11 +28,19 @@
 
 namespace HexEx{
 
+template <typename T>
+class Matrix4x4T;
+
+template <typename T>
+bool operator==(const Matrix4x4T<T>& _mat, const Matrix4x4T<T>& _other);
+
 template <typename T>
 class Matrix4x4T
 {
     using Matrix4x4 = Matrix4x4T<T>;
 
+    friend bool operator==<>(const Matrix4x4T<T>& _mat, const Matrix4x4T<T>& _other);
+
 public:
     Matrix4x4T()
     {
@@ -87,6 +95,11 @@ public:
         return res;
     }
 
+    bool operator==(const Matrix4x4& _other)
+    {
+      return entries == _other.entries;
+    }
+
     template <typename Vec>
     Vec transform_point(const Vec& _vec)
     {
@@ -257,6 +270,12 @@ private:
     std::array<T, 16> entries;
 };
 
+template <typename T>
+bool operator==(const Matrix4x4T<T>& _mat, const Matrix4x4T<T>& _other)
+{
+  return _mat.entries == _other.entries;
+}
+
 
 template <typename T>
 std::ostream& operator<<(std::ostream& _os, const Matrix4x4T<T>& _mat)
diff --git a/tests/cellextraction_test.cc b/tests/cellextraction_test.cc
index 6fcf5cdc03970b7410626fe069bf58182f6a4de6..47b975f688c504a075b62a81fb97a30aa0bb2f8a 100644
--- a/tests/cellextraction_test.cc
+++ b/tests/cellextraction_test.cc
@@ -1,3 +1,23 @@
+/*
+ * Copyright 2019 Computer Graphics Group, RWTH Aachen University
+ * Author: Max Lyon <lyon@cs.rwth-aachen.de>
+ *
+ * This file is part of HexEx.
+ *
+ * HexEx is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * HexEx is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HexEx.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
 #include <gtest/gtest.h>
 #include "common.hh"
 #include <HexExtractor.hh>
diff --git a/tests/fileaccess_test.cc b/tests/fileaccess_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9ee67a51922be642c31b473aa2c594cd4a2ece75
--- /dev/null
+++ b/tests/fileaccess_test.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019 Computer Graphics Group, RWTH Aachen University
+ * Author: Max Lyon <lyon@cs.rwth-aachen.de>
+ *
+ * This file is part of HexEx.
+ *
+ * HexEx is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * HexEx is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HexEx.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include "common.hh"
+#include <HexExtractor.hh>
+#include <FileAccessor.hh>
+#include <sstream>
+#include <stdio.h>
+
+using namespace HexEx;
+
+TEST(FileAccess, StreamTest) {
+
+    for (auto transition : {false, true})
+        for (auto size : {1.0,2.2,3 + 1.0/3.0,4.0,5.0})
+        {
+
+            TetrahedralMesh mesh;
+            if (transition)
+                createCubeWithTransition(mesh, size);
+            else
+                createCube(mesh, size);
+
+            OpenVolumeMesh::CellPropertyT<std::map<VertexHandle, HexEx::Vec3d>> parametrization = mesh.template request_cell_property<std::map<OpenVolumeMesh::VertexHandle, HexEx::Vec3d>>("Parametrization");
+
+            std::stringstream ss;
+            ASSERT_NO_FATAL_FAILURE(HexEx::writeToStream(ss, mesh, parametrization));
+
+            TetrahedralMesh mesh2;
+            OpenVolumeMesh::CellPropertyT<std::map<VertexHandle, HexEx::Vec3d>> parametrization2 = mesh2.template request_cell_property<std::map<OpenVolumeMesh::VertexHandle, HexEx::Vec3d>>("Parametrization");
+
+            ASSERT_NO_FATAL_FAILURE(HexEx::readFromStream(ss, mesh2, parametrization2));
+
+            ASSERT_EQ(mesh.n_vertices(), mesh2.n_vertices());
+            ASSERT_EQ(mesh.n_cells(), mesh2.n_cells());
+
+            for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
+                EXPECT_EQ(mesh.vertex(*v_it), mesh2.vertex(*v_it));
+
+            for (auto c_it = mesh.cells_begin(); c_it != mesh.cells_end(); ++c_it)
+                for (auto cv_it = mesh.cv_iter(*c_it); cv_it.valid(); ++cv_it)
+                    EXPECT_EQ(parametrization[*c_it][*cv_it], parametrization2[*c_it][*cv_it]);
+
+        }
+
+}
+
+TEST(FileAccess, FileTest) {
+
+    std::string fileName = "asdfahdfgsdfgadsfggfadgfgvnvhkilprt.test";
+
+    for (auto transition : {false, true})
+        for (auto size : {1.0,2.2,3 + 1.0/3.0,4.0,5.0})
+        {
+
+            TetrahedralMesh mesh;
+            if (transition)
+                createCubeWithTransition(mesh, size);
+            else
+                createCube(mesh, size);
+
+            OpenVolumeMesh::CellPropertyT<std::map<VertexHandle, HexEx::Vec3d>> parametrization = mesh.template request_cell_property<std::map<OpenVolumeMesh::VertexHandle, HexEx::Vec3d>>("Parametrization");
+
+            ASSERT_NO_FATAL_FAILURE(HexEx::writeToFile(fileName, mesh, parametrization));
+
+            TetrahedralMesh mesh2;
+            OpenVolumeMesh::CellPropertyT<std::map<VertexHandle, HexEx::Vec3d>> parametrization2 = mesh2.template request_cell_property<std::map<OpenVolumeMesh::VertexHandle, HexEx::Vec3d>>("Parametrization");
+
+            ASSERT_NO_FATAL_FAILURE(HexEx::readFromFile(fileName, mesh2, parametrization2));
+
+            ASSERT_EQ(mesh.n_vertices(), mesh2.n_vertices());
+            ASSERT_EQ(mesh.n_cells(), mesh2.n_cells());
+
+            for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
+                EXPECT_EQ(mesh.vertex(*v_it), mesh2.vertex(*v_it));
+
+            for (auto c_it = mesh.cells_begin(); c_it != mesh.cells_end(); ++c_it)
+                for (auto cv_it = mesh.cv_iter(*c_it); cv_it.valid(); ++cv_it)
+                  EXPECT_EQ(parametrization[*c_it][*cv_it], parametrization2[*c_it][*cv_it]);
+
+        }
+
+    std::remove(fileName.c_str());
+
+}
diff --git a/tests/gridisomorphism_test.cc b/tests/gridisomorphism_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a2a22b388672241a70bd54cf5d662f45f1235280
--- /dev/null
+++ b/tests/gridisomorphism_test.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2019 Computer Graphics Group, RWTH Aachen University
+ * Author: Max Lyon <lyon@cs.rwth-aachen.de>
+ *
+ * This file is part of HexEx.
+ *
+ * HexEx is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * HexEx is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HexEx.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <chrono>
+#include <gtest/gtest.h>
+#include "common.hh"
+#include "Utils.hh"
+#include <HexExtractor.hh>
+#include <GridIsomorphism.hh>
+
+using namespace HexEx;
+
+TEST(GridIsomorphismTest, matrixTest) {
+
+    double entries[] = {1, 0, 0, 0,
+                        0, 1, 0, 0,
+                        0, 0, 1, 0,
+                        3, 2, 0, 1 };
+    auto t1 = Matrix4x4d(entries);
+
+    Vec3d test = Vec3d(1,2,3);
+
+    auto test2 = test;
+
+    test2 = t1.transform_point(test2);
+
+    EXPECT_EQ(test + Vec3d(3,2,0), test2);
+}
+
+
+TEST(GridIsomorphismTest, coversionTest) {
+
+    for (auto i = 0; i < 24; i++)
+    {
+        auto rr = HexEx::RestrictedRotation(i);
+        auto m = rr.toMatrix();
+
+        auto rr2 = HexEx::RestrictedRotation(m);
+
+        EXPECT_EQ(rr, rr2);
+    }
+
+}
+
+TEST(GridIsomorphismTest, transformationTest) {
+
+    for (auto i = 0; i < 24; i++)
+    {
+        auto rr = HexEx::RestrictedRotation(i);
+        auto m = rr.toMatrix();
+
+        for (auto j = 0; j < 100; ++j)
+        {
+            auto randomVec = HexEx::getRandomVector(10);
+            EXPECT_EQ(m.transform_point(randomVec), rr.transform(randomVec)) << i;
+        }
+    }
+
+}
+
+TEST(GridIsomorphismTest, inversionTest) {
+
+    for (char i = 0; i < 24; i++)
+    {
+        auto rr = HexEx::RestrictedRotation(i);
+        auto m = rr.toMatrix();
+        m.invert();
+
+        auto rr2 = rr;
+        rr2.invert();
+        auto m2 = rr2.toMatrix();
+
+        EXPECT_EQ(m2, m) << i;
+
+    }
+
+}
+
+TEST(GridIsomorphismTest, multiplicationTest) {
+
+    for (auto i = 0u; i < 24; i++)
+        for (auto j = 0u; j < 24; j++)
+        {
+            auto rr1 = HexEx::RestrictedRotation(i);
+            auto rr2 = HexEx::RestrictedRotation(j);
+
+            auto res = rr1 * rr2;
+
+
+            EXPECT_EQ(res.toMatrix(), rr1.toMatrix()*rr2.toMatrix()) << i << " " << j;
+
+        }
+
+}
+
+TEST(GridIsomorphismTest, isomorphismInversionTest) {
+
+    for (auto j = 0; j < 3; ++j)
+        for (auto k = 0; k < 3; ++k)
+            for (auto l = 0; l < 3; ++l)
+            {
+                auto t = HexEx::GridIsomorphism::Translation(j,k,l);
+                for (auto i = 0; i < 24; i++)
+                {
+                    auto gi = HexEx::GridIsomorphism(i, t);
+                    auto m = gi.toMatrix();
+                    m.invert();
+
+                    auto gi2 = gi.inverted();
+                    auto m2 = gi2.toMatrix();
+
+                    EXPECT_EQ(m2, m) << i;
+
+                }
+            }
+
+}
+
+//TEST(GridIsomorphismTest, performanceMulitplicitionTest) {
+
+//    using namespace std::chrono;
+
+//    auto N = 1000000;
+//#ifdef DEBUG
+//    N /= 100;
+//#endif
+//    auto gis = std::vector<HexEx::GridIsomorphism>();
+//    for (auto i = 0; i < 24; ++i)
+//        gis.push_back(HexEx::GridIsomorphism(i));
+
+//    auto matrices = std::vector<Matrix4x4dd>();
+//    for (auto i = 0; i < 24; ++i)
+//        matrices.push_back(gis[i].toMatrix());
+
+//    auto startGI = steady_clock::now();
+
+//    auto res = HexEx::GridIsomorphism(0);
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            for (auto j = 0u; j < 24; j++)
+//            {
+//                res = gis[i] * gis[j];
+//            }
+//    }
+
+//    auto stopGI = steady_clock::now();
+
+//    auto durationGI = duration_cast<duration<double>>(stopGI - startGI);
+
+//    std::cout << res.transform_point(Vec3d(0,0,0)) << std::endl;
+
+//    auto startMatrices = steady_clock::now();
+
+//    auto res2 = Matrix4x4dd();
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            for (auto j = 0u; j < 24; j++)
+//            {
+//                res2 = matrices[i] * matrices[j];
+//            }
+//    }
+
+//    auto stopMatrices = steady_clock::now();
+
+//    auto durationMatrices = duration_cast<duration<double>>(stopMatrices - startMatrices);
+
+//    std::cout << res2.transform_point(Vec3d(0,0,0)) << std::endl;
+
+//    std::cout << "Duration GridIsomorphism: " << durationGI.count() << std::endl;
+//    std::cout << "Duration Matrices       : " << durationMatrices.count() << std::endl;
+//    std::cout << "Ratio                   : " << static_cast<double>(durationGI.count())/durationMatrices.count() << std::endl;
+
+//}
+
+
+
+
+//TEST(GridIsomorphismTest, performanceInversionTest) {
+
+//    using namespace std::chrono;
+
+//    auto N = 10000000;
+//#ifdef DEBUG
+//    N /= 100;
+//#endif
+//    auto gis = std::vector<HexEx::GridIsomorphism>();
+//    for (auto i = 0; i < 24; ++i)
+//        gis.push_back(HexEx::GridIsomorphism(i));
+
+//    auto matrices = std::vector<Matrix4x4dd>();
+//    for (size_t i = 0; i < 24; ++i)
+//        matrices.push_back(gis[i].toMatrix());
+
+//    auto startGI = steady_clock::now();
+
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            gis[i].invert();
+//    }
+
+//    auto stopGI = steady_clock::now();
+
+//    auto durationGI = duration_cast<duration<double>>(stopGI - startGI);
+
+//    auto startMatrices = steady_clock::now();
+
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            matrices[i].invert();
+//    }
+
+//    auto stopMatrices = steady_clock::now();
+
+//    auto durationMatrices = duration_cast<duration<double>>(stopMatrices - startMatrices);
+
+//    std::cout << "Duration GridIsomorphism: " << durationGI.count() << std::endl;
+//    std::cout << "Duration Matrices       : " << durationMatrices.count() << std::endl;
+//    std::cout << "Ratio                   : " << (double)durationGI.count()/durationMatrices.count() << std::endl;
+
+//}
+
+
+//TEST(GridIsomorphismTest, performanceTransformationTest) {
+
+//    using namespace std::chrono;
+
+//    auto N = 10000000;
+//#ifdef DEBUG
+//    N /= 100;
+//#endif
+//    auto gis = std::vector<HexEx::GridIsomorphism>();
+//    for (auto i = 0; i < 24; ++i)
+//        gis.push_back(HexEx::GridIsomorphism(i));
+
+//    auto matrices = std::vector<Matrix4x4dd>();
+//    for (auto i = 0; i < 24; ++i)
+//        matrices.push_back(gis[i].toMatrix());
+
+//    auto startGI = steady_clock::now();
+
+//    auto res = Vec3d(3,3,3);
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            res += gis[i].transform_point(res);
+//    }
+
+//    auto stopGI = steady_clock::now();
+
+//    auto durationGI = duration_cast<duration<double>>(stopGI - startGI);
+//    std::cout << res << std::endl;
+
+//    auto startMatrices = steady_clock::now();
+
+//    auto res2 = Vec3d(3,3,3);
+//    for (auto n = 0; n < N; ++n)
+//    {
+//        for (auto i = 0u; i < 24; i++)
+//            res2 += matrices[i].transform_point(res2);
+//    }
+
+//    auto stopMatrices = steady_clock::now();
+
+//    auto durationMatrices = duration_cast<duration<double>>(stopMatrices - startMatrices);
+
+//    std::cout << res2 << std::endl;
+
+
+//    std::cout << "Duration GridIsomorphism: " << durationGI.count() << std::endl;
+//    std::cout << "Duration Matrices       : " << durationMatrices.count() << std::endl;
+//    std::cout << "Ratio                   : " << (double)durationGI.count()/durationMatrices.count() << std::endl;
+
+//}
diff --git a/tests/navigation_test.cc b/tests/navigation_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4fb7e399901a3c9f8ada83ba8f2539d18c62a5e5
--- /dev/null
+++ b/tests/navigation_test.cc
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2019 Computer Graphics Group, RWTH Aachen University
+ * Author: Max Lyon <lyon@cs.rwth-aachen.de>
+ *
+ * This file is part of HexEx.
+ *
+ * HexEx is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * HexEx is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HexEx.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include "common.hh"
+#include <HexExtractor.hh>
+
+using namespace HexEx;
+
+class NavigationTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+
+        vhs.push_back(mesh.add_vertex(Vec3d(0,0,0)));
+        vhs.push_back(mesh.add_vertex(Vec3d(1,0,0)));
+        vhs.push_back(mesh.add_vertex(Vec3d(1,0,-1)));
+        vhs.push_back(mesh.add_vertex(Vec3d(0,1,0)));
+
+        hfhs.push_back(mesh.halfface_handle(mesh.add_face({vhs[0],vhs[1],vhs[2]}), 0));
+        hfhs.push_back(mesh.halfface_handle(mesh.add_face({vhs[0],vhs[2],vhs[3]}), 0));
+        hfhs.push_back(mesh.halfface_handle(mesh.add_face({vhs[0],vhs[3],vhs[1]}), 0));
+        hfhs.push_back(mesh.halfface_handle(mesh.add_face({vhs[1],vhs[3],vhs[2]}), 0));
+
+        ch  = mesh.add_cell({hfhs[0],hfhs[1],hfhs[2],hfhs[3]},true);
+
+        OpenVolumeMesh::CellPropertyT<std::map<OpenVolumeMesh::VertexHandle, Vec3d>> parametrization = mesh.request_cell_property<std::map<OpenVolumeMesh::VertexHandle, Vec3d>>("Parametrization");
+        mesh.set_persistent(parametrization);
+
+        for (auto vh : mesh.vertices())
+            parametrization[ch][vh] = mesh.vertex(vh);
+
+        hexExtractor = new HexExtractor(mesh, parametrization);
+
+        hexExtractor->parameter(ch,vhs[0]) = Parameter(  0, 0, 0);
+
+  }
+
+    virtual void TearDown() { delete hexExtractor; }
+
+    HexExtractor* hexExtractor;
+    std::vector<OpenVolumeMesh::VertexHandle> vhs;
+    std::vector<OpenVolumeMesh::HalfFaceHandle> hfhs;
+    OpenVolumeMesh::CellHandle ch;
+
+    TetrahedralMesh mesh;
+    TetrahedralMesh paramesh;
+    PolyhedralMesh outputMesh;
+    PolyhedralMesh irregEdglesMesh;
+    PolyhedralMesh hportMesh;
+    PolyhedralMesh intermediateMesh;
+    HexahedralMesh realHexMesh;
+};
+
+
+TEST_F(NavigationTest, HalfedgeRotation) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    auto halfedge = mesh.halfedge(vhs[2],vhs[3]);
+
+    auto traceHfh = hexEx.rotateAroundHalfedge(ch, halfedge, true);
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+TEST_F(NavigationTest, HalfedgeRotationInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5, 1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,-1, 0);
+
+    auto halfedge = mesh.halfedge(vhs[2],vhs[3]);
+
+    auto traceHfh = hexEx.rotateAroundHalfedge(ch, halfedge, true);
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+TEST_F(NavigationTest, SimpleFacePiercing) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0, 0);
+    hexEx.parameter(ch,vhs[1]) = Parameter(0.5,-1, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1,-1);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+TEST_F(NavigationTest, SimpleFacePiercingInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0, 0);
+    hexEx.parameter(ch,vhs[1]) = Parameter(0.5,-1, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5, 1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,-1,-1);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+
+TEST_F(NavigationTest, EdgePiercing) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+TEST_F(NavigationTest, EdgePiercingInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5, 1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,-1, 0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+}
+
+
+
+TEST_F(NavigationTest, DoubleEdgePiercing) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+}
+
+TEST_F(NavigationTest, DoubleEdgePiercingInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5, 1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,-1, 0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0, 1);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0,-1);
+    hexEx.parameter(ch,vhs[2]) = Parameter(0.5,-1, 0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5, 1, 0);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+TEST_F(NavigationTest, VertexPiercingSimple) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0,-0.5, 0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0,-0.5,-0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0, 0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+TEST_F(NavigationTest, VertexPiercingSimpleInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0.5, 0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0.5,-0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0,-0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0,-0.5,-0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0,-0.5, 0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0, 0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,-1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,0,-1), Direction(0,1,0));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+}
+
+
+
+TEST_F(NavigationTest, VertexPiercingWithEdge) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0,-0.5, 0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0,-0.5,-0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0, 0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+}
+
+
+
+TEST_F(NavigationTest, VertexPiercingWithEdgeInverted) {
+
+    auto& hexEx = *hexExtractor;
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0, 0.5, 0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0, 0.5,-0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0,-0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    auto traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,-1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,-1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+
+    hexEx.parameter(ch,vhs[0]) = Parameter(  0,-0.5,-0.5);
+    hexEx.parameter(ch,vhs[1]) = Parameter(  0,-0.5, 0.5);
+    hexEx.parameter(ch,vhs[2]) = Parameter(  0, 0.5,   0);
+    hexEx.parameter(ch,vhs[3]) = Parameter(0.5,   0,   0);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,1));
+
+    EXPECT_EQ(hfhs[3], traceHfh);
+
+    traceHfh = hexEx.alpha0NextFace(HalfFaceHandle(), ch, Parameter(0,0,0), Direction(1,0,0), Direction(0,1,0), Direction(0,0,-1));
+
+    EXPECT_EQ(hfhs[1], traceHfh);
+
+}
+
diff --git a/tests/predicate_test.cc b/tests/predicate_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9b8dd395427ae47b0d0a52402598a314aab31405
--- /dev/null
+++ b/tests/predicate_test.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2019 Computer Graphics Group, RWTH Aachen University
+ * Author: Max Lyon <lyon@cs.rwth-aachen.de>
+ *
+ * This file is part of HexEx.
+ *
+ * HexEx is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * HexEx is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HexEx.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include "common.hh"
+#include <DerivedExactPredicates.hh>
+#include <Direction.hh>
+
+using namespace HexEx;
+
+TEST(Predicates, PointsIntoTriangleFromEdgeTest) {
+    using Vec3 = Vec3d;
+    using Direction = HexEx::Direction;
+
+    auto u = Vec3(4,5,0);
+    auto v = Vec3(2,3,0);
+    auto w = Vec3(0,0,0);
+    auto p = Vec3(3,4,0);
+    auto dir = Direction(1,0,0);
+
+    EXPECT_TRUE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,dir));
+
+}
+
+TEST(Predicates, PointsIntoTriangleFromEdgeTest2) {
+    using Vec3 = Vec3d;
+    using Direction = HexEx::Direction;
+
+    auto u = Vec3(2,0,0);
+    auto v = Vec3(2,0,2);
+    auto w = Vec3(4,0,0);
+    auto p = Vec3(2,0,1);
+
+    EXPECT_TRUE( pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 1, 0, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction(-1, 0, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 1, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0,-1, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 0, 1)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 0,-1)));
+
+}
+
+TEST(Predicates, PointsIntoTriangleFromEdgeTest3) {
+    using Vec3 = Vec3d;
+    using Direction = HexEx::Direction;
+
+    auto u = Vec3(4,0,0);
+    auto v = Vec3(2,0,0);
+    auto w = Vec3(0,0,0);
+    auto p = Vec3(3,0,0);
+    auto dir = Direction(1,0,0);
+
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,dir));
+
+}
+
+
+TEST(Predicates, PointsIntoTriangleFromEdgeTest4) {
+    using Vec3 = Vec3d;
+    using Direction = HexEx::Direction;
+
+    auto u = Vec3(4,0,0);
+    auto v = Vec3(2,0,0);
+    auto w = Vec3(0,-1,0);
+    auto p = Vec3(3,0,0);
+    auto dir = Direction(1,0,0);
+
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,dir));
+}
+
+
+
+TEST(Predicates, PointsIntoTriangleFromEdgeTest5) {
+    using Vec3 = Vec3d;
+    using Direction = HexEx::Direction;
+
+    auto u = Vec3(2,2,0);
+    auto v = Vec3(2,2,2);
+    auto w = Vec3(2,0,2);
+    auto p = Vec3(2,2,1);
+
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 1, 0, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction(-1, 0, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 1, 0)));
+    EXPECT_TRUE( pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0,-1, 0)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 0, 1)));
+    EXPECT_FALSE(pointsIntoProjectedTriangleFromEdge(u,v,w,p,Direction( 0, 0,-1)));
+}
+
+TEST(Predicates, FaceIntersectionTest)
+{
+    using Vec3 = Vec3d;
+
+    auto u = Vec3(-1,0,-1);
+    auto v = Vec3(0,-1,-1);
+    auto w = Vec3(-1,-1,0);
+    auto start = Vec3(-1,0,-1);
+    auto end = Vec3(-1,0,0);
+
+    EXPECT_TRUE(HexEx::intersectsTriangleRelaxed(u,v,w,start,end));
+
+}
+
+