From 1ed284d6961505a1d57d94b4ab07fdb2b0eff2e5 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Thu, 18 May 2017 20:38:07 -0600 Subject: [PATCH 1/7] Added initial for apply_permutation --- example/apply_permutation_example.cpp | 68 ++++++++++ include/boost/algorithm/apply_permutation.hpp | 122 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 example/apply_permutation_example.cpp create mode 100644 include/boost/algorithm/apply_permutation.hpp diff --git a/example/apply_permutation_example.cpp b/example/apply_permutation_example.cpp new file mode 100644 index 0000000..feaa9f2 --- /dev/null +++ b/example/apply_permutation_example.cpp @@ -0,0 +1,68 @@ +/* + Copyright (c) Alexander Zaitsev , 2017 + + Distributed under 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) + + See http://www.boost.org/ for latest version. +*/ + +#include +#include + +#include + + +namespace ba = boost::algorithm; + +int main ( int /*argc*/, char * /*argv*/ [] ) +{ + { + std::cout << "apply_permutation with iterators:\n"; + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + for (const auto& x : vec) + { + std::cout << x << ", "; + } + std::cout << std::endl; + } + { + std::cout << "apply_reverse_permutation with iterators:\n"; + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + for (const auto& x : vec) + { + std::cout << x << ", "; + } + std::cout << std::endl; + } + { + std::cout << "apply_reverse_permutation with ranges:\n"; + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + + ba::apply_reverse_permutation(vec, order); + for (const auto& x : vec) + { + std::cout << x << ", "; + } + std::cout << std::endl; + } + { + std::cout << "apply_permutation with ranges:\n"; + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + + ba::apply_permutation(vec, order); + for (const auto& x : vec) + { + std::cout << x << ", "; + } + std::cout << std::endl; + } + + return 0; +} + diff --git a/include/boost/algorithm/apply_permutation.hpp b/include/boost/algorithm/apply_permutation.hpp new file mode 100644 index 0000000..f14ba65 --- /dev/null +++ b/include/boost/algorithm/apply_permutation.hpp @@ -0,0 +1,122 @@ +/* + Copyright (c) Alexander Zaitsev , 2017 + + Distributed under 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) + + See http://www.boost.org/ for latest version. + + + Based on https://blogs.msdn.microsoft.com/oldnewthing/20170104-00/?p=95115 +*/ + +/// \file apply_permutation.hpp +/// \brief Apply permutation to a sequence. +/// \author Alexander Zaitsev + +#ifndef BOOST_ALGORITHM_APPLY_PERMUTATION_HPP +#define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP + +#include +#include + +#include +#include + +namespace boost { namespace algorithm +{ + +/// \fn apply_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin ) +/// \brief Reorder item sequence with index sequence order +/// +/// \param item_begin The start of the item sequence +/// \param item_end One past the end of the item sequence +/// \param ind_begin The start of the index sequence. +/// +/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined. +/// Complexity: O(N). +template +void +apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, + RandomAccessIterator2 ind_begin) +{ + using Diff = typename std::iterator_traits::difference_type; + Diff size = std::distance(item_begin, item_end); + for (Diff i = 0; i < size; i++) + { + auto current = i; + while (i != ind_begin[current]) + { + auto next = ind_begin[current]; + std::swap(item_begin[current], item_begin[next]); + ind_begin[current] = current; + current = next; + } + ind_begin[current] = current; + } +} + +/// \fn apply_reverse_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin ) +/// \brief Reorder item sequence with index sequence order +/// +/// \param item_begin The start of the item sequence +/// \param item_end One past the end of the item sequence +/// \param ind_begin The start of the index sequence. +/// +/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined. +/// Complexity: O(N). +template +void +apply_reverse_permutation( + RandomAccessIterator1 item_begin, + RandomAccessIterator1 item_end, + RandomAccessIterator2 ind_begin) +{ + using Diff = typename std::iterator_traits::difference_type; + Diff length = std::distance(item_begin, item_end); + for (Diff i = 0; i < length; i++) + { + while (i != ind_begin[i]) + { + Diff next = ind_begin[i]; + std::swap(item_begin[i], item_begin[next]); + std::swap(ind_begin[i], ind_begin[next]); + } + } +} + +/// \fn apply_permutation ( Range1 item_range, Range2 ind_range ) +/// \brief Reorder item sequence with index sequence order +/// +/// \param item_range The item sequence +/// \param ind_range The index sequence +/// +/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined. +/// Complexity: O(N). +template +void +apply_permutation(Range1& item_range, Range2& ind_range) +{ + apply_permutation(boost::begin(item_range), boost::end(item_range), + boost::begin(ind_range)); +} + +/// \fn apply_reverse_permutation ( Range1 item_range, Range2 ind_range ) +/// \brief Reorder item sequence with index sequence order +/// +/// \param item_range The item sequence +/// \param ind_range The index sequence +/// +/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined. +/// Complexity: O(N). +template +void +apply_reverse_permutation(Range1& item_range, Range2& ind_range) +{ + apply_reverse_permutation(boost::begin(item_range), boost::end(item_range), + boost::begin(ind_range)); +} + +}} +#endif //BOOST_ALGORITHM_APPLY_PERMUTATION_HPP From 1970454ecbe0a5964a3b21552fcae6ecc27436a6 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Thu, 18 May 2017 23:33:26 -0600 Subject: [PATCH 2/7] Added test --- test/apply_permutation_test.cpp | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 test/apply_permutation_test.cpp diff --git a/test/apply_permutation_test.cpp b/test/apply_permutation_test.cpp new file mode 100644 index 0000000..4716520 --- /dev/null +++ b/test/apply_permutation_test.cpp @@ -0,0 +1,122 @@ +/* + Copyright (c) Alexander Zaitsev , 2017 + + Distributed under 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) + + See http://www.boost.org/ for latest version. +*/ + +#include +#include +#include +#include +#include + +#include + +#define BOOST_TEST_MAIN + +#include + +namespace ba = boost::algorithm; + + +void test_apply_permutation() +{ + //Empty + { + std::vector vec, order, result; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //1 element + { + std::vector vec{1}, order{0}, result{1}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //2 elements, no changes + { + std::vector vec{1, 2}, order{0, 1}, result{1, 2}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //2 elements, changed + { + std::vector vec{1, 2}, order{1, 0}, result{2, 1}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Multiple elements, no changes + { + std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Multiple elements, changed + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 3, 2, 1, 0}, result{5, 4, 3, 2, 1}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Just test range interface + { + std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + ba::apply_permutation(vec, order); + BOOST_CHECK(vec == result); + } +} + +void test_apply_reverse_permutation() +{ + //Empty + { + std::vector vec, order, result; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //1 element + { + std::vector vec{1}, order{0}, result{1}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //2 elements, no changes + { + std::vector vec{1, 2}, order{0, 1}, result{1, 2}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //2 elements, changed + { + std::vector vec{1, 2}, order{1, 0}, result{2, 1}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Multiple elements, no changes + { + std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Multiple elements, changed + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 3, 2, 1, 0}, result{5, 4, 3, 2, 1}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + BOOST_CHECK(vec == result); + } + //Just test range interface + { + std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + ba::apply_reverse_permutation(vec, order); + BOOST_CHECK(vec == result); + } +} + +BOOST_AUTO_TEST_CASE(test_main) +{ + test_apply_permutation(); + test_apply_reverse_permutation(); +} From ba7655eb3a7ac2cab16f6d573355fce4a497a933 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Fri, 19 May 2017 16:59:11 -0600 Subject: [PATCH 3/7] Add documentation --- doc/apply_permutation.qbk | 85 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 doc/apply_permutation.qbk diff --git a/doc/apply_permutation.qbk b/doc/apply_permutation.qbk new file mode 100644 index 0000000..952195d --- /dev/null +++ b/doc/apply_permutation.qbk @@ -0,0 +1,85 @@ +[/ File apply_permutation.qbk] + +[section:apply_permutation apply_permutation] + +[/license +Copyright (c) 2017 Alexander Zaitsev + +Distributed under 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) +] + +The header file 'apply_permutation.hpp' contains two algorithms, apply_permutation and apply_reverse_permutation. Also there are range-based versions. +The algorithms transform the item sequence according to index sequence order. + +The routine `apply_permutation` takes a item sequence and a order sequence. It reshuffles item sequence according to order sequence. Every value in order sequence means where the item comes from. +The routine `apply_reverse_permutation` takes a item sequence and a order sequence. It will reshuffle item sequence according to order sequence. Every value in order sequence means where the item goes to. + +The routines come in 2 forms; the first one takes two iterators to define the item range and one iterator to define the beginning of index range. The second form takes range to define the item sequence and range to define index sequence. + + +[heading interface] + +There are two versions of algorithms: +1) takes three iterators. +2) takes two ranges. +`` +template +void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, + RandomAccessIterator2 ind_begin); +template +void apply_permutation(Range1& item_range, Range2& ind_range); +template +void apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, + RandomAccessIterator2 ind_begin); +template +void apply_reverse_permutation(Range1& item_range, Range2& ind_range); +`` + + +[heading Examples] + +Given the containers: +std::vector emp_vec, emp_order, +std::vector one{1}, one_order{0}, +std::vector two{1,2}, two_order{1,0}, +std::vector vec{1, 2, 3, 4, 5}, +std::vector order{4, 2, 3, 1, 0}, then +`` + +apply_permutation(emp_vec, emp_order)) --> no changes +apply_reverse_permutation(emp_vec, emp_order)) --> no changes +apply_permutation(one, one_order) --> no changes +apply_reverse_permutation(one, one_order) --> no changes +apply_permutation(two, two_order) --> two:{2,1} +apply_reverse_permutation(two, two_order) --> two:{2,1} +apply_permutation(vec, order) --> vec:{5, 3, 4, 2, 1} +apply_reverse_permutation(vec, order) --> vec:{5, 4, 2, 3, 1} +`` + +[heading Iterator Requirements] + +`apply_permutation` and 'apply_reverse_permutation' work only on RandomAccess iterators. RandomAccess iterators required both for item and index sequences. + +[heading Complexity] + +All of the variants of `apply_permutation` and `apply_reverse_permutation` run in ['O(N)] (linear) time. + +[heading Exception Safety] + +All of the variants of `apply_permutation` and `apply_reverse_permutation` take their parameters by iterators or reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee. + +[heading Notes] +* If ItemSequence and IndexSequence are not equal, behavior is undefined. + +* `apply_permutation` and `apply_reverse_permutation` work also on empty sequences. + +* Order sequence must be zero-indexed. + +[endsect] + +[/ File apply_permutation.qbk +Copyright 2017 Alexander Zaitsev +Distributed under 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). +] From e07e8e65c680026aa9859a0fdc13751b0d18a6ed Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Fri, 19 May 2017 18:50:03 -0600 Subject: [PATCH 4/7] Fixed references --- doc/algorithm.qbk | 1 + example/Jamfile.v2 | 2 +- test/Jamfile.v2 | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/algorithm.qbk b/doc/algorithm.qbk index becad46..8ce6685 100644 --- a/doc/algorithm.qbk +++ b/doc/algorithm.qbk @@ -68,6 +68,7 @@ Thanks to all the people who have reviewed this library and made suggestions for [include hex.qbk] [include is_palindrome.qbk] [include is_partitioned_until.qbk] +[include apply_permutation.qbk] [endsect] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 4512a53..100878c 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -22,4 +22,4 @@ exe clamp_example : clamp_example.cpp ; exe search_example : search_example.cpp ; exe is_palindrome_example : is_palindrome_example.cpp; exe is_partitioned_until_example : is_partitioned_until_example.cpp; - +exe apply_permutation_example : apply_permutation_example.cpp; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index fad1578..0b5ae35 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -73,6 +73,9 @@ alias unit_test_framework # Is_partitioned_until tests [ run is_partitioned_until_test.cpp unit_test_framework : : : : is_partitioned_until_test ] + +# Apply_permutation tests + [ run apply_permutation_test.cpp unit_test_framework : : : : apply_permutation_test ] ; } From 15c7ff3828578cab37f2a4403c6f126fef90ef92 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Wed, 19 Jul 2017 02:15:06 +0300 Subject: [PATCH 5/7] Fixed compatibility with old compilers --- example/apply_permutation_example.cpp | 1 + include/boost/algorithm/apply_permutation.hpp | 18 ++--- test/apply_permutation_test.cpp | 79 +++++++++++++++---- 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/example/apply_permutation_example.cpp b/example/apply_permutation_example.cpp index feaa9f2..16c84b6 100644 --- a/example/apply_permutation_example.cpp +++ b/example/apply_permutation_example.cpp @@ -18,6 +18,7 @@ namespace ba = boost::algorithm; int main ( int /*argc*/, char * /*argv*/ [] ) { + // WARNING: Example require C++11 or newer compiler { std::cout << "apply_permutation with iterators:\n"; std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; diff --git a/include/boost/algorithm/apply_permutation.hpp b/include/boost/algorithm/apply_permutation.hpp index f14ba65..1f1cdcf 100644 --- a/include/boost/algorithm/apply_permutation.hpp +++ b/include/boost/algorithm/apply_permutation.hpp @@ -19,7 +19,7 @@ #define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP #include -#include +#include #include #include @@ -41,14 +41,14 @@ void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin) { - using Diff = typename std::iterator_traits::difference_type; + typedef typename std::iterator_traits::difference_type Diff; Diff size = std::distance(item_begin, item_end); - for (Diff i = 0; i < size; i++) + for (Diff i = 0; i < size; ++i) { - auto current = i; + Diff current = i; while (i != ind_begin[current]) { - auto next = ind_begin[current]; + Diff next = static_cast(ind_begin[current]); std::swap(item_begin[current], item_begin[next]); ind_begin[current] = current; current = next; @@ -68,14 +68,12 @@ apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_e /// Complexity: O(N). template void -apply_reverse_permutation( - RandomAccessIterator1 item_begin, - RandomAccessIterator1 item_end, +apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin) { - using Diff = typename std::iterator_traits::difference_type; + typedef typename std::iterator_traits::difference_type Diff; Diff length = std::distance(item_begin, item_end); - for (Diff i = 0; i < length; i++) + for (Diff i = 0; i < length; ++i) { while (i != ind_begin[i]) { diff --git a/test/apply_permutation_test.cpp b/test/apply_permutation_test.cpp index 4716520..4dc08e7 100644 --- a/test/apply_permutation_test.cpp +++ b/test/apply_permutation_test.cpp @@ -9,13 +9,10 @@ */ #include -#include -#include -#include -#include #include +#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include @@ -28,42 +25,67 @@ void test_apply_permutation() //Empty { std::vector vec, order, result; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //1 element { - std::vector vec{1}, order{0}, result{1}; + std::vector vec, order, result; + vec.push_back(1); + order.push_back(0); + result = vec; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //2 elements, no changes { - std::vector vec{1, 2}, order{0, 1}, result{1, 2}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); + order.push_back(0); order.push_back(1); + result = vec; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //2 elements, changed { - std::vector vec{1, 2}, order{1, 0}, result{2, 1}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); + order.push_back(1); order.push_back(0); + result.push_back(2); result.push_back(1); + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Multiple elements, no changes { - std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); + result = vec; + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Multiple elements, changed { - std::vector vec{1, 2, 3, 4, 5}, order{4, 3, 2, 1, 0}, result{5, 4, 3, 2, 1}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0); + result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1); + ba::apply_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Just test range interface { - std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); + result = vec; + ba::apply_permutation(vec, order); BOOST_CHECK(vec == result); } @@ -74,42 +96,67 @@ void test_apply_reverse_permutation() //Empty { std::vector vec, order, result; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //1 element { - std::vector vec{1}, order{0}, result{1}; + std::vector vec, order, result; + vec.push_back(1); + order.push_back(0); + result = vec; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //2 elements, no changes { - std::vector vec{1, 2}, order{0, 1}, result{1, 2}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); + order.push_back(0); order.push_back(1); + result = vec; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //2 elements, changed { - std::vector vec{1, 2}, order{1, 0}, result{2, 1}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); + order.push_back(1); order.push_back(0); + result.push_back(2); result.push_back(1); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Multiple elements, no changes { - std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); + result = vec; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Multiple elements, changed { - std::vector vec{1, 2, 3, 4, 5}, order{4, 3, 2, 1, 0}, result{5, 4, 3, 2, 1}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0); + result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); BOOST_CHECK(vec == result); } //Just test range interface { - std::vector vec{1, 2, 3, 4, 5}, order{0, 1, 2, 3, 4}, result{1, 2, 3, 4, 5}; + std::vector vec, order, result; + vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); + order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); + result = vec; + ba::apply_reverse_permutation(vec, order); BOOST_CHECK(vec == result); } From 32016b3c79a9c0c36724e56831964f603b85d398 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Wed, 19 Jul 2017 22:10:29 +0300 Subject: [PATCH 6/7] Fixed missed header for std::swap --- include/boost/algorithm/apply_permutation.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/algorithm/apply_permutation.hpp b/include/boost/algorithm/apply_permutation.hpp index 1f1cdcf..d4aa958 100644 --- a/include/boost/algorithm/apply_permutation.hpp +++ b/include/boost/algorithm/apply_permutation.hpp @@ -19,6 +19,7 @@ #define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP #include +#include #include #include From 7df35ffd5676cebf2ac4f0ac51dd4107087c3925 Mon Sep 17 00:00:00 2001 From: Alexander Zaitsev Date: Wed, 30 Aug 2017 20:25:15 +0300 Subject: [PATCH 7/7] Different fixes --- doc/apply_permutation.qbk | 21 ++++++++--- example/apply_permutation_example.cpp | 4 +-- include/boost/algorithm/apply_permutation.hpp | 36 ++++++++++--------- test/apply_permutation_test.cpp | 24 ++++++------- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/doc/apply_permutation.qbk b/doc/apply_permutation.qbk index 952195d..7f11457 100644 --- a/doc/apply_permutation.qbk +++ b/doc/apply_permutation.qbk @@ -12,8 +12,16 @@ Distributed under the Boost Software License, Version 1.0. The header file 'apply_permutation.hpp' contains two algorithms, apply_permutation and apply_reverse_permutation. Also there are range-based versions. The algorithms transform the item sequence according to index sequence order. -The routine `apply_permutation` takes a item sequence and a order sequence. It reshuffles item sequence according to order sequence. Every value in order sequence means where the item comes from. -The routine `apply_reverse_permutation` takes a item sequence and a order sequence. It will reshuffle item sequence according to order sequence. Every value in order sequence means where the item goes to. +The routine `apply_permutation` takes a item sequence and a order sequence. It reshuffles item sequence according to order sequence. Every value in order sequence means where the item comes from. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed). +The routine `apply_reverse_permutation` takes a item sequence and a order sequence. It will reshuffle item sequence according to order sequence. Every value in order sequence means where the item goes to. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed). + +Implementations are based on these articles: +https://blogs.msdn.microsoft.com/oldnewthing/20170102-00/?p=95095 +https://blogs.msdn.microsoft.com/oldnewthing/20170103-00/?p=95105 +https://blogs.msdn.microsoft.com/oldnewthing/20170104-00/?p=95115 +https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145 +https://blogs.msdn.microsoft.com/oldnewthing/20170110-00/?p=95155 +https://blogs.msdn.microsoft.com/oldnewthing/20170111-00/?p=95165 The routines come in 2 forms; the first one takes two iterators to define the item range and one iterator to define the beginning of index range. The second form takes range to define the item sequence and range to define index sequence. @@ -21,17 +29,17 @@ The routines come in 2 forms; the first one takes two iterators to define the it [heading interface] There are two versions of algorithms: -1) takes three iterators. +1) takes four iterators. 2) takes two ranges. `` template void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, - RandomAccessIterator2 ind_begin); + RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end); template void apply_permutation(Range1& item_range, Range2& ind_range); template void apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, - RandomAccessIterator2 ind_begin); + RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end); template void apply_reverse_permutation(Range1& item_range, Range2& ind_range); `` @@ -64,6 +72,7 @@ apply_reverse_permutation(vec, order) --> vec:{5, 4, 2, 3, 1} [heading Complexity] All of the variants of `apply_permutation` and `apply_reverse_permutation` run in ['O(N)] (linear) time. +More [heading Exception Safety] @@ -76,6 +85,8 @@ All of the variants of `apply_permutation` and `apply_reverse_permutation` take * Order sequence must be zero-indexed. +* Order sequence gets permuted. + [endsect] [/ File apply_permutation.qbk diff --git a/example/apply_permutation_example.cpp b/example/apply_permutation_example.cpp index 16c84b6..7ed91ae 100644 --- a/example/apply_permutation_example.cpp +++ b/example/apply_permutation_example.cpp @@ -23,7 +23,7 @@ int main ( int /*argc*/, char * /*argv*/ [] ) std::cout << "apply_permutation with iterators:\n"; std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); for (const auto& x : vec) { std::cout << x << ", "; @@ -34,7 +34,7 @@ int main ( int /*argc*/, char * /*argv*/ [] ) std::cout << "apply_reverse_permutation with iterators:\n"; std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); for (const auto& x : vec) { std::cout << x << ", "; diff --git a/include/boost/algorithm/apply_permutation.hpp b/include/boost/algorithm/apply_permutation.hpp index d4aa958..c844cfc 100644 --- a/include/boost/algorithm/apply_permutation.hpp +++ b/include/boost/algorithm/apply_permutation.hpp @@ -19,8 +19,7 @@ #define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP #include -#include -#include +#include #include #include @@ -40,17 +39,18 @@ namespace boost { namespace algorithm template void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, - RandomAccessIterator2 ind_begin) + RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end) { - typedef typename std::iterator_traits::difference_type Diff; + using Diff = typename std::iterator_traits::difference_type; + using std::swap; Diff size = std::distance(item_begin, item_end); - for (Diff i = 0; i < size; ++i) + for (Diff i = 0; i < size; i++) { - Diff current = i; + auto current = i; while (i != ind_begin[current]) { - Diff next = static_cast(ind_begin[current]); - std::swap(item_begin[current], item_begin[next]); + auto next = ind_begin[current]; + swap(item_begin[current], item_begin[next]); ind_begin[current] = current; current = next; } @@ -69,18 +69,22 @@ apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_e /// Complexity: O(N). template void -apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, - RandomAccessIterator2 ind_begin) +apply_reverse_permutation( + RandomAccessIterator1 item_begin, + RandomAccessIterator1 item_end, + RandomAccessIterator2 ind_begin, + RandomAccessIterator2 ind_end) { - typedef typename std::iterator_traits::difference_type Diff; + using Diff = typename std::iterator_traits::difference_type; + using std::swap; Diff length = std::distance(item_begin, item_end); - for (Diff i = 0; i < length; ++i) + for (Diff i = 0; i < length; i++) { while (i != ind_begin[i]) { Diff next = ind_begin[i]; - std::swap(item_begin[i], item_begin[next]); - std::swap(ind_begin[i], ind_begin[next]); + swap(item_begin[i], item_begin[next]); + swap(ind_begin[i], ind_begin[next]); } } } @@ -98,7 +102,7 @@ void apply_permutation(Range1& item_range, Range2& ind_range) { apply_permutation(boost::begin(item_range), boost::end(item_range), - boost::begin(ind_range)); + boost::begin(ind_range), boost::end(ind_range)); } /// \fn apply_reverse_permutation ( Range1 item_range, Range2 ind_range ) @@ -114,7 +118,7 @@ void apply_reverse_permutation(Range1& item_range, Range2& ind_range) { apply_reverse_permutation(boost::begin(item_range), boost::end(item_range), - boost::begin(ind_range)); + boost::begin(ind_range), boost::end(ind_range)); } }} diff --git a/test/apply_permutation_test.cpp b/test/apply_permutation_test.cpp index 4dc08e7..e9ab970 100644 --- a/test/apply_permutation_test.cpp +++ b/test/apply_permutation_test.cpp @@ -26,7 +26,7 @@ void test_apply_permutation() { std::vector vec, order, result; - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //1 element @@ -36,7 +36,7 @@ void test_apply_permutation() order.push_back(0); result = vec; - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //2 elements, no changes @@ -46,7 +46,7 @@ void test_apply_permutation() order.push_back(0); order.push_back(1); result = vec; - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //2 elements, changed @@ -56,7 +56,7 @@ void test_apply_permutation() order.push_back(1); order.push_back(0); result.push_back(2); result.push_back(1); - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Multiple elements, no changes @@ -66,7 +66,7 @@ void test_apply_permutation() order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); result = vec; - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Multiple elements, changed @@ -76,7 +76,7 @@ void test_apply_permutation() order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0); result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1); - ba::apply_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Just test range interface @@ -97,7 +97,7 @@ void test_apply_reverse_permutation() { std::vector vec, order, result; - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //1 element @@ -107,7 +107,7 @@ void test_apply_reverse_permutation() order.push_back(0); result = vec; - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //2 elements, no changes @@ -117,7 +117,7 @@ void test_apply_reverse_permutation() order.push_back(0); order.push_back(1); result = vec; - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //2 elements, changed @@ -127,7 +127,7 @@ void test_apply_reverse_permutation() order.push_back(1); order.push_back(0); result.push_back(2); result.push_back(1); - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Multiple elements, no changes @@ -137,7 +137,7 @@ void test_apply_reverse_permutation() order.push_back(0); order.push_back(1); order.push_back(2); order.push_back(3); order.push_back(4); result = vec; - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Multiple elements, changed @@ -147,7 +147,7 @@ void test_apply_reverse_permutation() order.push_back(4); order.push_back(3); order.push_back(2); order.push_back(1); order.push_back(0); result.push_back(5); result.push_back(4); result.push_back(3); result.push_back(2); result.push_back(1); - ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin()); + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); BOOST_CHECK(vec == result); } //Just test range interface