From 2e3ea5cf2373c8753887a2cb398baa48cb313bc1 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 02:47:03 +1000 Subject: [PATCH 01/10] Preparation for new SFS constructions. --- engine/manifold/sfs.cpp | 25 ++++++++++++++++++++++++ engine/testsuite/dim2/triangulation2.cpp | 2 ++ engine/testsuite/manifold/sfs.cpp | 2 ++ engine/triangulation/example2.h | 23 +++++++++++++++++----- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/engine/manifold/sfs.cpp b/engine/manifold/sfs.cpp index 40998bbdd..5c500e9de 100644 --- a/engine/manifold/sfs.cpp +++ b/engine/manifold/sfs.cpp @@ -39,6 +39,7 @@ #include "maths/numbertheory.h" #include "subcomplex/satannulus.h" #include "triangulation/dim3.h" +#include "triangulation/example2.h" // for Example<2>::disc() #include "utilities/exception.h" namespace regina { @@ -706,6 +707,28 @@ std::strong_ordering SFSpace::operator <=> (const SFSpace& rhs) const { return std::strong_ordering::equal; } +// ------------------------------------------------------------------------ +// Supporting classes for construct() +// ------------------------------------------------------------------------ +namespace { + + //TODO + + /** + */ + class OrientableCircleBundle { + }; // class OrientableCircleBundle + + /** + */ + class TriPrism { + }; // class TriPrism + +} // anonymous namespace + +// ------------------------------------------------------------------------ +// Implementation of construct() +// ------------------------------------------------------------------------ Triangulation<3> SFSpace::construct() const { // Things that we don't deal with just yet. if (punctures_ || puncturesTwisted_ || reflectors_ || reflectorsTwisted_) @@ -717,6 +740,8 @@ Triangulation<3> SFSpace::construct() const { if (auto lens = isLensSpace()) return lens->construct(); + //TODO The plan is to construct orientable SFS over any base. + // Currently we work over the 2-sphere only. if (genus_ != 0 || class_ != Class::o1) throw NotImplemented("SFSpace::construct() is currently not " diff --git a/engine/testsuite/dim2/triangulation2.cpp b/engine/testsuite/dim2/triangulation2.cpp index 5d60c53a2..7c5a6dd68 100644 --- a/engine/testsuite/dim2/triangulation2.cpp +++ b/engine/testsuite/dim2/triangulation2.cpp @@ -37,6 +37,8 @@ using regina::Triangulation; using regina::Example; +//TODO Test new Example<2> constructions. + class Dim2Test : public TriangulationTest<2> { protected: // Closed orientable triangulations: diff --git a/engine/testsuite/manifold/sfs.cpp b/engine/testsuite/manifold/sfs.cpp index 82ec61a9b..670ecccbf 100644 --- a/engine/testsuite/manifold/sfs.cpp +++ b/engine/testsuite/manifold/sfs.cpp @@ -35,6 +35,8 @@ using regina::SFSFibre; using regina::SFSpace; +//TODO Test new SFS constructions. + void verifyName(SFSpace::Class c, size_t genus, size_t punctures, size_t puncturesTwisted, size_t reflectors, size_t reflectorsTwisted, diff --git a/engine/triangulation/example2.h b/engine/triangulation/example2.h index 6a3dbbc48..08ab19bd6 100644 --- a/engine/triangulation/example2.h +++ b/engine/triangulation/example2.h @@ -61,6 +61,9 @@ namespace regina { template <> class Example<2> : public detail::ExampleBase<2> { public: + + //TODO The plan is for orientable() to always return minimal. + /** * Returns a triangulation of the given orientable surface. * @@ -77,6 +80,8 @@ class Example<2> : public detail::ExampleBase<2> { static Triangulation<2> orientable( unsigned genus, unsigned punctures); + //TODO The plan is for nonOrientable() to always return minimal. + /** * Returns a triangulation of the given non-orientable surface. * @@ -114,13 +119,19 @@ class Example<2> : public detail::ExampleBase<2> { static Triangulation<2> sphereOctahedron(); /** - * Returns a one-triangle disc. - * This is identical to the triangulation returned by the generic - * routine ball(). + * Returns an n-triangle disc (n = 1 by default). + * + * The default 1-triangle disc is identical to the triangulation + * returned by the generic routine ball(). + * + * This routine always returns an oriented triangulation of an + * (n + 2)-sided polygon. In particular, the returned + * triangulation always uses the minimum number of edge + * identifications for an n-triangle disc. * * \return the disc. */ - static Triangulation<2> disc(); + static Triangulation<2> disc(unsigned n = 1); /** * Returns a two-triangle annulus. @@ -176,7 +187,9 @@ inline Triangulation<2> Example<2>::kb() { return twistedSphereBundle(); } -inline Triangulation<2> Example<2>::disc() { + +inline Triangulation<2> Example<2>::disc(unsigned n) { + //TODO Construct polygonal discs with any number of triangles. return ball(); } From 95177359ab37ac418857a3642077dd3184d62c99 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 10:59:36 +1000 Subject: [PATCH 02/10] More preparation. --- engine/manifold/sfs.cpp | 2 +- engine/triangulation/example2.cpp | 4 ++++ engine/triangulation/example2.h | 35 +++++++++++++++++++------------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/engine/manifold/sfs.cpp b/engine/manifold/sfs.cpp index 5c500e9de..520041107 100644 --- a/engine/manifold/sfs.cpp +++ b/engine/manifold/sfs.cpp @@ -39,7 +39,7 @@ #include "maths/numbertheory.h" #include "subcomplex/satannulus.h" #include "triangulation/dim3.h" -#include "triangulation/example2.h" // for Example<2>::disc() +#include "triangulation/example2.h" // for Example<2>::polygon() #include "utilities/exception.h" namespace regina { diff --git a/engine/triangulation/example2.cpp b/engine/triangulation/example2.cpp index baf814f1e..cd988e272 100644 --- a/engine/triangulation/example2.cpp +++ b/engine/triangulation/example2.cpp @@ -127,6 +127,10 @@ Triangulation<2> Example<2>::nonOrientable(unsigned genus, unsigned punctures) { return ans; } +Triangulation<2> Example<2>::polygon(unsigned n) { + //TODO +} + Triangulation<2> Example<2>::sphereOctahedron() { Triangulation<2> ans; diff --git a/engine/triangulation/example2.h b/engine/triangulation/example2.h index 08ab19bd6..83b159e44 100644 --- a/engine/triangulation/example2.h +++ b/engine/triangulation/example2.h @@ -119,19 +119,13 @@ class Example<2> : public detail::ExampleBase<2> { static Triangulation<2> sphereOctahedron(); /** - * Returns an n-triangle disc (n = 1 by default). - * - * The default 1-triangle disc is identical to the triangulation - * returned by the generic routine ball(). - * - * This routine always returns an oriented triangulation of an - * (n + 2)-sided polygon. In particular, the returned - * triangulation always uses the minimum number of edge - * identifications for an n-triangle disc. + * Returns a one-triangle disc. + * This is identical to the triangulation returned by the generic + * routine ball(). * * \return the disc. */ - static Triangulation<2> disc(unsigned n = 1); + static Triangulation<2> disc(); /** * Returns a two-triangle annulus. @@ -173,6 +167,23 @@ class Example<2> : public detail::ExampleBase<2> { * \return the Klein bottle. */ static Triangulation<2> kb(); + + private: + + /** + * Returns an oriented n-triangle polygon with (n + 2) boundary edges. + * + * The triangulation is constructed by gluing edge (01) of triangle + * i to edge (02) of triangle (i - 1), for each i from 1 to (n - 1) + * (inclusive). + * + * \param n the number of triangles used to construct the polygon. + * + * \return the polygon. + */ + static Triangulation<2> polygon(unsigned n); + + friend class regina::SFSpace; }; inline Triangulation<2> Example<2>::sphereTetrahedron() { @@ -187,9 +198,7 @@ inline Triangulation<2> Example<2>::kb() { return twistedSphereBundle(); } - -inline Triangulation<2> Example<2>::disc(unsigned n) { - //TODO Construct polygonal discs with any number of triangles. +inline Triangulation<2> Example<2>::disc() { return ball(); } From 089f6baa62bad1529f3d83d378283ece7282c5cf Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 11:34:47 +1000 Subject: [PATCH 03/10] Private polygon() routine. --- engine/triangulation/example2.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/engine/triangulation/example2.cpp b/engine/triangulation/example2.cpp index cd988e272..7ad41a110 100644 --- a/engine/triangulation/example2.cpp +++ b/engine/triangulation/example2.cpp @@ -128,7 +128,16 @@ Triangulation<2> Example<2>::nonOrientable(unsigned genus, unsigned punctures) { } Triangulation<2> Example<2>::polygon(unsigned n) { - //TODO + // NOTE: The following routines all rely on this specific construction + // --> Example<2>::orientable() + // --> Example<2>::nonOrientable() + // --> SFSpace::construct() + Triangulation<2> ans; + ans.newTriangles(n); + for (unsigned i = 1; i < n; ++i) { + ans.triangle(i)->join( 2, ans.triangle(i - 1), Perm<3>(1, 2) ); + } + return ans; } Triangulation<2> Example<2>::sphereOctahedron() { From 7c451beaa4a5f59bcb7af05417fc3b2fb5fa0106 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 11:54:30 +1000 Subject: [PATCH 04/10] Make orientable() minimal. --- engine/triangulation/example2.cpp | 99 ++++++++++++++++++++++++------- engine/triangulation/example2.h | 35 ++++++----- 2 files changed, 97 insertions(+), 37 deletions(-) diff --git a/engine/triangulation/example2.cpp b/engine/triangulation/example2.cpp index 7ad41a110..cb90d42b2 100644 --- a/engine/triangulation/example2.cpp +++ b/engine/triangulation/example2.cpp @@ -35,39 +35,82 @@ namespace regina { Triangulation<2> Example<2>::orientable(unsigned genus, unsigned punctures) { - if (genus == 0 && punctures == 0) - return sphere(); + // Small cases that don't fit into the general constructions below. + if (genus == 0) { + if (punctures == 0) { + //TODO If sphere() were oriented(), then we could promise + // oriented for orientable(), since all the other constructions + // below are already oriented. + return sphere(); + } else if (punctures == 1) { + return disc(); + } + } Triangulation<2> ans; - if (genus == 0) { - // Fact: punctures >= 1. - unsigned n = 3 * punctures - 2; - unsigned i; - ans.newTriangles(n); - for (i = 0; i < n - 1; ++i) - ans.triangle(i)->join(1, ans.triangle(i + 1), Perm<3>(1, 2)); - ans.triangle(0)->join(0, ans.triangle(n - 1), Perm<3>(0, 1)); - for (i = 1; i < punctures; ++i) - ans.triangle(3 * i - 2)->join(0, ans.triangle(3 * i), - Perm<3>(1, 2)); - } else { - unsigned n = 4 * genus + 3 * punctures - 2; - unsigned i; - ans.newTriangles(n); - for (i = 0; i < n - 1; ++i) - ans.triangle(i)->join(1, ans.triangle(i + 1), Perm<3>(1, 2)); + //TODO Merge the punctures == 0 and punctures == 1 cases? + if (punctures == 0) { + // Already handled the sphere, so genus >= 1. + // + // The size of a minimal triangulation is 4*genus - 2. + // + // This is essentially the same as the old implementation, but without + // the punctures part of the construction (which was non-minimal). + unsigned n = 4 * genus - 2; + ans = polygon(n); ans.triangle(0)->join(2, ans.triangle(n - 1), Perm<3>(0, 2)); ans.triangle(0)->join(0, ans.triangle(n - 1), Perm<3>(0, 1)); - for (i = 1; i < genus; ++i) { + for (unsigned i = 1; i < genus; ++i) { ans.triangle(4 * i - 3)->join(0, ans.triangle(4 * i - 1), Perm<3>(1, 2)); ans.triangle(4 * i - 2)->join(0, ans.triangle(4 * i), Perm<3>(1, 2)); } - for (i = 0; i < punctures; ++i) - ans.triangle(4 * genus + 3 * i - 3)->join( - 0, ans.triangle(4 * genus + 3 * i - 1), Perm<3>(1, 2)); + } else if (punctures == 1) { + // Already handled the disc, so genus >= 1. + // + // The size of a minimal triangulation is 4*genus - 1. + ans = polygon( 4*genus - 1 ); + for (unsigned handle = 0; handle < genus; ++handle) { + for (unsigned faceIndex : {4*handle, 4*handle + 1}) { + if ( handle == genus - 1 && faceIndex == 4*genus - 3 ) { + // The very last gluing needs to be handled differently + // from the others. + ans.triangle(faceIndex)->join( + 0, ans.triangle(4*genus - 2), Perm<3>(0, 1) ); + } else { + ans.triangle(faceIndex)->join( + 0, ans.triangle(faceIndex + 2), Perm<3>(1, 2) ); + } + } + } + } else if (genus == 0) { + // Already handled the sphere and the disc, so punctures >= 2. + // + // The size of a minimal triangulation is 3*punctures - 4. + ans = polygon( 3*punctures - 4 ); + for (unsigned i = 0; i < punctures - 1; ++i) { + if (i == punctures - 2) { + // The very last gluing needs to be handled differently + // from the others. + ans.triangle(3*i)->join( + 0, ans.triangle( 3*punctures - 5 ), Perm<3>(0, 1) ); + } else { + ans.triangle(3*i)->join( + 0, ans.triangle( 3*i + 2 ), Perm<3>(1, 2) ); + } + } + } else { + // All that remains are the cases where genus >= 1 and punctures >= 2. + // + // The size of a minimal triangulation is 4*genus + 3*punctures - 4. + // + // We construct this by starting with one puncture (which has + // 4*genus - 1 triangles), and then adding all the extra punctures by + // attaching a gadget with 3*punctures - 3 triangles. + ans = orientable( genus, 1 ); + addPunctures( ans, punctures ); } return ans; @@ -140,6 +183,16 @@ Triangulation<2> Example<2>::polygon(unsigned n) { return ans; } +void Example<2>::addPunctures(Triangulation<2> surf, unsigned punctures) { + size_t initSize = surf.size(); + surf.insertTriangulation( polygon(3*punctures - 3) ); + for (unsigned i = 0; i < punctures - 1; ++i) { + surf.triangle( initSize + 3*i )->join( + 0, surf.triangle( initSize + 3*i + 2 ), Perm<3>(1, 2) ); + } + surf.triangle(0)->join( 2, surf.triangle(initSize), Perm<3>(0, 1) ); +} + Triangulation<2> Example<2>::sphereOctahedron() { Triangulation<2> ans; diff --git a/engine/triangulation/example2.h b/engine/triangulation/example2.h index 83b159e44..fc357a306 100644 --- a/engine/triangulation/example2.h +++ b/engine/triangulation/example2.h @@ -41,6 +41,7 @@ #include "regina-core.h" #include "triangulation/dim2.h" #include "triangulation/detail/example.h" +#include "manifold/sfs.h" // To make regina::SFSpace a friend. namespace regina { @@ -62,14 +63,8 @@ template <> class Example<2> : public detail::ExampleBase<2> { public: - //TODO The plan is for orientable() to always return minimal. - /** - * Returns a triangulation of the given orientable surface. - * - * If the number of punctures is 0, then the resulting triangulation - * will be minimal (which, for positive genus, means there is exactly - * one vertex). + * Returns a minimal triangulation of the given orientable surface. * * \param genus the genus of the surface; this must be greater * than or equal to zero. @@ -80,14 +75,8 @@ class Example<2> : public detail::ExampleBase<2> { static Triangulation<2> orientable( unsigned genus, unsigned punctures); - //TODO The plan is for nonOrientable() to always return minimal. - /** - * Returns a triangulation of the given non-orientable surface. - * - * If the number of punctures is 0 or 1, then the resulting - * triangulation will be minimal (which, with the exception of - * the projective plane, means there is exactly one vertex). + * Returns a minimal triangulation of the given non-orientable surface. * * \param genus the non-orientable genus of the surface, i.e., * the number of crosscaps that it contains; this must be greater @@ -183,6 +172,24 @@ class Example<2> : public detail::ExampleBase<2> { */ static Triangulation<2> polygon(unsigned n); + /** + * Adds punctures to the given once-punctured surface until it has + * the given number of punctures. + * + * This routine modifies \a surf directly. Adding the punctures + * increases the size of \a surf by `3*punctures - 3`. + * + * \pre \a surf has exactly one boundary edge, and this boundary edge + * is given by edge (01) of triangle 0. + * + * \param surf the once-punctured surface to which we should add + * extra punctures. + * \param punctures the total number of punctures that we should end + * up with. + */ + static void addPunctures( + Triangulation<2> surf, unsigned punctures); + friend class regina::SFSpace; }; From c1571bef0c18ad87def2f3c52c05574c16b3f375 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 17:17:19 +1000 Subject: [PATCH 05/10] Test and fix new version of orientable(). --- engine/testsuite/dim2/triangulation2.cpp | 57 ++++++++++++++++++++++++ engine/triangulation/example2.cpp | 8 ++-- engine/triangulation/example2.h | 2 +- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/engine/testsuite/dim2/triangulation2.cpp b/engine/testsuite/dim2/triangulation2.cpp index 7c5a6dd68..0d9f62b3c 100644 --- a/engine/testsuite/dim2/triangulation2.cpp +++ b/engine/testsuite/dim2/triangulation2.cpp @@ -49,6 +49,13 @@ class Dim2Test : public TriangulationTest<2> { // Closed non-orientable triangulations: TestCase rp2 { Example<2>::rp2(), "RP^2" }; + // Minimal bounded orientable triangulations: + TestCase pants { Example<2>::orientable(0, 3), "Pair of pants" }; + TestCase orPunc1 { Example<2>::orientable(2, 1), + "Or, g=2 + 1 puncture" }; + TestCase orPunc3 { Example<2>::orientable(2, 3), + "Or, g=2 + 3 punctures" }; + // Disconnected triangulations (we build these in the constructor): TestCase disjoint2 { {}, "Torus U Mobius" }; TestCase disjoint3 { {}, "KB U Annulus U S^2" }; @@ -74,6 +81,11 @@ class Dim2Test : public TriangulationTest<2> { f(rp2.tri, rp2.name); f(disjoint2.tri, disjoint2.name); f(disjoint3.tri, disjoint3.name); + + // Minimal bounded triangulations. + f(pants.tri, pants.name); + f(orPunc1.tri, orPunc1.name); + f(orPunc3.tri, orPunc3.name); } }; @@ -97,6 +109,11 @@ TEST_F(Dim2Test, validity) { verifyValid(rp2); verifyValid(disjoint2); verifyValid(disjoint3); + + // Minimal bounded triangulations. + verifyValid(pants); + verifyValid(orPunc1); + verifyValid(orPunc3); } TEST_F(Dim2Test, connectivity) { TriangulationTest<2>::connectivityGenericCases(); @@ -106,6 +123,11 @@ TEST_F(Dim2Test, connectivity) { EXPECT_TRUE(rp2.tri.isConnected()); EXPECT_FALSE(disjoint2.tri.isConnected()); EXPECT_FALSE(disjoint3.tri.isConnected()); + + // Minimal bounded triangulations. + EXPECT_TRUE(pants.tri.isConnected()); + EXPECT_TRUE(orPunc1.tri.isConnected()); + EXPECT_TRUE(orPunc3.tri.isConnected()); } TEST_F(Dim2Test, orientability) { TriangulationTest<2>::orientabilityGenericCases(); @@ -115,6 +137,11 @@ TEST_F(Dim2Test, orientability) { EXPECT_FALSE(rp2.tri.isOrientable()); EXPECT_FALSE(disjoint2.tri.isOrientable()); EXPECT_FALSE(disjoint3.tri.isOrientable()); + + // Minimal bounded triangulations. + EXPECT_TRUE(pants.tri.isOrientable()); + EXPECT_TRUE(orPunc1.tri.isOrientable()); + EXPECT_TRUE(orPunc3.tri.isOrientable()); } TEST_F(Dim2Test, orientedExamples) { // Ensure that the orientable Example<2> constructions are oriented. @@ -130,6 +157,11 @@ TEST_F(Dim2Test, orientedExamples) { EXPECT_TRUE(Example<2>::orientable(5, 3).isOriented()); EXPECT_TRUE(Example<2>::sphereOctahedron().isOriented()); EXPECT_TRUE(Example<2>::disc().isOriented()); + + // Minimal bounded triangulations. + EXPECT_TRUE(pants.tri.isOriented()); + EXPECT_TRUE(orPunc1.tri.isOriented()); + EXPECT_TRUE(orPunc3.tri.isOriented()); } TEST_F(Dim2Test, eulerChar) { @@ -140,6 +172,11 @@ TEST_F(Dim2Test, eulerChar) { EXPECT_EQ(rp2.tri.eulerCharTri(), 1); EXPECT_EQ(disjoint2.tri.eulerCharTri(), 0); EXPECT_EQ(disjoint3.tri.eulerCharTri(), 2); + + // Minimal bounded triangulations. + EXPECT_EQ(pants.tri.eulerCharTri(), -1); + EXPECT_EQ(orPunc1.tri.eulerCharTri(), -3); + EXPECT_EQ(orPunc3.tri.eulerCharTri(), -5); } TEST_F(Dim2Test, boundaryBasic) { TriangulationTest<2>::boundaryBasicGenericCases(); @@ -149,6 +186,11 @@ TEST_F(Dim2Test, boundaryBasic) { verifyBoundaryBasic(rp2, {}, {}, {}); verifyBoundaryBasic(disjoint2, {0}, {}, {}); verifyBoundaryBasic(disjoint3, {0, 0}, {}, {}); + + // Minimal bounded triangulations. + verifyBoundaryBasic(pants, {0, 0, 0}, {}, {}); + verifyBoundaryBasic(orPunc1, {0}, {}, {}); + verifyBoundaryBasic(orPunc3, {0, 0, 0}, {}, {}); } TEST_F(Dim2Test, vertexLinksBasic) { TriangulationTest<2>::vertexLinksBasicGenericCases(); @@ -158,6 +200,11 @@ TEST_F(Dim2Test, vertexLinksBasic) { verifyVertexLinksBasic(rp2, 2, 0); verifyVertexLinksBasic(disjoint2, 1, 1); verifyVertexLinksBasic(disjoint3, 4, 2); + + // Minimal bounded triangulations. + verifyVertexLinksBasic(pants, 0, 3); + verifyVertexLinksBasic(orPunc1, 0, 1); + verifyVertexLinksBasic(orPunc3, 0, 3); } TEST_F(Dim2Test, orient) { testManualCases(TriangulationTest<2>::verifyOrient); @@ -219,6 +266,11 @@ TEST_F(Dim2Test, homologyH1) { EXPECT_EQ(rp2.tri.homology<1>(), regina::AbelianGroup(0, {2})); EXPECT_EQ(disjoint2.tri.homology<1>(), regina::AbelianGroup(3)); EXPECT_EQ(disjoint3.tri.homology<1>(), regina::AbelianGroup(2, {2})); + + // Minimal bounded triangulations. + EXPECT_EQ(pants.tri.homology<1>(), regina::AbelianGroup(2)); + EXPECT_EQ(orPunc1.tri.homology<1>(), regina::AbelianGroup(4)); + EXPECT_EQ(orPunc3.tri.homology<1>(), regina::AbelianGroup(6)); } TEST_F(Dim2Test, fundGroup) { TriangulationTest<2>::fundGroupGenericCases(); @@ -228,6 +280,11 @@ TEST_F(Dim2Test, fundGroup) { EXPECT_EQ(rp2.tri.group().recogniseGroup(), "Z_2"); // We cannot call group() on disjoint triangulations. + + // Minimal bounded triangulations. + EXPECT_EQ(pants.tri.group().recogniseGroup(), "Free(2)"); + EXPECT_EQ(orPunc1.tri.group().recogniseGroup(), "Free(4)"); + EXPECT_EQ(orPunc3.tri.group().recogniseGroup(), "Free(6)"); } TEST_F(Dim2Test, chainComplex) { testManualCases(TriangulationTest<2>::verifyChainComplex); diff --git a/engine/triangulation/example2.cpp b/engine/triangulation/example2.cpp index cb90d42b2..85e33f6e6 100644 --- a/engine/triangulation/example2.cpp +++ b/engine/triangulation/example2.cpp @@ -49,7 +49,6 @@ Triangulation<2> Example<2>::orientable(unsigned genus, unsigned punctures) { Triangulation<2> ans; - //TODO Merge the punctures == 0 and punctures == 1 cases? if (punctures == 0) { // Already handled the sphere, so genus >= 1. // @@ -74,7 +73,7 @@ Triangulation<2> Example<2>::orientable(unsigned genus, unsigned punctures) { ans = polygon( 4*genus - 1 ); for (unsigned handle = 0; handle < genus; ++handle) { for (unsigned faceIndex : {4*handle, 4*handle + 1}) { - if ( handle == genus - 1 && faceIndex == 4*genus - 3 ) { + if ( faceIndex == 4*genus - 3 ) { // The very last gluing needs to be handled differently // from the others. ans.triangle(faceIndex)->join( @@ -183,7 +182,10 @@ Triangulation<2> Example<2>::polygon(unsigned n) { return ans; } -void Example<2>::addPunctures(Triangulation<2> surf, unsigned punctures) { +void Example<2>::addPunctures(Triangulation<2>& surf, unsigned punctures) { + // NOTE: The following routines rely on this specific construction + // --> Example<2>::orientable() + // --> Example<2>::nonOrientable() size_t initSize = surf.size(); surf.insertTriangulation( polygon(3*punctures - 3) ); for (unsigned i = 0; i < punctures - 1; ++i) { diff --git a/engine/triangulation/example2.h b/engine/triangulation/example2.h index fc357a306..910a1af96 100644 --- a/engine/triangulation/example2.h +++ b/engine/triangulation/example2.h @@ -188,7 +188,7 @@ class Example<2> : public detail::ExampleBase<2> { * up with. */ static void addPunctures( - Triangulation<2> surf, unsigned punctures); + Triangulation<2>& surf, unsigned punctures); friend class regina::SFSpace; }; From 8895bd609884956e4624802f087289babcf7445e Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 21:14:54 +1000 Subject: [PATCH 06/10] Make LinkTest recognise another trefoil complement iso sig. --- engine/testsuite/link/link.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/testsuite/link/link.cpp b/engine/testsuite/link/link.cpp index bf619c321..1e0741b5a 100644 --- a/engine/testsuite/link/link.cpp +++ b/engine/testsuite/link/link.cpp @@ -66,7 +66,8 @@ static bool isTrefoilComplement(const Triangulation<3>& tri) { for (const char* s : { "cPcbbbadh", "cPcbbbadu", "dLQbcbcdlcj", "dLQbcbcdlcn", "dLQabccbrwj", "dLQabccbrwn", - "eLAkbbcddaikhc", "eLAkbbcddainqv", "eLAkbcbddducqn", "eLAkbcbdddmcxj" }) + "eLAkbbcddaikhc", "eLAkbbcddainqv", "eLAkbcbddducqn", + "eLAkbcbdddmcxj", "gLLMQaeefedfbaapgjr" }) if (sig == s) return true; From 72f68cc3c88ecac9ecd10ca549a5a22abab5b832 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 21:49:25 +1000 Subject: [PATCH 07/10] Test that orientable() actually returns minimal triangulations. --- engine/testsuite/dim2/triangulation2.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/engine/testsuite/dim2/triangulation2.cpp b/engine/testsuite/dim2/triangulation2.cpp index 0d9f62b3c..fbd37e942 100644 --- a/engine/testsuite/dim2/triangulation2.cpp +++ b/engine/testsuite/dim2/triangulation2.cpp @@ -298,3 +298,12 @@ TEST_F(Dim2Test, dualToPrimal) { TEST_F(Dim2Test, copyMove) { testManualCases(TriangulationTest<2>::verifyCopyMove); } +TEST_F(Dim2Test, minimalSize) { + // Check that promises of minimality are fulfilled. + EXPECT_EQ(Example<2>::orientable(0, 0).size(), 2); + EXPECT_EQ(Example<2>::orientable(0, 1).size(), 1); + EXPECT_EQ(torus2.tri.size(), 6); + EXPECT_EQ(pants.tri.size(), 5); + EXPECT_EQ(orPunc1.tri.size(), 7); + EXPECT_EQ(orPunc3.tri.size(), 13); +} From b54ddaaa11b543673e103336c86688e53e0cc4b6 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 22:24:06 +1000 Subject: [PATCH 08/10] Make nonOrientable() minimal. --- engine/testsuite/dim2/triangulation2.cpp | 41 ++++++++++++- engine/triangulation/example2.cpp | 77 ++++++++++++------------ engine/triangulation/example2.h | 6 ++ 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/engine/testsuite/dim2/triangulation2.cpp b/engine/testsuite/dim2/triangulation2.cpp index fbd37e942..013910cfc 100644 --- a/engine/testsuite/dim2/triangulation2.cpp +++ b/engine/testsuite/dim2/triangulation2.cpp @@ -37,8 +37,6 @@ using regina::Triangulation; using regina::Example; -//TODO Test new Example<2> constructions. - class Dim2Test : public TriangulationTest<2> { protected: // Closed orientable triangulations: @@ -48,6 +46,7 @@ class Dim2Test : public TriangulationTest<2> { // Closed non-orientable triangulations: TestCase rp2 { Example<2>::rp2(), "RP^2" }; + TestCase kb { Example<2>::nonOrientable(2, 0), "KB" }; // Minimal bounded orientable triangulations: TestCase pants { Example<2>::orientable(0, 3), "Pair of pants" }; @@ -56,6 +55,12 @@ class Dim2Test : public TriangulationTest<2> { TestCase orPunc3 { Example<2>::orientable(2, 3), "Or, g=2 + 3 punctures" }; + // Minimal bounded non-orientable triangulations: + TestCase norPunc1 { Example<2>::nonOrientable(2, 1), + "Non-or, g=2 + 1 puncture" }; + TestCase norPunc3 { Example<2>::nonOrientable(2, 3), + "Non-or, g=2 + 3 punctures" }; + // Disconnected triangulations (we build these in the constructor): TestCase disjoint2 { {}, "Torus U Mobius" }; TestCase disjoint3 { {}, "KB U Annulus U S^2" }; @@ -79,6 +84,7 @@ class Dim2Test : public TriangulationTest<2> { f(s2Oct.tri, s2Oct.name); f(torus2.tri, torus2.name); f(rp2.tri, rp2.name); + f(kb.tri, kb.name); f(disjoint2.tri, disjoint2.name); f(disjoint3.tri, disjoint3.name); @@ -86,6 +92,8 @@ class Dim2Test : public TriangulationTest<2> { f(pants.tri, pants.name); f(orPunc1.tri, orPunc1.name); f(orPunc3.tri, orPunc3.name); + f(norPunc1.tri, norPunc1.name); + f(norPunc3.tri, norPunc3.name); } }; @@ -107,6 +115,7 @@ TEST_F(Dim2Test, validity) { verifyValid(s2Oct); verifyValid(torus2); verifyValid(rp2); + verifyValid(kb); verifyValid(disjoint2); verifyValid(disjoint3); @@ -114,6 +123,8 @@ TEST_F(Dim2Test, validity) { verifyValid(pants); verifyValid(orPunc1); verifyValid(orPunc3); + verifyValid(norPunc1); + verifyValid(norPunc3); } TEST_F(Dim2Test, connectivity) { TriangulationTest<2>::connectivityGenericCases(); @@ -121,6 +132,7 @@ TEST_F(Dim2Test, connectivity) { EXPECT_TRUE(s2Oct.tri.isConnected()); EXPECT_TRUE(torus2.tri.isConnected()); EXPECT_TRUE(rp2.tri.isConnected()); + EXPECT_TRUE(kb.tri.isConnected()); EXPECT_FALSE(disjoint2.tri.isConnected()); EXPECT_FALSE(disjoint3.tri.isConnected()); @@ -128,6 +140,8 @@ TEST_F(Dim2Test, connectivity) { EXPECT_TRUE(pants.tri.isConnected()); EXPECT_TRUE(orPunc1.tri.isConnected()); EXPECT_TRUE(orPunc3.tri.isConnected()); + EXPECT_TRUE(norPunc1.tri.isConnected()); + EXPECT_TRUE(norPunc3.tri.isConnected()); } TEST_F(Dim2Test, orientability) { TriangulationTest<2>::orientabilityGenericCases(); @@ -135,6 +149,7 @@ TEST_F(Dim2Test, orientability) { EXPECT_TRUE(s2Oct.tri.isOrientable()); EXPECT_TRUE(torus2.tri.isOrientable()); EXPECT_FALSE(rp2.tri.isOrientable()); + EXPECT_FALSE(kb.tri.isOrientable()); EXPECT_FALSE(disjoint2.tri.isOrientable()); EXPECT_FALSE(disjoint3.tri.isOrientable()); @@ -142,6 +157,8 @@ TEST_F(Dim2Test, orientability) { EXPECT_TRUE(pants.tri.isOrientable()); EXPECT_TRUE(orPunc1.tri.isOrientable()); EXPECT_TRUE(orPunc3.tri.isOrientable()); + EXPECT_FALSE(norPunc1.tri.isOrientable()); + EXPECT_FALSE(norPunc3.tri.isOrientable()); } TEST_F(Dim2Test, orientedExamples) { // Ensure that the orientable Example<2> constructions are oriented. @@ -170,6 +187,7 @@ TEST_F(Dim2Test, eulerChar) { EXPECT_EQ(s2Oct.tri.eulerCharTri(), 2); EXPECT_EQ(torus2.tri.eulerCharTri(), -2); EXPECT_EQ(rp2.tri.eulerCharTri(), 1); + EXPECT_EQ(kb.tri.eulerCharTri(), 0); EXPECT_EQ(disjoint2.tri.eulerCharTri(), 0); EXPECT_EQ(disjoint3.tri.eulerCharTri(), 2); @@ -177,6 +195,8 @@ TEST_F(Dim2Test, eulerChar) { EXPECT_EQ(pants.tri.eulerCharTri(), -1); EXPECT_EQ(orPunc1.tri.eulerCharTri(), -3); EXPECT_EQ(orPunc3.tri.eulerCharTri(), -5); + EXPECT_EQ(norPunc1.tri.eulerCharTri(), -1); + EXPECT_EQ(norPunc3.tri.eulerCharTri(), -3); } TEST_F(Dim2Test, boundaryBasic) { TriangulationTest<2>::boundaryBasicGenericCases(); @@ -184,6 +204,7 @@ TEST_F(Dim2Test, boundaryBasic) { verifyBoundaryBasic(s2Oct, {}, {}, {}); verifyBoundaryBasic(torus2, {}, {}, {}); verifyBoundaryBasic(rp2, {}, {}, {}); + verifyBoundaryBasic(kb, {}, {}, {}); verifyBoundaryBasic(disjoint2, {0}, {}, {}); verifyBoundaryBasic(disjoint3, {0, 0}, {}, {}); @@ -191,6 +212,8 @@ TEST_F(Dim2Test, boundaryBasic) { verifyBoundaryBasic(pants, {0, 0, 0}, {}, {}); verifyBoundaryBasic(orPunc1, {0}, {}, {}); verifyBoundaryBasic(orPunc3, {0, 0, 0}, {}, {}); + verifyBoundaryBasic(norPunc1, {0}, {}, {}); + verifyBoundaryBasic(norPunc3, {0, 0, 0}, {}, {}); } TEST_F(Dim2Test, vertexLinksBasic) { TriangulationTest<2>::vertexLinksBasicGenericCases(); @@ -198,6 +221,7 @@ TEST_F(Dim2Test, vertexLinksBasic) { verifyVertexLinksBasic(s2Oct, 6, 0); verifyVertexLinksBasic(torus2, 1, 0); verifyVertexLinksBasic(rp2, 2, 0); + verifyVertexLinksBasic(kb, 1, 0); verifyVertexLinksBasic(disjoint2, 1, 1); verifyVertexLinksBasic(disjoint3, 4, 2); @@ -205,6 +229,8 @@ TEST_F(Dim2Test, vertexLinksBasic) { verifyVertexLinksBasic(pants, 0, 3); verifyVertexLinksBasic(orPunc1, 0, 1); verifyVertexLinksBasic(orPunc3, 0, 3); + verifyVertexLinksBasic(norPunc1, 0, 1); + verifyVertexLinksBasic(norPunc3, 0, 3); } TEST_F(Dim2Test, orient) { testManualCases(TriangulationTest<2>::verifyOrient); @@ -264,6 +290,7 @@ TEST_F(Dim2Test, homologyH1) { EXPECT_EQ(s2Oct.tri.homology<1>(), regina::AbelianGroup()); EXPECT_EQ(torus2.tri.homology<1>(), regina::AbelianGroup(4)); EXPECT_EQ(rp2.tri.homology<1>(), regina::AbelianGroup(0, {2})); + EXPECT_EQ(kb.tri.homology<1>(), regina::AbelianGroup(1, {2})); EXPECT_EQ(disjoint2.tri.homology<1>(), regina::AbelianGroup(3)); EXPECT_EQ(disjoint3.tri.homology<1>(), regina::AbelianGroup(2, {2})); @@ -271,6 +298,8 @@ TEST_F(Dim2Test, homologyH1) { EXPECT_EQ(pants.tri.homology<1>(), regina::AbelianGroup(2)); EXPECT_EQ(orPunc1.tri.homology<1>(), regina::AbelianGroup(4)); EXPECT_EQ(orPunc3.tri.homology<1>(), regina::AbelianGroup(6)); + EXPECT_EQ(norPunc1.tri.homology<1>(), regina::AbelianGroup(2)); + EXPECT_EQ(norPunc3.tri.homology<1>(), regina::AbelianGroup(4)); } TEST_F(Dim2Test, fundGroup) { TriangulationTest<2>::fundGroupGenericCases(); @@ -278,6 +307,8 @@ TEST_F(Dim2Test, fundGroup) { EXPECT_EQ(s2Oct.tri.group().recogniseGroup(), "0"); EXPECT_EQ(torus2.tri.group().recogniseGroup(), ""); EXPECT_EQ(rp2.tri.group().recogniseGroup(), "Z_2"); + EXPECT_EQ(kb.tri.group().recogniseGroup(), + "Z~Z w/monodromy a ↦ a^-1"); // We cannot call group() on disjoint triangulations. @@ -285,6 +316,8 @@ TEST_F(Dim2Test, fundGroup) { EXPECT_EQ(pants.tri.group().recogniseGroup(), "Free(2)"); EXPECT_EQ(orPunc1.tri.group().recogniseGroup(), "Free(4)"); EXPECT_EQ(orPunc3.tri.group().recogniseGroup(), "Free(6)"); + EXPECT_EQ(norPunc1.tri.group().recogniseGroup(), "Free(2)"); + EXPECT_EQ(norPunc3.tri.group().recogniseGroup(), "Free(4)"); } TEST_F(Dim2Test, chainComplex) { testManualCases(TriangulationTest<2>::verifyChainComplex); @@ -303,7 +336,11 @@ TEST_F(Dim2Test, minimalSize) { EXPECT_EQ(Example<2>::orientable(0, 0).size(), 2); EXPECT_EQ(Example<2>::orientable(0, 1).size(), 1); EXPECT_EQ(torus2.tri.size(), 6); + EXPECT_EQ(rp2.tri.size(), 2); + EXPECT_EQ(kb.tri.size(), 2); EXPECT_EQ(pants.tri.size(), 5); EXPECT_EQ(orPunc1.tri.size(), 7); EXPECT_EQ(orPunc3.tri.size(), 13); + EXPECT_EQ(norPunc1.tri.size(), 3); + EXPECT_EQ(norPunc3.tri.size(), 9); } diff --git a/engine/triangulation/example2.cpp b/engine/triangulation/example2.cpp index 85e33f6e6..f6298e3e4 100644 --- a/engine/triangulation/example2.cpp +++ b/engine/triangulation/example2.cpp @@ -123,47 +123,46 @@ Triangulation<2> Example<2>::nonOrientable(unsigned genus, unsigned punctures) { Triangulation<2> ans; - // The generic code below will create one internal vertex, and one for - // each puncture. This is minimal for zero punctures, but non-minimal - // otherwise. For now, we use a different triangulation for the - // once-punctured case so at least that gets to be minimal also; - // ideally these should be minimal for all values of punctures. - - if (punctures == 1) { - // Thanks to Alex He for this code. - - // Let g denote the given genus. We use g-1 "inner" triangles and g - // "outer" triangles, for a total of 2*g-1 triangles. We start by using - // the g-1 "inner" triangles to build a (g+1)-sided polygon P. We then - // form each of the g "outer" triangles into a one-triangle Mobius band, - // and attach the boundary of each of these Mobius bands to one of the - // sides of P. It is clear that the resulting surface is once-punctured - // and one-vertex, and has non-orientable genus g. - unsigned n = 2*genus - 1; - unsigned i; - ans.newTriangles(n); - // Form "outer" triangles into Mobius bands. - for ( i = genus - 1; i < n; ++i ) { - ans.triangle(i)->join( - 0, ans.triangle(i), Perm<3>(1, 2, 0) ); - } - // Glue everything together. - for ( i = 1; i < n; ++i ) { - ans.triangle(i)->join( - 2, ans.triangle( (i-1)/2 ), Perm<3>( 2, i%2 ) ); + if (punctures == 0) { + // Already handled RP^2, so genus >= 2. + // + // The size of a minimal triangulation is 2*genus - 2. + // + // This is essentially the same as the old implementation, but without + // the punctures part of the construction (which was non-minimal). + unsigned n = 2 * genus - 2; + ans = polygon(n); + ans.triangle(0)->join(2, ans.triangle(n - 1), Perm<3>(2, 0, 1)); + for (unsigned i = 1; i < genus; ++i) + ans.triangle(2 * i - 2)->join( + 0, ans.triangle(2 * i - 1), Perm<3>()); + } else if (punctures == 1) { + // Trivially, we have genus >= 1. + // + // The size of a minimal triangulation is 2*genus - 1. + ans = polygon(2*genus - 1); + for (unsigned crosscap = 0; crosscap < genus; ++crosscap) { + if (crosscap == genus - 1) { + // The very last gluing needs to be handled differently from + // the others. + ans.triangle(2*crosscap)->join( + 0, ans.triangle(2*crosscap), Perm<3>(1, 2, 0) ); + } else { + ans.triangle(2*crosscap)->join( + 0, ans.triangle(2*crosscap + 1), Perm<3>() ); + } } } else { - unsigned n = 2 * genus + 3 * punctures - 2; - unsigned i; - ans.newTriangles(n); - for (i = 0; i < n - 1; ++i) - ans.triangle(i)->join(1, ans.triangle(i + 1), Perm<3>(1, 2)); - ans.triangle(0)->join(2, ans.triangle(n - 1), Perm<3>(2, 0, 1)); - for (i = 1; i < genus; ++i) - ans.triangle(2 * i - 2)->join(0, ans.triangle(2 * i - 1), Perm<3>()); - for (i = 0; i < punctures; ++i) - ans.triangle(2 * genus + 3 * i - 2)->join( - 0, ans.triangle(2 * genus + 3 * i), Perm<3>(1, 2)); + // All that remains are the cases where punctures >= 2. Again, + // trivially, we have genus >= 1. + // + // The size of a minimal triangulation is 2*genus + 3*punctures - 4. + // + // We construct this by starting with one puncture (which has + // 2*genus - 1 triangles), and then adding all the extra punctures by + // attaching a gadget with 3*punctures - 3 triangles. + ans = nonOrientable( genus, 1 ); + addPunctures( ans, punctures ); } return ans; diff --git a/engine/triangulation/example2.h b/engine/triangulation/example2.h index 910a1af96..cb2ad8f8c 100644 --- a/engine/triangulation/example2.h +++ b/engine/triangulation/example2.h @@ -71,6 +71,8 @@ class Example<2> : public detail::ExampleBase<2> { * \param punctures the number of punctures in the surface; * this must be greater than or equal to zero. * \return the requested orientable surface. + * + * \author Alex He, B.B. */ static Triangulation<2> orientable( unsigned genus, unsigned punctures); @@ -169,6 +171,8 @@ class Example<2> : public detail::ExampleBase<2> { * \param n the number of triangles used to construct the polygon. * * \return the polygon. + * + * \author Alex He */ static Triangulation<2> polygon(unsigned n); @@ -186,6 +190,8 @@ class Example<2> : public detail::ExampleBase<2> { * extra punctures. * \param punctures the total number of punctures that we should end * up with. + * + * \author Alex He */ static void addPunctures( Triangulation<2>& surf, unsigned punctures); From f75036e13694517a03ef01a82c343105472dcab8 Mon Sep 17 00:00:00 2001 From: alex-he Date: Mon, 10 Nov 2025 23:09:59 +1000 Subject: [PATCH 09/10] Update python skeleton test that uses nonOrientable(). --- python/testsuite/skeleton.out | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/python/testsuite/skeleton.out b/python/testsuite/skeleton.out index cce22f385..b1014a8a6 100644 --- a/python/testsuite/skeleton.out +++ b/python/testsuite/skeleton.out @@ -263,11 +263,11 @@ Size of the skeleton: Triangle gluing: Triangle | gluing: (01) (02) (12) ----------+--------------------------------------- - 0 | boundary 1 (01) 2 (10) - 1 | 0 (02) 3 (01) 4 (10) - 2 | 0 (21) 2 (21) 2 (20) - 3 | 1 (02) 3 (21) 3 (20) - 4 | 1 (21) 4 (21) 4 (20) + 0 | boundary 1 (01) 1 (12) + 1 | 0 (02) 2 (01) 0 (12) + 2 | 1 (02) 3 (01) 3 (12) + 3 | 2 (02) 4 (01) 2 (12) + 4 | 3 (02) 4 (21) 4 (20) Vertices: Triangle | vertex: 0 1 2 @@ -282,27 +282,27 @@ Edges: Triangle | edge: 01 02 12 ----------+-------------------- 0 | 0 1 2 - 1 | 1 3 4 - 2 | 2 5 5 - 3 | 3 6 6 - 4 | 4 7 7 + 1 | 1 3 2 + 2 | 3 4 5 + 3 | 4 6 5 + 4 | 6 7 7 -Vertex 0, boundary, degree 15: 0 (0), 1 (0), 3 (0), 3 (2), 3 (1), 1 (2), 4 (0), 4 (2), 4 (1), 1 (1), 0 (2), 2 (0), 2 (2), 2 (1), 0 (1) +Vertex 0, boundary, degree 15: 0 (0), 1 (0), 2 (0), 3 (0), 4 (0), 4 (2), 4 (1), 3 (2), 2 (2), 3 (1), 2 (1), 1 (2), 0 (2), 1 (1), 0 (1) Edge 0, boundary: 0 (01) Edge 1, internal: 1 (01), 0 (02) -Edge 2, internal: 0 (12), 2 (10) +Edge 2, internal: 0 (12), 1 (12) -Edge 3, internal: 3 (01), 1 (02) +Edge 3, internal: 1 (02), 2 (01) -Edge 4, internal: 1 (12), 4 (10) +Edge 4, internal: 2 (02), 3 (01) -Edge 5, internal: 2 (21), 2 (02) +Edge 5, internal: 3 (12), 2 (12) -Edge 6, internal: 3 (21), 3 (02) +Edge 6, internal: 4 (01), 3 (02) Edge 7, internal: 4 (21), 4 (02) From 3f02fa24200d311a76f0b34aca13f1bff2b0a97a Mon Sep 17 00:00:00 2001 From: alex-he Date: Sun, 8 Mar 2026 14:06:10 +1100 Subject: [PATCH 10/10] Remove code from separate unfinished feature. --- engine/manifold/sfs.cpp | 25 ------------------------- engine/testsuite/manifold/sfs.cpp | 2 -- 2 files changed, 27 deletions(-) diff --git a/engine/manifold/sfs.cpp b/engine/manifold/sfs.cpp index 520041107..40998bbdd 100644 --- a/engine/manifold/sfs.cpp +++ b/engine/manifold/sfs.cpp @@ -39,7 +39,6 @@ #include "maths/numbertheory.h" #include "subcomplex/satannulus.h" #include "triangulation/dim3.h" -#include "triangulation/example2.h" // for Example<2>::polygon() #include "utilities/exception.h" namespace regina { @@ -707,28 +706,6 @@ std::strong_ordering SFSpace::operator <=> (const SFSpace& rhs) const { return std::strong_ordering::equal; } -// ------------------------------------------------------------------------ -// Supporting classes for construct() -// ------------------------------------------------------------------------ -namespace { - - //TODO - - /** - */ - class OrientableCircleBundle { - }; // class OrientableCircleBundle - - /** - */ - class TriPrism { - }; // class TriPrism - -} // anonymous namespace - -// ------------------------------------------------------------------------ -// Implementation of construct() -// ------------------------------------------------------------------------ Triangulation<3> SFSpace::construct() const { // Things that we don't deal with just yet. if (punctures_ || puncturesTwisted_ || reflectors_ || reflectorsTwisted_) @@ -740,8 +717,6 @@ Triangulation<3> SFSpace::construct() const { if (auto lens = isLensSpace()) return lens->construct(); - //TODO The plan is to construct orientable SFS over any base. - // Currently we work over the 2-sphere only. if (genus_ != 0 || class_ != Class::o1) throw NotImplemented("SFSpace::construct() is currently not " diff --git a/engine/testsuite/manifold/sfs.cpp b/engine/testsuite/manifold/sfs.cpp index 670ecccbf..82ec61a9b 100644 --- a/engine/testsuite/manifold/sfs.cpp +++ b/engine/testsuite/manifold/sfs.cpp @@ -35,8 +35,6 @@ using regina::SFSFibre; using regina::SFSpace; -//TODO Test new SFS constructions. - void verifyName(SFSpace::Class c, size_t genus, size_t punctures, size_t puncturesTwisted, size_t reflectors, size_t reflectorsTwisted,