Line data Source code
1 : // SPDX-License-Identifier: BSD-3-Clause 2 : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 3 : /// @file 4 : /// @author Simon Heybrock 5 : #include <algorithm> 6 : #include <numeric> 7 : 8 : #include "scipp/common/except.h" 9 : #include "scipp/core/element/arithmetic.h" 10 : #include "scipp/core/element/cumulative.h" 11 : #include "scipp/core/subbin_sizes.h" 12 : 13 : namespace scipp::core { 14 : 15 94156 : SubbinSizes::SubbinSizes(const scipp::index offset, container_type &&sizes) 16 94156 : : m_offset(offset), m_sizes(std::move(sizes)) { 17 94156 : if (offset < 0) 18 0 : throw std::logic_error("Bad offset in class SubbinSizes."); 19 94156 : } 20 : 21 72028 : void SubbinSizes::operator=(const scipp::index value) { 22 13552013 : for (auto &size : m_sizes) 23 13479985 : size = value; 24 72028 : } 25 : 26 42814 : SubbinSizes &SubbinSizes::operator+=(const SubbinSizes &other) { 27 42814 : if (other.offset() < offset()) // avoid realloc if possible 28 2 : return *this = *this + other; 29 42812 : scipp::index current = other.offset() - offset(); 30 42812 : const auto length = current + scipp::size(other.sizes()); 31 42812 : if (length > scipp::size(sizes())) 32 14396 : m_sizes.resize(length); 33 2411581 : for (const auto &x : other.sizes()) 34 2368769 : m_sizes[current++] += x; 35 42812 : return *this; 36 : } 37 : 38 5 : SubbinSizes &SubbinSizes::operator-=(const SubbinSizes &other) { 39 5 : return *this = *this - other; 40 : } 41 : 42 29230 : SubbinSizes SubbinSizes::cumsum_exclusive() const { 43 29230 : auto out = sizes(); 44 29230 : scipp::index sum = 0; 45 11160338 : for (auto &x : out) 46 11131108 : element::exclusive_scan(sum, x); 47 58460 : return {offset(), std::move(out)}; 48 29230 : } 49 : 50 29230 : scipp::index SubbinSizes::sum() const { 51 29230 : return std::accumulate(sizes().begin(), sizes().end(), scipp::index{0}); 52 : } 53 : 54 111542 : SubbinSizes &SubbinSizes::add_intersection(const SubbinSizes &other) { 55 111542 : scipp::index delta = other.offset() - offset(); 56 111542 : auto i = std::max(scipp::index(0), delta); 57 111542 : auto j = std::max(scipp::index(0), -delta); 58 14223888 : for (; j < scipp::size(other.sizes()) && i < scipp::size(sizes()); ++j, ++i) { 59 14112346 : m_sizes[i] += other.sizes()[j]; 60 : } 61 111542 : return *this; 62 : } 63 : 64 43 : bool operator==(const SubbinSizes &a, const SubbinSizes &b) { 65 43 : return (a.offset() == b.offset()) && (a.sizes() == b.sizes()); 66 : } 67 : 68 : template <class Op> 69 17 : SubbinSizes binary(const SubbinSizes &a, const SubbinSizes &b, Op op) { 70 17 : const auto begin = std::min(a.offset(), b.offset()); 71 17 : const auto end = 72 17 : std::max(a.offset() + a.sizes().size(), b.offset() + b.sizes().size()); 73 17 : typename SubbinSizes::container_type sizes(end - begin); 74 17 : scipp::index current = a.offset() - begin; 75 81 : for (const auto &size : a.sizes()) 76 64 : sizes[current++] += size; 77 17 : current = b.offset() - begin; 78 44 : for (const auto &size : b.sizes()) 79 27 : op(sizes[current++], size); 80 34 : return {begin, std::move(sizes)}; 81 17 : } 82 : 83 7 : SubbinSizes operator+(const SubbinSizes &a, const SubbinSizes &b) { 84 7 : return binary(a, b, element::add_equals); 85 : } 86 : 87 10 : SubbinSizes operator-(const SubbinSizes &a, const SubbinSizes &b) { 88 10 : return binary(a, b, element::subtract_equals); 89 : } 90 : 91 : /** Perform a step of an exclusive scan. 92 : * 93 : * The instance is the accumulant, the argument is the next value to be added. 94 : * Trims the accumulant to the offset and size of the argument. 95 : * 96 : * Note that effective cache use matters here, so we do not implemented this 97 : * via suboperations but a single loop. 98 : */ 99 42811 : void SubbinSizes::exclusive_scan(SubbinSizes &x) { 100 42811 : const scipp::index osize = scipp::size(sizes()); 101 42811 : const scipp::index size = scipp::size(x.sizes()); 102 42811 : const auto delta = x.offset() - offset(); 103 42811 : m_sizes.reserve(size); 104 42811 : m_offset = x.m_offset; 105 2411581 : for (scipp::index i = 0; i < size; ++i) { 106 2368770 : const auto prev = delta + i >= osize ? 0 : m_sizes[delta + i]; 107 2368770 : if (i >= osize) 108 3710 : m_sizes.push_back(prev + x.m_sizes[i]); 109 : else 110 2365060 : m_sizes[i] = prev + x.m_sizes[i]; 111 2368770 : x.m_sizes[i] = prev; 112 : } 113 42811 : m_sizes.resize(size); 114 42811 : } 115 : 116 : } // namespace scipp::core