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