diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..a6b09a488 --- /dev/null +++ b/.clang-format @@ -0,0 +1,56 @@ +# Style: Boost.Algorithm +BasedOnStyle: LLVM +Language: Cpp +AccessModifierOffset: -4 +# ConstructorInitializerIndentWidth: 4 +# AlignEscapedNewlinesLeft: false +# AlignTrailingComments: true +# AllowAllParametersOfDeclarationOnNextLine: true +# AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +# AllowShortFunctionsOnASingleLine: All +AlwaysBreakAfterDefinitionReturnType: true # needs version 3.6.0 +# AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +# BreakBeforeBinaryOperators: false +# BreakBeforeTernaryOperators: true +# BreakConstructorInitializersBeforeComma: false +# BinPackParameters: true +ColumnLimit: 80 +# ConstructorInitializerAllOnOneLineOrOnePerLine: false +# DerivePointerAlignment: false +# ExperimentalAutoDetectBinPacking: false +# IndentCaseLabels: false +# IndentWrappedFunctionNames: false +# IndentFunctionDeclarationAfterType: false +# MaxEmptyLinesToKeep: 1 +# KeepEmptyLinesAtTheStartOfBlocks: true +# NamespaceIndentation: None +# ObjCSpaceAfterProperty: false +# ObjCSpaceBeforeProtocolList: true +# PenaltyBreakBeforeFirstCallParameter: 19 +# PenaltyBreakComment: 300 +# PenaltyBreakString: 1000 +# PenaltyBreakFirstLessLess: 120 +# PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 0 +PointerAlignment: Right +SpacesBeforeTrailingComments: 2 +# Cpp11BracedListStyle: true +Standard: Cpp03 +IndentWidth: 4 +# TabWidth: 4 +# UseTab: Never +BreakBeforeBraces: Stroustrup #GNU # Linux +SpacesInParentheses: true +SpacesInAngles: true +# SpaceInEmptyParentheses: false +# SpacesInCStyleCastParentheses: false +# SpacesInContainerLiterals: true +# SpaceBeforeAssignmentOperators: true +# ContinuationIndentWidth: 4 +# CommentPragmas: '^ IWYU pragma:' +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +SpaceBeforeParens: Always # ControlStatements +# DisableFormat: false \ No newline at end of file diff --git a/include/boost/algorithm/searching/longest_increasing_subsequence.hpp b/include/boost/algorithm/searching/longest_increasing_subsequence.hpp new file mode 100644 index 000000000..30c4287f6 --- /dev/null +++ b/include/boost/algorithm/searching/longest_increasing_subsequence.hpp @@ -0,0 +1,572 @@ +/* + Copyright (c) Marek Kurdej 2014. + + 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) + + For more information, see http://www.boost.org +*/ + +#ifndef BOOST_ALGORITHM_LONGEST_INCREASING_SUBSEQUENCE_HPP +#define BOOST_ALGORITHM_LONGEST_INCREASING_SUBSEQUENCE_HPP + +#include // for std::iterator_traits +#include + +#include +#include + +#include +#include + +#include +#include + +// #define BOOST_ALGORITHM_LONGEST_INCREASING_SUBSEQUENCE_DEBUG +#ifdef BOOST_ALGORITHM_LONGEST_INCREASING_SUBSEQUENCE_DEBUG +#include +#endif + +namespace boost { +namespace algorithm { + +/* + A templated version of the longest (increasing) subsequence algorithm. + + Requirements: + * Random access iterators + +https://en.wikipedia.org/wiki/Longest_increasing_subsequence + +*/ + +// TODO: doc +struct iterator_output_tag { +}; +// TODO: doc +struct value_output_tag { +}; + +/// Longest increasing subsequence algorithm. +/// +/// @time O(N logN) +/// @space O(N) +template < typename RandomIterator, + typename Comparator = std::less< + typename std::iterator_traits< RandomIterator >::value_type > > +class longest_increasing_subsequence { + typedef typename std::iterator_traits< RandomIterator >::difference_type + difference_type; + typedef + typename std::iterator_traits< RandomIterator >::value_type value_type; + +public: + typedef std::vector< RandomIterator > iterator_vector; + typedef std::vector< value_type > value_vector; + + longest_increasing_subsequence ( Comparator cmp = Comparator () ) + : compare ( cmp ) + { + } + + ~longest_increasing_subsequence () {} + + /// \brief Searches the longest (increasing) subsequence in the sequence + /// + /// The result is a vector of iterators defining the longest increasing + /// subsequence (possibly non-continuous elements). By giving a different + /// comparison predicate to the constructor, one can find as well, e.g., the + /// longest decreasing subsequence (with std::greater() predicate) or the + /// non-decreasing one (with std::not1(std::greater())). + /// The time complexity of the algorithm is log-linear O(n logn) where n is + /// the length of the sequence (`std::distance(first, last)`). + /// The space complexity is linear O(n). + /// + /// \param first The start of the sequence to search (Random Access + /// Iterator) + /// \param last One past the end of the sequence to search + /// + value_vector operator()( RandomIterator first, RandomIterator last, + value_output_tag tag ) const + { + return this->do_search_and_output ( first, last, tag ); + } + + iterator_vector operator()( RandomIterator first, RandomIterator last, + iterator_output_tag tag ) const + { + return this->do_search_and_output ( first, last, tag ); + } + + template < typename OutputIterator > + OutputIterator operator()( RandomIterator first, RandomIterator last, + OutputIterator d_first ) const + { + return this->do_search_and_output ( first, last, d_first ); + } + + template < typename Range > + value_vector operator()( const Range &r, value_output_tag tag ) const + { + return ( *this )( boost::begin ( r ), boost::end ( r ), tag ); + } + + template < typename Range > + iterator_vector operator()( const Range &r, iterator_output_tag tag ) const + { + return ( *this )( boost::begin ( r ), boost::end ( r ), tag ); + } + + template < typename Range, typename OutputIterator > + OutputIterator operator()( const Range &r, OutputIterator d_first ) const + { + return this->do_search_and_output ( boost::begin ( r ), + boost::end ( r ), d_first ); + } + + std::size_t + compute_length ( RandomIterator first, RandomIterator last ) const + { + return this->do_search ( first, last ); + } + + template < typename Range > + std::size_t + compute_length ( const Range &r ) const + { + // BOOST_STATIC_ASSERT ( ( boost::is_same< + // typename std::iterator_traits::value_type, + // typename std::iterator_traits::value_type>::value ) ); + return ( *this )( boost::begin ( r ), boost::end ( r ) ); + } + +private: + /// \cond DOXYGEN_HIDE + Comparator compare; + + typedef std::size_t size_type; + typedef std::vector< size_type > index_vector; + + size_type + do_search ( RandomIterator first, RandomIterator last ) const + { + index_vector predecessor; + index_vector lis_tail; + return do_search ( first, last, predecessor, lis_tail ); + } + + size_type + do_search ( RandomIterator first, RandomIterator last, + /*output*/ index_vector &predecessor, + /*output*/ index_vector &lis_tail ) const + { + difference_type const n = std::distance ( first, last ); + /// Length of the longest (increasing) subsequence found so far + size_type lis_length = 0; + /// predecessor[k, 0 <= k < n] - stores the index of the predecessor of + /// X[k] in the longest increasing subsequence ending at X[k] + predecessor.resize ( n ); + /// lis_tail[j, 0 <= k <= n] - stores the index k of the smallest value + /// X[k] such that there is an increasing subsequence of length j ending + /// at X[k] on the range k <= i (note we have j <= k <= i here, because + /// j represents the length of the increasing subsequence, and k + /// represents the index of its termination. + /// Obviously, we can never have an increasing subsequence of length 13 + /// ending at index 11. k <= i by definition). + lis_tail.resize ( n + 1 ); + + for ( difference_type i = 0; i < n; ++i ) { + // Binary search for the largest positive j <= lis_length, such that + // X[lis_tail[j]] < X[i]. + // After searching, lo is 1 greater than the length of the longest + // prefix of X[i]. + size_type new_lis_length; + { + size_type lo = 1; + size_type hi = lis_length; + while ( lo <= hi ) { + size_type mid = ( lo + hi ) / 2; + assert ( mid <= i ); + if ( compare ( + *( first + lis_tail[mid] ), + *( first + i ) ) ) { // X[lis_tail[mid]] < X[i] + lo = mid + 1; + } + else { + hi = mid - 1; + } + } + new_lis_length = lo; + } + + // The predecessor of X[i] is the last index of the subsequence of + // length new_lis_length-1 + assert ( new_lis_length > 0 ); + predecessor[i] = lis_tail[new_lis_length - 1]; + + assert ( new_lis_length <= n ); + assert ( lis_tail[new_lis_length] < n ); + if ( new_lis_length > lis_length ) { + // If we found a subsequence longer than any we have found yet, + // update lis_tail and lis_length + lis_tail[new_lis_length] = i; + lis_length = new_lis_length; + } + else if ( compare ( *( first + i ), + *( first + lis_tail[new_lis_length] ) ) ) { + // X[i] < X[lis_tail[new_lis_length]] + // If we found a smaller last value for the subsequence of + // length new_lis_length, only update lis_tail + lis_tail[new_lis_length] = i; + } + } + return lis_length; + } + + template < typename OutputIterator > + OutputIterator + do_output ( RandomIterator first, RandomIterator /*last*/, + size_type lis_length, index_vector const &predecessor, + index_vector const &lis_tail, OutputIterator d_first ) const + { + // Reconstruct the longest increasing subsequence + index_vector lis ( lis_length ); + size_type k = lis_tail[lis_length]; + for ( size_type i = lis_length; i--; ) { + lis[i] = k; // X[k]; + k = predecessor[k]; + } + // Output the found subsequence + for ( size_type i = 0; i < lis_length; ++i ) { + *d_first++ = *( first + lis[i] ); + } + return d_first; + } + + value_vector + do_output ( RandomIterator first, RandomIterator /*last*/, + size_type lis_length, index_vector const &predecessor, + index_vector const &lis_tail, value_output_tag /*tag*/ ) const + { + // Reconstruct the longest increasing subsequence values + value_vector lis ( lis_length ); + size_type k = lis_tail[lis_length]; + for ( size_type i = lis_length; i--; ) { + lis[i] = *( first + k ); // X[k]; + k = predecessor[k]; + } + return lis; + } + + iterator_vector + do_output ( RandomIterator first, RandomIterator /*last*/, + size_type lis_length, index_vector const &predecessor, + index_vector const &lis_tail, + iterator_output_tag /*tag*/ ) const + { + // Reconstruct the longest increasing subsequence iterators + iterator_vector lis ( lis_length ); + size_type k = lis_tail[lis_length]; + for ( size_type i = lis_length; i--; ) { + lis[i] = first + k; // &X[k]; + k = predecessor[k]; + } + return lis; + } + + template < typename OutputIterator > + OutputIterator + do_search_and_output ( RandomIterator first, RandomIterator last, + OutputIterator d_first ) const + { + index_vector predecessor; + index_vector lis_tail; + size_type lis_length = do_search ( first, last, predecessor, lis_tail ); + return do_output ( first, last, lis_length, predecessor, lis_tail, + d_first ); + } + + value_vector + do_search_and_output ( RandomIterator first, RandomIterator last, + value_output_tag tag ) const + { + index_vector predecessor; + index_vector lis_tail; + size_type lis_length = do_search ( first, last, predecessor, lis_tail ); + return this->do_output ( first, last, lis_length, predecessor, lis_tail, + tag ); + } + + iterator_vector + do_search_and_output ( RandomIterator first, RandomIterator last, + iterator_output_tag tag ) const + { + index_vector predecessor; + index_vector lis_tail; + size_type lis_length = do_search ( first, last, predecessor, lis_tail ); + return this->do_output ( first, last, lis_length, predecessor, lis_tail, + tag ); + } + // \endcond +}; + +/// \fn longest_increasing_subsequence_length ( RandomIterator first, +/// RandomIterator last ) +/// \brief Searches the range [first, last) for the length of the longest +/// increasing subsequence. +/// +/// \param first The start of the sequence to search for (Forward Input +/// Iterator) +/// \param last One past the end of the sequence to search for +/// +template < typename RandomIterator > +std::size_t +longest_increasing_subsequence_length ( RandomIterator first, + RandomIterator last ) +{ + longest_increasing_subsequence< RandomIterator > lis; + return lis.compute_length ( first, last ); +} + +template < typename Range > +std::size_t +longest_increasing_subsequence_length ( const Range &r ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator > lis; + return lis.compute_length ( boost::begin ( r ), boost::end ( r ) ); +} +// TODO: do we need a non-const range overload? + +/// \fn longest_increasing_subsequence_length ( RandomIterator first, +/// RandomIterator last, Comparator +/// cmp ) +/// \brief Searches the range [first, last) for the length of the longest +/// subsequence ordered by custom predicate cmp. +/// +/// \param first The start of the sequence to search for (Forward Input +/// Iterator) +/// \param last One past the end of the sequence to search for +/// \param cmp Comparison predicate defining the ordering +/// +template < typename RandomIterator, typename Comparator > +std::size_t +longest_increasing_subsequence_length ( RandomIterator first, + RandomIterator last, Comparator cmp ) +{ + longest_increasing_subsequence< RandomIterator, Comparator > lis ( cmp ); + return lis.compute_length ( first, last ); +} + +template < typename Range, typename Comparator > +typename boost::disable_if< boost::is_same< Range, Comparator >, + std::size_t >::type +longest_increasing_subsequence_length ( const Range &r, Comparator cmp ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator, Comparator > lis ( cmp ); + return lis.compute_length ( boost::begin ( r ), boost::end ( r ) ); +} +// TODO: do we need a non-const range overload? + +/// \fn longest_increasing_subsequence_search ( RandomIterator first, +/// RandomIterator last ) +/// \brief Searches the range [first, last) for the longest increasing +/// subsequence. +/// +/// \param first The start of the sequence to search for (Forward Input +/// Iterator) +/// \param last One past the end of the sequence to search for + +/// \fn longest_increasing_subsequence_search ( RandomIterator first, +/// RandomIterator last, Comparator cmp ) +/// \brief Searches the range [first, last) for the longest subsequence ordered +/// by custom predicate cmp. +/// \param first The start of the sequence to search for (Forward Input +/// Iterator) +/// \param last One past the end of the sequence to search for +/// \param cmp Comparison predicate defining the ordering +/// + +// Vector of values +template < typename RandomIterator > +typename longest_increasing_subsequence< RandomIterator >::value_vector +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + value_output_tag tag ) +{ + longest_increasing_subsequence< RandomIterator > lis; + return lis ( first, last, tag ); +} + +template < typename RandomIterator, typename Comparator > +typename longest_increasing_subsequence< RandomIterator, + Comparator >::value_vector +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + value_output_tag tag, Comparator cmp ) +{ + longest_increasing_subsequence< RandomIterator, Comparator > lis ( cmp ); + return lis ( first, last, tag ); +} + +// Vector of iterators +template < typename RandomIterator > +typename longest_increasing_subsequence< RandomIterator >::iterator_vector +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + iterator_output_tag tag ) +{ + longest_increasing_subsequence< RandomIterator > lis; + return lis ( first, last, tag ); +} + +template < typename RandomIterator, typename Comparator > +typename longest_increasing_subsequence< RandomIterator, + Comparator >::iterator_vector +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + iterator_output_tag tag, + Comparator cmp ) +{ + longest_increasing_subsequence< RandomIterator, Comparator > lis ( cmp ); + return lis ( first, last, tag ); +} + +// Output iterator +template < typename RandomIterator, typename OutputIterator > +OutputIterator +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + OutputIterator d_first ) +{ + longest_increasing_subsequence< RandomIterator > lis; + return lis ( first, last, d_first ); +} + +template < typename RandomIterator, typename OutputIterator, + typename Comparator > +OutputIterator +longest_increasing_subsequence_search ( RandomIterator first, + RandomIterator last, + OutputIterator d_first, Comparator cmp ) +{ + longest_increasing_subsequence< RandomIterator, Comparator > lis ( cmp ); + return lis ( first, last, d_first ); +} + +// Range - Vector of values +template < typename Range > +typename longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type >::value_vector +longest_increasing_subsequence_search ( const Range &sequence, + value_output_tag tag ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator > lis; + return lis ( boost::begin ( sequence ), boost::end ( sequence ), tag ); +} + +template < typename Range, typename Comparator > +typename longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type, + Comparator >::value_vector +longest_increasing_subsequence_search ( const Range &sequence, + value_output_tag tag, Comparator cmp ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator, Comparator > lis ( cmp ); + return lis ( boost::begin ( sequence ), boost::end ( sequence ), tag ); +} + +// Range - Vector of iterators +template < typename Range > +typename longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type >::iterator_vector +longest_increasing_subsequence_search ( const Range &sequence, + iterator_output_tag tag ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator > lis; + return lis ( boost::begin ( sequence ), boost::end ( sequence ), tag ); +} + +template < typename Range, typename Comparator > +typename longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type, + Comparator >::iterator_vector +longest_increasing_subsequence_search ( const Range &sequence, + iterator_output_tag tag, + Comparator cmp ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator, Comparator > lis ( cmp ); + return lis ( boost::begin ( sequence ), boost::end ( sequence ), tag ); +} + +// Range - Output Iterator +template < typename Range, typename OutputIterator > +OutputIterator +longest_increasing_subsequence_search ( const Range &sequence, + OutputIterator d_first ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator > lis; + return lis ( boost::begin ( sequence ), boost::end ( sequence ), d_first ); +} + +// Use a bit of TMP to disambiguate the 3-argument templates +template < typename Range, typename OutputIterator, typename Comparator > +typename boost::disable_if< boost::is_same< Range, OutputIterator >, + OutputIterator >::type +longest_increasing_subsequence_search ( const Range &sequence, + OutputIterator d_first, Comparator cmp ) +{ + typedef typename boost::range_iterator< const Range >::type range_iterator; + longest_increasing_subsequence< range_iterator, Comparator > lis ( cmp ); + return lis ( boost::begin ( sequence ), boost::end ( sequence ), d_first ); +} + +// Creator functions -- take a range, return an object +template < typename Range > +boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type > +make_longest_increasing_subsequence ( const Range &r ) +{ + return boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type > (); +} + +template < typename Range, typename Comparator > +boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< const Range >::type, Comparator > +make_longest_increasing_subsequence ( const Range &r, Comparator cmp ) +{ + return boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< const Range, Comparator >::type > ( + cmp ); +} + +template < typename Range > +boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< Range >::type > +make_longest_increasing_subsequence ( Range &r ) +{ + return boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< Range >::type > ( boost::begin ( r ), + boost::end ( r ) ); +} + +template < typename Range, typename Comparator > +boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< Range >::type, Comparator > +make_longest_increasing_subsequence ( Range &r, Comparator cmp ) +{ + return boost::algorithm::longest_increasing_subsequence< + typename boost::range_iterator< Range, Comparator >::type > ( cmp ); +} + +} // namespace algorithm +} // namespace boost + +#endif // BOOST_ALGORITHM_LONGEST_INCREASING_SUBSEQUENCE_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index bb4dff653..f54953a6c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -27,6 +27,9 @@ alias unit_test_framework [ compile-fail search_fail2.cpp : : : : ] [ compile-fail search_fail3.cpp : : : : ] +# Longest increasing/common subsequence/substring tests + [ run subsequence_test1.cpp unit_test_framework : : : : subsequence_test1 ] + # Clamp tests [ run clamp_test.cpp unit_test_framework : : : : clamp_test ] diff --git a/test/subsequence_test1.cpp b/test/subsequence_test1.cpp new file mode 100644 index 000000000..ebfcf02bc --- /dev/null +++ b/test/subsequence_test1.cpp @@ -0,0 +1,461 @@ +/* + Copyright (c) Marek Kurdej 2014. + + 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) + + For more information, see http://www.boost.org +*/ + +#include + +#define BOOST_TEST_MAIN +#include + +#include + +#include +#include +#include +#include + +#include // for 'list_of()', tuple_list_of +using namespace boost::assign; // bring 'list_of()' into scope + +namespace ba = boost::algorithm; + +struct null_insert_iterator + : std::iterator< std::output_iterator_tag, null_insert_iterator > { + // Null-op assignment + template < typename T > + void operator=( T const & ) + { + } + + // Null-op operators + null_insert_iterator &operator++() { return *this; } + null_insert_iterator operator++( int ) { return *this; } + null_insert_iterator &operator*() { return *this; } +}; + +namespace { + +// Length checking + +template < typename Container, typename Comparator > +void +check_length_iter ( const Container &sequence, std::size_t expected_length, + Comparator cmp ) +{ + // TODO: move to separate test + (void)ba::longest_increasing_subsequence_length ( + sequence.begin (), sequence.end () ); // check interface, ignore result + std::size_t size = ba::longest_increasing_subsequence_length ( + sequence.begin (), sequence.end (), cmp ); + BOOST_CHECK_EQUAL ( size, expected_length ); +} + +template < typename Container, typename Comparator > +void +check_length_range ( const Container &sequence, std::size_t expected_length, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + boost::iterator_range< const_iterator > range ( sequence.begin (), + sequence.end () ); + // TODO: move to separate test + (void)ba::longest_increasing_subsequence_length ( + range ); // check interface, ignore result + std::size_t size = ba::longest_increasing_subsequence_length ( range, cmp ); + BOOST_CHECK_EQUAL ( size, expected_length ); + // TODO: range creator functions make_* +} + +template < typename Container, typename Comparator > +void +check_length_pointer ( const Container &sequence, std::size_t expected_length, + Comparator cmp ) +{ + typedef const typename Container::value_type *ptr_type; + ptr_type sBeg = sequence.size () == 0 ? NULL : &*sequence.begin (); + ptr_type sEnd = sBeg + sequence.size (); + + // TODO: move to separate test + (void)ba::longest_increasing_subsequence_length ( + sBeg, sEnd ); // check interface, ignore result + std::size_t size = + ba::longest_increasing_subsequence_length ( sBeg, sEnd, cmp ); + BOOST_CHECK_EQUAL ( size, expected_length ); +} + +template < typename Container, typename Comparator > +void +check_length_object ( const Container &sequence, std::size_t expected_length, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + + // TODO: move to separate test + ba::longest_increasing_subsequence< const_iterator > + ls_no_comparator; // check interface, ignore result + (void)ls_no_comparator; + ba::longest_increasing_subsequence< const_iterator, Comparator > ls ( cmp ); + std::size_t size = ls.compute_length ( sequence.begin (), sequence.end () ); + BOOST_CHECK_EQUAL ( size, expected_length ); + // TODO: range-based make_* +} + +// Content checking + +template < typename Container, typename Comparator > +void +check_lis_iter ( const Container &sequence, const Container &expected_lis, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + typedef typename ba::longest_increasing_subsequence< + const_iterator >::value_vector value_vector; + typedef typename ba::longest_increasing_subsequence< + const_iterator >::iterator_vector iterator_vector; + + // value output + (void)ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), + ba::value_output_tag () ); // check interface + value_vector lis_value = ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), ba::value_output_tag (), cmp ); + BOOST_CHECK_EQUAL_COLLECTIONS ( lis_value.begin (), lis_value.end (), + expected_lis.begin (), + expected_lis.end () ); + + // iterator output + (void)ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), + ba::iterator_output_tag () ); // check interface + iterator_vector iter = ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), ba::iterator_output_tag (), cmp ); + value_vector lis_iter ( iter.size () ); + for ( std::size_t i = 0; i < iter.size (); ++i ) { + lis_iter[i] = *iter[i]; + } + BOOST_CHECK_EQUAL_COLLECTIONS ( lis_iter.begin (), lis_iter.end (), + expected_lis.begin (), + expected_lis.end () ); + + // output iterator + (void)ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), + null_insert_iterator () ); // check interface + value_vector lis_out_iter; + std::back_insert_iterator< value_vector > end_out_iter = + ba::longest_increasing_subsequence_search ( + sequence.begin (), sequence.end (), + std::back_inserter ( lis_out_iter ), cmp ); + (void)end_out_iter; // ignore result + BOOST_CHECK_EQUAL_COLLECTIONS ( lis_out_iter.begin (), lis_out_iter.end (), + expected_lis.begin (), + expected_lis.end () ); +} + +template < typename Container, typename Comparator > +void +check_lis_range ( const Container &sequence, const Container &expected_lis, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + typedef typename ba::longest_increasing_subsequence< + const_iterator >::value_vector value_vector; + boost::iterator_range< const_iterator > range ( sequence.begin (), + sequence.end () ); + (void)ba::longest_increasing_subsequence_search ( + range, ba::value_output_tag () ); // check interface + value_vector lis = ba::longest_increasing_subsequence_search ( + range, ba::value_output_tag (), cmp ); + BOOST_CHECK_EQUAL_COLLECTIONS ( + lis.begin (), lis.end (), expected_lis.begin (), expected_lis.end () ); + + (void)ba::longest_increasing_subsequence_search ( + range, null_insert_iterator () ); // check interface + value_vector lis2; + ba::longest_increasing_subsequence_search ( + range, std::back_inserter ( lis2 ), cmp ); + BOOST_CHECK_EQUAL_COLLECTIONS ( lis2.begin (), lis2.end (), + expected_lis.begin (), + expected_lis.end () ); +} + +template < typename Container, typename Comparator > +void +check_lis_pointer ( const Container &sequence, const Container &expected_lis, + Comparator cmp ) + +{ + typedef const typename Container::value_type *ptr_type; + typedef typename ba::longest_increasing_subsequence< + ptr_type >::value_vector value_vector; + ptr_type sBeg = sequence.size () == 0 ? NULL : &*sequence.begin (); + ptr_type sEnd = sBeg + sequence.size (); + + // TODO: move to separate test + (void)ba::longest_increasing_subsequence_search ( + sBeg, sEnd, + ba::value_output_tag () ); // check interface, ignore result + value_vector lis = ba::longest_increasing_subsequence_search ( + sBeg, sEnd, ba::value_output_tag (), cmp ); + BOOST_CHECK_EQUAL_COLLECTIONS ( + lis.begin (), lis.end (), expected_lis.begin (), expected_lis.end () ); + + (void)ba::longest_increasing_subsequence_search ( + sBeg, sEnd, + null_insert_iterator () ); // check interface, ignore result + value_vector lis2; + (void)ba::longest_increasing_subsequence_search ( + sBeg, sEnd, std::back_inserter ( lis2 ), cmp ); // ignore result + BOOST_CHECK_EQUAL_COLLECTIONS ( lis2.begin (), lis2.end (), + expected_lis.begin (), + expected_lis.end () ); +} + +template < typename Container, typename Comparator > +void +check_lis_object ( const Container &sequence, const Container &expected_lis, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + typedef typename ba::longest_increasing_subsequence< + const_iterator >::value_vector value_vector; + + ba::longest_increasing_subsequence< const_iterator > ls ( cmp ); + value_vector lis = + ls ( sequence.begin (), sequence.end (), ba::value_output_tag () ); + BOOST_CHECK_EQUAL_COLLECTIONS ( + lis.begin (), lis.end (), expected_lis.begin (), expected_lis.end () ); + + value_vector lis2; + ls ( sequence.begin (), sequence.end (), std::back_inserter ( lis2 ) ); + BOOST_CHECK_EQUAL_COLLECTIONS ( lis2.begin (), lis2.end (), + expected_lis.begin (), + expected_lis.end () ); + // TODO: range-based make_* +} + +template < typename Container, typename Comparator > +void +check_one_iter ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis, + Comparator cmp ) +{ + std::cout << "(Iterators) Sequence " << test_name << " is " + << sequence.size () << " elements long; " << std::endl; + check_length_iter ( sequence, expected_length, cmp ); + BOOST_REQUIRE_EQUAL ( expected_lis.size (), expected_length ); + check_lis_iter ( sequence, expected_lis, cmp ); +} + +template < typename Container, typename Comparator > +void +check_one_range ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis, + Comparator cmp ) +{ + std::cout << "(Ranges) Sequence " << test_name << " is " << sequence.size () + << " elements long; " << std::endl; + check_length_range ( sequence, expected_length, cmp ); + BOOST_REQUIRE_EQUAL ( expected_lis.size (), expected_length ); + check_lis_range ( sequence, expected_lis, cmp ); +} + +template < typename Container, typename Comparator > +void +check_one_pointer ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis, + Comparator cmp ) +{ + std::cout << "(Pointers) Sequence " << test_name << " is " + << sequence.size () << " elements long; " << std::endl; + check_length_pointer ( sequence, expected_length, cmp ); + BOOST_REQUIRE_EQUAL ( expected_lis.size (), expected_length ); + check_lis_pointer ( sequence, expected_lis, cmp ); +} + +template < typename Container, typename Comparator > +void +check_one_object ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis, + Comparator cmp ) +{ + std::cout << "(Objects) Sequence " << test_name << " is " + << sequence.size () << " elements long; " << std::endl; + check_length_object ( sequence, expected_length, cmp ); + BOOST_REQUIRE_EQUAL ( expected_lis.size (), expected_length ); + check_lis_object ( sequence, expected_lis, cmp ); +} + +template < typename Container, typename Comparator > +void +check_one ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis, + Comparator cmp ) +{ + check_one_iter ( test_name, sequence, expected_length, expected_lis, cmp ); + check_one_range ( test_name, sequence, expected_length, expected_lis, cmp ); + check_one_pointer ( test_name, sequence, expected_length, expected_lis, + cmp ); + check_one_object ( test_name, sequence, expected_length, expected_lis, + cmp ); +} + +template < typename Container > +void +check_one ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, const Container &expected_lis ) +{ + typedef typename Container::value_type value_type; + // TODO: check again defaults + // check_one ( test_name, sequence, expected_length, expected_lis ); + check_one ( test_name, sequence, expected_length, expected_lis, + std::less< value_type > () ); +} + +// Multiple possible results + +template < typename Container, typename Comparator, + typename ContainerContainer > +void +check_lis ( const Container &sequence, const ContainerContainer &expected_lises, + Comparator cmp ) +{ + typedef typename Container::const_iterator const_iterator; + + ba::longest_increasing_subsequence< const_iterator > ls ( cmp ); + Container lis = + ls ( sequence.begin (), sequence.end (), ba::value_output_tag () ); + + bool any_equal = false; + for ( std::size_t i = 0; i < expected_lises.size (); ++i ) { + const Container &expected_lis = expected_lises[i]; + BOOST_REQUIRE_EQUAL ( lis.size (), expected_lis.size () ); + any_equal |= + std::equal ( lis.begin (), lis.end (), + expected_lis.begin () /*, expected_lis.end()*/ ); + // try { + // BOOST_CHECK_EQUAL_COLLECTIONS(lis.begin(), lis.end(), + // expected_lis.begin(), expected_lis.end()); + // } catch (...) { + // any_equal = false; + // } + } + BOOST_CHECK ( any_equal ); +} + +template < typename Container, typename Comparator, + typename ContainerContainer > +void +check_one ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, + const ContainerContainer &expected_lises, Comparator cmp ) +{ + BOOST_TEST_CHECKPOINT ( test_name ); + check_length_iter ( sequence, expected_length, cmp ); + for ( std::size_t i = 0; i < expected_lises.size (); ++i ) { + BOOST_REQUIRE_EQUAL ( expected_lises[i].size (), expected_length ); + } + check_lis ( sequence, expected_lises, cmp ); +} + +template < typename Container, typename ContainerContainer > +void +check_one ( const std::string &test_name, const Container &sequence, + std::size_t expected_length, + const ContainerContainer &expected_lises ) +{ + typedef typename Container::value_type value_type; + check_one ( test_name, sequence, expected_length, expected_lises, + std::less< value_type > () ); +} +} + +template < typename Container > +void +ReadFromFile ( const char *name, + std::back_insert_iterator< Container > inserter ) +{ + std::ifstream in ( name, std::ios_base::binary | std::ios_base::in ); + std::istream_iterator< char, char > begin ( in ); + std::istream_iterator< char, char > end; + + std::copy ( begin, end, inserter ); +} + +BOOST_AUTO_TEST_CASE ( test_main ) +{ + typedef std::vector< int > vec; + typedef vec res; + typedef std::vector< res > resvec; + using std::string; + using std::wstring; + + vec seq1 = list_of ( 5 ); + res exp1 = list_of ( 5 ); + vec seq2 = list_of ( 5 )( 6 )( 7 ); + res exp2 = list_of ( 5 )( 6 )( 7 ); + vec seq3 = list_of ( 5 )( 7 )( 6 ); + res exp3a = list_of ( 5 )( 6 ); + res exp3b = list_of ( 5 )( 7 ); + resvec exp3 = list_of ( exp3a )( exp3b ); + vec seq4 = list_of ( 7 )( 2 )( 8 )( 1 )( 3 )( 4 )( 10 )( 6 )( 9 )( 5 ); + res exp4 = list_of ( 1 )( 3 )( 4 )( 6 )( 9 ); + vec seq5 = list_of ( 3 )( 2 )( 6 )( 4 )( 5 )( 1 ); + res exp5a = list_of ( 2 )( 4 )( 5 ); + res exp5b = list_of ( 3 )( 4 )( 5 ); + resvec exp5 = list_of ( exp5a )( exp5b ); + vec seq6 = list_of ( 0 )( 8 )( 4 )( 12 )( 2 )( 10 )( 6 )( 14 )( 1 )( 9 )( + 5 )( 13 )( 3 )( 11 )( 7 )( 15 ); + res exp6a = list_of ( 0 )( 2 )( 6 )( 9 )( 11 )( 15 ); + res exp6b = list_of ( 0 )( 4 )( 6 )( 9 )( 13 )( 15 ); + resvec exp6 = list_of ( exp6a )( exp6b ); + string str1 = "abcdef"; + string estr1 = str1; + string str2 = + "All human beings are born free and equal in dignity and rights."; + string estr2 = " abdeginrs"; + wstring wstr1 = L"abcdef"; + wstring ewstr1 = wstr1; + check_one ( "seq1", seq1, 1, exp1 ); + check_one ( "seq2", seq2, 3, exp2 ); + check_one ( "seq3", seq3, 2, exp3 ); + check_one ( "seq5", seq4, 5, exp4 ); + check_one ( "seq5", seq5, 3, exp5 ); + check_one ( "seq6", seq6, 6, exp6 ); + + check_one ( "str1", str1, 6, estr1 ); + check_one ( "str2", str2, 10, estr2 ); + check_one ( "wstr1", wstr1, 6, ewstr1 ); + + string c1; + ReadFromFile ( "search_test_data/0001.corpus", std::back_inserter ( c1 ) ); + string ec1 = + "+/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + string p1b; + ReadFromFile ( "search_test_data/0001b.pat", std::back_inserter ( p1b ) ); + string ep1b = "0AKPScdehlnpwxz"; + + string p1e; + ReadFromFile ( "search_test_data/0001e.pat", std::back_inserter ( p1e ) ); + string ep1e = "BCGHIJVdg"; + + string p1f; + ReadFromFile ( "search_test_data/0001f.pat", std::back_inserter ( p1f ) ); + string ep1f = "/3568BENOPQUgxz"; + + string p1n; + ReadFromFile ( "search_test_data/0001n.pat", std::back_inserter ( p1n ) ); + string ep1n = "013DEIMNTUY"; + + check_one ( "c1", c1, 64, ec1 ); + check_one ( "p1b", p1b, 15, ep1b ); + check_one ( "p1e", p1e, 9, ep1e ); + check_one ( "p1f", p1f, 15, ep1f ); + check_one ( "p1n", p1n, 11, ep1n ); +}