From 18edb763fbcc105ebe8cd2d7a9ca9de2fe89a0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Tue, 17 Mar 2026 21:48:13 +0100 Subject: [PATCH] Add segmented_mismatch algorithm --- experimental/bench_segmented_algos.cpp | 37 ++++ experimental/segmented_mismatch_test.cpp | 183 ++++++++++++++++++ .../experimental/segmented_mismatch.hpp | 157 +++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 experimental/segmented_mismatch_test.cpp create mode 100644 include/boost/container/experimental/segmented_mismatch.hpp diff --git a/experimental/bench_segmented_algos.cpp b/experimental/bench_segmented_algos.cpp index ec84c8e..a3f2c97 100644 --- a/experimental/bench_segmented_algos.cpp +++ b/experimental/bench_segmented_algos.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1254,6 +1255,32 @@ void bench_merge(C c, C& c2, std::size_t iters, const char* cname) print_ratio("merge", cname, r1, r2); } +template +void bench_mismatch(C c, C& c2, std::size_t iters, const char* cname, + const char* label) +{ + int result = 0; + + cpu_timer t1; + for (std::size_t i = 0; i < iters; ++i) { + t1.resume(); + result = (std::mismatch(c.begin(), c.end(), c2.begin()).first == c.end()) ? 1 : 0; + t1.stop(); + escape(&result); + } + double r1 = calc_ns_per_elem(t1.elapsed().wall, iters, c.size()); + + cpu_timer t2; + for (std::size_t i = 0; i < iters; ++i) { + t2.resume(); + result = (bc::segmented_mismatch(c.begin(), c.end(), c2.begin()).first == c.end()) ? 1 : 0; + t2.stop(); + escape(&result); + } + double r2 = calc_ns_per_elem(t2.elapsed().wall, iters, c.size()); + print_ratio(label, cname, r1, r2); +} + template void bench_swap_ranges(C c, std::size_t iters, const char* cname) { @@ -1663,6 +1690,16 @@ void run_all(const C& c, std::size_t iters, const char* cname) bench_merge(c, c2, iters, cname); } + //mismatch + { + C c2(c); + bench_mismatch(c, c2, iters, cname, "mismatch(hit)"); + typename C::iterator last = c2.end(); + --last; + *last = VT(-1); + bench_mismatch(c, c2, iters, cname, "mismatch(miss)"); + } + //partition bench_partition(c, iters, cname, is_odd(), "partition(hit)"); bench_partition(c, iters, cname, is_negative(), "partition(miss)"); diff --git a/experimental/segmented_mismatch_test.cpp b/experimental/segmented_mismatch_test.cpp new file mode 100644 index 0000000..d673968 --- /dev/null +++ b/experimental/segmented_mismatch_test.cpp @@ -0,0 +1,183 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. 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/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "segmented_test_helper.hpp" +#include +#include + +using namespace boost::container; + +void test_mismatch_matching() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv.add_segment_range(a1, a1 + 3); + sv.add_segment_range(a2, a2 + 2); + sv.add_segment_range(a3, a3 + 4); + + int ref[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = segmented_mismatch(sv.begin(), sv.end(), ref); + BOOST_TEST(r.first == sv.end()); + BOOST_TEST(r.second == ref + 9); +} + +void test_mismatch_found() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + sv.add_segment_range(a1, a1 + 3); + sv.add_segment_range(a2, a2 + 2); + + int ref[] = {1, 2, 3, 4, 99}; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = segmented_mismatch(sv.begin(), sv.end(), ref); + BOOST_TEST(r.first != sv.end()); + BOOST_TEST_EQ(*r.first, 5); + BOOST_TEST_EQ(*r.second, 99); +} + +void test_mismatch_first_segment() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + sv.add_segment_range(a1, a1 + 3); + sv.add_segment_range(a2, a2 + 2); + + int ref[] = {1, 99, 3, 4, 5}; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = segmented_mismatch(sv.begin(), sv.end(), ref); + BOOST_TEST(r.first != sv.end()); + BOOST_TEST_EQ(*r.first, 2); + BOOST_TEST_EQ(*r.second, 99); +} + +void test_mismatch_empty() +{ + test_detail::seg_vector sv; + int dummy = 0; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = segmented_mismatch(sv.begin(), sv.end(), &dummy); + BOOST_TEST(r.first == sv.end()); + BOOST_TEST(r.second == &dummy); +} + +void test_mismatch_single_segment() +{ + test_detail::seg_vector sv; + int a[] = {10, 20, 30}; + sv.add_segment_range(a, a + 3); + + int ref_match[] = {10, 20, 30}; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = segmented_mismatch(sv.begin(), sv.end(), ref_match); + BOOST_TEST(r.first == sv.end()); + + int ref_fail[] = {10, 20, 99}; + r = segmented_mismatch(sv.begin(), sv.end(), ref_fail); + BOOST_TEST(r.first != sv.end()); + BOOST_TEST_EQ(*r.first, 30); + BOOST_TEST_EQ(*r.second, 99); +} + +void test_mismatch_non_segmented() +{ + std::vector v; + v.push_back(1); v.push_back(2); v.push_back(3); + + int ref_match[] = {1, 2, 3}; + typedef std::vector::iterator vec_it; + std::pair r = segmented_mismatch(v.begin(), v.end(), ref_match); + BOOST_TEST(r.first == v.end()); + + int ref_fail[] = {1, 2, 99}; + r = segmented_mismatch(v.begin(), v.end(), ref_fail); + BOOST_TEST(r.first != v.end()); + BOOST_TEST_EQ(*r.first, 3); + BOOST_TEST_EQ(*r.second, 99); +} + +void test_mismatch_sentinel_segmented() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv.add_segment_range(a1, a1 + 3); + sv.add_segment_range(a2, a2 + 2); + sv.add_segment_range(a3, a3 + 4); + + int ref[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + typedef test_detail::seg_vector::iterator seg_it; + std::pair r = + segmented_mismatch(sv.begin(), test_detail::make_sentinel(sv.end()), ref); + BOOST_TEST(r.first == sv.end()); + BOOST_TEST(r.second == ref + 9); +} + +void test_mismatch_sentinel_non_segmented() +{ + std::vector v; + v.push_back(1); v.push_back(2); v.push_back(3); + + int ref_match[] = {1, 2, 3}; + typedef std::vector::iterator vec_it; + std::pair r = + segmented_mismatch(v.begin(), test_detail::make_sentinel(v.end()), ref_match); + BOOST_TEST(r.first == v.end()); + + int ref_fail[] = {1, 2, 99}; + r = segmented_mismatch(v.begin(), test_detail::make_sentinel(v.end()), ref_fail); + BOOST_TEST(r.first != v.end()); + BOOST_TEST_EQ(*r.first, 3); + BOOST_TEST_EQ(*r.second, 99); +} + +void test_mismatch_seg2() +{ + test_detail::seg2_vector sv2; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv2.add_flat_segment_range(a1, a1 + 3); + sv2.add_flat_segment_range(a2, a2 + 2); + sv2.add_flat_segment_range(a3, a3 + 4); + + int ref[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + typedef test_detail::seg2_vector::iterator seg2_it; + std::pair r = segmented_mismatch(sv2.begin(), sv2.end(), ref); + BOOST_TEST(r.first == sv2.end()); + + int ref_bad[] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; + r = segmented_mismatch(sv2.begin(), sv2.end(), ref_bad); + BOOST_TEST(r.first != sv2.end()); + BOOST_TEST_EQ(*r.first, 9); + BOOST_TEST_EQ(*r.second, 0); +} + +int main() +{ + test_mismatch_matching(); + test_mismatch_found(); + test_mismatch_first_segment(); + test_mismatch_empty(); + test_mismatch_single_segment(); + test_mismatch_non_segmented(); + test_mismatch_sentinel_segmented(); + test_mismatch_sentinel_non_segmented(); + test_mismatch_seg2(); + return boost::report_errors(); +} diff --git a/include/boost/container/experimental/segmented_mismatch.hpp b/include/boost/container/experimental/segmented_mismatch.hpp new file mode 100644 index 0000000..1ad3c2d --- /dev/null +++ b/include/boost/container/experimental/segmented_mismatch.hpp @@ -0,0 +1,157 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. 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/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +#ifndef BOOST_CONTAINER_EXPERIMENTAL_SEGMENTED_MISMATCH_HPP +#define BOOST_CONTAINER_EXPERIMENTAL_SEGMENTED_MISMATCH_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include +#include +#include + +namespace boost { +namespace container { + +template +std::pair segmented_mismatch(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred); + +template +std::pair segmented_mismatch(InpIter1 first1, Sent last1, InpIter2 first2); + +namespace detail_algo { + +struct mismatch_equal +{ + template + BOOST_CONTAINER_FORCEINLINE bool operator()(const T& a, const U& b) const { return a == b; } +}; + +template +bool mismatch_scan(InpIter1& first1_out, Sent last1, InpIter2& first2_out, BinaryPred pred) +{ + InpIter1 first1 = first1_out; + InpIter2 first2 = first2_out; + bool all_match = true; + + for (; first1 != last1; ++first1, ++first2) { + if (!pred(*first1, *first2)) { + all_match = false; + break; + } + } + + first1_out = first1; + first2_out = first2; + return all_match; +} + +template +bool segmented_mismatch_ref + (SegIter first1, SegIter last1, InpIter2& first2, OutIter1& mismatch1, BinaryPred pred, segmented_iterator_tag) +{ + typedef segmented_iterator_traits traits; + typedef typename traits::local_iterator local_iterator; + typedef typename traits::segment_iterator segment_iterator; + + segment_iterator sfirst = traits::segment(first1); + segment_iterator slast = traits::segment(last1); + + if(sfirst == slast) { + local_iterator lf = traits::local(first1); + if(!(mismatch_scan)(lf, traits::local(last1), first2, pred)) { + mismatch1 = traits::compose(sfirst, lf); + return false; + } + } + else { + { + local_iterator lf = traits::local(first1); + if(!(mismatch_scan)(lf, traits::end(sfirst), first2, pred)) { + mismatch1 = traits::compose(sfirst, lf); + return false; + } + } + for(++sfirst; sfirst != slast; ++sfirst) { + local_iterator lb = traits::begin(sfirst); + if(!(mismatch_scan)(lb, traits::end(sfirst), first2, pred)) { + mismatch1 = traits::compose(sfirst, lb); + return false; + } + } + { + local_iterator lb = traits::begin(sfirst); + if(!(mismatch_scan)(lb, traits::local(last1), first2, pred)) { + mismatch1 = traits::compose(sfirst, lb); + return false; + } + } + } + return true; +} + +template +typename algo_enable_if_c< + !Tag::value || is_sentinel::value, bool>::type +segmented_mismatch_ref(InpIter1 first1, Sent last1, InpIter2& first2_out, OutIter1& mismatch1, BinaryPred pred, Tag) +{ + InpIter2 first2 = first2_out; + bool all_match = true; + + for (; first1 != last1; ++first1, ++first2) { + if (!pred(*first1, *first2)) { + mismatch1 = first1; + all_match = false; + break; + } + } + mismatch1 = first1; + first2_out = first2; + return all_match; +} + +} // namespace detail_algo + +//! Returns a pair of iterators to the first elements where +//! \c pred(*it1, *it2) is false in [first1, last1) and the range +//! starting at \c first2, or {last1, first2 + N} if all match. +template +BOOST_CONTAINER_FORCEINLINE std::pair +segmented_mismatch(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred) +{ + typedef segmented_iterator_traits traits; + InpIter1 mismatch1(last1); + detail_algo::segmented_mismatch_ref + (first1, last1, first2, mismatch1, pred, typename traits::is_segmented_iterator()); + return std::pair(mismatch1, first2); +} + +//! Returns a pair of iterators to the first mismatching elements +//! in [first1, last1) and the range starting at \c first2, or +//! {last1, first2 + N} if all elements match. +template +BOOST_CONTAINER_FORCEINLINE std::pair +segmented_mismatch(InpIter1 first1, Sent last1, InpIter2 first2) +{ + return boost::container::segmented_mismatch(first1, last1, first2, detail_algo::mismatch_equal()); +} + +} // namespace container +} // namespace boost + +#include + +#endif // BOOST_CONTAINER_EXPERIMENTAL_SEGMENTED_MISMATCH_HPP