1+ /* **************************************************************************
2+ * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
3+ * Martin Renou *
4+ * Copyright (c) QuantStack *
5+ * Copyright (c) Serge Guelton *
6+ * *
7+ * Distributed under the terms of the BSD 3-Clause License. *
8+ * *
9+ * The full license is in the file LICENSE, distributed with this software. *
10+ ****************************************************************************/
11+
12+ #ifndef XSIMD_ALGORITHMS_MIN_ELEMENT_HPP
13+ #define XSIMD_ALGORITHMS_MIN_ELEMENT_HPP
14+
15+ #include " xsimd/xsimd.hpp"
16+
17+ #include < iterator>
18+
19+ namespace xsimd
20+ {
21+ namespace detail
22+ {
23+ template <class Arch = default_arch, class ForwardIterator , class Compare >
24+ ForwardIterator sequential_min_element (ForwardIterator first, ForwardIterator last, Compare&& comp) noexcept
25+ {
26+ if (first == last)
27+ {
28+ return last;
29+ }
30+
31+ ForwardIterator min_it = first;
32+
33+ for (++first; first != last; ++first)
34+ {
35+ if (comp (*min_it, *first))
36+ {
37+ min_it = first;
38+ }
39+ }
40+
41+ return min_it;
42+ }
43+ }
44+
45+ template <class Arch = default_arch, class ContiguousIterator , class Compare >
46+ ContiguousIterator min_element (ContiguousIterator first, ContiguousIterator last, Compare&& comp) noexcept
47+ {
48+ using value_type = typename std::decay<decltype (*first)>::type;
49+ using batch_type = batch<value_type, Arch>;
50+
51+ const std::size_t size = static_cast <std::size_t >(std::distance (first, last));
52+ constexpr std::size_t simd_size = batch_type::size;
53+
54+ // Quick return for small ranges
55+ if (size < simd_size)
56+ {
57+ return detail::sequential_min_element (first, last, std::forward<Compare>(comp));
58+ }
59+
60+ // prologue: process elements until the first aligned one
61+ // compute alignment offsets
62+ const auto * const ptr_begin = &(*first);
63+ const std::size_t align_begin = xsimd::get_alignment_offset (ptr_begin, size, simd_size);
64+ const std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1 ));
65+
66+ const auto align_begin_it = std::next (first, align_begin);
67+ const auto align_end_it = std::next (first, align_end);
68+
69+ auto min_it = detail::sequential_min_element (first, align_begin_it, std::forward<Compare>(comp));
70+ auto current_min = *min_it;
71+ auto current_min_index = std::distance (first, min_it);
72+
73+ // Generate a batch of the current min value
74+ batch_type current_min_batch = batch_type (current_min);
75+
76+ // main loop: process aligned elements
77+ for (auto current = align_begin_it; current != align_end_it; std::advance (current, simd_size))
78+ {
79+ batch_type batch_val = batch_type::load_aligned (&(*current));
80+
81+ // Compare the current batch with the current min batch
82+ auto mask = comp (current_min_batch, batch_val);
83+
84+ // Update ?
85+ if (xsimd::any (mask))
86+ {
87+ // Update the current min batch
88+ current_min_batch = xsimd::select (mask, batch_val, current_min_batch);
89+ // extract the min value
90+ current_min = xsimd::reduce_min (current_min_batch);
91+ // retrieve position of the min value in the current batch
92+ auto min_pos = std::find (current, current + simd_size, current_min);
93+ if (min_pos != current + simd_size)
94+ {
95+ current_min_index = std::distance (first, min_pos);
96+ }
97+ }
98+ }
99+
100+ min_it = std::next (first, current_min_index);
101+
102+ for (auto current = align_end_it; current != last; ++current)
103+ {
104+ if (comp (current_min, *current))
105+ {
106+ current_min = *current;
107+ min_it = current;
108+ }
109+ }
110+ return min_it;
111+ }
112+
113+ template <class Arch = default_arch, class ContiguousIterator >
114+ ContiguousIterator min_element (ContiguousIterator first, ContiguousIterator last) noexcept
115+ {
116+ return xsimd::min_element<Arch>(first, last, [](const auto & lhs, const auto & rhs) { return lhs > rhs; });
117+ }
118+
119+ } // namespace xsimd
120+
121+ #endif // XSIMD_ALGORITHMS_MIN_ELEMENT_HPP
0 commit comments