#ifndef PYTHONIC_ITERTOOLS_PERMUTATIONS_HPP #define PYTHONIC_ITERTOOLS_PERMUTATIONS_HPP #include "pythonic/include/itertools/permutations.hpp" #include "pythonic/utils/functor.hpp" #include "pythonic/types/dynamic_tuple.hpp" #include "pythonic/builtins/range.hpp" #include PYTHONIC_NS_BEGIN namespace itertools { template permutations_iterator::permutations_iterator() { } template permutations_iterator::permutations_iterator( std::vector const &iter, size_t num_elts, bool end) : pool(iter), curr_permut(pool.size()), _size(num_elts), end(end) { std::iota(curr_permut.begin(), curr_permut.end(), 0); if (num_elts > iter.size()) { end = true; } } template types::dynamic_tuple permutations_iterator:: operator*() const { std::vector res(_size); for (size_t i = 0; i < _size; i++) res[i] = pool[curr_permut[i]]; // Ok because types::dynamic_tuple is // indeed a vector return {res.begin(), res.end()}; } template permutations_iterator &permutations_iterator::operator++() { if (_size != pool.size()) { // Slow path, the iterator is a "view" of a prefix smaller // than the the pool size // FIXME a better implementation would be to avoid // std::next_permutation, but only in the slow path types::dynamic_tuple prev_permut(curr_permut.begin(), curr_permut.begin() + _size); while ((end = std::next_permutation(curr_permut.begin(), curr_permut.end()))) { // Check if the prefix of the new permutation is // different of the previous one types::dynamic_tuple new_permut(curr_permut.begin(), curr_permut.begin() + _size); if (!(prev_permut == new_permut)) break; } } else end = std::next_permutation(curr_permut.begin(), curr_permut.end()); return *this; } template bool permutations_iterator:: operator!=(permutations_iterator const &other) const { return !(*this == other); } template bool permutations_iterator:: operator==(permutations_iterator const &other) const { if (other.end != end) return false; return std::equal(curr_permut.begin(), curr_permut.end(), other.curr_permut.begin()); } template bool permutations_iterator:: operator<(permutations_iterator const &other) const { if (end != other.end) return end > other.end; for (long i = 0; i < pool.size(); i++) if (other.curr_permut[i] < curr_permut[i]) return false; else if (other.curr_permut[i] > curr_permut[i]) return true; return false; } template _permutations::_permutations() { } template _permutations::_permutations(T iter, long elts) : iterator(std::vector(iter.begin(), iter.end()), elts, true) { } template typename _permutations::iterator const &_permutations::begin() const { return *this; } template typename _permutations::iterator _permutations::begin() { return *this; } template typename _permutations::iterator _permutations::end() const { return iterator(iterator::pool, iterator::_size, false); } template _permutations permutations(T0 iter, long num_elts) { return _permutations(iter, num_elts); } template _permutations permutations(T0 iter) { return _permutations(iter, std::distance(iter.begin(), iter.end())); } } PYTHONIC_NS_END #endif