Stratax
Scientific computing containers and operations
Loading...
Searching...
No Matches
Shape.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Buffer.hpp"
4#include "Concepts.hpp"
5#include "Exceptions.hpp"
6#include "Validation.hpp"
7
8#include <ostream>
9
10namespace stratax::core {
11
19class Shape
20{
21private:
23
24 void validate_dimensions() const
25 {
26 for (std::size_t dim : dims_)
27 {
29 dim,
30 "Shape dimensions cannot be negative.");
31 }
32 }
33
34public:
35 struct allow_zero_t {};
36 static constexpr allow_zero_t allow_zero{};
37
43 Shape() noexcept = default;
44
54 template<Integral... Dims>
55 requires (sizeof...(Dims) > 0)
56 Shape(Dims... dims)
57 : dims_{static_cast<std::size_t>(dims)...}
58 {
59 validate_dimensions();
60 }
61
71 Shape(std::initializer_list<std::size_t> list, allow_zero_t allow_zero) : dims_(list)
72 {
73 (void)allow_zero;
74 }
75
82 : dims_(dims)
83 {
84 validate_dimensions();
85 }
86
94 : dims_(dims)
95 {
96 (void)allow_zero;
97 }
98
105 : dims_(std::move(dims))
106 {
107 validate_dimensions();
108 }
109
117 : dims_(std::move(dims))
118 {
119 (void)allow_zero;
120 }
121
125 ~Shape() = default;
126
137 [[nodiscard]]
138 std::size_t elements() const
139 {
140 if (empty())
141 {
142 return 0;
143 }
144 std::size_t prod = 1;
145 for (std::size_t dim : dims_)
146 {
147 prod = validation::checked_multiply(prod, dim, "Shape elements overflow");
148 }
149 return prod;
150 }
151
157 [[nodiscard]] std::size_t rank() const
158 {
159 return dims_.size();
160 }
161
171 const std::size_t& operator()(std::size_t index) const
172 {
173 validation::require_index(index, rank(), "Shape dimension index out of bounds");
174 return dims_[index];
175 }
176
182 [[nodiscard]] bool empty() const noexcept
183 {
184 return dims_.empty();
185 }
186
194 [[nodiscard]] bool operator==(const Shape& other) const noexcept
195 {
196 if (rank() != other.rank())
197 {
198 return false;
199 }
200 for (std::size_t i = 0; i < rank(); ++i)
201 {
202 if (dims_[i] != other.dims_[i])
203 {
204 return false;
205 }
206 }
207 return true;
208 }
209
217 [[nodiscard]] bool operator!=(const Shape& other) const noexcept
218 {
219 return !(*this == other);
220 }
221
227 auto begin() noexcept
228 {
229 return dims_.begin();
230 }
231
237 auto end() noexcept
238 {
239 return dims_.end();
240 }
241
247 auto begin() const noexcept
248 {
249 return dims_.begin();
250 }
251
257 auto end() const noexcept
258 {
259 return dims_.end();
260 }
261
267 void swap(Shape& other) noexcept
268 {
269 dims_.swap(other.dims_);
270 }
271
272};
273
282inline std::ostream& operator<<(std::ostream& os, const Shape& shape)
283{
284 os << "(";
285
286 bool first = true;
287 for (std::size_t dim : shape)
288 {
289 if (!first)
290 os << ", ";
291
292 os << dim;
293 first = false;
294 }
295
296 if (shape.rank() == 1)
297 {
298 os << ",";
299 }
300
301 os << ")";
302
303 return os;
304}
305
306}
Shared runtime validation helpers.
Owns contiguous dynamically allocated storage.
Definition Buffer.hpp:38
Stores a list of dimension lengths for an array shape.
Definition Shape.hpp:20
~Shape()=default
Destroys the shape.
std::size_t elements() const
Returns the total number of elements described by the shape.
Definition Shape.hpp:138
Shape(Buffer< std::size_t > &&dims, allow_zero_t allow_zero)
Creates a shape by taking ownership of dimensions without rejecting zero dimensions.
Definition Shape.hpp:116
auto begin() const noexcept
Returns a const iterator to the first stored dimension.
Definition Shape.hpp:247
bool operator!=(const Shape &other) const noexcept
Returns whether two shapes differ in rank or dimension values.
Definition Shape.hpp:217
const std::size_t & operator()(std::size_t index) const
Returns the length of a specific dimension.
Definition Shape.hpp:171
bool empty() const noexcept
Returns whether the shape has no dimensions.
Definition Shape.hpp:182
auto end() const noexcept
Returns a const iterator one past the last stored dimension.
Definition Shape.hpp:257
bool operator==(const Shape &other) const noexcept
Compares two shapes for exact rank and dimension equality.
Definition Shape.hpp:194
void swap(Shape &other) noexcept
Swaps the stored dimensions with another shape.
Definition Shape.hpp:267
auto end() noexcept
Returns an iterator one past the last stored dimension.
Definition Shape.hpp:237
Shape() noexcept=default
Creates an empty shape.
std::size_t rank() const
Returns the number of stored dimensions.
Definition Shape.hpp:157
Shape(const Buffer< std::size_t > &dims)
Creates a shape by copying dimension lengths from a buffer.
Definition Shape.hpp:81
auto begin() noexcept
Returns an iterator to the first stored dimension.
Definition Shape.hpp:227
static constexpr allow_zero_t allow_zero
Definition Shape.hpp:36
Shape(Buffer< std::size_t > &&dims)
Creates a shape by taking ownership of dimension lengths from a buffer.
Definition Shape.hpp:104
Shape(std::initializer_list< std::size_t > list, allow_zero_t allow_zero)
Creates a shape from dimension lengths without rejecting zero dimensions.
Definition Shape.hpp:71
Shape(const Buffer< std::size_t > &dims, allow_zero_t allow_zero)
Creates a shape by copying dimension lengths without rejecting zero dimensions.
Definition Shape.hpp:93
Matches signed and unsigned integral types, excluding character-like types.
Definition Concepts.hpp:55
std::size_t checked_multiply(std::size_t lhs, std::size_t rhs, const char *message)
Multiplies two sizes while checking for overflow.
void require_index(std::size_t index, std::size_t size, const char *message)
Requires an index to be less than a size.
std::size_t nonnegative_shape_dimension(long long value, const char *message)
Converts a signed shape dimension to std::size_t after rejecting negatives.
std::ostream & operator<<(std::ostream &os, const Shape &shape)
Writes a shape to a stream in tuple-like form.
Definition Shape.hpp:282