9#include "arg_wrappers.hpp"
10#include "bin_increment_cluster_encoding.hpp"
24namespace tcspc::internal {
26struct saturate_on_internal_overflow {
27 explicit saturate_on_internal_overflow() =
default;
30struct stop_on_internal_overflow {
31 explicit stop_on_internal_overflow() =
default;
35template <
typename BinIndex>
36class bin_increment_cluster_journal_encoding_adapter {
37 std::reference_wrapper<std::vector<BinIndex>> vec;
40 explicit bin_increment_cluster_journal_encoding_adapter(
41 std::vector<BinIndex> &storage)
44 [[nodiscard]]
auto available_capacity() const -> std::
size_t {
46 if constexpr (
sizeof(std::size_t) >= 8)
47 return std::numeric_limits<std::size_t>::max();
49 return vec.get().max_size() - vec.get().size();
52 [[nodiscard]]
auto make_space(std::size_t size) -> std::span<BinIndex> {
53 auto const old_size = vec.get().size();
54 vec.get().resize(old_size + size);
55 return std::span(vec.get()).subspan(old_size);
59template <
typename BinIndex>
class bin_increment_cluster_journal {
60 std::vector<BinIndex> encoded;
63 using bin_index_type = BinIndex;
66 [[nodiscard]]
auto size() const noexcept -> std::
size_t {
67 auto const decoder = bin_increment_cluster_decoder(std::span(encoded));
68 return std::size_t(std::distance(decoder.begin(), decoder.end()));
71 [[nodiscard]]
constexpr auto empty() const noexcept ->
bool {
72 return encoded.empty();
75 void clear() noexcept { encoded.clear(); }
77 void append_cluster(std::span<bin_index_type const> cluster) {
78 [[maybe_unused]]
bool const ok = encode_bin_increment_cluster(
79 bin_increment_cluster_journal_encoding_adapter(encoded), cluster);
83 using const_iterator =
84 typename bin_increment_cluster_decoder<bin_index_type>::const_iterator;
86 [[nodiscard]]
auto begin() const noexcept -> const_iterator {
88 bin_increment_cluster_decoder<bin_index_type>(encoded);
89 return const_iterator(decoder.begin());
92 [[nodiscard]]
auto end() const noexcept -> const_iterator {
93 auto const decoder = bin_increment_cluster_decoder(std::span(encoded));
94 return const_iterator(decoder.end());
97 auto operator==(bin_increment_cluster_journal
const &)
const noexcept
100 friend auto operator<<(std::ostream &s,
101 bin_increment_cluster_journal
const &j)
104 for (
auto const cluster : j) {
106 for (
auto const i : cluster)
115template <
typename BinIndex>
struct null_journal {
116 using bin_index_type = BinIndex;
117 void append_cluster(std::span<bin_index_type const> ) {}
118 void clear() noexcept {}
122template <
typename BinIndex,
typename Bin,
typename OverflowPolicy>
123class single_histogram {
125 using bin_index_type = BinIndex;
126 using bin_type = Bin;
127 static_assert(same_as_any_of<OverflowPolicy, saturate_on_internal_overflow,
128 stop_on_internal_overflow>);
131 std::span<bin_type> hist;
132 bin_type bin_max = 0;
133 std::size_t n_bins{};
137 explicit single_histogram(std::span<bin_type>
histogram,
138 arg::max_per_bin<bin_type> max_per_bin,
139 arg::num_bins<std::size_t> num_bins) noexcept
140 : hist(
histogram), bin_max(max_per_bin.value), n_bins(num_bins.value) {
144 explicit single_histogram(std::span<bin_type>
histogram,
145 single_histogram
const ¶ms) noexcept
146 : single_histogram(
histogram, arg::max_per_bin{params.bin_max},
147 arg::num_bins{params.n_bins}) {}
149 [[nodiscard]]
auto num_bins() const noexcept -> std::
size_t {
154 void clear() noexcept { std::fill(hist.begin(), hist.end(), bin_type(0)); }
156 auto max_per_bin() const noexcept -> bin_type {
return bin_max; }
161 auto apply_increments(std::span<bin_index_type const> increments)
163 assert(not hist.empty());
164 std::size_t n_applied = 0;
165 for (
auto it = increments.begin(); it != increments.end(); ++it) {
166 assert(*it >= 0 && *it < hist.size());
167 bin_type &bin = hist[*it];
171 }
else if constexpr (std::is_same_v<
173 saturate_on_internal_overflow>) {
175 }
else if constexpr (std::is_same_v<OverflowPolicy,
176 stop_on_internal_overflow>) {
179 static_assert(always_false_v<OverflowPolicy>);
188 void undo_increments(std::span<bin_index_type const> increments) {
190 std::is_same_v<OverflowPolicy, stop_on_internal_overflow>);
191 assert(not hist.empty());
192 for (bin_index_type i : increments) {
193 assert(i >= 0 && i < hist.size());
201template <
typename BinIndex,
typename Bin,
typename OverflowPolicy>
202class multi_histogram {
204 using bin_index_type = BinIndex;
205 using bin_type = Bin;
206 static_assert(same_as_any_of<OverflowPolicy, saturate_on_internal_overflow,
207 stop_on_internal_overflow>);
210 std::span<bin_type> hist_arr;
211 std::size_t element_index = 0;
212 bin_type bin_max = 0;
213 std::size_t n_bins = 0;
214 std::size_t n_elements = 0;
215 bool need_to_clear =
false;
218 explicit multi_histogram(std::span<bin_type> hist_array,
219 arg::max_per_bin<bin_type> max_per_bin,
220 arg::num_bins<std::size_t> num_bins,
221 arg::num_elements<std::size_t> num_elements,
223 : hist_arr(hist_array), bin_max(max_per_bin.value),
224 n_bins(num_bins.value), n_elements(num_elements.value),
225 need_to_clear(clear) {
226 assert(hist_array.empty() || hist_array.size() == n_bins * n_elements);
230 explicit multi_histogram(std::span<bin_type> hist_array,
231 multi_histogram
const ¶ms,
233 : multi_histogram(hist_array, arg::max_per_bin{params.bin_max},
234 arg::num_bins{params.n_bins},
235 arg::num_elements{params.n_elements}, clear) {}
237 [[nodiscard]]
auto max_per_bin() const noexcept -> bin_type {
241 [[nodiscard]]
auto num_bins() const noexcept -> std::
size_t {
245 [[nodiscard]]
auto num_elements() const noexcept -> std::
size_t {
250 [[nodiscard]]
auto is_started() const noexcept ->
bool {
251 return element_index > 0;
255 [[nodiscard]]
auto is_complete() const noexcept ->
bool {
256 return element_index >= n_elements;
270 [[nodiscard]]
auto is_consistent() const noexcept ->
bool {
271 return (not is_started() && not need_to_clear) || is_complete();
274 [[nodiscard]]
auto next_element_index() const noexcept -> std::
size_t {
275 return element_index;
281 template <
typename Journal>
282 auto apply_increment_cluster(std::span<bin_index_type const> cluster,
283 Journal &journal) ->
bool {
285 std::is_same_v<typename Journal::bin_index_type, bin_index_type>);
286 assert(not hist_arr.empty());
287 assert(not is_complete());
288 single_histogram<bin_index_type, bin_type, OverflowPolicy> single_hist(
289 hist_arr.subspan(n_bins * element_index, n_bins),
290 arg::max_per_bin{bin_max}, arg::num_bins{n_bins});
294 auto n_applied = single_hist.apply_increments(cluster);
296 if constexpr (std::is_same_v<OverflowPolicy,
297 saturate_on_internal_overflow>) {
298 journal.append_cluster(cluster);
300 return n_applied == cluster.size();
301 }
else if constexpr (std::is_same_v<OverflowPolicy,
302 stop_on_internal_overflow>) {
303 if (n_applied == cluster.size()) {
304 journal.append_cluster(cluster);
309 single_hist.undo_increments(cluster.first(n_applied));
313 static_assert(always_false_v<OverflowPolicy>);
320 void skip_remaining() {
321 assert(not hist_arr.empty());
323 auto remaining = hist_arr.subspan(n_bins * element_index);
324 std::fill(remaining.begin(), remaining.end(), bin_type(0));
325 need_to_clear =
false;
327 element_index = n_elements;
333 template <
typename Journal>
void roll_back(Journal
const &journal) {
335 std::is_same_v<typename Journal::bin_index_type, bin_index_type>);
337 std::is_same_v<OverflowPolicy, stop_on_internal_overflow>);
338 assert(not hist_arr.empty());
339 std::size_t cluster_index = 0;
340 for (
auto const cluster : journal) {
341 single_histogram<bin_index_type, bin_type, OverflowPolicy>
342 single_hist(hist_arr.subspan(n_bins * cluster_index, n_bins),
343 arg::max_per_bin{bin_max}, arg::num_bins{n_bins});
344 single_hist.undo_increments(cluster);
357 template <
typename Journal>
void replay(Journal
const &journal) {
359 std::is_same_v<typename Journal::bin_index_type, bin_index_type>);
361 std::is_same_v<OverflowPolicy, stop_on_internal_overflow>);
362 assert(not hist_arr.empty());
363 assert(not is_started());
364 std::size_t cluster_index = 0;
365 for (
auto const cluster : journal) {
366 single_histogram<bin_index_type, bin_type, OverflowPolicy>
367 single_hist(hist_arr.subspan(n_bins * cluster_index, n_bins),
368 arg::max_per_bin{bin_max}, arg::num_bins{n_bins});
371 [[maybe_unused]]
auto n_applied =
372 single_hist.apply_increments(cluster);
375 assert(n_applied ==
static_cast<std::size_t
>(cluster.size()));
378 element_index = cluster_index;
382 void reset(
bool clear)
noexcept {
384 need_to_clear = clear;
390template <
typename BinIndex,
typename Bin,
typename OverflowPolicy>
391class multi_histogram_accumulation {
393 using bin_index_type = BinIndex;
394 using bin_type = Bin;
395 static_assert(same_as_any_of<OverflowPolicy, saturate_on_internal_overflow,
396 stop_on_internal_overflow>);
399 std::size_t scan_idx = 0;
400 multi_histogram<bin_index_type, bin_type, OverflowPolicy> cur_scan;
403 explicit multi_histogram_accumulation(
404 std::span<bin_type> hist_array, arg::max_per_bin<bin_type> max_per_bin,
405 arg::num_bins<std::size_t> num_bins,
406 arg::num_elements<std::size_t> num_elements,
bool clear_first) noexcept
407 : cur_scan(hist_array, max_per_bin, num_bins, num_elements,
411 explicit multi_histogram_accumulation(
412 std::span<bin_type> hist_array,
413 multi_histogram_accumulation
const ¶ms,
bool clear_first) noexcept
414 : multi_histogram_accumulation(
415 hist_array, arg::max_per_bin{params.max_per_bin()},
416 arg::num_bins{params.num_bins()},
417 arg::num_elements{params.num_elements()}, clear_first) {}
419 [[nodiscard]]
auto max_per_bin() const noexcept -> bin_type {
420 return cur_scan.max_per_bin();
423 [[nodiscard]]
auto num_bins() const noexcept -> std::
size_t {
424 return cur_scan.num_bins();
427 [[nodiscard]]
auto num_elements() const noexcept -> std::
size_t {
428 return cur_scan.num_elements();
431 [[nodiscard]]
auto is_scan_started() const noexcept ->
bool {
432 return cur_scan.is_started();
435 [[nodiscard]]
auto is_scan_complete() const noexcept ->
bool {
436 return cur_scan.is_complete();
439 [[nodiscard]]
auto is_consistent() const noexcept ->
bool {
440 return cur_scan.is_consistent();
443 [[nodiscard]]
auto next_element_index() const noexcept -> std::
size_t {
444 return cur_scan.next_element_index();
447 [[nodiscard]]
auto scan_index() const noexcept -> std::
size_t {
454 template <
typename Journal>
455 void new_scan(Journal &journal,
bool clear =
false) {
456 assert(is_scan_complete());
458 cur_scan.reset(clear);
462 template <
typename Journal>
463 auto apply_increment_cluster(std::span<bin_index_type const> cluster,
464 Journal &journal) ->
bool {
465 assert(not is_scan_complete());
466 return cur_scan.apply_increment_cluster(cluster, journal);
469 void skip_remainder_of_current_scan() { cur_scan.skip_remaining(); }
473 template <
typename Journal>
474 void roll_back_current_scan(Journal
const &journal) {
475 cur_scan.roll_back(journal);
478 void reset(
bool clear_first =
true) noexcept {
480 cur_scan.reset(clear_first);
483 template <
typename Journal>
void replay(Journal
const &journal) {
484 cur_scan.replay(journal);
auto histogram(arg::num_bins< std::size_t > num_bins, arg::max_per_bin< typename DataTypes::bin_type > max_per_bin, std::shared_ptr< bucket_source< typename DataTypes::bin_type > > buffer_provider, Downstream downstream)
Create a processor that collects a histogram.
Definition histogram.hpp:263