/// \file // Range v3 library // // Copyright Eric Niebler 2014 // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // //===-------------------------- algorithm ---------------------------------===// // // The LLVM Compiler Infrastructure // // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef RANGES_V3_ALGORITHM_PERMUTATION_HPP #define RANGES_V3_ALGORITHM_PERMUTATION_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { inline namespace v3 { /// \ingroup group-concepts template using IsPermutationable = meta::strict_and< ForwardIterator, ForwardIterator, Comparable>; /// \addtogroup group-algorithms /// @{ struct is_permutation_fn { private: template static bool four_iter_impl(I1 begin1, S1 end1, I2 begin2, S2 end2, C pred, P1 proj1, P2 proj2) { // shorten sequences as much as possible by lopping off any equal parts for(; begin1 != end1 && begin2 != end2; ++begin1, ++begin2) if(!invoke(pred, invoke(proj1, *begin1), invoke(proj2, *begin2))) goto not_done; return begin1 == end1 && begin2 == end2; not_done: // begin1 != end1 && begin2 != end2 && *begin1 != *begin2 auto l1 = distance(begin1, end1); auto l2 = distance(begin2, end2); if(l1 != l2) return false; // For each element in [f1, l1) see if there are the same number of // equal elements in [f2, l2) for(I1 i = begin1; i != end1; ++i) { // Have we already counted the number of *i in [f1, l1)? for(I1 j = begin1; j != i; ++j) if(invoke(pred, invoke(proj1, *j), invoke(proj1, *i))) goto next_iter; { // Count number of *i in [f2, l2) difference_type_t c2 = 0; for(I2 j = begin2; j != end2; ++j) if(invoke(pred, invoke(proj1, *i), invoke(proj2, *j))) ++c2; if(c2 == 0) return false; // Count number of *i in [i, l1) (we can start with 1) difference_type_t c1 = 1; for(I1 j = next(i); j != end1; ++j) if(invoke(pred, invoke(proj1, *i), invoke(proj1, *j))) ++c1; if(c1 != c2) return false; } next_iter:; } return true; } public: template() && IsPermutationable())> bool operator()(I1 begin1, S1 end1, I2 begin2, C pred = C{}, P1 proj1 = P1{}, P2 proj2 = P2{}) const { // shorten sequences as much as possible by lopping off any equal parts for(; begin1 != end1; ++begin1, ++begin2) if(!invoke(pred, invoke(proj1, *begin1), invoke(proj2, *begin2))) goto not_done; return true; not_done: // begin1 != end1 && *begin1 != *begin2 auto l1 = distance(begin1, end1); if(l1 == 1) return false; I2 end2 = next(begin2, l1); // For each element in [f1, l1) see if there are the same number of // equal elements in [f2, l2) for(I1 i = begin1; i != end1; ++i) { // Have we already counted the number of *i in [f1, l1)? for(I1 j = begin1; j != i; ++j) if(invoke(pred, invoke(proj1, *j), invoke(proj1, *i))) goto next_iter; { // Count number of *i in [f2, l2) difference_type_t c2 = 0; for(I2 j = begin2; j != end2; ++j) if(invoke(pred, invoke(proj1, *i), invoke(proj2, *j))) ++c2; if(c2 == 0) return false; // Count number of *i in [i, l1) (we can start with 1) difference_type_t c1 = 1; for(I1 j = next(i); j != end1; ++j) if(invoke(pred, invoke(proj1, *i), invoke(proj1, *j))) ++c1; if(c1 != c2) return false; } next_iter:; } return true; } template() && Sentinel() && IsPermutationable())> bool operator()(I1 begin1, S1 end1, I2 begin2, S2 end2, C pred = C{}, P1 proj1 = P1{}, P2 proj2 = P2{}) const { if(SizedSentinel() && SizedSentinel()) return distance(begin1, end1) == distance(begin2, end2) && (*this)(std::move(begin1), std::move(end1), std::move(begin2), std::move(pred), std::move(proj1), std::move(proj2)); return is_permutation_fn::four_iter_impl(std::move(begin1), std::move(end1), std::move(begin2), std::move(end2), std::move(pred), std::move(proj1), std::move(proj2)); } template, typename I2 = uncvref_t, CONCEPT_REQUIRES_(ForwardRange() && Iterator() && IsPermutationable())> bool operator()(Rng1 &&rng1, I2Ref &&begin2, C pred = C{}, P1 proj1 = P1{}, P2 proj2 = P2{}) const { return (*this)(begin(rng1), end(rng1), (I2Ref &&) begin2, std::move(pred), std::move(proj1), std::move(proj2)); } template, typename I2 = iterator_t, CONCEPT_REQUIRES_(ForwardRange() && ForwardRange() && IsPermutationable())> bool operator()(Rng1 &&rng1, Rng2 &&rng2, C pred = C{}, P1 proj1 = P1{}, P2 proj2 = P2{}) const { if(SizedRange() && SizedRange()) return distance(rng1) == distance(rng2) && (*this)(begin(rng1), end(rng1), begin(rng2), std::move(pred), std::move(proj1), std::move(proj2)); return is_permutation_fn::four_iter_impl(begin(rng1), end(rng1), begin(rng2), end(rng2), std::move(pred), std::move(proj1), std::move(proj2)); } }; /// \sa `is_permutation_fn` /// \ingroup group-algorithms RANGES_INLINE_VARIABLE(with_braced_init_args, is_permutation) struct next_permutation_fn { template() && Sentinel() && Sortable())> bool operator()(I begin, S end_, C pred = C{}, P proj = P{}) const { if(begin == end_) return false; I end = ranges::next(begin, end_), i = end; if(begin == --i) return false; while(true) { I ip1 = i; if(invoke(pred, invoke(proj, *--i), invoke(proj, *ip1))) { I j = end; while(!invoke(pred, invoke(proj, *i), invoke(proj, *--j))) ; ranges::iter_swap(i, j); ranges::reverse(ip1, end); return true; } if(i == begin) { ranges::reverse(begin, end); return false; } } } template, CONCEPT_REQUIRES_(BidirectionalRange() && Sortable())> bool operator()(Rng &&rng, C pred = C{}, P proj = P{}) const { return (*this)(begin(rng), end(rng), std::move(pred), std::move(proj)); } }; /// \sa `next_permutation_fn` /// \ingroup group-algorithms RANGES_INLINE_VARIABLE(with_braced_init_args, next_permutation) struct prev_permutation_fn { template() && Sentinel() && Sortable())> bool operator()(I begin, S end_, C pred = C{}, P proj = P{}) const { if(begin == end_) return false; I end = ranges::next(begin, end_), i = end; if(begin == --i) return false; while(true) { I ip1 = i; if(invoke(pred, invoke(proj, *ip1), invoke(proj, *--i))) { I j = end; while(!invoke(pred, invoke(proj, *--j), invoke(proj, *i))) ; ranges::iter_swap(i, j); ranges::reverse(ip1, end); return true; } if(i == begin) { ranges::reverse(begin, end); return false; } } } template, CONCEPT_REQUIRES_(BidirectionalRange() && Sortable())> bool operator()(Rng &&rng, C pred = C{}, P proj = P{}) const { return (*this)(begin(rng), end(rng), std::move(pred), std::move(proj)); } }; /// \sa `prev_permutation_fn` /// \ingroup group-algorithms RANGES_INLINE_VARIABLE(with_braced_init_args, prev_permutation) /// @} } // namespace v3 } // namespace ranges #endif // include guard