|
Stratax 0.3.1
|
Version: v0.3.1
Status: Complete
Header: include/stratax/core/Shape.hpp
Shape stores the dimensions of a multidimensional array in outermost-to-innermost order. It reports the dimensional rank, computes the total element count with overflow checking, supports checked Python-style indexing, and exposes read-only contiguous iterators.
Dimension storage is owned by a Buffer<std::size_t>. Shape contains only layout metadata; it does not own an array's numeric elements.
Shape is responsible for:
It is not responsible for array element storage, stride calculation, multidimensional offset calculation, or numerical operations.
Depends on:
Buffer<std::size_t> for owned contiguous storageindexing::normalize_index for checked signed indexingExceptions::IndexError and Exceptions::DimensionErrorUsed by:
ArrayBaseVector, Matrix, and Tensorrank() equals the number of stored dimensions.empty() is equivalent to rank() == 0.Both aliases provide read-only random-access traversal. Shape intentionally does not expose mutable dimension iterators.
Constructs an empty, rank-zero shape. Its element count is zero.
Complexity: O(1).
Copies dimensions in list order. An empty list constructs a rank-zero shape, and zero-valued dimensions are accepted.
Complexity: O(dims.size()).
Throws std::bad_alloc if dimension storage cannot be allocated.
Copies dimensions from a vector while preserving their order. An empty vector constructs a rank-zero shape.
Complexity: O(dims.size()).
Throws std::bad_alloc if dimension storage cannot be allocated.
Copy construction creates independent dimension storage in O(rank()) time. Move construction transfers storage in O(1) and leaves the source empty.
Destroys the underlying dimension buffer.
Complexity: O(rank()).
Copy assignment performs a deep copy and inherits Buffer's strong exception guarantee. Its complexity is O(old rank + copied rank).
Move assignment destroys the destination's previous dimension storage and then transfers ownership. Its complexity is O(old rank), with O(1) ownership transfer. The source becomes empty. Self-copy and self-move assignment leave the shape unchanged.
Returns the number of dimensions.
Complexity: O(1).
Returns true when no dimensions are stored. A shape containing a zero dimension is not empty because it still has nonzero rank.
Complexity: O(1).
Computes canonical row-major strides and returns them as another Shape. The last stride is one, and every earlier stride is the product of dimensions to its right. A rank-zero shape produces a rank-zero stride shape.
Throws Exceptions::DimensionError when a stride product cannot be represented by std::size_t, and std::bad_alloc if result storage cannot be allocated.
Complexity: O(rank()).
Returns the product of the dimensions. A rank-zero shape or a shape with a zero-valued dimension has zero elements. Each multiplication is checked before it is performed.
Complexity: O(rank()).
Throws Exceptions::DimensionError if a nonzero product cannot be represented by std::size_t.
Returns the dimension at a zero-based index without checking bounds. Behavior is undefined unless index < rank().
Complexity: O(1).
Accepts indices in [-rank(), rank()). Non-negative indices count from the first dimension, while negative indices count backward from the last:
Complexity: O(1).
Throws Exceptions::IndexError for an out-of-range index, including every index applied to an empty shape.
Each accessor is O(1). Traversing every dimension is O(rank()). For an empty shape, each begin/end pair compares equal.
Two shapes compare equal when their ranks and corresponding dimensions are equal. In C++20, operator!= is rewritten from operator==.
Complexity: O(rank()) in the worst case; differing ranks return in O(1).
Exchanges dimension storage without copying individual values. Iterators and references continue to refer to the same dimensions, which become owned by the other shape. Swapping with an empty shape and self-swap are supported.
Complexity: O(1).
Writes tuple-style output and returns os:
Complexity: O(shape.rank()).
| Operation | Complexity |
|---|---|
| Default construction | O(1) |
| List/vector construction | O(n) |
| Copy construction | O(n) |
| Move construction | O(1) |
| Copy assignment | O(old rank + copied rank) |
| Move assignment | O(old rank) |
| Destruction | O(n) |
elements() | O(n) |
strides() | O(n) |
rank(), empty(), and individual access | O(1) |
| Iterator creation | O(1) |
| Full traversal or comparison | O(n) |
swap() | O(1) |
| Stream output | O(n) |
Shape is deliberately immutable through its public access API. To represent different dimensions, construct or assign another shape. This protects the relationship between shape metadata and the strides and storage owned by higher-level containers.
The class uses a dynamic buffer rather than a fixed-rank representation so one type can represent vectors, matrices, tensors, rank-zero shapes, and arbitrary higher-dimensional layouts.