• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

celerity / celerity-runtime / 9945915519

15 Jul 2024 08:07PM UTC coverage: 93.077% (-1.3%) from 94.362%
9945915519

push

github

fknorr
Rename existing backend / executor -> legacy_backend / legacy_executor

Names 'backend' and 'executor' will be re-used, but we want to keep the
old APIs around in the meantime to keep changesets small.

3188 of 3687 branches covered (86.47%)

Branch coverage included in aggregate %.

17 of 23 new or added lines in 6 files covered. (73.91%)

95 existing lines in 8 files now uncovered.

7232 of 7508 relevant lines covered (96.32%)

169246.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.74
/include/grid.h
1
#pragma once
2

3
#include <algorithm>
4
#include <iterator>
5
#include <limits>
6
#include <numeric>
7

8
#include <gch/small_vector.hpp>
9

10
#include "ranges.h"
11
#include "workaround.h"
12

13
namespace celerity::detail {
14

15
template <int Dims>
16
class box;
17

18
template <int Dims>
19
class region;
20

21
} // namespace celerity::detail
22

23
namespace celerity::detail::grid_detail {
24

25
struct normalized_t {
26
} inline constexpr normalized;
27

28
struct non_empty_t {
29
} inline constexpr non_empty;
30

31
template <int Dims, typename... Params>
32
box<Dims> make_box(Params&&... args) {
130,511✔
33
        return box<Dims>(std::forward<Params>(args)...);
130,511✔
34
}
35

36
template <int Dims, typename... Params>
37
region<Dims> make_region(Params&&... args) {
64,226✔
38
        return region<Dims>(std::forward<Params>(args)...);
64,226✔
39
}
40

41
template <typename InputIterator>
42
int get_effective_dims(const InputIterator begin, const InputIterator end) {
197,691✔
43
        return std::accumulate(begin, end, 0, [](const int min_dims, const auto& box) { return std::max(min_dims, box.get_effective_dims()); });
435,045✔
44
}
45

46
} // namespace celerity::detail::grid_detail
47

