From a3d550e8ce10d40694dbd8b091516777eee17e70 Mon Sep 17 00:00:00 2001 From: Matthias Goerner <1239022+unhyperbolic@users.noreply.github.com> Date: Wed, 3 May 2023 23:27:40 -0700 Subject: [PATCH] Adding simplify flag to group(). --- engine/triangulation/detail/algebra-impl.h | 37 +++++++++------ engine/triangulation/detail/skeleton-impl.h | 2 + engine/triangulation/detail/triangulation.h | 46 ++++++++++++++++--- .../explicit/triangulation10.cpp | 2 +- .../explicit/triangulation11.cpp | 2 +- .../explicit/triangulation12.cpp | 2 +- .../explicit/triangulation13.cpp | 2 +- .../explicit/triangulation14.cpp | 2 +- .../explicit/triangulation15.cpp | 2 +- .../triangulation/explicit/triangulation2.cpp | 2 +- .../triangulation/explicit/triangulation3.cpp | 2 +- .../triangulation/explicit/triangulation4.cpp | 2 +- .../triangulation/explicit/triangulation5.cpp | 2 +- .../triangulation/explicit/triangulation6.cpp | 2 +- .../triangulation/explicit/triangulation7.cpp | 2 +- .../triangulation/explicit/triangulation8.cpp | 2 +- .../triangulation/explicit/triangulation9.cpp | 2 +- python/dim2/triangulation2.cpp | 4 +- python/dim3/triangulation3.cpp | 2 + python/dim4/triangulation4.cpp | 2 + python/generic/triangulation-bindings.h | 2 + python/testsuite/refs.out | 3 ++ python/testsuite/refs.test | 10 ++++ 23 files changed, 100 insertions(+), 36 deletions(-) diff --git a/engine/triangulation/detail/algebra-impl.h b/engine/triangulation/detail/algebra-impl.h index 14c2db9c2f..ceadd8d866 100644 --- a/engine/triangulation/detail/algebra-impl.h +++ b/engine/triangulation/detail/algebra-impl.h @@ -174,15 +174,27 @@ AbelianGroup TriangulationBase::homology() const { } template -const GroupPresentation& TriangulationBase::group() const { - if (fundGroup_.has_value()) - return *fundGroup_; - +const GroupPresentation& TriangulationBase::group(bool simplify) const { + if (simplify) { + if (simplifiedFundGroup_) + return *simplifiedFundGroup_; + if (isEmpty()) + return *(simplifiedFundGroup_ = GroupPresentation()); + GroupPresentation ans = + fundGroup_ ? *fundGroup_ : unsimplifiedGroup(); + ans.intelligentSimplify(); + return *(simplifiedFundGroup_ = std::move(ans)); + } else { + if (isEmpty()) + return *(fundGroup_ = GroupPresentation()); + return *(fundGroup_ = unsimplifiedGroup()); + } +} + +template +GroupPresentation TriangulationBase::unsimplifiedGroup() const { GroupPresentation ans; - - if (isEmpty()) - return *(fundGroup_ = std::move(ans)); - + // Calculate a maximal forest in the dual 1-skeleton. ensureSkeleton(); @@ -199,7 +211,8 @@ const GroupPresentation& TriangulationBase::group() const { ans.addGenerator(nGens); // Find out which (dim-1)-face corresponds to which generator. - long* genIndex = new long[countFaces()]; + std::unique_ptr genIndex = + std::make_unique(countFaces()); long i = 0; for (Face* f : faces()) if (! (f->isBoundary() || f->inMaximalForest())) @@ -234,11 +247,7 @@ const GroupPresentation& TriangulationBase::group() const { } } - // Tidy up. - delete[] genIndex; - ans.intelligentSimplify(); - - return *(fundGroup_ = std::move(ans)); + return ans; } template diff --git a/engine/triangulation/detail/skeleton-impl.h b/engine/triangulation/detail/skeleton-impl.h index 7919e8f5f1..238b20372a 100644 --- a/engine/triangulation/detail/skeleton-impl.h +++ b/engine/triangulation/detail/skeleton-impl.h @@ -607,6 +607,7 @@ void TriangulationBase::clearBaseProperties() { // Clear properties. if (! topologyLock_) { fundGroup_.reset(); + simplifiedFundGroup_.reset(); H1_.reset(); } } @@ -634,6 +635,7 @@ void TriangulationBase::swapBaseData(TriangulationBase& other) { faces_.swap(other.faces_); nBoundaryFaces_.swap(other.nBoundaryFaces_); fundGroup_.swap(other.fundGroup_); + simplifiedFundGroup_.swap(other.simplifiedFundGroup_); H1_.swap(other.H1_); } diff --git a/engine/triangulation/detail/triangulation.h b/engine/triangulation/detail/triangulation.h index e87bce568e..939d2227cc 100644 --- a/engine/triangulation/detail/triangulation.h +++ b/engine/triangulation/detail/triangulation.h @@ -241,6 +241,12 @@ class TriangulationBase : mutable std::optional fundGroup_; /**< Fundamental group of the triangulation. This is std::nullopt if it has not yet been computed. */ + mutable std::optional simplifiedFundGroup_; + /**< Simplified fundamental group of the triangulation. + It was either computed from fundGroup_ by + GroupPresentation::intelligentSimplify or set by the + user through setGroupPresentation. + This is std::nullopt if it has not yet been computed. */ mutable std::optional H1_; /**< First homology group of the triangulation. This is std::nullopt if it has not yet been computed. */ @@ -1327,7 +1333,8 @@ class TriangulationBase : /*@{*/ /** - * Returns the fundamental group of this triangulation. + * Returns the unsimplified or simplified (default) presentation of + * the fundamental group of this triangulation. * * The fundamental group is computed in the dual 2-skeleton. This * means: @@ -1350,6 +1357,15 @@ class TriangulationBase : * barycentric subdivision is performed on a such a triangulation, * the result of group() might change. * + * By default, the presentation will be simplified. The unsimplified + * presentation can be obtained by passing simplify = false as + * argument. In the unsimplified presentation, the generators + * correspond to those facet-pairings that are not dual to edges + * in the maximal forest in the dual 2-skeleton chosen by regina + * (those facet-pairings can be found by checking that neither + * Face::isBoundary() nor + * Face::inMaximalForest returns true). + * * Bear in mind that each time the triangulation changes, the * fundamental group will be deleted. Thus the reference that is * returned from this routine should not be kept for later use. @@ -1370,9 +1386,12 @@ class TriangulationBase : * fundamental group with fillings, call * SnapPeaTriangulation::fundamentalGroupFilled() instead. * - * \return the fundamental group. + * \param simplify \c true if the presentation should be simplified + * (default to \c true). + * + * \return A presentation of the fundamental group. */ - const GroupPresentation& group() const; + const GroupPresentation& group(bool simplify = true) const; /** * An alias for group(), which returns the fundamental group of this * triangulation. @@ -1394,9 +1413,9 @@ class TriangulationBase : * fundamental group with fillings, call * SnapPeaTriangulation::fundamentalGroupFilled() instead. * - * \return the fundamental group. + * \return a presentation of the fundamental group. */ - const GroupPresentation& fundamentalGroup() const; + const GroupPresentation& fundamentalGroup(bool simplify = true) const; /** * Allows the specific presentation of the fundamental group to be * changed by some other (external) means. @@ -3582,6 +3601,14 @@ class TriangulationBase : bool sameDegreesAt(const TriangulationBase& other, std::integer_sequence) const; + /** + * Computes unsimplified presentation of fundamental group. + * + * \return Unsimplified presentation of fundamental group. + */ + GroupPresentation + unsimplifiedGroup() const; + protected: /** * An object that facilitates both firing change events and @@ -3828,6 +3855,7 @@ TriangulationBase::TriangulationBase(const TriangulationBase& src, // Clone properties: if (cloneProps) { fundGroup_ = src.fundGroup_; + simplifiedFundGroup_ = src.simplifiedFundGroup_; H1_ = src.H1_; } } @@ -3842,6 +3870,7 @@ TriangulationBase::TriangulationBase(TriangulationBase&& src) calculatedSkeleton_(src.calculatedSkeleton_), orientable_(src.orientable_), fundGroup_(std::move(src.fundGroup_)), + simplifiedFundGroup_(std::move(src.simplifiedFundGroup_)), H1_(std::move(src.H1_)) { // For our simplices and skeletal components, we use swaps instead // of moves because we need to ensure that src's lists finish empty. @@ -3910,6 +3939,7 @@ TriangulationBase& TriangulationBase::operator = // Clone properties: fundGroup_ = src.fundGroup_; + simplifiedFundGroup_ = src.simplifiedFundGroup_; H1_ = src.H1_; return *this; @@ -3948,6 +3978,7 @@ TriangulationBase& TriangulationBase::operator = std::swap(calculatedSkeleton_, src.calculatedSkeleton_); fundGroup_ = std::move(src.fundGroup_); + simplifiedFundGroup_ = std::move(src.simplifiedFundGroup_); H1_ = std::move(src.H1_); // Let src dispose of the original simplices and skeletal objects in @@ -4966,9 +4997,10 @@ std::vector> } template -inline const GroupPresentation& TriangulationBase::fundamentalGroup() +inline const GroupPresentation& TriangulationBase::fundamentalGroup( + const bool simplify) const { - return group(); + return group(simplify); } template diff --git a/engine/triangulation/explicit/triangulation10.cpp b/engine/triangulation/explicit/triangulation10.cpp index 7fd006552a..b2620549c8 100644 --- a/engine/triangulation/explicit/triangulation10.cpp +++ b/engine/triangulation/explicit/triangulation10.cpp @@ -89,7 +89,7 @@ template MatrixInt TriangulationBase<10>::dualToPrimal<7>() const; template MatrixInt TriangulationBase<10>::dualToPrimal<8>() const; template MatrixInt TriangulationBase<10>::dualToPrimal<9>() const; -template const GroupPresentation& TriangulationBase<10>::group() const; +template const GroupPresentation& TriangulationBase<10>::group(bool) const; template void TriangulationBase<10>::calculateSkeleton(); template void TriangulationBase<10>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation11.cpp b/engine/triangulation/explicit/triangulation11.cpp index e1ac3d108a..10f64a8898 100644 --- a/engine/triangulation/explicit/triangulation11.cpp +++ b/engine/triangulation/explicit/triangulation11.cpp @@ -93,7 +93,7 @@ template MatrixInt TriangulationBase<11>::dualToPrimal<8>() const; template MatrixInt TriangulationBase<11>::dualToPrimal<9>() const; template MatrixInt TriangulationBase<11>::dualToPrimal<10>() const; -template const GroupPresentation& TriangulationBase<11>::group() const; +template const GroupPresentation& TriangulationBase<11>::group(bool) const; template void TriangulationBase<11>::calculateSkeleton(); template void TriangulationBase<11>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation12.cpp b/engine/triangulation/explicit/triangulation12.cpp index 3f0d75e9ae..d899fb3c40 100644 --- a/engine/triangulation/explicit/triangulation12.cpp +++ b/engine/triangulation/explicit/triangulation12.cpp @@ -97,7 +97,7 @@ template MatrixInt TriangulationBase<12>::dualToPrimal<9>() const; template MatrixInt TriangulationBase<12>::dualToPrimal<10>() const; template MatrixInt TriangulationBase<12>::dualToPrimal<11>() const; -template const GroupPresentation& TriangulationBase<12>::group() const; +template const GroupPresentation& TriangulationBase<12>::group(bool) const; template void TriangulationBase<12>::calculateSkeleton(); template void TriangulationBase<12>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation13.cpp b/engine/triangulation/explicit/triangulation13.cpp index 2e4f414763..03c7fbdfff 100644 --- a/engine/triangulation/explicit/triangulation13.cpp +++ b/engine/triangulation/explicit/triangulation13.cpp @@ -101,7 +101,7 @@ template MatrixInt TriangulationBase<13>::dualToPrimal<10>() const; template MatrixInt TriangulationBase<13>::dualToPrimal<11>() const; template MatrixInt TriangulationBase<13>::dualToPrimal<12>() const; -template const GroupPresentation& TriangulationBase<13>::group() const; +template const GroupPresentation& TriangulationBase<13>::group(bool) const; template void TriangulationBase<13>::calculateSkeleton(); template void TriangulationBase<13>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation14.cpp b/engine/triangulation/explicit/triangulation14.cpp index 610f74124d..b6b7ee4a56 100644 --- a/engine/triangulation/explicit/triangulation14.cpp +++ b/engine/triangulation/explicit/triangulation14.cpp @@ -105,7 +105,7 @@ template MatrixInt TriangulationBase<14>::dualToPrimal<11>() const; template MatrixInt TriangulationBase<14>::dualToPrimal<12>() const; template MatrixInt TriangulationBase<14>::dualToPrimal<13>() const; -template const GroupPresentation& TriangulationBase<14>::group() const; +template const GroupPresentation& TriangulationBase<14>::group(bool) const; template void TriangulationBase<14>::calculateSkeleton(); template void TriangulationBase<14>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation15.cpp b/engine/triangulation/explicit/triangulation15.cpp index 4f29b95bbb..1c66018ccb 100644 --- a/engine/triangulation/explicit/triangulation15.cpp +++ b/engine/triangulation/explicit/triangulation15.cpp @@ -109,7 +109,7 @@ template MatrixInt TriangulationBase<15>::dualToPrimal<12>() const; template MatrixInt TriangulationBase<15>::dualToPrimal<13>() const; template MatrixInt TriangulationBase<15>::dualToPrimal<14>() const; -template const GroupPresentation& TriangulationBase<15>::group() const; +template const GroupPresentation& TriangulationBase<15>::group(bool) const; template void TriangulationBase<15>::calculateSkeleton(); template void TriangulationBase<15>::cloneSkeleton( diff --git a/engine/triangulation/explicit/triangulation2.cpp b/engine/triangulation/explicit/triangulation2.cpp index 4c090e316c..f08eac1342 100644 --- a/engine/triangulation/explicit/triangulation2.cpp +++ b/engine/triangulation/explicit/triangulation2.cpp @@ -58,7 +58,7 @@ template MatrixInt TriangulationBase<2>::dualBoundaryMap<2>() const; template MatrixInt TriangulationBase<2>::dualToPrimal<0>() const; template MatrixInt TriangulationBase<2>::dualToPrimal<1>() const; -template const GroupPresentation& TriangulationBase<2>::group() const; +template const GroupPresentation& TriangulationBase<2>::group(bool) const; template void TriangulationBase<2>::calculateSkeleton(); template void TriangulationBase<2>::cloneSkeleton(const TriangulationBase<2>&); diff --git a/engine/triangulation/explicit/triangulation3.cpp b/engine/triangulation/explicit/triangulation3.cpp index 481f3d0f84..638c4b2e54 100644 --- a/engine/triangulation/explicit/triangulation3.cpp +++ b/engine/triangulation/explicit/triangulation3.cpp @@ -62,7 +62,7 @@ template MatrixInt TriangulationBase<3>::dualToPrimal<0>() const; template MatrixInt TriangulationBase<3>::dualToPrimal<1>() const; template MatrixInt TriangulationBase<3>::dualToPrimal<2>() const; -template const GroupPresentation& TriangulationBase<3>::group() const; +template const GroupPresentation& TriangulationBase<3>::group(bool) const; template void TriangulationBase<3>::calculateSkeleton(); template void TriangulationBase<3>::cloneSkeleton(const TriangulationBase<3>&); diff --git a/engine/triangulation/explicit/triangulation4.cpp b/engine/triangulation/explicit/triangulation4.cpp index 54a313389c..ec7cefb759 100644 --- a/engine/triangulation/explicit/triangulation4.cpp +++ b/engine/triangulation/explicit/triangulation4.cpp @@ -66,7 +66,7 @@ template MatrixInt TriangulationBase<4>::dualToPrimal<1>() const; template MatrixInt TriangulationBase<4>::dualToPrimal<2>() const; template MatrixInt TriangulationBase<4>::dualToPrimal<3>() const; -template const GroupPresentation& TriangulationBase<4>::group() const; +template const GroupPresentation& TriangulationBase<4>::group(bool) const; template void TriangulationBase<4>::calculateSkeleton(); template void TriangulationBase<4>::cloneSkeleton(const TriangulationBase<4>&); diff --git a/engine/triangulation/explicit/triangulation5.cpp b/engine/triangulation/explicit/triangulation5.cpp index 1eebb97cdb..ba58d5f3b8 100644 --- a/engine/triangulation/explicit/triangulation5.cpp +++ b/engine/triangulation/explicit/triangulation5.cpp @@ -68,7 +68,7 @@ template MatrixInt TriangulationBase<5>::dualToPrimal<2>() const; template MatrixInt TriangulationBase<5>::dualToPrimal<3>() const; template MatrixInt TriangulationBase<5>::dualToPrimal<4>() const; -template const GroupPresentation& TriangulationBase<5>::group() const; +template const GroupPresentation& TriangulationBase<5>::group(bool) const; template void TriangulationBase<5>::calculateSkeleton(); template void TriangulationBase<5>::cloneSkeleton(const TriangulationBase<5>&); diff --git a/engine/triangulation/explicit/triangulation6.cpp b/engine/triangulation/explicit/triangulation6.cpp index 6a1d05ac60..5773fb0bab 100644 --- a/engine/triangulation/explicit/triangulation6.cpp +++ b/engine/triangulation/explicit/triangulation6.cpp @@ -72,7 +72,7 @@ template MatrixInt TriangulationBase<6>::dualToPrimal<3>() const; template MatrixInt TriangulationBase<6>::dualToPrimal<4>() const; template MatrixInt TriangulationBase<6>::dualToPrimal<5>() const; -template const GroupPresentation& TriangulationBase<6>::group() const; +template const GroupPresentation& TriangulationBase<6>::group(bool) const; template void TriangulationBase<6>::calculateSkeleton(); template void TriangulationBase<6>::cloneSkeleton(const TriangulationBase<6>&); diff --git a/engine/triangulation/explicit/triangulation7.cpp b/engine/triangulation/explicit/triangulation7.cpp index 8e106c3484..4bcfed4f00 100644 --- a/engine/triangulation/explicit/triangulation7.cpp +++ b/engine/triangulation/explicit/triangulation7.cpp @@ -76,7 +76,7 @@ template MatrixInt TriangulationBase<7>::dualToPrimal<4>() const; template MatrixInt TriangulationBase<7>::dualToPrimal<5>() const; template MatrixInt TriangulationBase<7>::dualToPrimal<6>() const; -template const GroupPresentation& TriangulationBase<7>::group() const; +template const GroupPresentation& TriangulationBase<7>::group(bool) const; template void TriangulationBase<7>::calculateSkeleton(); template void TriangulationBase<7>::cloneSkeleton(const TriangulationBase<7>&); diff --git a/engine/triangulation/explicit/triangulation8.cpp b/engine/triangulation/explicit/triangulation8.cpp index 6ff490077f..b671b1a90f 100644 --- a/engine/triangulation/explicit/triangulation8.cpp +++ b/engine/triangulation/explicit/triangulation8.cpp @@ -80,7 +80,7 @@ template MatrixInt TriangulationBase<8>::dualToPrimal<5>() const; template MatrixInt TriangulationBase<8>::dualToPrimal<6>() const; template MatrixInt TriangulationBase<8>::dualToPrimal<7>() const; -template const GroupPresentation& TriangulationBase<8>::group() const; +template const GroupPresentation& TriangulationBase<8>::group(bool) const; template void TriangulationBase<8>::calculateSkeleton(); template void TriangulationBase<8>::cloneSkeleton(const TriangulationBase<8>&); diff --git a/engine/triangulation/explicit/triangulation9.cpp b/engine/triangulation/explicit/triangulation9.cpp index 91a35630fe..02ca14521e 100644 --- a/engine/triangulation/explicit/triangulation9.cpp +++ b/engine/triangulation/explicit/triangulation9.cpp @@ -84,7 +84,7 @@ template MatrixInt TriangulationBase<9>::dualToPrimal<6>() const; template MatrixInt TriangulationBase<9>::dualToPrimal<7>() const; template MatrixInt TriangulationBase<9>::dualToPrimal<8>() const; -template const GroupPresentation& TriangulationBase<9>::group() const; +template const GroupPresentation& TriangulationBase<9>::group(bool) const; template void TriangulationBase<9>::calculateSkeleton(); template void TriangulationBase<9>::cloneSkeleton(const TriangulationBase<9>&); diff --git a/python/dim2/triangulation2.cpp b/python/dim2/triangulation2.cpp index de2e702e3c..5df388b617 100644 --- a/python/dim2/triangulation2.cpp +++ b/python/dim2/triangulation2.cpp @@ -228,12 +228,14 @@ void addTriangulation2(pybind11::module_& m) { .def("isConnected", &Triangulation<2>::isConnected, rbase::isConnected) .def("group", &Triangulation<2>::group, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::group) .def("fundamentalGroup", &Triangulation<2>::fundamentalGroup, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::fundamentalGroup) .def("setGroupPresentation", - &Triangulation<2>::setGroupPresentation, + &Triangulation<2>::setGroupPresentation, rbase::setGroupPresentation) .def("simplifiedFundamentalGroup", // deprecated &Triangulation<2>::setGroupPresentation, diff --git a/python/dim3/triangulation3.cpp b/python/dim3/triangulation3.cpp index 4b1dc3716e..9b8626fc80 100644 --- a/python/dim3/triangulation3.cpp +++ b/python/dim3/triangulation3.cpp @@ -316,8 +316,10 @@ void addTriangulation3(pybind11::module_& m) { .def("isConnected", &Triangulation<3>::isConnected, rbase::isConnected) .def("group", &Triangulation<3>::group, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::group) .def("fundamentalGroup", &Triangulation<3>::fundamentalGroup, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::fundamentalGroup) .def("setGroupPresentation", diff --git a/python/dim4/triangulation4.cpp b/python/dim4/triangulation4.cpp index 0a501ab4c2..33262b1eeb 100644 --- a/python/dim4/triangulation4.cpp +++ b/python/dim4/triangulation4.cpp @@ -256,8 +256,10 @@ void addTriangulation4(pybind11::module_& m) { .def("isConnected", &Triangulation<4>::isConnected, rbase::isConnected) .def("group", &Triangulation<4>::group, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::group) .def("fundamentalGroup", &Triangulation<4>::fundamentalGroup, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::fundamentalGroup) .def("setGroupPresentation", diff --git a/python/generic/triangulation-bindings.h b/python/generic/triangulation-bindings.h index 224cb43517..52d32e687f 100644 --- a/python/generic/triangulation-bindings.h +++ b/python/generic/triangulation-bindings.h @@ -188,8 +188,10 @@ void addTriangulation(pybind11::module_& m, const char* name) { .def("eulerCharTri", &Triangulation::eulerCharTri, rbase::eulerCharTri) .def("group", &Triangulation::group, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::group) .def("fundamentalGroup", &Triangulation::fundamentalGroup, + pybind11::arg("simplify") = true, pybind11::return_value_policy::reference_internal, rbase::fundamentalGroup) .def("setGroupPresentation", diff --git a/python/testsuite/refs.out b/python/testsuite/refs.out index c42b3d0468..cd524eedf3 100644 --- a/python/testsuite/refs.out +++ b/python/testsuite/refs.out @@ -44,6 +44,9 @@ g0^6 g1 g0^-1 g1 g0 g1^-1 g0^6 g0^7 +g1^-1 g0 g1 g2 g0^-1 +g0 g2 g1 g2^-1 + foo foo foo diff --git a/python/testsuite/refs.test b/python/testsuite/refs.test index 3b5ba6bf06..6631f5eba7 100644 --- a/python/testsuite/refs.test +++ b/python/testsuite/refs.test @@ -116,6 +116,16 @@ g.relation(0).term(0).exponent = 7 print(t) print() +T = Example3.figureEight() +g = T.group(simplify=False) +# Ensure that python cannot modify a triangulations's group presentation. +h = g.intelligentSimplify() +g = T.group(simplify=False) +for r in g.relations(): + print(r) + +print() + n = NormalSurfaces(Example3.figureEight(), NS_QUAD) for s in n: