Stratax 0.2.0
Loading...
Searching...
No Matches
Slice.hpp
1#pragma once
2
3#include <stratax/core/containers/Matrix.hpp>
4#include <stratax/core/containers/Tensor.hpp>
5#include <stratax/core/containers/Vector.hpp>
6#include <stratax/core/Exceptions.hpp>
7#include <stratax/core/containers/Shape.hpp>
8#include <stratax/core/Slice.hpp>
9#include <stratax/core/containers/Strides.hpp>
11
12#include <array>
13#include <cstddef>
14#include <limits>
15#include <vector>
16#include <type_traits>
17#include <utility>
18
19namespace stratax::ops::detail {
20
23{
25 std::ptrdiff_t start;
26
28 std::ptrdiff_t step;
29
31 std::size_t size;
32};
33
34inline std::ptrdiff_t clamp(std::ptrdiff_t value, std::ptrdiff_t lower, std::ptrdiff_t upper)
35{
36 if (value < lower)
37 {
38 return lower;
39 }
40 if (value > upper)
41 {
42 return upper;
43 }
44 return value;
45}
46
47inline ResolvedSlice normalize_slice(
48 const stratax::core::Slice& slice,
49 std::size_t extent,
50 const char* message)
51{
52 if (extent > static_cast<std::size_t>(std::numeric_limits<std::ptrdiff_t>::max()))
53 {
54 throw Exceptions::IndexError(message);
55 }
56
57 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(extent);
58 std::ptrdiff_t start = slice.start();
59 std::ptrdiff_t stop = slice.stop();
60 const std::ptrdiff_t step = slice.step();
61
62 if (start < 0)
63 {
64 start += n;
65 }
66 if (stop < 0)
67 {
68 stop += n;
69 }
70
71 if (step > 0)
72 {
73 start = clamp(start, 0, n);
74 stop = clamp(stop, 0, n);
75
76 if (start >= stop)
77 {
78 return ResolvedSlice{start, step, 0};
79 }
80
81 const std::ptrdiff_t distance = stop - start;
82 const std::size_t count = static_cast<std::size_t>((distance + step - 1) / step);
83 return ResolvedSlice{start, step, count};
84 }
85
86 start = clamp(start, -1, n - 1);
87 stop = clamp(stop, -1, n - 1);
88
89 if (start <= stop)
90 {
91 return ResolvedSlice{start, step, 0};
92 }
93
94 const std::ptrdiff_t stride = -step;
95 const std::ptrdiff_t distance = start - stop;
96 const std::size_t count = static_cast<std::size_t>((distance + stride - 1) / stride);
97 return ResolvedSlice{start, step, count};
98}
99
109template<std::size_t N, std::size_t... Is>
110stratax::core::Shape shape_from_slices_impl(
111 const std::array<stratax::core::Slice, N>& ranges,
112 std::index_sequence<Is...>)
113{
114 return stratax::core::Shape{ranges[Is].size()...};
115}
116
125template<std::size_t N>
126stratax::core::Shape shape_from_slices(
127 const std::array<stratax::core::Slice, N>& ranges)
128{
129 return shape_from_slices_impl(ranges, std::make_index_sequence<N>{});
130}
131
132}
133
134template<typename T>
149slice(
151 const stratax::core::Slice& slice
152)
153{
154 const auto resolved = stratax::ops::detail::normalize_slice(
155 slice,
156 vec.size(),
157 "Vector slice out of bounds.");
158
159 stratax::container::Vector<T> result(resolved.size);
160
161 std::ptrdiff_t source = resolved.start;
162 for (std::size_t i = 0; i < result.size(); ++i)
163 {
164 result[i] = vec[static_cast<std::size_t>(source)];
165 source += resolved.step;
166 }
167
168 return result;
169
170}
171
172template<typename T>
188slice(
190 const stratax::core::Slice& rows,
191 const stratax::core::Slice& cols
192)
193{
194 const auto resolved_rows = stratax::ops::detail::normalize_slice(
195 rows,
196 mat.rows(),
197 "Matrix row slice out of bounds.");
198 const auto resolved_cols = stratax::ops::detail::normalize_slice(
199 cols,
200 mat.cols(),
201 "Matrix column slice out of bounds.");
202
203 stratax::container::Matrix<T> result(resolved_rows.size, resolved_cols.size);
204
205 std::ptrdiff_t source_row = resolved_rows.start;
206 for (std::size_t out_row = 0; out_row < result.rows(); ++out_row)
207 {
208 std::ptrdiff_t source_col = resolved_cols.start;
209 for (std::size_t out_col = 0; out_col < result.cols(); ++out_col)
210 {
211 result(out_row, out_col) = mat(
212 static_cast<std::size_t>(source_row),
213 static_cast<std::size_t>(source_col));
214 source_col += resolved_cols.step;
215 }
216 source_row += resolved_rows.step;
217 }
218
219 return result;
220}
221
222template<typename T, typename... Slices>
238slice(
239 const stratax::container::Tensor<T>& tensor,
240 Slices... slices
241)
242{
243 static_assert(
244 (std::is_same_v<Slices, stratax::core::Slice> && ...),
245 "All arguments must be Slice."
246 );
247
248 std::array<stratax::core::Slice, sizeof...(Slices)> ranges{ slices... };
249
250 stratax::core::validation::require_rank(
251 ranges.size(),
252 tensor.rank(),
253 "Slice rank must match tensor rank.");
254
255 std::array<stratax::ops::detail::ResolvedSlice, sizeof...(Slices)> resolved{};
256 std::array<std::size_t, sizeof...(Slices)> out_dims{};
257 for (std::size_t dim = 0; dim < ranges.size(); ++dim)
258 {
259 resolved[dim] = stratax::ops::detail::normalize_slice(
260 ranges[dim],
261 tensor.shape()(dim),
262 "Tensor slice out of bounds.");
263 out_dims[dim] = resolved[dim].size;
264 }
265
266 const auto result_shape = stratax::core::Shape(
267 std::vector<std::size_t>(out_dims.begin(), out_dims.end()));
268 stratax::container::Tensor<T> result(result_shape);
269 const stratax::core::Strides result_strides(result_shape);
270 const auto& tensor_strides = tensor.strides();
271
272 if (result.empty())
273 {
274 return result;
275 }
276
277 for (std::size_t flat = 0; flat < result.size(); ++flat)
278 {
279 std::size_t remainder = flat;
280 std::size_t source_offset = 0;
281
282 for (std::size_t dim = 0; dim < resolved.size(); ++dim)
283 {
284 const std::size_t index = remainder / result_strides(dim);
285 remainder %= result_strides(dim);
286
287 const std::ptrdiff_t source_index =
288 resolved[dim].start + static_cast<std::ptrdiff_t>(index) * resolved[dim].step;
289 const std::size_t term =
290 stratax::core::validation::checked_multiply(
291 static_cast<std::size_t>(source_index),
292 tensor_strides(dim),
293 "Tensor slice offset overflow.");
294 source_offset =
295 stratax::core::validation::checked_add(
296 source_offset,
297 term,
298 "Tensor slice offset overflow.");
299 }
300
301 result[flat] = tensor[source_offset];
302 }
303
304 return result;
305}
306
307template<typename T>
323slice(
324 const stratax::container::Tensor<T>& tensor,
325 const std::vector<stratax::core::Slice>& slices
326)
327{
328 stratax::core::validation::require_rank(
329 slices.size(),
330 tensor.rank(),
331 "Slice rank must match tensor rank.");
332
333 std::vector<stratax::ops::detail::ResolvedSlice> resolved(slices.size());
334 std::vector<std::size_t> out_dims(slices.size());
335
336 for (std::size_t dim = 0; dim < slices.size(); ++dim)
337 {
338 resolved[dim] = stratax::ops::detail::normalize_slice(
339 slices[dim],
340 tensor.shape()(dim),
341 "Tensor slice out of bounds.");
342 out_dims[dim] = resolved[dim].size;
343 }
344
345 const auto result_shape = stratax::core::Shape(out_dims);
346 stratax::container::Tensor<T> result(result_shape);
347
348 if (result.empty())
349 {
350 return result;
351 }
352
353 const stratax::core::Strides result_strides(result_shape);
354 const auto& tensor_strides = tensor.strides();
355
356 for (std::size_t flat = 0; flat < result.size(); ++flat)
357 {
358 std::size_t remainder = flat;
359 std::size_t source_offset = 0;
360
361 for (std::size_t dim = 0; dim < resolved.size(); ++dim)
362 {
363 const std::size_t index = remainder / result_strides(dim);
364 remainder %= result_strides(dim);
365
366 const std::ptrdiff_t source_index =
367 resolved[dim].start + static_cast<std::ptrdiff_t>(index) * resolved[dim].step;
368 const std::size_t term =
369 stratax::core::validation::checked_multiply(
370 static_cast<std::size_t>(source_index),
371 tensor_strides(dim),
372 "Tensor slice offset overflow.");
373 source_offset =
374 stratax::core::validation::checked_add(
375 source_offset,
376 term,
377 "Tensor slice offset overflow.");
378 }
379
380 result[flat] = tensor[source_offset];
381 }
382
383 return result;
384}
385
Shared runtime validation helpers.
Signals an invalid index access.
Stores a rank-2 Stratax array in row-major order.
Definition Matrix.hpp:29
std::size_t rows() const noexcept
Returns the number of rows.
Definition Matrix.hpp:195
std::size_t cols() const noexcept
Returns the number of columns.
Definition Matrix.hpp:205
Stores an N-dimensional Stratax array in contiguous memory.
Definition Tensor.hpp:31
const core::Shape & shape() const noexcept
Returns the tensor shape.
Definition Tensor.hpp:159
std::size_t rank() const noexcept
Returns the tensor rank.
Definition Tensor.hpp:149
const core::Strides & strides() const noexcept
Returns the tensor strides.
Definition Tensor.hpp:169
Stores a rank-1 Stratax array in contiguous memory.
Definition Vector.hpp:27
std::size_t size() const noexcept
Returns the number of stored elements.
Definition Vector.hpp:277
Stores a list of dimension lengths for an array shape.
Definition Shape.hpp:22
Represents a half-open strided range of indices.
Definition Slice.hpp:16
Stores strides for a shape in contiguous memory.
Definition Strides.hpp:23
Normalized slice metadata for a concrete axis extent.
Definition Slice.hpp:23
std::ptrdiff_t step
Step between selected indices.
Definition Slice.hpp:28
std::ptrdiff_t start
First flat index selected by the slice.
Definition Slice.hpp:25
std::size_t size
Number of selected elements.
Definition Slice.hpp:31