diff --git a/engine/maths/CMakeLists.txt b/engine/maths/CMakeLists.txt index cc504cd4ab..16e3f707f1 100644 --- a/engine/maths/CMakeLists.txt +++ b/engine/maths/CMakeLists.txt @@ -1,6 +1,7 @@ # maths ADD_SUBDIRECTORY("spec") +ADD_SUBDIRECTORY("detail") # Files to compile SET ( FILES diff --git a/engine/maths/detail/CMakeLists.txt b/engine/maths/detail/CMakeLists.txt new file mode 100644 index 0000000000..39d7a209a0 --- /dev/null +++ b/engine/maths/detail/CMakeLists.txt @@ -0,0 +1,8 @@ +# maths/detail + +if (${REGINA_INSTALL_DEV}) + INSTALL( FILES + permContractFront.h + DESTINATION "${INCLUDEDIR}/maths/detail" COMPONENT Development) +endif (${REGINA_INSTALL_DEV}) + diff --git a/engine/maths/detail/permContractFront.h b/engine/maths/detail/permContractFront.h new file mode 100644 index 0000000000..ff4a9d28bf --- /dev/null +++ b/engine/maths/detail/permContractFront.h @@ -0,0 +1,157 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Computational Engine * + * * + * Copyright (c) 1999-2023, Ben Burton * + * For further details contact Ben Burton (bab@debian.org). * + * * + * This program 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 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program 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 this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + +#ifndef __REGINA_PERM_CONTRACT_FRONT_H +#ifndef __DOXYGEN +#define __REGINA_PERM_CONTRACT_FRONT_H +#endif + +/*! \file maths/detail/permContractFront.h + * \brief Implements Perm::contractFront. + */ + +namespace regina { + +namespace detail { + +/** + * A helper template to implement Perm::contractFront(Perm). + */ +template::codeType, + PermCodeType kPermCodeType = Perm::codeType> +struct PermContractFront; + +/* + * Generic implementation using the Perm(p[0], ..., p[n-1]) + * contructor which is only available when Perm encoding + * is PERM_CODE_INDEX. + */ +template +struct PermContractFront +{ + static_assert(n < k); + + Perm operator() (Perm p) { + return helper(p, std::make_integer_sequence()); + } + +private: + template + Perm helper(Perm p, std::integer_sequence) + { + constexpr int s = k - n; + return Perm(p[s + i] - s...); + } +}; + +/* + * Generic implementation where Perm is constructed using the + * bit-coded images, so internal encoding must be PERM_CODE_IMAGES. + */ +template +struct PermContractFront +{ + static_assert(n < k); + + Perm operator() (Perm p) { + constexpr int s = k - n; + + using Code = typename Perm::Code; + + Code c = 0; + for (int i = 0, bits = 0; i < n; ++i, bits += Perm::imageBits) + c |= static_cast(p[s + i] - s) << bits; + + return Perm::fromPermCode(c); + } +}; + +template +struct PermContractFront +{ + Perm operator() (Perm p) { + using Code = typename Perm::Code2; + return Perm::fromPermCode2(static_cast(p.permCode2())); + } +}; + +template +struct PermContractFront<2, k, PERM_CODE_INDEX, kPermCodeType> +{ + static_assert(2 < k); + + Perm<2> operator() (Perm p) { + using Code = typename Perm<2>::Code; + return Perm<2>::fromPermCode(static_cast( + p[k-1] == k - 1 ? 0 : 1)); + } +}; + +template<> +struct PermContractFront<2, 3, PERM_CODE_INDEX, PERM_CODE_INDEX> +{ + Perm<2> operator() (Perm<3> p) { + using Code = typename Perm<2>::Code; + return Perm<2>::fromPermCode(static_cast( + p.permCode() == 0 ? 0 : 1)); + } +}; + +template<> +struct PermContractFront<2, 4, PERM_CODE_INDEX, PERM_CODE_INDEX> +{ + Perm<2> operator() (Perm<4> p) { + const Perm<4>::Code2 c = p.permCode2(); + return Perm<2>::fromPermCode(static_cast::Code>( + c == 1 || c == 6 ? 1 : 0)); + } +}; + +template<> +struct PermContractFront<2, 5, PERM_CODE_INDEX, PERM_CODE_INDEX> +{ + Perm<2> operator() (Perm<5> p) { + const Perm<5>::Code2 c = p.permCode2(); + return Perm<2>::fromPermCode( + static_cast::Code>( + c == 1 || c == 6 || c == 24 || c == 31 || c == 49 || c == 54 + ? 1 : 0)); + } +}; + +} + +} + +#endif diff --git a/engine/maths/perm.h b/engine/maths/perm.h index 4d73ca488f..6977077284 100644 --- a/engine/maths/perm.h +++ b/engine/maths/perm.h @@ -1273,6 +1273,32 @@ class Perm { template static constexpr Perm contract(Perm p); + /** + * Restricts a k-element permutation to an n-element + * permutation, where \a k > \a n. + * + * This is similar to Perm::contract but is considering the last + * n elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - \a n - 1]. + * + * The resulting permutation maps 0,...,n-1 to + * \a p[k - \a n] + \a n - k, ..., + * \a p[k - 1] + \a n - k. + * + * \pre The given permutation maps k - \a n, ..., k - 1 + * to k - \a n, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than \a n. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last \a n elements. + */ + template + static constexpr Perm contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * @@ -2261,6 +2287,7 @@ constexpr Perm Perm::contract(Perm p) { return Perm(c); } + #endif // __DOXYGEN template @@ -2560,6 +2587,8 @@ inline PermClass PermClass::operator ++(int) { #include "maths/spec/perm5.h" #include "maths/spec/perm7.h" +#include "maths/detail/permContractFront.h" + // Explicitly declare the non-specialised classes as extern. Otherwise the // linker on Windows (and *only* Windows) fails to unify their static data // members between the DLL and the executable (even though they are correctly @@ -2579,10 +2608,25 @@ extern template class regina::Perm<16>; namespace regina { +template +template +constexpr Perm Perm::contractFront(Perm p) { + static_assert( + k > n, + "Given permutation needs to have more elements then permutation " + "we contract to."); + return detail::PermContractFront()(p); +} + // What follows are implementations that use these specialised classes. // We hide them from doxygen since specialisations can confuse it. #ifndef __DOXYGEN +template +inline constexpr Perm<2> Perm<2>::contractFront(Perm p) { + return detail::PermContractFront<2, k>()(p); +} + template inline constexpr Perm<2> Perm<2>::contract(Perm p) { static_assert(k >= 8, "The generic implementation of Perm<2>::contract " @@ -2635,6 +2679,11 @@ inline constexpr Perm<3> Perm<3>::contract(Perm p) { return Perm<3>(p[0], p[1], p[2]); } +template +inline constexpr Perm<3> Perm<3>::contractFront(Perm p) { + return detail::PermContractFront<3, k>()(p); +} + template <> inline constexpr Perm<3> Perm<3>::contract(Perm<4> p) { // Code map: 0,3,8,7,12,15 -> 0,1,2,3,4,5. @@ -2666,6 +2715,11 @@ inline constexpr Perm<4> Perm<4>::contract(Perm p) { return Perm<4>(p[0], p[1], p[2], p[3]); } +template +inline constexpr Perm<4> Perm<4>::contractFront(Perm p) { + return detail::PermContractFront<4, k>()(p); +} + inline void Perm<4>::clear(unsigned from) { if (from <= 1) code_ = 0; @@ -2697,6 +2751,11 @@ constexpr Perm<5> Perm<5>::contract(Perm p) { return Perm<5>(p[0], p[1], p[2], p[3], p[4]); } +template +inline constexpr Perm<5> Perm<5>::contractFront(Perm p) { + return detail::PermContractFront<5, k>()(p); +} + inline void Perm<5>::clear(unsigned from) { if (from <= 1) code2_ = 0; @@ -2747,6 +2806,11 @@ constexpr Perm<6> Perm<6>::contract(Perm p) { return Perm<6>(p[0], p[1], p[2], p[3], p[4], p[5]); } +template +inline constexpr Perm<6> Perm<6>::contractFront(Perm p) { + return detail::PermContractFront<6, k>()(p); +} + inline void Perm<6>::clear(unsigned from) { switch (from) { case 0: @@ -2800,6 +2864,11 @@ constexpr Perm<7> Perm<7>::contract(Perm p) { return Perm<7>(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); } +template +inline constexpr Perm<7> Perm<7>::contractFront(Perm p) { + return detail::PermContractFront<7, k>()(p); +} + inline void Perm<7>::clear(unsigned from) { switch (from) { case 0: diff --git a/engine/maths/spec/perm2.h b/engine/maths/spec/perm2.h index 4e7616ea5e..3b97eadccd 100644 --- a/engine/maths/spec/perm2.h +++ b/engine/maths/spec/perm2.h @@ -121,6 +121,12 @@ class Perm<2> { */ using Code = uint8_t; + /** + * Alias for Code so that generic code can use Perm::Code2 + * for small n. + */ + using Code2 = Code; + private: /** * A lightweight array-like object used to implement Perm<2>::S2. @@ -354,6 +360,12 @@ class Perm<2> { */ constexpr Code permCode() const; + /** + * Alias for permCode so that generic code can use Perm::permCode2() + * for small n. + */ + constexpr Code permCode2() const; + /** * Sets this permutation to that represented by the given * internal code. @@ -377,6 +389,12 @@ class Perm<2> { */ static constexpr Perm<2> fromPermCode(Code code); + /** + * Alias for fromPermCode so that generic code can use + * Perm::fromPermCode2(c) for small n. + */ + static constexpr Perm<2> fromPermCode2(Code code); + /** * Determines whether the given integer is a valid internal * permutation code. Valid permutation codes can be passed to @@ -990,6 +1008,32 @@ class Perm<2> { template static constexpr Perm<2> contract(Perm p); + /** + * Restricts a k-element permutation to an 2-element + * permutation, where \a k > 2. + * + * This is similar to Perm::contract but is considering the last + * two elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ..., \a p[k - 3]. + * + * The resulting permutation maps 0 and 1 to + * \a p[k - 2] + 2 - k and + * \a p[k - 1] + 2 - k. + * + * \pre The given permutation maps k - 2 and k - 1 + * to k - 2 and k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 2. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last two elements. + */ + template + static constexpr Perm<2> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * @@ -1082,6 +1126,10 @@ inline constexpr Perm<2>::Code Perm<2>::permCode() const { return code_; } +inline constexpr Perm<2>::Code Perm<2>::permCode2() const { + return code_; +} + inline void Perm<2>::setPermCode(Code code) { code_ = code; } @@ -1090,6 +1138,10 @@ inline constexpr Perm<2> Perm<2>::fromPermCode(Code code) { return Perm<2>(code); } +inline constexpr Perm<2> Perm<2>::fromPermCode2(Code code) { + return Perm<2>(code); +} + inline constexpr bool Perm<2>::isPermCode(Code code) { // code >= 0 is a no-op because we are using an unsigned data type. return (code < 2); diff --git a/engine/maths/spec/perm3.h b/engine/maths/spec/perm3.h index e988a90ced..bb6ee961d0 100644 --- a/engine/maths/spec/perm3.h +++ b/engine/maths/spec/perm3.h @@ -110,6 +110,12 @@ class Perm<3> { */ using Code = uint8_t; + /** + * Alias for Code so that generic code can use Perm::Code2 + * for small n. + */ + using Code2 = Code; + private: /** * A lightweight array-like object used to implement Perm<3>::S3. @@ -421,6 +427,12 @@ class Perm<3> { */ constexpr Code permCode() const; + /** + * Alias for permCode so that generic code can use Perm::permCode2() + * for small n. + */ + constexpr Code permCode2() const; + /** * Sets this permutation to that represented by the given * internal code. @@ -444,6 +456,12 @@ class Perm<3> { */ static constexpr Perm<3> fromPermCode(Code code); + /** + * Alias for fromPermCode so that generic code can use + * Perm::fromPermCode2(c) for small n. + */ + static constexpr Perm<3> fromPermCode2(Code code); + /** * Determines whether the given integer is a valid internal * permutation code. Valid permutation codes can be passed to @@ -1090,6 +1108,32 @@ class Perm<3> { template static constexpr Perm<3> contract(Perm p); + /** + * Restricts a k-element permutation to a 3-element + * permutation, where \a k > 3. + * + * This is similar to Perm::contract but is considering the last + * three elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - 4]. + * + * The resulting permutation maps 0,...,2 to + * \a p[k - 3] + 3 - k, ..., + * \a p[k - 1] + 3 - k. + * + * \pre The given permutation maps k - 3, ..., k - 1 + * to k - 3, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 3. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last three elements. + */ + template + static constexpr Perm<3> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * @@ -1284,6 +1328,10 @@ inline constexpr Perm<3>::Code Perm<3>::permCode() const { return code_; } +inline constexpr Perm<3>::Code Perm<3>::permCode2() const { + return code_; +} + inline void Perm<3>::setPermCode(Code code) { code_ = code; } @@ -1292,6 +1340,10 @@ inline constexpr Perm<3> Perm<3>::fromPermCode(Code code) { return Perm<3>(code); } +inline constexpr Perm<3> Perm<3>::fromPermCode2(Code code) { + return Perm<3>(code); +} + inline constexpr bool Perm<3>::isPermCode(Code code) { // code >= 0 is a no-op because we are using an unsigned data type. return (code < 6); diff --git a/engine/maths/spec/perm4.h b/engine/maths/spec/perm4.h index e0e60461d9..aa912b4456 100644 --- a/engine/maths/spec/perm4.h +++ b/engine/maths/spec/perm4.h @@ -1358,6 +1358,32 @@ class Perm<4> { template static constexpr Perm<4> contract(Perm p); + /** + * Restricts a k-element permutation to a 4-element + * permutation, where \a k > 4. + * + * This is similar to Perm::contract but is considering the last + * three elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - 5]. + * + * The resulting permutation maps 0,...,2 to + * \a p[k - 4] + 4 - k, ..., + * \a p[k - 1] + 4 - k. + * + * \pre The given permutation maps k - 4, ..., k - 1 + * to k - 4, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 4. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last three elements. + */ + template + static constexpr Perm<4> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * diff --git a/engine/maths/spec/perm5.h b/engine/maths/spec/perm5.h index 7ace39b12b..123bafc074 100644 --- a/engine/maths/spec/perm5.h +++ b/engine/maths/spec/perm5.h @@ -1460,6 +1460,32 @@ class Perm<5> { template static constexpr Perm<5> contract(Perm p); + /** + * Restricts a k-element permutation to a 5-element + * permutation, where \a k > 5. + * + * This is similar to Perm::contract but is considering the last + * three elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - 6]. + * + * The resulting permutation maps 0,...,2 to + * \a p[k - 5] + 5 - k, ..., + * \a p[k - 1] + 5 - k. + * + * \pre The given permutation maps k - 5, ..., k - 1 + * to k - 5, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 5. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last three elements. + */ + template + static constexpr Perm<5> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * diff --git a/engine/maths/spec/perm6.h b/engine/maths/spec/perm6.h index 6ca0e6e63e..c2628d1f48 100644 --- a/engine/maths/spec/perm6.h +++ b/engine/maths/spec/perm6.h @@ -1174,6 +1174,32 @@ class Perm<6> { template static constexpr Perm<6> contract(Perm p); + /** + * Restricts a k-element permutation to a 6-element + * permutation, where \a k > 6. + * + * This is similar to Perm::contract but is considering the last + * three elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - 7]. + * + * The resulting permutation maps 0,...,2 to + * \a p[k - 6] + 6 - k, ..., + * \a p[k - 1] + 6 - k. + * + * \pre The given permutation maps k - 6, ..., k - 1 + * to k - 6, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 6. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last three elements. + */ + template + static constexpr Perm<6> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * diff --git a/engine/maths/spec/perm7.h b/engine/maths/spec/perm7.h index 1b47042a7a..f4eba5fa0a 100644 --- a/engine/maths/spec/perm7.h +++ b/engine/maths/spec/perm7.h @@ -1229,6 +1229,32 @@ class Perm<7> { template static constexpr Perm<7> contract(Perm p); + /** + * Restricts a k-element permutation to a 7-element + * permutation, where \a k > 7. + * + * This is similar to Perm::contract but is considering the last + * three elements of the k-element permutation rather than + * the first and ignores the "unused" images + * \a p[0], ...,\a p[k - 8]. + * + * The resulting permutation maps 0,...,2 to + * \a p[k - 7] + 7 - k, ..., + * \a p[k - 1] + 7 - k. + * + * \pre The given permutation maps k - 7, ..., k - 1 + * to k - 7, ..., k - 1 in some order. + * + * \tparam k the number of elements for the input permutation; + * this must be strictly greater than 7. + * + * \param p a permutation on \a k elements. + * \return the same permutation restricted to a permutation on + * the last three elements. + */ + template + static constexpr Perm<7> contractFront(Perm p); + /** * Is this permutation minimal in its conjugacy class? * diff --git a/engine/regina-core.h b/engine/regina-core.h index 29f1eefd48..67eaccff78 100644 --- a/engine/regina-core.h +++ b/engine/regina-core.h @@ -76,6 +76,17 @@ constexpr int maxDim() { #endif } +/** + * Indicates the smallest dimension of triangulation that Regina can work with. + * + * \return Regina's minimum dimension of triangulation. + * + * \ingroup engine + */ +constexpr int minDim() { + return 2; +} + /** * Represents various classes of algorithms that Regina can use for * computations. A function that takes an Algorithm argument need not diff --git a/engine/triangulation/detail/CMakeLists.txt b/engine/triangulation/detail/CMakeLists.txt index bb49883d74..9040830559 100644 --- a/engine/triangulation/detail/CMakeLists.txt +++ b/engine/triangulation/detail/CMakeLists.txt @@ -22,6 +22,7 @@ if (${REGINA_INSTALL_DEV}) facetpairing.h facetpairing-impl.h isosig-impl.h + linkbuilder.h retriangulate.h simplex.h strings.h diff --git a/engine/triangulation/detail/face.h b/engine/triangulation/detail/face.h index 2a56ec2937..03044d8970 100644 --- a/engine/triangulation/detail/face.h +++ b/engine/triangulation/detail/face.h @@ -45,6 +45,7 @@ #include "triangulation/facenumbering.h" #include "triangulation/alias/face.h" #include "triangulation/alias/facenumber.h" +#include "triangulation/detail/linkbuilder.h" #include "triangulation/detail/strings.h" #include "triangulation/forward.h" #include "utilities/markedvector.h" @@ -338,14 +339,19 @@ class FaceBase : static constexpr int subdimension = subdim; /**< A compile-time constant that gives the dimension of this face. */ - - static constexpr bool allowsNonOrientableLinks = (subdim <= dim - 3); + static constexpr int linkDimension = dim - subdim - 1; + /**< Gives the dimension of the triangulation of the link of + this face. */ + static constexpr bool allowsNonOrientableLinks = (linkDimension >= 2); /**< Indicates whether it is possible for a face of this dimension to have a non-orientable link. */ static constexpr bool allowsInvalidFaces = (dim >= 3 && subdim <= dim - 2); /**< Indicates whether it is possible for a face of this dimension to be invalid. */ + static constexpr bool canBuildLink = (linkDimension >= minDim()); + /**< Indicates whether Regina supports building the triangulation + of the link of this face. */ private: /** @@ -374,6 +380,14 @@ class FaceBase : top-dimensional simplices of the underlying triangulation. */ Component* component_; /**< The component that this face belongs to. */ + mutable EnableIfUseDefault< + canBuildLink, + typename LinkBuilder::UniquePtr> linkTri_; + /**< A triangulation of the link of this face. This will be + constructed on demand; until then it will be null. + We keep this as a unique pointer with custom deleter to + avoid instantiating the lower-dimensional triangulation + classes here in the header. */ BoundaryComponent* boundaryComponent_; /**< The boundary component that this face is a part of, or \c null if this face is internal. */ @@ -389,7 +403,48 @@ class FaceBase : dimensions, where we only test for one type of validity (bad self-identifications). */ + public: + /** + * Returns the triangulation of the link of this face. + * + * This routine caches the resulting triangulation so subsequent + * calls will be fast and the result is a read-only triangulation + * (which you can clone if you need to modify it). + * + * Note that the labelling of the simplices in the link is different + * than in the triangulation returned by `buildLink`: + * + * - The simplices of the link are numbered as follows. + * Simplex i (that is `link().simplex(i)`) in the link corresponds + * to a face of the simplex `embedding(i).simplex()` in the + * triangulation. Namely, the face that is opposite to the face + * indexed by `embedding(i).face()`. + * - Vertex v of this simplex i in the link corresponds to vertex + * `embedding(i).vertices()[subdim + 1 + v]` of the simplex in the + * triangulation. + * - In other words, to construct an embedding of the link into the + * triangulation combinatorially, sending simplex i to + * `embedding(i).simplex()` using the map + * {0, ..., dim - subdim} -> {0, ..., dim + 1} obtained + * by composing the dim+1-permutation `embedding(i).vertices()` with + * the injection taking v to subdim + 1 + v. + * + * \return Triangulation of the link of this face. + */ + const Triangulation & + link() const + { + static_assert( + canBuildLink, + "Link has a triangulation of dimension 0 or 1 which " + "is not supported by Regina"); + if (!linkTri_.value) { + linkTri_.value = LinkBuilder::build(*this); + } + return *(linkTri_.value); + } + /** * Returns the index of this face within the underlying * triangulation. diff --git a/engine/triangulation/detail/linkbuilder-impl.h b/engine/triangulation/detail/linkbuilder-impl.h new file mode 100644 index 0000000000..12a06d97a6 --- /dev/null +++ b/engine/triangulation/detail/linkbuilder-impl.h @@ -0,0 +1,168 @@ +#ifndef __REGINA_LINK_BUILDER_IMPL_H +#ifndef __DOXYGEN +#define __REGINA_LINK_BUILDER_IMPL_H +#endif + +#include "triangulation/detail/linkbuilder.h" +#include "triangulation/detail/face.h" +#include "maths/perm.h" + + +namespace regina::detail { + +template +void LinkBuilder::Deleter::operator() ( + const LinkTriangulation * const trig) +{ + delete trig; +} + +template +typename LinkBuilder::UniquePtr +LinkBuilder::build(const FaceBase &face) +{ + auto ans = new LinkTriangulation; + + typename LinkTriangulation::ChangeEventSpan span(*ans); + + ans->newSimplices(face.degree()); + + constexpr int nFaces = FaceNumbering::nFaces; + + // Given an FaceEmbedding embedding, we can easily construct the pair + // (embedding.simplex()->index, embedding.face()). + // + // However, the code below also needs to quickly find the embedding + // given the pair. + // + // simplexAndFaceToEmbeddingIndex is there to facilitate this look-up. + // Namely, the index of the embedding is given by + // simplexAndFaceToEmbedding[nFaces * simplexIndex + face ]. + // + // Note that simplexAndFaceToEmbedding will have uninitialized entries + // but we won't query them. + std::unique_ptr simplexAndFaceToEmbeddingIndex = + std::make_unique( + nFaces * face.triangulation().size()); + + { + size_t embeddingIndex = 0; + for (const FaceEmbedding &embedding : face) { + const size_t i = + nFaces * embedding.simplex()->index() + embedding.face(); + simplexAndFaceToEmbeddingIndex[i] = embeddingIndex; + ++embeddingIndex; + } + } + + size_t embeddingIndex = 0; + + for (const FaceEmbedding &embedding : face) { + Simplex* const simplex = embedding.simplex(); + // Simplex in link corresponding to this embedding. + Simplex * const linkSimplex = + ans->simplex(embeddingIndex); + // Permutation such that the first subdim + 1 entries + // correspond to the vertices of the simplex spanning + // this face. + // + // The remaining dim - subdim - 1 vertices span the + // "co-face" that forms part of the link. + // + // For visualization, think of the link of a vertex V + // with dim = 3, sudim = 0. + // vertices[0] is the vertex of this tetrahedron corresponding to + // V. + // vertices[1], vertices[2], vertices[3] form the triangle + // (co-face) that is part of the link. + const Perm &vertices = embedding.vertices(); + + // Consider each subface f of the "co-face". We need to + // glue it to another subface of a "co-face". + // + // In the above example, we need to consider each edge f of the + // triangle and glue it to another edge of a triangle. + for (size_t f = 0; f < dim - subdim; ++f) { + + // The gluing has already happened. + // + // In the above example, it is because we already went + // through the neighboring triangle and glued it to this + // triangle along the current edge. + if (linkSimplex->adjacentSimplex(f)) + continue; + + // Find the facet (dim - 1 face) corresponding to the + // subface f of the "co-face". + // + // In the above example, we are looking for the face of + // the tetrahedron that is spanned by vertices[0] + // (corresponding to V) and the edge f we are considering. + const int facet = vertices[subdim + 1 + f]; + + // If the facet is not glued, we have nothing to glue. + Simplex* const adjacentSimplex = + simplex->adjacentSimplex(facet); + if (! adjacentSimplex) + continue; + + const Perm &adjacentGluing = + simplex->adjacentGluing(facet); + + // Compute what the "co-face" is taken to in the neighboring + // simplex by the the gluing. + // Note that we using here that the index of a face and of its + // opposite face are the same in Regina. + // + // For the special case of subdim = 0, we thus just need to + // determine the image of vertices[0]. + // + // In the above example, we are looking for the triangle in the + // neighboring tetrahedron sharing the current edge. + // + const int adjacentFace = + subdim == 0 + ? adjacentGluing[vertices[0]] + : FaceNumbering::faceNumber( + adjacentGluing * vertices); + + const size_t i = + nFaces * adjacentSimplex->index() + adjacentFace; + + const size_t adjacentEmbeddingIndex = + simplexAndFaceToEmbeddingIndex[i]; + + // Find the embedding corresponding to the "co-face" glued to + // the current "co-face" along the subface f. + // + const FaceEmbedding &adjacentEmbedding = + face.embedding(adjacentEmbeddingIndex); + + // In the above example, we have that the triangle in the + // neighboring tetrahedron is spanned by adjacentVertices[1], + // adjacentVertices[2] and adjacentVertices[3]. + const Perm adjacentVertices = + adjacentEmbedding.vertices(); + + // The neighboring simplex. + Simplex * const adjacentLinkSimplex = + ans->simplex(adjacentEmbeddingIndex); + + // Glue the subface f to the other "co-face". + linkSimplex->join( + f, + adjacentLinkSimplex, + Perm::contractFront( + adjacentVertices.inverse() * + adjacentGluing * + vertices)); + } + ++embeddingIndex; + } + + return UniquePtr(ans); +} + +} + +#endif diff --git a/engine/triangulation/detail/linkbuilder.h b/engine/triangulation/detail/linkbuilder.h new file mode 100644 index 0000000000..fdc1d601d8 --- /dev/null +++ b/engine/triangulation/detail/linkbuilder.h @@ -0,0 +1,96 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Computational Engine * + * * + * Copyright (c) 1999-2023, Ben Burton * + * For further details contact Ben Burton (bab@debian.org). * + * * + * This program 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 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program 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 this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + +/*! \file triangulation/detail/linkbuilder.h + * \brief Contains helper class that implements building the triangulation + * of the link of a face. The methods are implemented in + * linkbuilder-impl.h. + */ + +#ifndef __REGINA_LINK_BUILDER_H +#ifndef __DOXYGEN +#define __REGINA_LINK_BUILDER_H +#endif + +#include "triangulation/forward.h" + +#include + +namespace regina::detail { + +template class FaceBase; + +/** Helper class that implements building the triangulation of a link of a + * face Face. + */ +template +struct LinkBuilder +{ + static constexpr int linkDimension = dim - subdim - 1; + /**< Gives the dimension of the triangulation of the link of + the face. */ + + /** + * The type of triangulation of the link of the face. + */ + using LinkTriangulation = Triangulation; + + /** + * A custom deleter simply calling the destructor of + * `LinkTriangulation` through delete. + * + * Note that the implementation is elsewhere so that files + * including LinkBuilder do not need to include the header + * definining the `Triangulation` class. + */ + struct Deleter + { + void operator() (const LinkTriangulation * trig); + }; + + /** + * Clients can use this type to hold on to the link triangulation + * while only forward-declaring the `Triangulation` class. + */ + using UniquePtr = std::unique_ptr; + + /** + * Construct the triangulation of the link of the given face. + */ + static + UniquePtr + build(const FaceBase &face); +}; + +} + +#endif diff --git a/engine/triangulation/explicit/CMakeLists.txt b/engine/triangulation/explicit/CMakeLists.txt index 3ade646776..c6e242c30b 100644 --- a/engine/triangulation/explicit/CMakeLists.txt +++ b/engine/triangulation/explicit/CMakeLists.txt @@ -14,6 +14,7 @@ SET ( FILES facetpairing6.cpp facetpairing7.cpp facetpairing8.cpp + linkbuilder.cpp triangulation2.cpp triangulation3.cpp triangulation4.cpp diff --git a/engine/triangulation/explicit/linkbuilder.cpp b/engine/triangulation/explicit/linkbuilder.cpp new file mode 100644 index 0000000000..4f790f6d94 --- /dev/null +++ b/engine/triangulation/explicit/linkbuilder.cpp @@ -0,0 +1,106 @@ +#include "triangulation/detail/linkbuilder-impl.h" + +#include "triangulation/dim2/triangulation2.h" +#include "triangulation/dim3/triangulation3.h" +#include "triangulation/dim4/triangulation4.h" +#include "triangulation/generic/triangulation.h" + +namespace regina::detail { + +template class LinkBuilder<3, 0>; +template class LinkBuilder<4, 0>; +template class LinkBuilder<4, 1>; +template class LinkBuilder<5, 0>; +template class LinkBuilder<5, 1>; +template class LinkBuilder<5, 2>; +template class LinkBuilder<6, 0>; +template class LinkBuilder<6, 1>; +template class LinkBuilder<6, 2>; +template class LinkBuilder<6, 3>; +template class LinkBuilder<7, 0>; +template class LinkBuilder<7, 1>; +template class LinkBuilder<7, 2>; +template class LinkBuilder<7, 3>; +template class LinkBuilder<7, 4>; +template class LinkBuilder<8, 0>; +template class LinkBuilder<8, 1>; +template class LinkBuilder<8, 2>; +template class LinkBuilder<8, 3>; +template class LinkBuilder<8, 4>; +template class LinkBuilder<8, 5>; + +#ifdef REGINA_HIGHDIM +template class LinkBuilder<9, 0>; +template class LinkBuilder<9, 1>; +template class LinkBuilder<9, 2>; +template class LinkBuilder<9, 3>; +template class LinkBuilder<9, 4>; +template class LinkBuilder<9, 5>; +template class LinkBuilder<9, 6>; +template class LinkBuilder<10, 0>; +template class LinkBuilder<10, 1>; +template class LinkBuilder<10, 2>; +template class LinkBuilder<10, 3>; +template class LinkBuilder<10, 4>; +template class LinkBuilder<10, 5>; +template class LinkBuilder<10, 6>; +template class LinkBuilder<10, 7>; +template class LinkBuilder<11, 0>; +template class LinkBuilder<11, 1>; +template class LinkBuilder<11, 2>; +template class LinkBuilder<11, 3>; +template class LinkBuilder<11, 4>; +template class LinkBuilder<11, 5>; +template class LinkBuilder<11, 6>; +template class LinkBuilder<11, 7>; +template class LinkBuilder<11, 8>; +template class LinkBuilder<12, 0>; +template class LinkBuilder<12, 1>; +template class LinkBuilder<12, 2>; +template class LinkBuilder<12, 3>; +template class LinkBuilder<12, 4>; +template class LinkBuilder<12, 5>; +template class LinkBuilder<12, 6>; +template class LinkBuilder<12, 7>; +template class LinkBuilder<12, 8>; +template class LinkBuilder<12, 9>; +template class LinkBuilder<13, 0>; +template class LinkBuilder<13, 1>; +template class LinkBuilder<13, 2>; +template class LinkBuilder<13, 3>; +template class LinkBuilder<13, 4>; +template class LinkBuilder<13, 5>; +template class LinkBuilder<13, 6>; +template class LinkBuilder<13, 7>; +template class LinkBuilder<13, 8>; +template class LinkBuilder<13, 9>; +template class LinkBuilder<13, 10>; +template class LinkBuilder<14, 0>; +template class LinkBuilder<14, 1>; +template class LinkBuilder<14, 2>; +template class LinkBuilder<14, 3>; +template class LinkBuilder<14, 4>; +template class LinkBuilder<14, 5>; +template class LinkBuilder<14, 6>; +template class LinkBuilder<14, 7>; +template class LinkBuilder<14, 8>; +template class LinkBuilder<14, 9>; +template class LinkBuilder<14, 10>; +template class LinkBuilder<14, 11>; +template class LinkBuilder<15, 0>; +template class LinkBuilder<15, 1>; +template class LinkBuilder<15, 2>; +template class LinkBuilder<15, 3>; +template class LinkBuilder<15, 4>; +template class LinkBuilder<15, 5>; +template class LinkBuilder<15, 6>; +template class LinkBuilder<15, 7>; +template class LinkBuilder<15, 8>; +template class LinkBuilder<15, 9>; +template class LinkBuilder<15, 10>; +template class LinkBuilder<15, 11>; +template class LinkBuilder<15, 12>; + +#endif + +} diff --git a/engine/utilities/typeutils.h b/engine/utilities/typeutils.h index 4715446b15..3826f5410f 100644 --- a/engine/utilities/typeutils.h +++ b/engine/utilities/typeutils.h @@ -72,10 +72,33 @@ struct EnableIf { /**< The data member to store if \a condition is \c true. */ }; +/** + * A struct that holds either a single value of type \a T or nothing at all, + * depending on whether the given compile-time condition holds. + * + * If \a condition is \c true, then this struct holds a single data member + * \a value of type \a T, which will be initialised using the default + * constructor of T. + * + * \tparam condition \c true if the data member should be included, or + * \c false if this struct should be empty. + * \tparam T the data type to store. + * + * \ingroup utilities + */ +template +struct EnableIfUseDefault { + T value; +}; + #ifndef __DOXYGEN template struct EnableIf { }; + +template +struct EnableIfUseDefault { +}; #endif // __DOXYGEN /** diff --git a/python/dim3/vertex3.cpp b/python/dim3/vertex3.cpp index 53c230a068..d8a2758c77 100644 --- a/python/dim3/vertex3.cpp +++ b/python/dim3/vertex3.cpp @@ -113,6 +113,7 @@ being reserved for a different purpose in a future release.)doc") }, rdoc::buildLink) .def("buildLinkInclusion", &Vertex<3>::buildLinkInclusion, rdoc::buildLinkInclusion) + .def("link", &Vertex<3>::link, rbase::link) .def("isLinkClosed", &Vertex<3>::isLinkClosed, rdoc::isLinkClosed) .def("isIdeal", &Vertex<3>::isIdeal, rdoc::isIdeal) .def("isBoundary", &Vertex<3>::isBoundary, rbase::isBoundary) diff --git a/python/dim4/edge4.cpp b/python/dim4/edge4.cpp index 8f32e03e2f..3ab60076f7 100644 --- a/python/dim4/edge4.cpp +++ b/python/dim4/edge4.cpp @@ -115,6 +115,7 @@ void addEdge4(pybind11::module_& m) { }, rdoc::buildLink) .def("buildLinkInclusion", &Edge<4>::buildLinkInclusion, rdoc::buildLinkInclusion) + .def("link", &Edge<4>::link, rbase::link) .def("linkingSurface", &Edge<4>::linkingSurface, rdoc::linkingSurface) .def_static("ordering", &Edge<4>::ordering) .def_static("faceNumber", &Edge<4>::faceNumber) diff --git a/python/dim4/vertex4.cpp b/python/dim4/vertex4.cpp index bb62859d34..debb704bbd 100644 --- a/python/dim4/vertex4.cpp +++ b/python/dim4/vertex4.cpp @@ -96,6 +96,7 @@ void addVertex4(pybind11::module_& m) { }, rdoc::buildLink) .def("buildLinkInclusion", &Vertex<4>::buildLinkInclusion, rdoc::buildLinkInclusion) + .def("link", &Vertex<4>::link, rbase::link) .def("isLinkOrientable", &Vertex<4>::isLinkOrientable, rbase::isLinkOrientable) .def("isValid", &Vertex<4>::isValid, rbase::isValid) diff --git a/python/docstrings/maths/perm.h b/python/docstrings/maths/perm.h index 17b4374a6c..1fd06f02af 100644 --- a/python/docstrings/maths/perm.h +++ b/python/docstrings/maths/perm.h @@ -689,6 +689,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on *n* elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to an *n*-element permutation, +where *k* > *n*. + +This is similar to Perm::contract but is considering the last *n* +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - *n* - 1]. + +The resulting permutation maps 0,...,*n*-1 to *p*[*k* - *n*] + *n* - +*k*, ..., *p*[*k* - 1] + *n* - *k*. + +Precondition: + The given permutation maps *k* - *n*, ..., *k* - 1 to *k* - *n*, + ..., *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than *n*. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last *n* + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to an *n*-element permutation, where diff --git a/python/docstrings/maths/perm2.h b/python/docstrings/maths/perm2.h index 9d9837d0a3..1bb9d4023f 100644 --- a/python/docstrings/maths/perm2.h +++ b/python/docstrings/maths/perm2.h @@ -433,6 +433,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 2 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to an 2-element permutation, where +*k* > 2. + +This is similar to Perm::contract but is considering the last two +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ..., *p*[*k* - 3]. + +The resulting permutation maps 0 and 1 to *p*[*k* - 2] + 2 - *k* and +*p*[*k* - 1] + 2 - *k*. + +Precondition: + The given permutation maps *k* - 2 and *k* - 1 to *k* - 2 and *k* + - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 2. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last two + elements.)doc"; + // Docstring regina::python::doc::Perm_::fromPermCode static const char *fromPermCode = R"doc(Creates a permutation from the given internal code. @@ -447,6 +474,11 @@ Parameter ``code``: Returns: the permutation represented by the given internal code.)doc"; +// Docstring regina::python::doc::Perm_::fromPermCode2 +static const char *fromPermCode2 = +R"doc(Alias for fromPermCode so that generic code can use +Perm::fromPermCode2(c) for small n.)doc"; + // Docstring regina::python::doc::Perm_::hash static const char *hash = R"doc(Hashes this permutation to a non-negative integer, allowing it to be @@ -558,6 +590,11 @@ isPermCode(). Returns: the internal code.)doc"; +// Docstring regina::python::doc::Perm_::permCode2 +static const char *permCode2 = +R"doc(Alias for permCode so that generic code can use Perm::permCode2() +for small n.)doc"; + // Docstring regina::python::doc::Perm_::pow static const char *pow = R"doc(Computes the given power of this permutation. diff --git a/python/docstrings/maths/perm3.h b/python/docstrings/maths/perm3.h index 593b8a8685..445377bef2 100644 --- a/python/docstrings/maths/perm3.h +++ b/python/docstrings/maths/perm3.h @@ -470,6 +470,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 3 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to a 3-element permutation, where +*k* > 3. + +This is similar to Perm::contract but is considering the last three +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - 4]. + +The resulting permutation maps 0,...,2 to *p*[*k* - 3] + 3 - *k*, ..., +*p*[*k* - 1] + 3 - *k*. + +Precondition: + The given permutation maps *k* - 3, ..., *k* - 1 to *k* - 3, ..., + *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 3. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last three + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to an 3-element permutation. where 2 @@ -504,6 +531,11 @@ Parameter ``code``: Returns: the permutation represented by the given internal code.)doc"; +// Docstring regina::python::doc::Perm_::fromPermCode2 +static const char *fromPermCode2 = +R"doc(Alias for fromPermCode so that generic code can use +Perm::fromPermCode2(c) for small n.)doc"; + // Docstring regina::python::doc::Perm_::hash static const char *hash = R"doc(Hashes this permutation to a non-negative integer, allowing it to be @@ -615,6 +647,11 @@ isPermCode(). Returns: the internal code.)doc"; +// Docstring regina::python::doc::Perm_::permCode2 +static const char *permCode2 = +R"doc(Alias for permCode so that generic code can use Perm::permCode2() +for small n.)doc"; + // Docstring regina::python::doc::Perm_::pow static const char *pow = R"doc(Computes the given power of this permutation. diff --git a/python/docstrings/maths/perm4.h b/python/docstrings/maths/perm4.h index 3a2a1294be..73d661f453 100644 --- a/python/docstrings/maths/perm4.h +++ b/python/docstrings/maths/perm4.h @@ -506,6 +506,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 4 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to a 4-element permutation, where +*k* > 4. + +This is similar to Perm::contract but is considering the last three +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - 5]. + +The resulting permutation maps 0,...,2 to *p*[*k* - 4] + 4 - *k*, ..., +*p*[*k* - 1] + 4 - *k*. + +Precondition: + The given permutation maps *k* - 4, ..., *k* - 1 to *k* - 4, ..., + *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 4. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last three + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to a 4-element permutation, where 2 diff --git a/python/docstrings/maths/perm5.h b/python/docstrings/maths/perm5.h index fd01e1056a..ed23ea24c1 100644 --- a/python/docstrings/maths/perm5.h +++ b/python/docstrings/maths/perm5.h @@ -522,6 +522,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 5 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to a 5-element permutation, where +*k* > 5. + +This is similar to Perm::contract but is considering the last three +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - 6]. + +The resulting permutation maps 0,...,2 to *p*[*k* - 5] + 5 - *k*, ..., +*p*[*k* - 1] + 5 - *k*. + +Precondition: + The given permutation maps *k* - 5, ..., *k* - 1 to *k* - 5, ..., + *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 5. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last three + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to a 5-element permutation, where 2 diff --git a/python/docstrings/maths/perm6.h b/python/docstrings/maths/perm6.h index 90dd64e7bb..4349f84844 100644 --- a/python/docstrings/maths/perm6.h +++ b/python/docstrings/maths/perm6.h @@ -512,6 +512,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 6 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to a 6-element permutation, where +*k* > 6. + +This is similar to Perm::contract but is considering the last three +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - 7]. + +The resulting permutation maps 0,...,2 to *p*[*k* - 6] + 6 - *k*, ..., +*p*[*k* - 1] + 6 - *k*. + +Precondition: + The given permutation maps *k* - 6, ..., *k* - 1 to *k* - 6, ..., + *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 6. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last three + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to a 6-element permutation, where 2 diff --git a/python/docstrings/maths/perm7.h b/python/docstrings/maths/perm7.h index 44cb830d0d..0fc0cb7863 100644 --- a/python/docstrings/maths/perm7.h +++ b/python/docstrings/maths/perm7.h @@ -548,6 +548,33 @@ Parameter ``p``: Returns: the same permutation restricted to a permutation on 7 elements.)doc"; +// Docstring regina::python::doc::Perm_::contractFront +static const char *contractFront = +R"doc(Restricts a *k*-element permutation to a 7-element permutation, where +*k* > 7. + +This is similar to Perm::contract but is considering the last three +elements of the *k*-element permutation rather than the first and +ignores the "unused" images *p*[0], ...,*p*[*k* - 8]. + +The resulting permutation maps 0,...,2 to *p*[*k* - 7] + 7 - *k*, ..., +*p*[*k* - 1] + 7 - *k*. + +Precondition: + The given permutation maps *k* - 7, ..., *k* - 1 to *k* - 7, ..., + *k* - 1 in some order. + +Template parameter ``k``: + the number of elements for the input permutation; this must be + strictly greater than 7. + +Parameter ``p``: + a permutation on *k* elements. + +Returns: + the same permutation restricted to a permutation on the last three + elements.)doc"; + // Docstring regina::python::doc::Perm_::extend static const char *extend = R"doc(Extends a *k*-element permutation to a 7-element permutation, where 2 diff --git a/python/docstrings/maths/permContractBackImpl.h b/python/docstrings/maths/permContractBackImpl.h new file mode 100644 index 0000000000..e1e0af60d5 --- /dev/null +++ b/python/docstrings/maths/permContractBackImpl.h @@ -0,0 +1,19 @@ +/* + This file contains docstrings for use in the Python bindings. + Do not edit! They were automatically extracted by ../gendoc.sh. + */ + +#if defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif + +namespace regina::python::doc { + + +} // namespace regina::python::doc + +#if defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + diff --git a/python/docstrings/triangulation/detail/face.h b/python/docstrings/triangulation/detail/face.h index 95dd4a8242..b2dbc80200 100644 --- a/python/docstrings/triangulation/detail/face.h +++ b/python/docstrings/triangulation/detail/face.h @@ -535,6 +535,13 @@ failure is due to conditions (1) or (2) respectively. for non-standard dimensions *dim*, returns ``True`` if and only if this face is valid according to condition (1).)doc"; +// Docstring regina::python::doc::detail::FaceBase_::link +constexpr const char *link = +R"doc(Returns the triangulation of the link of this face. + +Returns: + Triangulation of the link of this face.)doc"; + // Docstring regina::python::doc::detail::FaceBase_::lock constexpr const char *lock = R"doc(Locks this codimension-1-face. diff --git a/python/generic/face-bindings.h b/python/generic/face-bindings.h index c921f3f9a1..2176e61cab 100644 --- a/python/generic/face-bindings.h +++ b/python/generic/face-bindings.h @@ -32,6 +32,9 @@ #include "../pybind11/pybind11.h" #include "triangulation/generic.h" +#include "triangulation/dim2.h" +#include "triangulation/dim3.h" +#include "triangulation/dim4.h" #include "../helpers.h" #include "../generic/facehelper.h" #include "../docstrings/triangulation/alias/facenumber.h" @@ -162,6 +165,8 @@ void addFace(pybind11::module_& m, const char* name, const char* embName) { c.def("inMaximalForest", &Face::inMaximalForest, rbase::inMaximalForest); } + if constexpr (Face::canBuildLink) + c.def("link", &Face::link, rbase::link); regina::python::add_output(c); regina::python::add_eq_operators(c); diff --git a/python/maths/perm.cpp b/python/maths/perm.cpp index 375999af14..fac534b7bf 100644 --- a/python/maths/perm.cpp +++ b/python/maths/perm.cpp @@ -114,6 +114,10 @@ void addPerm(pybind11::module_& m, const char* name) { c.def_static("contract", &Perm::template contract, rdoc::contract); }); + regina::for_constexpr([&c](auto i) { + c.def_static("contractFront", &Perm::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm2.cpp b/python/maths/perm2.cpp index 325bbe3b56..6b6d71ee07 100644 --- a/python/maths/perm2.cpp +++ b/python/maths/perm2.cpp @@ -107,6 +107,10 @@ void addPerm2(pybind11::module_& m) { c.def_static("contract", &Perm<2>::template contract, rdoc::contract); }); + regina::for_constexpr<3, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<2>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm3.cpp b/python/maths/perm3.cpp index dd9fa5505e..e95f897983 100644 --- a/python/maths/perm3.cpp +++ b/python/maths/perm3.cpp @@ -117,6 +117,10 @@ void addPerm3(pybind11::module_& m) { c.def_static("contract", &Perm<3>::template contract, rdoc::contract); }); + regina::for_constexpr<4, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<3>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm4.cpp b/python/maths/perm4.cpp index 5b33a7e653..84977559ca 100644 --- a/python/maths/perm4.cpp +++ b/python/maths/perm4.cpp @@ -130,6 +130,10 @@ void addPerm4(pybind11::module_& m) { c.def_static("contract", &Perm<4>::template contract, rdoc::contract); }); + regina::for_constexpr<5, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<4>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm5.cpp b/python/maths/perm5.cpp index fb0efe14b0..fd807a71a1 100644 --- a/python/maths/perm5.cpp +++ b/python/maths/perm5.cpp @@ -133,6 +133,10 @@ void addPerm5(pybind11::module_& m) { c.def_static("contract", &Perm<5>::template contract, rdoc::contract); }); + regina::for_constexpr<6, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<5>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm6.cpp b/python/maths/perm6.cpp index 57c21ba6f5..c9d9d1c433 100644 --- a/python/maths/perm6.cpp +++ b/python/maths/perm6.cpp @@ -124,6 +124,10 @@ void addPerm6(pybind11::module_& m) { c.def_static("contract", &Perm<6>::template contract, rdoc::contract); }); + regina::for_constexpr<7, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<6>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/python/maths/perm7.cpp b/python/maths/perm7.cpp index 122bf2e906..4fc2bb06d3 100644 --- a/python/maths/perm7.cpp +++ b/python/maths/perm7.cpp @@ -128,6 +128,10 @@ void addPerm7(pybind11::module_& m) { c.def_static("contract", &Perm<7>::template contract, rdoc::contract); }); + regina::for_constexpr<8, 17>([&c](auto i) { + c.def_static("contractFront", &Perm<7>::template contractFront, + rdoc::contractFront); + }); regina::python::add_output_basic(c, rdoc::str); regina::python::add_tight_encoding(c, rdoc::tightEncoding, rdoc::tightDecoding, rdoc::hash); diff --git a/testsuite/maths/perm.cpp b/testsuite/maths/perm.cpp index 616a1af9dd..85bf54380e 100644 --- a/testsuite/maths/perm.cpp +++ b/testsuite/maths/perm.cpp @@ -735,6 +735,23 @@ class LargePermTest : public GeneralPermTest { for (int i = 0; i < nIdx; ++i) verifyTightEncoding(Perm::orderedSn[idx[i]]); } + + void contractFront() { + if (regina::Perm(n-6, n-4) != + regina::Perm::contractFront(Perm(n-5, n-3))) { + std::ostringstream msg; + msg << "Perm::contractFront(Perm(n-5, n-3)) " + "failed for n = " << n << "."; + CPPUNIT_FAIL(msg.str()); + } + if (regina::Perm(n-7, n-6) != + regina::Perm::contractFront(Perm(n-3, n-2))) { + std::ostringstream msg; + msg << "Perm::contractFront(Perm(n-3, n-2)) " + "failed for n = " << n << "."; + CPPUNIT_FAIL(msg.str()); + } + } }; template @@ -752,6 +769,7 @@ class PermTest : public LargePermTest { CPPUNIT_TEST(order); CPPUNIT_TEST(rot); CPPUNIT_TEST(tightEncoding); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); }; @@ -779,6 +797,7 @@ class PermTest<8> : public LargePermTest<8> { CPPUNIT_TEST(increment); CPPUNIT_TEST(conjugacyMinimal); CPPUNIT_TEST(tightEncoding); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); }; @@ -803,6 +822,7 @@ class PermTest<9> : public LargePermTest<9> { CPPUNIT_TEST(increment); CPPUNIT_TEST(conjugacyMinimal); CPPUNIT_TEST(tightEncoding); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); }; @@ -827,6 +847,7 @@ class PermTest<10> : public LargePermTest<10> { CPPUNIT_TEST(increment); CPPUNIT_TEST(conjugacyMinimal); CPPUNIT_TEST(tightEncoding); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); }; @@ -851,6 +872,7 @@ class PermTest<11> : public LargePermTest<11> { CPPUNIT_TEST(increment); CPPUNIT_TEST(conjugacyMinimal); CPPUNIT_TEST(tightEncoding); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); }; diff --git a/testsuite/maths/perm2.cpp b/testsuite/maths/perm2.cpp index 1619a0b0a7..e279bf41a2 100644 --- a/testsuite/maths/perm2.cpp +++ b/testsuite/maths/perm2.cpp @@ -62,6 +62,7 @@ class Perm2Test : public SmallPermTest<2> { // Tests specific to Perm<2>: CPPUNIT_TEST(productsViaPerm4); CPPUNIT_TEST(aliases); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -102,6 +103,64 @@ class Perm2Test : public SmallPermTest<2> { if (Perm<2>::S1[i] != Perm<2>::Sn_1[i]) CPPUNIT_FAIL("Arrays S1 and Sn_1 disagree for Perm<2>."); } + + void contractFront() { + if (Perm<2>() != + Perm<2>::contractFront(Perm<3>(0,1,2))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<3>(0,1,2)) failed."); + } + if (Perm<2>(1,0) != + Perm<2>::contractFront(Perm<3>(0,2,1))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<3>(0,2,1)) failed."); + } + + contractFrontChecks(Perm<2>(), Perm<4>()); + contractFrontChecks(Perm<2>(1,0), Perm<4>(0,1,3,2)); + + contractFrontChecks(Perm<2>(), Perm<5>()); + contractFrontChecks(Perm<2>(1,0), Perm<5>(0,1,2,4,3)); + + contractFrontChecks(Perm<2>(), Perm<6>()); + contractFrontChecks(Perm<2>(1,0), Perm<6>(0,1,2,3,5,4)); + + contractFrontChecks(Perm<2>(), Perm<7>()); + contractFrontChecks(Perm<2>(1,0), Perm<7>(0,1,2,3,4,6,5)); + + if (Perm<2>() != + Perm<2>::contractFront(Perm<8>())) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<8>()) failed."); + } + if (Perm<2>() != + Perm<2>::contractFront(Perm<8>(4,5))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<8>(4,5)) failed."); + } + if (Perm<2>(1,0) != + Perm<2>::contractFront(Perm<8>(6,7))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<8>(6,7)) failed."); + } + if (Perm<2>(1,0) != + Perm<2>::contractFront(Perm<8>(6,7)*Perm<8>(4,5))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<8>(6,7)*Perm<8>(4,5)) failed."); + } + + if (Perm<2>() != + Perm<2>::contractFront(Perm<12>())) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<12>()) failed."); + } + if (Perm<2>() != + Perm<2>::contractFront(Perm<12>(7,8))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<12>(7,8)) failed."); + } + if (Perm<2>(1,0) != + Perm<2>::contractFront(Perm<12>(10,11))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<12>(10,11)) failed."); + } + if (Perm<2>(1,0) != + Perm<2>::contractFront(Perm<12>(10,11)*Perm<12>(7,8))) { + CPPUNIT_FAIL("Perm<2>::contractFront(Perm<12>(10,11)*Perm<12>(7,8)) failed."); + } + + } }; void addPerm2(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/perm3.cpp b/testsuite/maths/perm3.cpp index 15d0507030..77b0b14f08 100644 --- a/testsuite/maths/perm3.cpp +++ b/testsuite/maths/perm3.cpp @@ -63,6 +63,7 @@ class Perm3Test : public SmallPermTest<3> { CPPUNIT_TEST(productsViaPerm4); CPPUNIT_TEST(aliases); CPPUNIT_TEST(S2); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -131,6 +132,52 @@ class Perm3Test : public SmallPermTest<3> { "match Perm<2>."); } } + + void contractFront() { + if (Perm<3>(0,1,2) != + Perm<3>::contractFront(Perm<4>(0,1,2,3))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,1,2,3)) failed."); + } + if (Perm<3>(0,2,1) != + Perm<3>::contractFront(Perm<4>(0,1,3,2))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,1,3,2)) failed."); + } + if (Perm<3>(1,0,2) != + Perm<3>::contractFront(Perm<4>(0,2,1,3))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,2,1,3)) failed."); + } + if (Perm<3>(1,2,0) != + Perm<3>::contractFront(Perm<4>(0,2,3,1))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,2,3,1)) failed."); + } + if (Perm<3>(2,0,1) != + Perm<3>::contractFront(Perm<4>(0,3,1,2))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,3,1,2)) failed."); + } + if (Perm<3>(2,1,0) != + Perm<3>::contractFront(Perm<4>(0,3,2,1))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<4>(0,3,2,1)) failed."); + } + + contractFrontChecks(Perm<3>(0,1,2), Perm<5>(0,1,2,3,4)); + contractFrontChecks(Perm<3>(0,2,1), Perm<5>(0,1,2,4,3)); + contractFrontChecks(Perm<3>(1,0,2), Perm<5>(0,1,3,2,4)); + contractFrontChecks(Perm<3>(1,2,0), Perm<5>(0,1,3,4,2)); + contractFrontChecks(Perm<3>(2,0,1), Perm<5>(0,1,4,2,3)); + contractFrontChecks(Perm<3>(2,1,0), Perm<5>(0,1,4,3,2)); + + contractFrontChecks(Perm<3>(0,1,2), Perm<6>(0,1,2,3,4,5)); + contractFrontChecks(Perm<3>(0,2,1), Perm<6>(0,1,2,3,5,4)); + contractFrontChecks(Perm<3>(1,0,2), Perm<6>(0,1,2,4,3,5)); + contractFrontChecks(Perm<3>(1,2,0), Perm<6>(0,1,2,4,5,3)); + contractFrontChecks(Perm<3>(2,0,1), Perm<6>(0,1,2,5,3,4)); + contractFrontChecks(Perm<3>(2,1,0), Perm<6>(0,1,2,5,4,3)); + + if (Perm<3>(1,2,0) != + Perm<3>::contractFront(Perm<10>(3,4) * Perm<10>(7,8) * Perm<10>(8,9))) { + CPPUNIT_FAIL("Perm<3>::contractFront(Perm<10>(...)) failed."); + } + } }; void addPerm3(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/perm4.cpp b/testsuite/maths/perm4.cpp index 6e823cd28e..e71af054e0 100644 --- a/testsuite/maths/perm4.cpp +++ b/testsuite/maths/perm4.cpp @@ -67,6 +67,7 @@ class Perm4Test : public SmallPermTest<4> { CPPUNIT_TEST(aliases); CPPUNIT_TEST(S2); CPPUNIT_TEST(S3); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -156,6 +157,38 @@ class Perm4Test : public SmallPermTest<4> { "match Perm<3>."); } } + + void contractFront() { + if (Perm<4>(1,2,0,3) != + Perm<4>::contractFront(Perm<5>(0,2,3,1,4))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<5>(0,2,3,1,4)) failed"); + } + if (Perm<4>(1,2,3,0) != + Perm<4>::contractFront(Perm<5>(0,2,3,4,1))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<5>(0,2,3,4,1)) failed"); + } + if (Perm<4>(1,3,0,2) != + Perm<4>::contractFront(Perm<5>(0,2,4,1,3))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<5>(0,2,4,1,3)) failed"); + } + if (Perm<4>(1,3,2,0) != + Perm<4>::contractFront(Perm<5>(0,2,4,3,1))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<5>(0,2,4,3,1)) failed"); + } + + contractFrontChecks(Perm<4>(2,0,1,3), Perm<6>(0,1,4,2,3,5)); + contractFrontChecks(Perm<4>(2,0,3,1), Perm<7>(0,1,2,5,3,6,4)); + + if (Perm<4>(0,1) != + Perm<4>::contractFront(Perm<10>(6,7))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<10>(6,7))"); + } + + if (Perm<4>(1,2) != + Perm<4>::contractFront(Perm<11>(8,9))) { + CPPUNIT_FAIL("Perm<4>::contractFront(Perm<11>(8,9))"); + } + } }; void addPerm4(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/perm5.cpp b/testsuite/maths/perm5.cpp index b1e9a68a16..1741df4e6a 100644 --- a/testsuite/maths/perm5.cpp +++ b/testsuite/maths/perm5.cpp @@ -65,6 +65,7 @@ class Perm5Test : public SmallPermTest<5> { CPPUNIT_TEST(S2); CPPUNIT_TEST(S3); CPPUNIT_TEST(S4); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -165,6 +166,28 @@ class Perm5Test : public SmallPermTest<5> { "match Perm<4>."); } } + + void contractFront() { + if (Perm<5>(1,2,0,3,4) != + Perm<5>::contractFront(Perm<6>(0,2,3,1,4,5))) { + CPPUNIT_FAIL("Perm<5>::contractFront(Perm<6>(0,2,3,1,4,5)) failed"); + } + if (Perm<5>(1,2,0,4,3) != + Perm<5>::contractFront(Perm<6>(0,2,3,1,5,4))) { + CPPUNIT_FAIL("Perm<5>::contractFront(Perm<6>(0,2,3,1,5,4)) failed"); + } + if (Perm<5>(1,2,3,0,4) != + Perm<5>::contractFront(Perm<6>(0,2,3,4,1,5))) { + CPPUNIT_FAIL("Perm<5>::contractFront(Perm<6>(0,2,3,4,1,5)) failed"); + } + + contractFrontChecks(Perm<5>(1,2,3,4,0), Perm<7>(0,1,3,4,5,6,2)); + + if (Perm<5>(1,0,2,4,3) != + Perm<5>::contractFront(Perm<10>(3,4) * Perm<10>(5,6) * Perm<10>(8,9))) { + CPPUNIT_FAIL("Perm<5>::contractFront(Perm<10>(...)) failed"); + } + } }; void addPerm5(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/perm6.cpp b/testsuite/maths/perm6.cpp index 5e929d97e6..fdeefd3752 100644 --- a/testsuite/maths/perm6.cpp +++ b/testsuite/maths/perm6.cpp @@ -62,6 +62,7 @@ class Perm6Test : public SmallPermTest<6> { // Tests specific to Perm<6>: CPPUNIT_TEST(aliases); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -71,6 +72,27 @@ class Perm6Test : public SmallPermTest<6> { if (Perm<6>::S6[i] != Perm<6>::Sn[i]) CPPUNIT_FAIL("Arrays S6 and Sn disagree for Perm<6>."); } + + void contractFront() { + if (Perm<6>(1,2,0,3,4,5) != + Perm<6>::contractFront(Perm<7>(0,2,3,1,4,5,6))) { + CPPUNIT_FAIL("Perm<6>::contractFront(Perm<7>(0,2,3,1,4,5,6))"); + } + if (Perm<6>(1,2,0,3,5,4) != + Perm<6>::contractFront(Perm<7>(0,2,3,1,4,6,5))) { + CPPUNIT_FAIL("Perm<6>::contractFront(Perm<7>(0,2,3,1,4,6,5))"); + } + + contractFrontChecks( + Perm<6>(1,2,0,4,3,5), Perm<8>({0,1,3,4,2,6,5,7})); + contractFrontChecks( + Perm<6>(1,2,0,4,3,5), Perm<9>({0,1,2,4,5,3,7,6,8})); + + if (Perm<6>(3,4) != + Perm<6>::contractFront(Perm<13>(10,11))) { + CPPUNIT_FAIL("Perm<6>::contractFront(Perm<13>(10,11))"); + } + } }; void addPerm6(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/perm7.cpp b/testsuite/maths/perm7.cpp index 2db11f75b4..0afd117137 100644 --- a/testsuite/maths/perm7.cpp +++ b/testsuite/maths/perm7.cpp @@ -62,6 +62,7 @@ class Perm7Test : public SmallPermTest<7> { // Tests specific to Perm<7>: CPPUNIT_TEST(aliases); + CPPUNIT_TEST(contractFront); CPPUNIT_TEST_SUITE_END(); @@ -71,6 +72,15 @@ class Perm7Test : public SmallPermTest<7> { if (Perm<7>::S7[i] != Perm<7>::Sn[i]) CPPUNIT_FAIL("Arrays S7 and Sn disagree for Perm<7>."); } + + void contractFront() { + if (Perm<7>(0,2,1,3,4,6,5) != + Perm<7>::contractFront(Perm<8>(2,3) * Perm<8>(6,7))) { + CPPUNIT_FAIL("Perm<7>::contractFront(Perm<8>(2,3) * Perm<8>(6,7)) failed"); + } + contractFrontChecks(Perm<7>(2,3), Perm<9>(4,5)); + contractFrontChecks(Perm<7>(1,4), Perm<10>(4,7)); + } }; void addPerm7(CppUnit::TextUi::TestRunner& runner) { diff --git a/testsuite/maths/permtest.h b/testsuite/maths/permtest.h index 54a8bd1a08..3f4514fde2 100644 --- a/testsuite/maths/permtest.h +++ b/testsuite/maths/permtest.h @@ -254,6 +254,28 @@ class GeneralPermTest : public CppUnit::TestFixture, ++p; } while (! p.isIdentity()); } + + template + void contractFrontChecks(Perm expected, Perm given) + { + if (expected != Perm::contractFront(given)) { + std::ostringstream msg; + msg << "Perm<" << n << ">" + "::contractFront(" << given << ") gives wrong result."; + CPPUNIT_FAIL(msg.str()); + } + + constexpr int s = k - n; + for (int i = 0; i < Perm::nPerms; ++i) { + const Perm p = Perm::extend(Perm::Sn[i]) * given; + if (expected != Perm::contractFront(p)) { + std::ostringstream msg; + msg << "Perm<" << n << ">" + "::contractFront(" << p << ") gives wrong result."; + CPPUNIT_FAIL(msg.str()); + } + } + } }; /**