48
namespace celerity::detail {
49

50
/// An arbitrary-dimensional box described by its minimum and maximum points.
51
template <int Dims>
52
class box /* class instead of struct: enforces min <= max invariant */ {
53
  public:
54
        static_assert(Dims >= 0);
55
        static constexpr int dimensions = Dims;
56

57
        /// Construct an empty box for Dims > 0, and a unit-sized box for Dims == 0
58
        box() = default;
100,262✔
59

60
        /// Construct a box from two points where `min` must be less or equal to `max` in every dimension.
61
        /// Empty boxes are normalized to [0,0,0] - [0,0,0], meaning that every box-shaped set of points has a unique representation.
62
        box(const id<Dims>& min, const id<Dims>& max) {
502,544✔
63
                bool non_empty = true;
502,504✔
64
                for(int d = 0; d < Dims; ++d) {
1,533,597✔
65
                        // Ideally all coordinates would be signed types, but since id and range must be unsigned to conform with SYCL, we trap size_t overflows and
66
                        // incorrect casts from negative integers in user code in this assertion.
67
                        CELERITY_DETAIL_ASSERT_ON_HOST(std::max(min[d], max[d]) < std::numeric_limits<size_t>::max() / 2 && "potential integer overflow detected");
1,031,228✔
68
                        // Precondition:
69
                        CELERITY_DETAIL_ASSERT_ON_HOST(min[d] <= max[d]);
1,031,122✔
70
                        non_empty &= min[d] < max[d];
1,031,089✔
71
                }
72
                m_min = non_empty ? min : id<Dims>{};
515,809✔
73
                m_max = non_empty ? max : id<Dims>{};
515,819✔
74
        }
502,401✔
75

76
        box(const subrange<Dims>& other) : box(other.offset, other.offset + other.range) {
112,309✔
77
#if CELERITY_DETAIL_ENABLE_DEBUG
78
                for(int d = 0; d < Dims; ++d) {
376,622✔
79
                        CELERITY_DETAIL_ASSERT_ON_HOST(other.range[d] < std::numeric_limits<size_t>::max() - other.offset[d]);
264,433✔
80
                }
81
#endif
82
        }
112,189✔
83

84
        static box full_range(const range<Dims>& range) { return box(id<Dims>(), id<Dims>(range)); }
2,462✔
85

86
        bool empty() const {
750,275✔
87
                if constexpr(Dims > 0) {
88
                        return m_max[0] == 0; // empty boxes are normalized to [0,0,0] - [0,0,0]
750,240✔
89
                } else {
90
                        return false; // edge case: min == max, but 0-dimensional boxes are always size 1
35✔
91
                }
92
        }
93

94
        const id<Dims>& get_min() const { return m_min; }
4,842,574✔
95
        const id<Dims>& get_max() const { return m_max; }
3,164,598✔
96

97
        const id<Dims>& get_offset() const { return m_min; }
108,709✔
98
        range<Dims> get_range() const { return range_cast<Dims>(m_max - m_min); }
1,671,437✔
99
        subrange<Dims> get_subrange() const { return {get_offset(), get_range()}; }
79,817✔
100
        operator subrange<Dims>() const { return get_subrange(); }
3✔
101

102
        /// Counts the number of points covered by the region.
103
        size_t get_area() const { return get_range().size(); }
27,346✔
104

105
        /// Returns the smallest dimensionality that `*this` can be `box_cast` to.
106
        int get_effective_dims() const {
286,954✔
107
                if(empty()) return 1; // edge case: a 0-dimensional box is always non-empty
286,954✔
108
                for(int dims = Dims; dims > 0; --dims) {
631,526✔
109
                        if(m_max[dims - 1] > 1) { return dims; }
530,654✔
110
                }
111
                return 0;
100,872✔
112
        }
113

114
        bool covers(const box& other) const {
34,614✔
115
                // empty boxes are normalized and thus may not intersect in coordinates
116
                if(other.empty()) return true;
34,614✔
117

118
                for(int d = 0; d < Dims; ++d) {
78,581✔
119
                        if(other.m_min[d] < m_min[d]) return false;
47,974✔
120
                        if(other.m_max[d] > m_max[d]) return false;
46,553✔
121
                }
122
                return true;
30,607✔
123
        }
124

125
        friend bool operator==(const box& lhs, const box& rhs) { return lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max; }
3,666,757✔
126
        friend bool operator!=(const box& lhs, const box& rhs) { return !operator==(lhs, rhs); }
127

128
  private:
129
        template <int D, typename... P>
130
        friend box<D> grid_detail::make_box(P&&... args);
131

132
        id<Dims> m_min;
133
        id<Dims> m_max;
134

135
        // fast code path for grid algorithms that does not attempt to normalize empty boxes
136
        box(grid_detail::non_empty_t /* tag */, const id<Dims>& min, const id<Dims>& max) : m_min(min), m_max(max) {
130,504✔
137
#if CELERITY_DETAIL_ENABLE_DEBUG
138
                for(int d = 0; d < Dims; ++d) {
498,251✔
139
                        CELERITY_DETAIL_ASSERT_ON_HOST(min[d] < max[d]);
367,748✔
140
                }
141
#endif
142
        }
130,503✔
143
};
144

145
/// Boxes can be cast between dimensionalities as long as no information is lost (i.e. a cast to a higher dimensionality is always round-trip safe).
146
template <int DimsOut, int DimsIn>
147
box<DimsOut> box_cast(const box<DimsIn>& in) {
68,488✔
148
        CELERITY_DETAIL_ASSERT_ON_HOST(in.get_effective_dims() <= DimsOut);
68,488✔
149
        return box<DimsOut>(subrange_cast<DimsOut>(in.get_subrange())); // cast through subrange to fill missing range dimensions with 1s
104,274✔
150
}
151

152
template <int Dims>
153
box<Dims> bounding_box(const box<Dims>& box1, const box<Dims>& box2) {
972✔
154
        // empty boxes are covered by any other box, but their normalized coordinates should not contribute to the bounding box
155
        if(box1.empty()) return box2;
972!
156
        if(box2.empty()) return box1;
972!
157

158
        const auto min = id_min(box1.get_min(), box2.get_min());
972✔
159
        const auto max = id_max(box1.get_max(), box2.get_max());
972✔
160
        return box(min, max);
972✔
161
}
162

163
template <typename InputIterator>
164
auto bounding_box(InputIterator begin, const InputIterator end) {
12,591✔
165
        using box_type = typename std::iterator_traits<InputIterator>::value_type;
166
        if(begin == end) {
12,591✔
167
                assert(box_type::dimensions > 0); // box<0> can never be empty
168
                return box_type();
5✔
169
        }
170

171
        const auto init = *begin;
12,590✔
172
        return std::accumulate(++begin, end, init, bounding_box<box_type::dimensions>);
12,590✔
173
}
174

175
template <typename Range>
176
auto bounding_box(const Range& range) {
547✔
177
        using std::begin, std::end;
178
        return bounding_box(begin(range), end(range));
547✔
179
}
180

181
template <int Dims>
182
box<Dims> box_intersection(const box<Dims>& box1, const box<Dims>& box2) {
120,806✔
183
        const auto min = id_max(box1.get_min(), box2.get_min());
120,806✔
184
        const auto max = id_min(box1.get_max(), box2.get_max());
120,806✔
185
        for(int d = 0; d < Dims; ++d) {
283,945✔
186
                if(min[d] >= max[d]) return {};
289,832✔
187
        }
188
        return {min, max};
79,555✔
189
}
190

191
/// Comparison operator (similar to std::less) that orders boxes by their minimum, then their maximum, both starting with the first ("slowest") dimension.
192
/// This ordering is somewhat arbitrary but allows equality comparisons between ordered sequences of boxes (i.e., regions)
193
struct box_coordinate_order {
194
        template <int Dims>
195
        bool operator()(const box<Dims>& lhs, const box<Dims>& rhs) const {
30,648✔
196
                for(int d = 0; d < Dims; ++d) {
40,946✔
197
                        if(lhs.get_min()[d] < rhs.get_min()[d]) return true;
40,941✔
198
                        if(lhs.get_min()[d] > rhs.get_min()[d]) return false;
27,295✔
199
                }
200
                for(int d = 0; d < Dims; ++d) {
5!
201
                        if(lhs.get_max()[d] < rhs.get_max()[d]) return true;
5✔
202
                        if(lhs.get_max()[d] > rhs.get_max()[d]) return false;
2!
203
                }
UNCOV
204
                return false;
×
205
        }
206
};
207

208
template <int Dims>
209
using box_vector = gch::small_vector<box<Dims>>;
210

211
template <int DimsOut, int DimsIn>
212
box_vector<DimsOut> boxes_cast(const box_vector<DimsIn>& in) {
10✔
213
        assert(grid_detail::get_effective_dims(in.begin(), in.end()) <= DimsOut);
10✔
214
        box_vector<DimsOut> out(in.size(), box<DimsOut>());
70✔
215
        std::transform(in.begin(), in.end(), out.begin(), box_cast<DimsOut, DimsIn>);
10✔
216
        return out;
10✔
217
}
×
218

219
/// An arbitrary-dimensional set of points described by a normalized tiling of boxes.
220
template <int Dims>
221
class region {
222
  public:
223
        constexpr static int dimensions = Dims;
224
        using box = detail::box<Dims>;
225
        using box_vector = detail::box_vector<Dims>;
226

227
        region() = default;
95,784✔
228
        region(const box& single_box);
229
        region(const subrange<Dims>& single_sr);
230

231
        /// Constructs a region by normalizing an arbitrary, potentially-overlapping tiling of boxes.
232
        explicit region(box_vector&& boxes);
233

234
        const box_vector& get_boxes() const& { return m_boxes; }
447,764✔
235

236
        box_vector into_boxes() && { return std::move(m_boxes); }
5,235✔
237

238
        bool empty() const { return m_boxes.empty(); }
335,046✔
239

240
        /// Counts the number of points covered by the region.
241
        size_t get_area() const {
3,353✔
242
                return std::accumulate(m_boxes.begin(), m_boxes.end(), size_t{0}, [](const size_t area, const box& box) { return area + box.get_area(); });
6,679✔
243
        }
244

245
        /// Returns the smallest dimensionality that `*this` can be `region_cast` to.
246
        int get_effective_dims() const { return grid_detail::get_effective_dims(m_boxes.begin(), m_boxes.end()); }
182,124✔
247

248
        friend bool operator==(const region& lhs, const region& rhs) { return lhs.m_boxes == rhs.m_boxes; }
16,383✔
249
        friend bool operator!=(const region& lhs, const region& rhs) { return !(lhs == rhs); }
1,997✔
250

251
  private:
252
        template <int D, typename... P>
253
        friend region<D> grid_detail::make_region(P&&... args);
254

255
        box_vector m_boxes;
256

257
        region(grid_detail::normalized_t, box_vector&& boxes);
258
};
259

260
} // namespace celerity::detail
261

