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>
19namespace stratax::ops::detail {
34inline std::ptrdiff_t clamp(std::ptrdiff_t value, std::ptrdiff_t lower, std::ptrdiff_t upper)
47inline ResolvedSlice normalize_slice(
52 if (extent >
static_cast<std::size_t
>(std::numeric_limits<std::ptrdiff_t>::max()))
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();
73 start = clamp(start, 0, n);
74 stop = clamp(stop, 0, n);
81 const std::ptrdiff_t distance = stop - start;
82 const std::size_t count =
static_cast<std::size_t
>((distance + step - 1) / step);
86 start = clamp(start, -1, n - 1);
87 stop = clamp(stop, -1, n - 1);
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);
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...>)
114 return stratax::core::Shape{ranges[Is].size()...};
125template<std::
size_t N>
126stratax::core::Shape shape_from_slices(
127 const std::array<stratax::core::Slice, N>& ranges)
129 return shape_from_slices_impl(ranges, std::make_index_sequence<N>{});
154 const auto resolved = stratax::ops::detail::normalize_slice(
157 "Vector slice out of bounds.");
161 std::ptrdiff_t source = resolved.start;
162 for (std::size_t i = 0; i < result.size(); ++i)
164 result[i] = vec[
static_cast<std::size_t
>(source)];
165 source += resolved.step;
194 const auto resolved_rows = stratax::ops::detail::normalize_slice(
197 "Matrix row slice out of bounds.");
198 const auto resolved_cols = stratax::ops::detail::normalize_slice(
201 "Matrix column slice out of bounds.");
205 std::ptrdiff_t source_row = resolved_rows.start;
206 for (std::size_t out_row = 0; out_row < result.rows(); ++out_row)
208 std::ptrdiff_t source_col = resolved_cols.start;
209 for (std::size_t out_col = 0; out_col < result.cols(); ++out_col)
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;
216 source_row += resolved_rows.step;
222template<
typename T,
typename... Slices>
244 (std::is_same_v<Slices, stratax::core::Slice> && ...),
245 "All arguments must be Slice."
250 stratax::core::validation::require_rank(
253 "Slice rank must match tensor rank.");
256 std::array<std::size_t,
sizeof...(Slices)> out_dims{};
257 for (std::size_t dim = 0; dim < ranges.size(); ++dim)
259 resolved[dim] = stratax::ops::detail::normalize_slice(
262 "Tensor slice out of bounds.");
263 out_dims[dim] = resolved[dim].size;
267 std::vector<std::size_t>(out_dims.begin(), out_dims.end()));
270 const auto& tensor_strides = tensor.
strides();
277 for (std::size_t flat = 0; flat < result.size(); ++flat)
279 std::size_t remainder = flat;
280 std::size_t source_offset = 0;
282 for (std::size_t dim = 0; dim < resolved.size(); ++dim)
284 const std::size_t index = remainder / result_strides(dim);
285 remainder %= result_strides(dim);
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),
293 "Tensor slice offset overflow.");
295 stratax::core::validation::checked_add(
298 "Tensor slice offset overflow.");
301 result[flat] = tensor[source_offset];
325 const std::vector<stratax::core::Slice>& slices
328 stratax::core::validation::require_rank(
331 "Slice rank must match tensor rank.");
333 std::vector<stratax::ops::detail::ResolvedSlice> resolved(slices.size());
334 std::vector<std::size_t> out_dims(slices.size());
336 for (std::size_t dim = 0; dim < slices.size(); ++dim)
338 resolved[dim] = stratax::ops::detail::normalize_slice(
341 "Tensor slice out of bounds.");
342 out_dims[dim] = resolved[dim].size;
354 const auto& tensor_strides = tensor.
strides();
356 for (std::size_t flat = 0; flat < result.size(); ++flat)
358 std::size_t remainder = flat;
359 std::size_t source_offset = 0;
361 for (std::size_t dim = 0; dim < resolved.size(); ++dim)
363 const std::size_t index = remainder / result_strides(dim);
364 remainder %= result_strides(dim);
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),
372 "Tensor slice offset overflow.");
374 stratax::core::validation::checked_add(
377 "Tensor slice offset overflow.");
380 result[flat] = tensor[source_offset];
Shared runtime validation helpers.
Signals an invalid index access.
Stores a rank-2 Stratax array in row-major order.
std::size_t rows() const noexcept
Returns the number of rows.
std::size_t cols() const noexcept
Returns the number of columns.
Stores an N-dimensional Stratax array in contiguous memory.
const core::Shape & shape() const noexcept
Returns the tensor shape.
std::size_t rank() const noexcept
Returns the tensor rank.
const core::Strides & strides() const noexcept
Returns the tensor strides.
Stores a rank-1 Stratax array in contiguous memory.
std::size_t size() const noexcept
Returns the number of stored elements.
Stores a list of dimension lengths for an array shape.
Represents a half-open strided range of indices.
Stores strides for a shape in contiguous memory.
Normalized slice metadata for a concrete axis extent.
std::ptrdiff_t step
Step between selected indices.
std::ptrdiff_t start
First flat index selected by the slice.
std::size_t size
Number of selected elements.