Molassembler  1.1.0
Molecule graph and conformer library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Permutations.h
Go to the documentation of this file.
1 
8 #ifndef INCLUDE_MOLASSEMBLER_TEMPLE_PERMUTATIONS_H
9 #define INCLUDE_MOLASSEMBLER_TEMPLE_PERMUTATIONS_H
10 
11 #include <algorithm>
12 #include <stdexcept>
13 
14 namespace Scine {
15 namespace Molassembler {
16 namespace Temple {
17 
26 template<class Container>
27 constexpr std::size_t permutationIndex(const Container& container) {
28  const std::size_t size = container.size();
29 
30  std::size_t index = 0;
31  std::size_t position = 2;// position 1 is paired with factor 0 and so is skipped
32  std::size_t factor = 1;
33 
34  for(std::size_t p = size - 2; p != std::numeric_limits<std::size_t>::max(); --p) {
35  std::size_t largerSuccessors = 0;
36 
37  for(std::size_t q = p + 1; q < size; ++q) {
38  if(container[q] < container[p]) {
39  ++largerSuccessors;
40  }
41  }
42 
43  index += (largerSuccessors * factor);
44  factor *= position;
45  ++position;
46  }
47 
48  return index;
49 }
50 
55 template<typename Container>
56 constexpr void inPlaceSwap(
57  Container& data,
58  const std::size_t a,
59  const std::size_t b
60 ) {
61  auto intermediate = std::move(data.at(b));
62  data.at(b) = std::move(data.at(a));
63  data.at(a) = std::move(intermediate);
64 }
65 
70 template<typename Container>
71 constexpr void inPlaceReverse(
72  Container& data,
73  const std::size_t indexFrom,
74  const std::size_t indexTo
75 ) {
76  std::size_t a = indexFrom;
77  std::size_t b = indexTo;
78  while(a != b && a != --b) {
79  inPlaceSwap(data, a++, b);
80  }
81 }
82 
91 template<typename Container>
92 constexpr bool inPlaceNextPermutation(
93  Container& data,
94  const std::size_t first,
95  const std::size_t last
96 ) {
97  std::size_t size = data.size();
98  if(!(
99  first < last
100  && first < size
101  && last <= size
102  )) {
103  throw "Call parameters to inPlaceNextPermutation make no sense!";
104  }
105 
106  std::size_t i = last - 1;
107  std::size_t j = 0;
108  std::size_t k = 0;
109 
110  while(true) {
111  j = i;
112 
113  if(
114  i != 0
115  && data.at(--i) < data.at(j)
116  ) {
117  k = last;
118 
119  while(
120  k != 0
121  && !(data.at(i) < data.at(--k))
122  ) {}
123 
124  inPlaceSwap(data, i, k);
125  inPlaceReverse(data, j, last);
126  return true;
127  }
128 
129  if(i == first) {
130  inPlaceReverse(data, first, last);
131  return false;
132  }
133  }
134 }
135 
137 template<typename Container>
138 constexpr bool inPlaceNextPermutation(Container& data) {
139  return inPlaceNextPermutation(data, 0, data.size());
140 }
141 
150 template<typename Container>
152  Container& data,
153  const std::size_t first,
154  const std::size_t last
155 ) {
156  unsigned size = data.size();
157  if(!(
158  first < last
159  && first < size
160  && last <= size
161  )) {
162  throw "Call parameters to inPlaceNextPermutation make no sense!";
163  }
164 
165  std::size_t i = last - 1;
166  std::size_t j = 0;
167  std::size_t k = 0;
168 
169  while(true) {
170  j = i;
171 
172  if(
173  i != 0
174  && data.at(j) < data.at(--i)
175  ) {
176  k = last;
177 
178  while(
179  k != 0
180  && !(data.at(--k) < data.at(i))
181  ) {}
182 
183  inPlaceSwap(data, i, k);
184  inPlaceReverse(data, j, last);
185  return true;
186  }
187 
188  if(i == first) {
189  inPlaceReverse(data, first, last);
190  return false;
191  }
192  }
193 }
194 
196 template<typename Container>
197 constexpr bool inPlacePreviousPermutation(Container& data) {
198  return inPlacePreviousPermutation(data, 0, data.size());
199 }
200 
201 
203 template<class Container>
204 bool next_permutation(Container& container) {
205  return std::next_permutation(
206  std::begin(container),
207  std::end(container)
208  );
209 }
210 
212 template<class Container>
213 bool prev_permutation(Container& container) {
214  return std::prev_permutation(
215  std::begin(container),
216  std::end(container)
217  );
218 }
219 
235 template<class Container>
237  Container& toPermute,
238  const Container& limits
239 ) {
240  assert(toPermute.size() == limits.size());
241  const unsigned cols = toPermute.size();
242 
243  // Check if all columns are full
244  bool allFull = true;
245  for(unsigned i = 0; i < cols; ++i) {
246  if(toPermute[i] != limits[i]) {
247  allFull = false;
248  break;
249  }
250  }
251 
252  if(allFull) {
253  return false;
254  }
255 
256  // Make next permutation
257  for(int i = cols - 1; i >= 0; --i) {
258  if(toPermute[i] == limits[i]) {
259  toPermute[i] = 0;
260  } else {
261  ++toPermute[i];
262  return true;
263  }
264  }
265 
266  return true;
267 }
268 
284 template<typename Container>
285 struct Permutation {
286  using T = std::decay_t<decltype(std::declval<Container>().at(0))>;
287  static_assert(
288  std::is_convertible<T, unsigned>::value,
289  "Permutation container value type must be convertible to an integral type"
290  );
291 
292  constexpr Permutation() = default;
293 
295  constexpr explicit Permutation(unsigned N) : sigma(N) {
296  for(unsigned i = 0; i < N; ++i) {
297  sigma.at(i) = i;
298  }
299  }
300 
302  constexpr explicit Permutation(Container p) : sigma(std::move(p)) {}
303 
305  constexpr Permutation(const unsigned N, unsigned i) : sigma(N) {
306  Container factorials(N);
307  factorials.at(0) = 1;
308  for(unsigned k = 1; k < N; ++k) {
309  factorials.at(k) = factorials.at(k - 1) * k;
310  }
311 
312  for(unsigned k = 0; k < N; ++k) {
313  const unsigned fac = factorials.at(N - 1 - k);
314  sigma.at(k) = i / fac;
315  i %= fac;
316  }
317 
318  for(int k = static_cast<int>(N) - 1; k > 0; --k) {
319  for(int j = k - 1; j >= 0; --j) {
320  if(sigma.at(j) <= sigma.at(k)) {
321  ++sigma.at(k);
322  }
323  }
324  }
325  }
326 
328  template<typename OtherContainer>
329  static constexpr Permutation ordering(const OtherContainer& unordered) {
330  Permutation p(unordered.size());
331  std::sort(
332  std::begin(p.sigma),
333  std::end(p.sigma),
334  [&](const auto i, const auto j) {
335  return unordered.at(i) < unordered.at(j);
336  }
337  );
338  return p;
339  }
340 
341  constexpr bool next() {
342  return inPlaceNextPermutation(sigma);
343  }
344 
345  constexpr bool prev() {
346  return inPlacePreviousPermutation(sigma);
347  }
348 
349  constexpr std::size_t index() const {
350  return permutationIndex(sigma);
351  }
352 
353  constexpr Permutation inverse() const {
354  auto inverted = sigma;
355  const unsigned size = inverted.size();
356  for(unsigned i = 0; i < size; ++i) {
357  inverted.at(sigma.at(i)) = i;
358  }
359  return Permutation(inverted);
360  }
361 
362  constexpr auto at(const std::size_t i) const -> decltype(auto) {
363  return sigma.at(i);
364  }
365 
366  constexpr auto operator() (const std::size_t i) const -> decltype(auto) {
367  return sigma.at(i);
368  }
369 
370  template<typename OtherContainer>
371  constexpr OtherContainer apply(const OtherContainer& other) const {
372  const unsigned size = other.size();
373  OtherContainer result(size);
374  for(std::size_t i = 0; i < size; ++i) {
375  result.at(i) = other.at(sigma.at(i));
376  }
377  return result;
378  }
379 
385  constexpr Permutation compose(const Permutation& other) const {
386  return Permutation {apply(other.sigma)};
387  }
388 
391 };
392 
393 template<typename Container>
394 Permutation<Container> make_permutation(Container p) {
395  return Permutation<Container>(std::move(p));
396 }
397 
398 } // namespace Temple
399 } // namespace Molassembler
400 } // namespace Scine
401 
402 #endif
void sort(Container &container)
Calls std::sort on a container.
Definition: Functional.h:261
constexpr Permutation(unsigned N)
Construct the identity permutation of size N.
Definition: Permutations.h:295
bool next_permutation(Container &container)
Calls std::next_permutation.
Definition: Permutations.h:204
Constexpr-compatible container-abstracted permutation.
Definition: Permutations.h:285
bool nextCombinationPermutation(Container &toPermute, const Container &limits)
For when you have to implement variable-depth for loops, each with different limits.
Definition: Permutations.h:236
auto at(Container &&container)
Make functor calling at on arguments.
Definition: Functor.h:58
constexpr Permutation(const unsigned N, unsigned i)
Construct the i-th permutation of size N.
Definition: Permutations.h:305
constexpr Permutation compose(const Permutation &other) const
Compose two permutations.
Definition: Permutations.h:385
constexpr std::size_t permutationIndex(const Container &container)
Calculate the index of permutation of elements in a container.
Definition: Permutations.h:27
constexpr void inPlaceReverse(Container &data, const std::size_t indexFrom, const std::size_t indexTo)
Index-based in-place reversal of elements in an array-like container.
Definition: Permutations.h:71
unsigned size(Shape shape)
Fetch the number of vertices of a shape.
Container sigma
The permutation in &#39;one-line&#39; representation.
Definition: Permutations.h:390
static constexpr Permutation ordering(const OtherContainer &unordered)
Discover on ordering permutation of an unordered container.
Definition: Permutations.h:329
bool prev_permutation(Container &container)
Calls std::prev_permutation.
Definition: Permutations.h:213
constexpr Permutation(Container p)
Construct a permutation from data.
Definition: Permutations.h:302
std::vector< Vertex > Permutation
Representation of a shape vertex permutation.
Definition: Data.h:35
constexpr bool inPlaceNextPermutation(Container &data, const std::size_t first, const std::size_t last)
In-place next permutation.
Definition: Permutations.h:92
constexpr void inPlaceSwap(Container &data, const std::size_t a, const std::size_t b)
Index-based in-place swapping of elements in an array-like container.
Definition: Permutations.h:56
constexpr Get< 0 > first
Calls std::get&lt;0&gt; on any argument it is invoked with.
Definition: Functor.h:123
constexpr bool inPlacePreviousPermutation(Container &data, const std::size_t first, const std::size_t last)
In-place previous permutation.
Definition: Permutations.h:151