Skip to main content

rlst/dense/array/
empty_axis.rs

1//! Extend an array by a singleton (extent-one) axis at the front or back.
2
3use crate::{
4    ContainerType,
5    base_types::NumberType,
6    traits::{
7        accessors::{
8            UnsafeRandom1DAccessByRef, UnsafeRandom1DAccessByValue, UnsafeRandom1DAccessMut,
9        },
10        base_operations::BaseItem,
11        number_relations::IsSmallerByOne,
12    },
13};
14
15use super::{
16    Array, RawAccess, RawAccessMut, Shape, Stride, UnsafeRandomAccessByRef,
17    UnsafeRandomAccessByValue, UnsafeRandomAccessMut,
18};
19
20/// Position at which to insert the singleton axis.
21#[derive(Clone, Copy)]
22pub enum AxisPosition {
23    /// Insert axis at the front.
24    Front,
25    /// Insert axis at the back.
26    Back,
27}
28
29/// Array view with one additional extent-one axis.
30pub struct ArrayAppendAxis<ArrayImpl, const ADIM: usize, const NDIM: usize>
31where
32    NumberType<ADIM>: IsSmallerByOne<NDIM>,
33{
34    arr: Array<ArrayImpl, ADIM>,
35    axis_position: AxisPosition,
36}
37
38impl<ArrayImpl, const ADIM: usize, const NDIM: usize> ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
39where
40    NumberType<ADIM>: IsSmallerByOne<NDIM>,
41{
42    /// Create a view with a singleton axis at `axis_position`.
43    pub fn new(arr: Array<ArrayImpl, ADIM>, axis_position: AxisPosition) -> Self {
44        Self { arr, axis_position }
45    }
46}
47
48impl<ArrayImpl, const ADIM: usize, const NDIM: usize> ContainerType
49    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
50where
51    NumberType<ADIM>: IsSmallerByOne<NDIM>,
52    ArrayImpl: ContainerType,
53{
54    type Type = ArrayImpl::Type;
55}
56
57impl<ArrayImpl, const ADIM: usize, const NDIM: usize> BaseItem
58    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
59where
60    NumberType<ADIM>: IsSmallerByOne<NDIM>,
61    ArrayImpl: BaseItem,
62{
63    type Item = ArrayImpl::Item;
64}
65
66impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandomAccessByValue<NDIM>
67    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
68where
69    NumberType<ADIM>: IsSmallerByOne<NDIM>,
70    ArrayImpl: UnsafeRandomAccessByValue<ADIM>,
71{
72    #[inline(always)]
73    unsafe fn get_value_unchecked(&self, multi_index: [usize; NDIM]) -> Self::Item {
74        unsafe {
75            self.arr
76                .get_value_unchecked(multi_index_to_orig(multi_index, self.axis_position))
77        }
78    }
79}
80
81impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandom1DAccessByValue
82    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
83where
84    NumberType<ADIM>: IsSmallerByOne<NDIM>,
85    ArrayImpl: UnsafeRandom1DAccessByValue,
86{
87    #[inline(always)]
88    unsafe fn get_value_1d_unchecked(&self, index: usize) -> Self::Item {
89        unsafe { self.arr.imp().get_value_1d_unchecked(index) }
90    }
91}
92
93impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandom1DAccessByRef
94    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
95where
96    NumberType<ADIM>: IsSmallerByOne<NDIM>,
97    ArrayImpl: UnsafeRandom1DAccessByRef,
98{
99    #[inline(always)]
100    unsafe fn get_1d_unchecked(&self, index: usize) -> &Self::Item {
101        unsafe { self.arr.imp().get_1d_unchecked(index) }
102    }
103}
104
105impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandom1DAccessMut
106    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
107where
108    NumberType<ADIM>: IsSmallerByOne<NDIM>,
109    ArrayImpl: UnsafeRandom1DAccessMut,
110{
111    #[inline(always)]
112    unsafe fn get_1d_unchecked_mut(&mut self, index: usize) -> &mut Self::Item {
113        unsafe { self.arr.imp_mut().get_1d_unchecked_mut(index) }
114    }
115}
116
117impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandomAccessByRef<NDIM>
118    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
119where
120    NumberType<ADIM>: IsSmallerByOne<NDIM>,
121    ArrayImpl: UnsafeRandomAccessByRef<ADIM>,
122{
123    #[inline(always)]
124    unsafe fn get_unchecked(&self, multi_index: [usize; NDIM]) -> &Self::Item {
125        unsafe {
126            self.arr
127                .get_unchecked(multi_index_to_orig(multi_index, self.axis_position))
128        }
129    }
130}
131
132impl<ArrayImpl, const ADIM: usize, const NDIM: usize> UnsafeRandomAccessMut<NDIM>
133    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
134where
135    NumberType<ADIM>: IsSmallerByOne<NDIM>,
136    ArrayImpl: UnsafeRandomAccessMut<ADIM>,
137{
138    #[inline(always)]
139    unsafe fn get_unchecked_mut(&mut self, multi_index: [usize; NDIM]) -> &mut Self::Item {
140        unsafe {
141            self.arr
142                .get_unchecked_mut(multi_index_to_orig(multi_index, self.axis_position))
143        }
144    }
145}
146
147impl<ArrayImpl, const ADIM: usize, const NDIM: usize> Shape<NDIM>
148    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
149where
150    NumberType<ADIM>: IsSmallerByOne<NDIM>,
151    ArrayImpl: Shape<ADIM>,
152{
153    fn shape(&self) -> [usize; NDIM] {
154        let mut result = [1; NDIM];
155        let orig_shape = self.arr.shape();
156
157        match self.axis_position {
158            AxisPosition::Front => result[1..].copy_from_slice(&orig_shape),
159            AxisPosition::Back => result[..ADIM].copy_from_slice(&orig_shape),
160        }
161        result
162    }
163}
164
165impl<ArrayImpl: RawAccess, const ADIM: usize, const NDIM: usize> RawAccess
166    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
167where
168    NumberType<ADIM>: IsSmallerByOne<NDIM>,
169{
170    #[inline(always)]
171    fn data(&self) -> Option<&[Self::Item]> {
172        self.arr.data()
173    }
174}
175
176impl<ArrayImpl: RawAccessMut, const ADIM: usize, const NDIM: usize> RawAccessMut
177    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
178where
179    NumberType<ADIM>: IsSmallerByOne<NDIM>,
180{
181    #[inline(always)]
182    fn data_mut(&mut self) -> Option<&mut [Self::Item]> {
183        self.arr.data_mut()
184    }
185}
186
187impl<ArrayImpl, const ADIM: usize, const NDIM: usize> Stride<NDIM>
188    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
189where
190    NumberType<ADIM>: IsSmallerByOne<NDIM>,
191    ArrayImpl: Stride<ADIM> + Shape<ADIM>,
192{
193    fn stride(&self) -> [usize; NDIM] {
194        let mut result = [0; NDIM];
195        let orig_stride = self.arr.stride();
196
197        match self.axis_position {
198            AxisPosition::Front => {
199                result[0] = 1;
200                result[1..].copy_from_slice(&orig_stride)
201            }
202            AxisPosition::Back => {
203                result[NDIM - 1] = orig_stride[ADIM - 1] * self.arr.shape()[ADIM - 1];
204                result[..ADIM].copy_from_slice(&orig_stride)
205            }
206        }
207        result
208    }
209}
210
211impl<ArrayImpl, const ADIM: usize> Array<ArrayImpl, ADIM> {
212    /// Insert a singleton axis at the front or back.
213    ///
214    /// Despite the historical method name, the inserted axis has extent one, not zero. The view
215    /// owns its array operand and does not allocate or copy element storage.
216    pub fn insert_empty_axis<const NDIM: usize>(
217        self,
218        axis_position: AxisPosition,
219    ) -> Array<ArrayAppendAxis<ArrayImpl, ADIM, NDIM>, NDIM>
220    where
221        NumberType<ADIM>: IsSmallerByOne<NDIM>,
222    {
223        Array::new(ArrayAppendAxis::new(self, axis_position))
224    }
225}
226
227/// Map an extended index of dimension NDIM to an index of dimension ADIM with NDIM = ADIM + 1.
228fn multi_index_to_orig<const ADIM: usize, const NDIM: usize>(
229    multi_index: [usize; NDIM],
230    axis_position: AxisPosition,
231) -> [usize; ADIM]
232where
233    NumberType<ADIM>: IsSmallerByOne<NDIM>,
234{
235    let mut orig = [0; ADIM];
236    match axis_position {
237        AxisPosition::Front => orig.copy_from_slice(&multi_index[1..]),
238        AxisPosition::Back => orig.copy_from_slice(&multi_index[..ADIM]),
239    }
240
241    orig
242}
243
244// SAFETY: adding a unit axis does not change the one-dimensional index mapping.
245unsafe impl<ArrayImpl, const ADIM: usize, const NDIM: usize> crate::UnsafeDisjointRandom1DAccessMut
246    for ArrayAppendAxis<ArrayImpl, ADIM, NDIM>
247where
248    NumberType<ADIM>: IsSmallerByOne<NDIM>,
249    ArrayImpl: crate::UnsafeDisjointRandom1DAccessMut,
250{
251}