Molassembler  3.0.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 
13 #include <algorithm>
14 #include <tuple>
15 #include <stdexcept>
16 
17 namespace Scine {
18 namespace Molassembler {
19 namespace Temple {
20 
29 template<class Container>
30 constexpr std::size_t permutationIndex(const Container& container) {
31  const std::size_t size = container.size();
32 
33  std::size_t index = 0;
34  std::size_t position = 2;// position 1 is paired with factor 0 and so is skipped
35  std::size_t factor = 1;
36 
37  for(std::size_t p = size - 2; p != std::numeric_limits<std::size_t>::max(); --p) {
38  std::size_t largerSuccessors = 0;
39 
40  for(std::size_t q = p + 1; q < size; ++q) {
41  if(container[q] < container[p]) {
42  ++largerSuccessors;
43  }
44  }
45 
46  index += (largerSuccessors * factor);
47  factor *= position;
48  ++position;
49  }
50 
51  return index;
52 }
53 
58 template<typename Container>
59 constexpr void inPlaceSwap(
60  Container& data,
61  const std::size_t a,
62  const std::size_t b
63 ) {
64  auto intermediate = std::move(data.at(b));
65  data.at(b) = std::move(data.at(a));
66  data.at(a) = std::move(intermediate);
67 }
68 
73 template<typename Container>
74 constexpr void inPlaceReverse(
75  Container& data,
76  const std::size_t indexFrom,
77  const std::size_t indexTo
78 ) {
79  std::size_t a = indexFrom;
80  std::size_t b = indexTo;
81  while(a != b && a != --b) {
82  inPlaceSwap(data, a++, b);
83  }
84 }
85 
94 template<typename Container>
95 constexpr bool inPlaceNextPermutation(
96  Container& data,
97  const std::size_t first,
98  const std::size_t last
99 ) {
100  std::size_t size = data.size();
101  if(!(
102  first < last
103  && first < size
104  && last <= size
105  )) {
106  throw "Call parameters to inPlaceNextPermutation make no sense!";
107  }
108 
109  std::size_t i = last - 1;
110  std::size_t j = 0;
111  std::size_t k = 0;
112 
113  while(true) {
114  j = i;
115 
116  if(
117  i != 0
118  && data.at(--i) < data.at(j)
119  ) {
120  k = last;
121 
122  while(
123  k != 0
124  && !(data.at(i) < data.at(--k))
125  ) {}
126 
127  inPlaceSwap(data, i, k);
128  inPlaceReverse(data, j, last);
129  return true;
130  }
131 
132  if(i == first) {
133  inPlaceReverse(data, first, last);
134  return false;
135  }
136  }
137 }
138 
140 template<typename Container>
141 constexpr bool inPlaceNextPermutation(Container& data) {
142  return inPlaceNextPermutation(data, 0, data.size());
143 }
144 
153 template<typename Container>
155  Container& data,
156  const std::size_t first,
157  const std::size_t last
158 ) {
159  unsigned size = data.size();
160  if(!(
161  first < last
162  && first < size
163  && last <= size
164  )) {
165  throw "Call parameters to inPlaceNextPermutation make no sense!";
166  }
167 
168  std::size_t i = last - 1;
169  std::size_t j = 0;
170  std::size_t k = 0;
171 
172  while(true) {
173  j = i;
174 
175  if(
176  i != 0
177  && data.at(j) < data.at(--i)
178  ) {
179  k = last;
180 
181  while(
182  k != 0
183  && !(data.at(--k) < data.at(i))
184  ) {}
185 
186  inPlaceSwap(data, i, k);
187  inPlaceReverse(data, j, last);
188  return true;
189  }
190 
191  if(i == first) {
192  inPlaceReverse(data, first, last);
193  return false;
194  }
195  }
196 }
197 
199 template<typename Container>
200 constexpr bool inPlacePreviousPermutation(Container& data) {
201  return inPlacePreviousPermutation(data, 0, data.size());
202 }
203 
204 
206 template<class Container>
207 bool next_permutation(Container& container) {
208  return std::next_permutation(
209  std::begin(container),
210  std::end(container)
211  );
212 }
213 
215 template<class Container>
216 bool prev_permutation(Container& container) {
217  return std::prev_permutation(
218  std::begin(container),
219  std::end(container)
220  );
221 }
222 
238 template<class Container>
240  Container& toPermute,
241  const Container& limits
242 ) {
243  assert(toPermute.size() == limits.size());
244  const unsigned cols = toPermute.size();
245 
246  // Check if all columns are full
247  bool allFull = true;
248  for(unsigned i = 0; i < cols; ++i) {
249  if(toPermute[i] != limits[i]) {
250  allFull = false;
251  break;
252  }
253  }
254 
255  if(allFull) {
256  return false;
257  }
258 
259  // Make next permutation
260  for(int i = cols - 1; i >= 0; --i) {
261  if(toPermute[i] == limits[i]) {
262  toPermute[i] = 0;
263  } else {
264  ++toPermute[i];
265  return true;
266  }
267  }
268 
269  return true;
270 }
271 
287 struct Permutation : public Crtp::LexicographicComparable<Permutation> {
288  using Sigma = std::vector<unsigned>;
289 
290  Permutation() = default;
291 
292  // Construct from data (unchecked)
293  explicit Permutation(Sigma p) : sigma(std::move(p)) {}
294 
295  template<typename T, std::enable_if_t<std::is_convertible<T, unsigned>::value, int>* = nullptr>
296  explicit Permutation(std::initializer_list<T> vs) {
297  sigma.reserve(vs.size());
298  for(auto v : vs) {
299  sigma.push_back(v);
300  }
301  }
302 
304  Permutation(const std::size_t N, std::size_t i) : sigma(N) {
305  std::vector<std::size_t> factorials(N);
306  factorials.at(0) = 1;
307  for(std::size_t k = 1; k < N; ++k) {
308  factorials.at(k) = factorials.at(k - 1) * k;
309  }
310 
311  for(std::size_t k = 0; k < N; ++k) {
312  const std::size_t fac = factorials.at(N - 1 - k);
313  sigma.at(k) = i / fac;
314  i %= fac;
315  }
316 
317  for(int k = static_cast<int>(N) - 1; k > 0; --k) {
318  for(int j = k - 1; j >= 0; --j) {
319  if(sigma.at(j) <= sigma.at(k)) {
320  ++sigma.at(k);
321  }
322  }
323  }
324  }
325 
327  static Permutation identity(unsigned N) {
328  Sigma sigma;
329  sigma.resize(N);
330  for(unsigned i = 0; i < N; ++i) {
331  sigma.at(i) = i;
332  }
333  return Permutation {std::move(sigma)};
334  }
335 
337  template<typename Container>
338  static std::enable_if_t<!std::is_same<Container, Sigma>::value, Permutation> from(const Container& p) {
339  static_assert(
340  std::is_convertible<Traits::getValueType<Container>, unsigned>::value,
341  "Value type of container must be convertible to unsigned!"
342  );
343  Sigma sigma;
344  for(auto v : p) {
345  sigma.emplace_back(static_cast<unsigned>(v));
346  }
347  return Permutation {std::move(sigma)};
348  }
349 
350  template<typename Container>
351  static std::enable_if_t<std::is_same<std::decay_t<Container>, Sigma>::value, Permutation> from(Container&& p) {
352  return Permutation {std::forward<Container>(p)};
353  }
354 
356  template<typename OtherContainer>
357  static Permutation ordering(const OtherContainer& unordered) {
358  Permutation p = Permutation::identity(unordered.size());
359  std::sort(
360  std::begin(p.sigma),
361  std::end(p.sigma),
362  [&](const auto i, const auto j) {
363  return unordered.at(i) < unordered.at(j);
364  }
365  );
366  return p.inverse();
367  }
368 
369  bool next() {
371  }
372 
373  bool prev() {
375  }
376 
377  unsigned index() const {
378  return permutationIndex(sigma);
379  }
380 
381  unsigned indexOf(unsigned v) const {
382  const auto begin = std::begin(sigma);
383  const auto end = std::end(sigma);
384  const auto findIter = std::find(begin, end, v);
385 
386  if(findIter == end) {
387  throw std::out_of_range("Item not found");
388  }
389 
390  return findIter - begin;
391  }
392 
393  Permutation inverse() const {
394  const unsigned size = sigma.size();
395  Sigma inv;
396  inv.resize(size);
397  for(unsigned i = 0; i < size; ++i) {
398  inv.at(sigma.at(i)) = i;
399  }
400  return Permutation {inv};
401  }
402 
403  auto at(const unsigned i) const -> decltype(auto) {
404  return sigma.at(i);
405  }
406 
407  auto operator() (const unsigned i) const -> decltype(auto) {
408  return sigma.at(i);
409  }
410 
411  void push() {
412  sigma.push_back(sigma.size());
413  }
414 
415  void clear() {
416  sigma.clear();
417  }
418 
419  unsigned size() const {
420  return sigma.size();
421  }
422 
430  template<typename OtherContainer>
431  OtherContainer apply(const OtherContainer& other) const {
432  const unsigned otherSize = other.size();
433  if(otherSize > size()) {
434  throw std::invalid_argument("Container of elements to apply permutation to larger than permutation itself");
435  }
436  OtherContainer result(otherSize);
437  for(unsigned i = 0; i < otherSize; ++i) {
438  result.at(sigma.at(i)) = other.at(i);
439  }
440  return result;
441  }
442 
448  Permutation compose(const Permutation& other) const {
449  if(size() != other.size()) {
450  throw std::invalid_argument("Mismatched permutation sizes!");
451  }
452  return Permutation::from(inverse().apply(other.sigma));
453  }
454 
455  auto tie() const {
456  return std::tie(sigma);
457  }
458 
460  Sigma sigma;
461 };
462 
463 } // namespace Temple
464 } // namespace Molassembler
465 } // namespace Scine
466 
467 #endif
void sort(Container &container)
Calls std::sort on a container.
Definition: Functional.h:261
bool next_permutation(Container &container)
Calls std::next_permutation.
Definition: Permutations.h:207
static Permutation ordering(const OtherContainer &unordered)
Discover on ordering permutation of an unordered container.
Definition: Permutations.h:357
Container-abstracted permutation.
Definition: Permutations.h:287
bool nextCombinationPermutation(Container &toPermute, const Container &limits)
For when you have to implement variable-depth for loops, each with different limits.
Definition: Permutations.h:239
Generates all operators using a method returning a tuple.
Definition: OperatorSuppliers.h:78
Operator-supplying CRTP base classes.
static std::enable_if_t<!std::is_same< Container, Sigma >::value, Permutation > from(const Container &p)
Construct a permutation from data (unchecked!)
Definition: Permutations.h:338
constexpr std::size_t permutationIndex(const Container &container)
Calculate the index of permutation of elements in a container.
Definition: Permutations.h:30
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:74
Sigma sigma
The permutation in &#39;one-line&#39; representation.
Definition: Permutations.h:460
Permutation compose(const Permutation &other) const
Compose two permutations.
Definition: Permutations.h:448
bool prev_permutation(Container &container)
Calls std::prev_permutation.
Definition: Permutations.h:216
static Permutation identity(unsigned N)
Construct the identity permutation of size N.
Definition: Permutations.h:327
constexpr bool inPlaceNextPermutation(Container &data, const std::size_t first, const std::size_t last)
In-place next permutation.
Definition: Permutations.h:95
Provides a few basic function and container traits.
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:59
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:154
Permutation(const std::size_t N, std::size_t i)
Construct the i-th permutation of size N.
Definition: Permutations.h:304
auto find(const Container &container, const T &needle)
std::find shorthand
Definition: Functional.h:346