262
namespace celerity::detail::grid_detail {
263

264
// forward-declaration for tests (explicitly instantiated)
265
template <int StorageDims>
266
void dissect_box(const box<StorageDims>& in_box, const std::vector<std::vector<size_t>>& cuts, box_vector<StorageDims>& out_dissected, int dim);
267

268
// forward-declaration for tests (explicitly instantiated)
269
template <int MergeDim, int EffectiveDims, typename BidirectionalIterator>
270
BidirectionalIterator merge_connected_boxes_along_dim(const BidirectionalIterator begin, const BidirectionalIterator end);
271

272
// forward-declaration for tests (explicitly instantiated)
273
template <int Dims>
274
void normalize(box_vector<Dims>& boxes);
275

276
// rvalue shortcut for normalize(lvalue)
277
template <int Dims>
278
box_vector<Dims>&& normalize(box_vector<Dims>&& boxes) {
8✔
279
        normalize(boxes);
8✔
280
        return std::move(boxes);
8✔
281
}
282

283
} // namespace celerity::detail::grid_detail
284

285
namespace celerity::detail {
286

287
template <int DimsOut, int DimsIn>
288
region<DimsOut> region_cast(const region<DimsIn>& in) {
10✔
289
        assert(in.get_effective_dims() <= DimsOut);
10✔
290
        // a normalized region will remain normalized after the cast
291
        return grid_detail::make_region<DimsOut>(grid_detail::normalized, boxes_cast<DimsOut>(in.get_boxes()));
10✔
292
}
293

294
template <int Dims>
295
box<Dims> bounding_box(const region<Dims>& region) {
12,044✔
296
        return bounding_box(region.get_boxes().begin(), region.get_boxes().end());
12,044✔
297
}
298

299
template <int Dims>
300
void merge_connected_boxes(box_vector<Dims>& boxes);
301

302
template <int Dims>
303
region<Dims> region_union(const region<Dims>& lhs, const region<Dims>& rhs);
304

305
template <int Dims>
306
region<Dims> region_union(const region<Dims>& lhs, const box<Dims>& rhs) {
31,270✔
307
        return region_union(lhs, region(rhs));
62,539✔
308
}
309

310
template <int Dims>
311
region<Dims> region_union(const box<Dims>& lhs, const region<Dims>& rhs) {
3,697✔
312
        return region_union(region(lhs), rhs);
7,394✔
313
}
314

315
template <int Dims>
316
region<Dims> region_union(const box<Dims>& lhs, const box<Dims>& rhs) {
3✔
317
        return region(box_vector<Dims>{lhs, rhs});
12✔
318
}
319

320
template <int Dims>
321
region<Dims> region_intersection(const region<Dims>& lhs, const region<Dims>& rhs);
322

323
template <int Dims>
324
region<Dims> region_intersection(const region<Dims>& lhs, const box<Dims>& rhs) {
43,896✔
325
        return region_intersection(lhs, region(rhs));
87,793✔
326
}
327

328
template <int Dims>
329
region<Dims> region_intersection(const box<Dims>& lhs, const region<Dims>& rhs) {
18,205✔
330
        return region_intersection(region(lhs), rhs);
36,410✔
331
}
332

333
template <int Dims>
334
region<Dims> region_difference(const region<Dims>& lhs, const region<Dims>& rhs);
335

336
template <int Dims>
337
region<Dims> region_difference(const region<Dims>& lhs, const box<Dims>& rhs) {
4,360✔
338
        return region_difference(lhs, region(rhs));
8,720✔
339
}
340

341
template <int Dims>
342
region<Dims> region_difference(const box<Dims>& lhs, const region<Dims>& rhs) {
8,579✔
343
        return region_difference(region(lhs), rhs);
17,151✔
344
}
345

346
template <int Dims>
347
region<Dims> region_difference(const box<Dims>& lhs, const box<Dims>& rhs) {
422✔
348
        return region_difference(region(lhs), region(rhs));
844✔
349
}
350

351
} // namespace celerity::detail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc