1 //****************************************************************************// 2 // Copyright (C) 2016 Florent Hivert <Florent.Hivert@lri.fr>, // 3 // // 4 // Distributed under the terms of the GNU General Public License (GPL) // 5 // // 6 // This code is distributed in the hope that it will be useful, // 7 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // 9 // General Public License for more details. // 10 // // 11 // The full text of the GPL is available at: // 12 // // 13 // http://www.gnu.org/licenses/ // 14 //****************************************************************************// 15 16 #ifndef HPCOMBI_PERM16_HPP_INCLUDED 17 #define HPCOMBI_PERM16_HPP_INCLUDED 18 19 #include <array> 20 #include <cassert> 21 #include <cstdint> 22 #include <functional> // less<> 23 #include <ostream> 24 #include <vector> 25 #include <x86intrin.h> 26 27 #include "epu.hpp" 28 #include "vect16.hpp" 29 30 namespace HPCombi { 31 32 // Forward declaration 33 struct Perm16; 34 struct PTransf16; 35 struct Transf16; 36 37 /** Partial transformation of @f$\{0\dots 15\}@f$ 38 * 39 */ 40 struct alignas(16) PTransf16 : public Vect16 { 41 sizeHPCombi::PTransf1642 static constexpr size_t size() { return 16; }; 43 44 using vect = HPCombi::Vect16; 45 using array = TPUBuild<epu8>::array; 46 47 PTransf16() = default; 48 HPCOMBI_CONSTEXPR_CONSTRUCTOR PTransf16(const PTransf16 &v) = default; PTransf16HPCombi::PTransf1649 HPCOMBI_CONSTEXPR_CONSTRUCTOR PTransf16(const vect v) : Vect16(v) {} PTransf16HPCombi::PTransf1650 HPCOMBI_CONSTEXPR_CONSTRUCTOR PTransf16(const epu8 x) : Vect16(x) {} 51 PTransf16(std::vector<uint8_t> dom, std::vector<uint8_t> rng, 52 size_t = 0 /* unused */); 53 PTransf16(std::initializer_list<uint8_t> il); 54 55 PTransf16 &operator=(const PTransf16 &) = default; operator =HPCombi::PTransf1656 PTransf16 &operator=(const epu8 &vv) { v = vv; return *this; } 57 58 //! Return whether \c *this is a well constructed object validateHPCombi::PTransf1659 bool validate(size_t k = 16) const { 60 return HPCombi::is_partial_transformation(v, k); 61 } 62 63 //! The identity partial transformation. oneHPCombi::PTransf1664 static HPCOMBI_CONSTEXPR PTransf16 one() { return epu8id; } 65 //! The product of two partial transformations. operator *HPCombi::PTransf1666 PTransf16 operator*(const PTransf16 &p) const { 67 return HPCombi::permuted(v, p.v) | (p.v == Epu8(0xFF)); 68 } 69 70 /** @copydoc common_inverse_pperm 71 * @par Algorithm: 72 * @f$O(n)@f$ algorithm using reference cast to arrays 73 */ 74 epu8 image_mask(bool complement=false) const; 75 uint32_t image_bitset(bool complement=false) const; 76 epu8 domain_mask(bool complement=false) const; 77 uint32_t domain_bitset(bool complement=false) const; 78 79 PTransf16 right_one() const; 80 PTransf16 left_one() const; 81 82 uint32_t rank_ref() const; 83 uint32_t rank() const; 84 }; 85 86 /** Full transformation of @f$\{0\dots 15\}@f$ 87 * 88 */ 89 struct Transf16 : public PTransf16 { 90 91 Transf16() = default; 92 HPCOMBI_CONSTEXPR_CONSTRUCTOR Transf16(const Transf16 &v) = default; Transf16HPCombi::Transf1693 HPCOMBI_CONSTEXPR_CONSTRUCTOR Transf16(const vect v) : PTransf16(v) {} Transf16HPCombi::Transf1694 HPCOMBI_CONSTEXPR_CONSTRUCTOR Transf16(const epu8 x) : PTransf16(x) {} Transf16HPCombi::Transf1695 Transf16(std::initializer_list<uint8_t> il) : PTransf16(il) {} 96 Transf16 &operator=(const Transf16 &) = default; 97 98 //! Return whether \c *this is a well constructed object validateHPCombi::Transf1699 bool validate(size_t k = 16) const { 100 return HPCombi::is_transformation(v, k); 101 } 102 103 //! The identity transformation. oneHPCombi::Transf16104 static HPCOMBI_CONSTEXPR Transf16 one() { return epu8id; } 105 //! The product of two transformations. operator *HPCombi::Transf16106 Transf16 operator*(const Transf16 &p) const { 107 return HPCombi::permuted(v, p.v); 108 } 109 110 //! Construct a transformation from its 64 bits compressed. 111 explicit Transf16(uint64_t compressed); 112 //! The 64 bit compressed form of a transformation. 113 explicit operator uint64_t() const; 114 }; 115 116 /** Partial permutationof @f$\{0\dots 15\}@f$ 117 * 118 */ 119 struct PPerm16 : public PTransf16 { 120 121 PPerm16() = default; 122 HPCOMBI_CONSTEXPR_CONSTRUCTOR PPerm16(const PPerm16 &v) = default; PPerm16HPCombi::PPerm16123 HPCOMBI_CONSTEXPR_CONSTRUCTOR PPerm16(const vect v) : PTransf16(v) {} PPerm16HPCombi::PPerm16124 HPCOMBI_CONSTEXPR_CONSTRUCTOR PPerm16(const epu8 x) : PTransf16(x) {} PPerm16HPCombi::PPerm16125 PPerm16(std::vector<uint8_t> dom, std::vector<uint8_t> rng, 126 size_t = 0 /* unused */) : PTransf16(dom, rng) {} PPerm16HPCombi::PPerm16127 PPerm16(std::initializer_list<uint8_t> il) : PTransf16(il) {} 128 PPerm16 &operator=(const PPerm16 &) = default; 129 130 //! Return whether \c *this is a well constructed object validateHPCombi::PPerm16131 bool validate(size_t k = 16) const { 132 return HPCombi::is_partial_permutation(v, k); 133 } 134 135 //! The identity partial permutations. oneHPCombi::PPerm16136 static HPCOMBI_CONSTEXPR PPerm16 one() { return epu8id; } 137 //! The product of two partial perrmutations. operator *HPCombi::PPerm16138 PPerm16 operator*(const PPerm16 &p) const { 139 return this->PTransf16::operator*(p); 140 } 141 142 /** @class common_inverse_pperm 143 * @brief The inverse of a partial permutation 144 * @details 145 * @returns the inverse of \c *this. The inverse of @f$p@f$ is the unique 146 * partial permutation @f$i@f$ such that @f$ p * i * p = p@f$ and 147 * @f$ i * p * i = i@f$ 148 * @par Example: 149 * @code 150 * Perm16 x = {0,3,2,4,0xFF,5,6,0xFF,8,9,11,0xFF,12,0xFF,0xFF,0xFF}; 151 * x.inverse() 152 * @endcode 153 * Returns 154 * @verbatim {0,0xFF,2,1,3,5,6,0xFF,8,9,0xFF,10,12,0xFF,0xFF,0xFF} @endverbatim 155 */ 156 /** @copydoc common_inverse_pperm 157 * @par Algorithm: 158 * @f$O(n)@f$ algorithm using reference cast to arrays 159 */ 160 PPerm16 inverse_ref() const; 161 /** @copydoc common_inverse_pperm 162 * @par Algorithm: 163 * @f$O(\log n)@f$ algorithm using some kind of vectorized dichotomic 164 * search. 165 */ 166 PPerm16 inverse_find() const; 167 right_oneHPCombi::PPerm16168 PPerm16 right_one() const { return PTransf16::right_one(); } left_oneHPCombi::PPerm16169 PPerm16 left_one() const { return PTransf16::left_one(); } 170 }; 171 172 /** Permutations of @f$\{0\dots 15\}@f$ 173 * 174 */ 175 struct Perm16 : public Transf16 /* public PPerm : diamond problem */ { 176 177 Perm16() = default; 178 HPCOMBI_CONSTEXPR_CONSTRUCTOR Perm16(const Perm16 &) = default; Perm16HPCombi::Perm16179 HPCOMBI_CONSTEXPR_CONSTRUCTOR Perm16(const vect v) : Transf16(v) {} Perm16HPCombi::Perm16180 HPCOMBI_CONSTEXPR_CONSTRUCTOR Perm16(const epu8 x) : Transf16(x) {} 181 Perm16 &operator=(const Perm16 &) = default; Perm16HPCombi::Perm16182 Perm16(std::initializer_list<uint8_t> il) : Transf16(il) {} 183 184 //! Return whether \c *this is a well constructed object validateHPCombi::Perm16185 bool validate(size_t k = 16) const { 186 return HPCombi::is_permutation(v, k); 187 } 188 189 // It's not possible to have a static constexpr member of same type as class 190 // being defined (see https://stackoverflow.com/questions/11928089/) 191 // therefore we chose to have functions. 192 //! The identity partial permutation. oneHPCombi::Perm16193 static HPCOMBI_CONSTEXPR Perm16 one() { return epu8id; } 194 //! The product of two permutations operator *HPCombi::Perm16195 Perm16 operator*(const Perm16 &p) const { 196 return HPCombi::permuted(v, p.v); 197 } 198 199 //! Construct a permutations from its 64 bits compressed. Perm16HPCombi::Perm16200 explicit Perm16(uint64_t compressed) : Transf16(compressed) {} 201 202 /** @class common_inverse 203 * @brief The inverse permutation 204 * @details 205 * @returns the inverse of \c *this 206 * @par Example: 207 * @code 208 * Perm16 x = {0,3,2,4,1,5,6,7,8,9,10,11,12,13,14,15}; 209 * x.inverse() 210 * @endcode 211 * Returns 212 * @verbatim {0,4,2,1,3,5,6,7,8,9,10,11,12,13,14,15} @endverbatim 213 */ 214 /** @copydoc common_inverse 215 * @par Algorithm: 216 * Reference @f$O(n)@f$ algorithm using loop and indexed access 217 */ 218 Perm16 inverse_ref() const; 219 /** @copydoc common_inverse 220 * @par Algorithm: 221 * @f$O(n)@f$ algorithm using reference cast to arrays 222 */ 223 Perm16 inverse_arr() const; 224 /** @copydoc common_inverse 225 * @par Algorithm: 226 * Insert the identity in the least significant bits and sort using a 227 * sorting network. The number of round of the optimal sorting network is 228 * open as far as I know, therefore the complexity is unknown. 229 */ 230 Perm16 inverse_sort() const; 231 /** @copydoc common_inverse 232 * @par Algorithm: 233 * @f$O(\log n)@f$ algorithm using some kind of vectorized dichotomic 234 * search. 235 */ inverse_findHPCombi::Perm16236 Perm16 inverse_find() const { return permutation_of(v, one()); } 237 /** @copydoc common_inverse 238 * @par Algorithm: 239 * 240 * Raise \e *this to power @f$\text{LCM}(1, 2, ..., n) - 1@f$ so complexity 241 * is in @f$O(log (\text{LCM}(1, 2, ..., n) - 1)) = O(n)@f$ 242 */ 243 Perm16 inverse_pow() const; 244 /** @copydoc common_inverse 245 * @par Algorithm: 246 * Compute power from @f$n/2@f$ to @f$n@f$, when @f$\sigma^k(i)=i@f$ then 247 * @f$\sigma^{-1}(i)=\sigma^{k-1}(i)@f$. Complexity @f$O(n)@f$ 248 */ 249 Perm16 inverse_cycl() const; 250 /** @copydoc common_inverse 251 * 252 * Frontend method: currently aliased to #inverse_cycl */ inverseHPCombi::Perm16253 Perm16 inverse() const { return inverse_cycl(); } 254 255 /** The elementary transposition exchanging @f$i@f$ and @f$i+1@f$ */ 256 static Perm16 elementary_transposition(uint64_t i); 257 /** A random permutation of size @f$n@f$*/ 258 static Perm16 random(uint64_t n = 16); 259 /** The \c r -th permutation of size \c n for the 260 * Steinhaus–Johnson–Trotter order. 261 */ 262 static Perm16 unrankSJT(int n, int r); 263 264 /** @class common_lehmer 265 * @brief The Lehmer code of a permutation 266 * @details 267 * @returns the Lehmer code of \c *this 268 * @par Example: 269 * @code 270 * Perm16 x = {0,3,2,4,1,5,6,7,8,9,10,11,12,13,14,15}; 271 * x.lehmer() 272 * @endcode 273 * Returns 274 * @verbatim {0,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0} @endverbatim 275 */ 276 /** @copydoc common_lehmer 277 * @par Algorithm: 278 * Reference @f$O(n^2)@f$ algorithm using loop and indexed access 279 */ 280 epu8 lehmer_ref() const; 281 /** @copydoc common_lehmer 282 * @par Algorithm: 283 * Reference @f$O(n^2)@f$ algorithm using array, loop and indexed access 284 */ 285 epu8 lehmer_arr() const; 286 /** @copydoc common_lehmer 287 * @par Algorithm: 288 * Fast @f$O(n)@f$ algorithm using vector comparison 289 */ 290 epu8 lehmer() const; 291 /** @class common_length 292 * @brief The Coxeter length (ie: number of inversion) of a permutation 293 * @details 294 * @returns the number of inversions of \c *this 295 * @par Example: 296 * @code 297 * Perm16 x = {0,3,2,4,1,5,6,7,8,9,10,11,12,13,14,15}; 298 * x.length() 299 * @endcode 300 * Returns @verbatim 4 @endverbatim 301 */ 302 /** @copydoc common_lehmer 303 * @par Algorithm: 304 * Reference @f$O(n^2)@f$ algorithm using loop and indexed access 305 */ 306 uint8_t length_ref() const; 307 /** @copydoc common_lehmer 308 * @par Algorithm: 309 * Reference @f$O(n^2)@f$ algorithm using loop and indexed access after 310 * a cast to \c std::array 311 */ 312 uint8_t length_arr() const; 313 /** @copydoc common_lehmer 314 * @par Algorithm: 315 * Reference @f$O(n^2)@f$ using vector lehmer and fast horizontal sum 316 */ 317 uint8_t length() const; 318 319 /** @class common_nb_descent 320 * @brief The number of descent of a permutation 321 * @details 322 * @returns the number of inversions of \c *this 323 * @par Example: 324 * @code 325 * Perm16 x {0,3,2,4,1,5,6,7,8,9,10,11,12,13,14,15}; 326 * x.length() 327 * @endcode 328 * Returns @verbatim 2 @endverbatim 329 */ 330 /** @copydoc common_lehmer 331 * @par Algorithm: 332 * Reference @f$O(n)@f$ using a loop 333 */ 334 uint8_t nb_descents_ref() const; 335 /** @copydoc common_lehmer 336 * @par Algorithm: 337 * Reference @f$O(1)@f$ using vector shift and comparison 338 */ 339 uint8_t nb_descents() const; 340 341 /** The set partition of the cycles of a permutation 342 * @details 343 * @returns the a vector @f$v@f$ where @$fv[i]@$f contains the smallest 344 * element in the cycle of $i$ in \c *this 345 * @par Example: 346 * @code 347 * Perm16 x {1,2,3,6,0,5,4,7,8,9,10,11,12,15,14,13} 348 * x.cycles_partition() 349 * @endcode 350 * Returns 351 @verbatim 352 [ 0, 0, 0, 0, 0, 5, 0, 7, 8, 9,10,11,12,13,14,13] 353 @endverbatim 354 */ 355 epu8 cycles_partition() const; 356 357 /** @class common_nb_cycles 358 * @brief The number of cycles of a permutation 359 * @details 360 * @returns the number of cycles of \c *this 361 * @par Example: 362 * @code 363 * Perm16 x {1,2,3,6,0,5,4,7,8,9,10,11,12,15,14,13} 364 * x.nb_cycles() 365 * @endcode 366 * Returns @verbatim 10 @endverbatim 367 */ 368 /** @copydoc common_nb_cycles 369 * @par Algorithm: 370 * Reference @f$O(n)@f$ using a boolean vector 371 */ 372 uint8_t nb_cycles_ref() const; 373 /** @copydoc common_nb_cycles 374 * @par Algorithm: 375 * Reference @f$O(\log(n))@f$ using #cycles_partition 376 */ 377 uint8_t nb_cycles_unroll() const; 378 /** @copydoc common_nb_cycles 379 * @par Algorithm: aliased to #nb_cycles_unroll 380 */ nb_cyclesHPCombi::Perm16381 uint8_t nb_cycles() const { return nb_cycles_unroll(); } 382 383 /** @class common_left_weak_leq 384 * @brief Compare two permutations for the left weak order 385 * @par Example: 386 * @code 387 * Perm16 x{2,0,3,1}, y{3,0,2,1}; 388 * x.left_weak_leq(y) 389 * @endcode 390 * Returns @verbatim true @endverbatim 391 */ 392 /** @copydoc common_left_weak_leq 393 * @par Algorithm: 394 * Reference @f$O(n^2)@f$ testing inclusion of inversions one by one 395 */ 396 bool left_weak_leq_ref(Perm16 other) const; 397 /** @copydoc common_left_weak_leq 398 * @par Algorithm: 399 * Reference @f$O(n)@f$ with vectorized test of inclusion 400 */ 401 bool left_weak_leq(Perm16 other) const; 402 403 /** Returns the smallest fix point of \c *this */ 404 uint8_t smallest_fix_point() const; 405 /** Returns the smallest non fix point of \c *this */ 406 uint8_t smallest_moved_point() const; 407 /** Returns the largest fix point of \c *this */ 408 uint8_t largest_fix_point() const; 409 /** Returns the largest non fix point of \c *this */ 410 uint8_t largest_moved_point() const; 411 412 }; 413 414 /*****************************************************************************/ 415 /** Memory layout concepts check ********************************************/ 416 /*****************************************************************************/ 417 418 static_assert(sizeof(epu8) == sizeof(Perm16), 419 "epu8 and Perm16 have a different memory layout !"); 420 static_assert(std::is_trivial<epu8>(), "epu8 is not a trivial class !"); 421 static_assert(std::is_trivial<Perm16>(), "Perm16 is not a trivial class !"); 422 423 } // namespace HPCombi 424 425 #include "perm16_impl.hpp" 426 427 namespace std { 428 429 template <> struct hash<HPCombi::PTransf16> { 430 //! A hash operator for #HPCombi::PTransf16 operator ()std::hash431 size_t operator()(const HPCombi::PTransf16 &ar) const { 432 return std::hash<HPCombi::epu8>{}(ar.v); 433 } 434 }; 435 436 template <> struct hash<HPCombi::Transf16> { 437 //! A hash operator for #HPCombi::Transf16 operator ()std::hash438 size_t operator()(const HPCombi::Transf16 &ar) const { 439 return uint64_t(ar); 440 } 441 }; 442 443 template <> struct hash<HPCombi::PPerm16> { 444 //! A hash operator for #HPCombi::PPerm16 operator ()std::hash445 size_t operator()(const HPCombi::PPerm16 &ar) const { 446 return std::hash<HPCombi::epu8>{}(ar.v); 447 } 448 }; 449 450 template <> struct hash<HPCombi::Perm16> { 451 //! A hash operator for #HPCombi::Perm16 operator ()std::hash452 size_t operator()(const HPCombi::Perm16 &ar) const { 453 return uint64_t(ar); 454 } 455 }; 456 457 } // namespace std 458 459 #endif // HPCOMBI_PERM16_HPP_INCLUDED 460