ndim/
core.rs

1use std::{
2    fmt::Debug,
3    ops::{Index, IndexMut},
4};
5
6/// Type alias for `1usize`. Used while intializing as default values in `shape` and `strides`
7const USIZE_ONE: usize = 1;
8/// Type alias for `[usize; N]`. Typically used in `shape` and `strides` of an NdArray object
9type SizedArray<const N: usize> = [usize; N];
10
11/// Used in the creation of N-dimensional arrays
12///
13/// ## Examples
14///
15/// #### Create a two dimensional (2D) array
16///
17/// ```
18/// use ndim::core::{NdArray, Array2};
19///
20/// # fn main() {
21/// let data: NdArray<u16, 2> = Array2::<u16>::new();
22/// # }
23/// ```
24///
25/// #### Create using Range function
26///
27/// ```
28/// use ndim::core::{NdArray, Array3};
29///
30/// # fn main() {
31/// let range: usize = u16::MAX as usize;
32/// let data: NdArray<u16, 3> = Array3::<u16>::arange(range);
33/// # }
34/// ```
35///
36/// #### Reshape, Access data from an index of the NdArray
37///
38/// ```
39/// use ndim::core::{NdArray, Array3};
40///
41/// # fn main() {
42/// let range: usize = u16::MAX as usize;
43/// let mut shape: [usize; 3] = [1, 1, range];
44/// let mut data: NdArray<u16, 3> = Array3::<u16>::arange(range); // create a NdArray ranging from 0..66535
45/// assert_eq!(*data.shape(), shape);
46///
47/// shape = [3, 5, 4369];
48/// data.reshape(shape); // reshaping NdArray's shape
49/// assert_eq!(data[[1, 2, 1234]], 31817); // accessing value from memory
50/// # }
51/// ```
52///
53/// For more examples, view this link on [github](https://github.com/noobsiecoder/ndim)
54#[derive(Debug)]
55pub struct NdArray<T, const N: usize> {
56    ptr: *mut T,
57    len: usize,
58    shape: SizedArray<N>,
59    strides: SizedArray<N>,
60}
61
62/// Type alias for a one Dimensional (1-D) array
63pub type Array<T> = NdArray<T, 1>;
64/// Type alias for a two Dimensional (2-D) array
65pub type Array2<T> = NdArray<T, 2>;
66/// Type alias for a three Dimensional (3-D) array
67pub type Array3<T> = NdArray<T, 3>;
68/// Type alias for a four Dimensional (4-D) array
69pub type Array4<T> = NdArray<T, 4>;
70
71impl<T: Debug + Copy + Default, const N: usize> NdArray<T, N> {
72    /// Calculate the stride of the array from the given `shape` and return in type `SizedArray<N>`
73    /// Helps in index navigation and the explanation is shown [here](https://github.com/noobsiecoder/ndim)
74    fn stride(shape: &SizedArray<N>) -> SizedArray<N> {
75        let mut strides: SizedArray<N> = [1usize; N];
76        strides[N - 1] = std::mem::size_of::<T>();
77        for idx in (0..N - 1).rev() {
78            // shape: [1, 2, 3]
79            // strides[0] = size_of<T>
80            // strides[1] = strides[0] * shape[0] -> size_of<T> * 3
81            // strides[2] = strides[1] * shape[1] -> size_of<T> * 3 * 2
82
83            // For (i, j, k):
84            // Index = i * strides[0] + j * strides[1] + k * strides[2]
85            // This `Index` will be used to access the memory location of the sized array
86            strides[idx] = strides[idx + 1] * shape[idx + 1];
87        }
88
89        strides
90    }
91
92    /// Calulate the size of the array from the given `shape` and return in `usize`
93    fn size_from_shape(shape: &SizedArray<N>) -> usize {
94        let mut t_size: usize = 1;
95        for val in *shape {
96            t_size *= val;
97        }
98
99        t_size
100    }
101
102    /// Calulate the size of the array from the given `range`, `step` and return in `usize`
103    /// Explanation behind the calculation can be viewed [here](https://github.com/noobsiecoder/ndim)
104    fn size_from_range(pos: (isize, isize), step: usize) -> usize {
105        let range: usize = (pos.1 - pos.0).abs() as usize;
106        // Avoid Zero Division Error
107        if step == 0 {
108            return range;
109        }
110
111        // Even Range and step
112        // range    = -1..5 (6ct)
113        // step     = 2
114        // values   = 4
115
116        // Even Range and an odd step
117        // range    = -1..5 (6ct)
118        // step     = 3
119        // values   = 3
120
121        // Odd Range and an even step
122        // range    = -1..6 (7ct)
123        // step     = 2
124        // values   = 4
125
126        // Odd Range and step
127        // range    = -1..6 (7ct)
128        // step     = 3
129        // values   = 3
130        if range % 2 == 0 {
131            if step % 2 == 0 {
132                return (range / step) + 1;
133            } else {
134                return range / step;
135            }
136        } else {
137            if step % 2 == 0 {
138                return range / step;
139            } else {
140                return (range / step) + 1;
141            }
142        }
143    }
144
145    /// Returns the length of the NdArray object's sized array
146    pub fn len(&self) -> &usize {
147        &self.len
148    }
149
150    /// Returns the shape of the NdArray object
151    pub fn shape(&self) -> &SizedArray<N> {
152        &self.shape
153    }
154
155    /// Returns stride of the NDArray object
156    pub fn strides(&self) -> &SizedArray<N> {
157        &self.strides
158    }
159
160    /// Creates an empty NdArray object. Requires shape size of N` to determine the dimension of the array
161    ///
162    /// ## Example
163    ///
164    /// ```
165    /// # use ndim::core::NdArray;
166    /// #
167    /// # fn main() {
168    /// // Creates a null pointer for the sized array
169    /// // Hence, length is zero and the shape and strides are iniialized with 1's of size `N`
170    /// let arr = NdArray::<i8, 4>::new();
171    /// # }
172    /// ```
173    pub fn new() -> Self {
174        NdArray {
175            ptr: std::ptr::null_mut(),
176            len: 0,
177            shape: [USIZE_ONE; N],
178            strides: [USIZE_ONE; N],
179        }
180    }
181
182    /// Creates a NdArray object from a sized T. Requires shape of size `N`
183    ///
184    /// ## Panics
185    /// If shape is not equivalent to current array size (or length), panics, and returns **Shape(`shape`) don't match with current Size(`size`)**
186    ///
187    /// ## Example
188    ///
189    /// ```
190    /// # use ndim::core::NdArray;
191    /// #
192    /// # fn main() {
193    /// let vec: Vec<i8> = (-2..22).collect();
194    /// let shape: [usize; 4] = [2, 2, 3, 2];
195    /// let arr = NdArray::<i8, 4>::from(&vec, shape);
196    /// # }
197    /// ```
198    pub fn from(arr: &[T], shape: SizedArray<N>) -> Self {
199        let len: usize = arr.len();
200        if len != Self::size_from_shape(&shape) {
201            panic!("Shape({:?}) don't match with array Size({})", shape, len);
202        }
203
204        let slice_as_ptr: *const T = arr.as_ptr();
205        let ptr: *mut T = unsafe { std::mem::transmute(slice_as_ptr) }; // converts pointer type from *const T to *mut T by reinterpreting its bits
206        let strides: SizedArray<N> = Self::stride(&shape);
207
208        NdArray {
209            ptr,
210            len,
211            shape,
212            strides,
213        }
214    }
215
216    /// Reshape the sized array for a new shape of type `SizedArray<N>`
217    ///
218    /// ## Panics
219    /// If new (given as an argument) shape is not equivalent to current array size (or length), panics, and returns **New Shape(`shape`) don't match with current Size(`size`)**
220    ///
221    /// ## Example
222    ///
223    /// ```
224    /// # use ndim::core::NdArray;
225    /// #
226    /// # fn main() {
227    /// let shape: [usize; 3] = [1, 1, 15];
228    /// let mut arr = NdArray::<i8, 3>::zeros(shape);
229    /// assert_eq!(*arr.shape(), shape);
230    ///
231    /// let new_shape = [1, 3, 5];
232    /// arr.reshape(new_shape);
233    /// assert_eq!(*arr.shape(), new_shape);
234    /// # }
235    /// ```
236    pub fn reshape(&mut self, shape: SizedArray<N>) {
237        if Self::size_from_shape(&shape) != self.len {
238            panic!(
239                "New Shape({:?}) don't match with current Size({})",
240                shape, self.len
241            )
242        }
243
244        self.shape = shape;
245        self.strides = Self::stride(&shape);
246    }
247
248    /// Helper function to create a sized array from a range containing `start` and an `end` value along with a `step` value
249    ///
250    /// ## Note
251    /// - Accepts both positive and negative integers
252    /// - This is a private method in the implementation and cannot (and should never) be used outside this `impl` block
253    /// - `end` will not be included while creating the array. Hence the array range is `start..=(end - 1)`
254    ///
255    /// ## Panics
256    /// - May panic if `start > end`, and returns **Index out of bound**
257    /// - If `T::from(i)` conversion fails, panics, and returns **Unable to convert to type T**
258    ///
259    /// ## Example
260    ///
261    /// ```
262    /// # use ndim::core::NdArray;
263    /// #
264    /// # fn main() {
265    /// let range: usize = 5; // `arr` ranges from 0 to 4 w/o step
266    /// let step: usize = 2;
267    /// let arr = NdArray::<i8, 2>::arange(range); // uses range(...) to construct a sized array
268    /// assert_eq!(*arr.len(), 5);
269    /// # }
270    /// ```
271    fn range(range: (isize, isize, usize)) -> Self
272    where
273        T: num_traits::NumCast + num_traits::ToPrimitive,
274    {
275        if range.0 > range.1 {
276            panic!("Index out of bound");
277        }
278
279        let end_range: usize = Self::size_from_range((range.0, range.1), range.2);
280        let mut arr: Vec<T> = Vec::<T>::with_capacity(end_range);
281        if range.2 == 0 {
282            for i in range.0..range.1 {
283                let val: T = T::from(i).expect("Unable to convert to type T"); // panics if it cannot construct to type T
284                arr.push(val);
285            }
286        } else {
287            for i in (range.0..range.1).step_by(range.2) {
288                let val: T = T::from(i).expect("Unable to convert to type T"); // panics if it cannot construct to type T
289                arr.push(val);
290            }
291        }
292
293        let len: usize = arr.len();
294        let ptr: *mut T = arr[..].as_mut_ptr();
295        std::mem::forget(arr); // prevents the Vec<T> from being dropped, ensuring the buffer remains valid
296
297        let mut shape: SizedArray<N> = [USIZE_ONE; N];
298        shape[N - 1] = len; // [1, .., x]: row-wise contiguous storage format
299        let mut strides: SizedArray<N> = [USIZE_ONE; N];
300        strides[N - 1] = std::mem::size_of::<T>(); // [1, .., x_stride]
301
302        NdArray {
303            ptr,
304            len,
305            shape,
306            strides,
307        }
308    }
309
310    /// Create a sized array with an `end` value starting from 0 within `usize` range
311    ///
312    /// ## Note
313    /// - Accepts only positive integers
314    /// - `end` will not be included while creating the array. Hence the array range is `start..=(end - 1)`
315    ///
316    /// ## Panics
317    /// Check `NdArray<T, N>::range(...)`
318    ///
319    /// ## Example
320    ///
321    /// ```
322    /// # use ndim::core::NdArray;
323    /// #
324    /// # fn main() {
325    /// let range: usize = 5; // `arr` ranges from 0 to 4 w/o step
326    /// let step: usize = 2;
327    /// let arr = NdArray::<i8, 2>::arange(range);
328    /// assert_eq!(*arr.len(), 5);
329    /// # }
330    /// ```
331    pub fn arange(range: usize) -> Self
332    where
333        T: num_traits::NumCast + num_traits::ToPrimitive,
334    {
335        Self::range((0, range as isize, 0))
336    }
337
338    /// Create a sized array with an `end` value starting from 0 within `usize` range and a step value of range `usize`
339    ///
340    /// ## Note
341    /// - Accepts only positive integers
342    /// - `end` will not be included while creating the array. Hence the array range is `start..=(end - 1)`
343    ///
344    /// ## Example
345    ///
346    /// ```
347    /// # use ndim::core::NdArray;
348    /// #
349    /// # fn main() {
350    /// let range: usize = 5; // `arr` ranges from 0 to 4 w/o step
351    /// let step: usize = 2;
352    /// let arr = NdArray::<i8, 2>::arange_with_step(range, step);
353    /// assert_eq!(*arr.len(), 3);
354    /// # }
355    /// ```
356    pub fn arange_with_step(range: usize, step: usize) -> Self
357    where
358        T: num_traits::NumCast + num_traits::ToPrimitive + Default + Copy,
359    {
360        Self::range((0, range as isize, step))
361    }
362
363    /// Create a sized array with `start` and `end` values within `isize` range
364    ///
365    /// ## Note
366    /// - `end` will not be included while creating the array. Hence the array range is `start..=(end - 1)`
367    ///
368    /// ## Example
369    ///
370    /// ```
371    /// # use ndim::core::NdArray;
372    /// #
373    /// # fn main() {
374    /// let ranges: (isize, isize) = (-1, 5); // `arr` ranges from -1 to 4
375    /// let arr = NdArray::<i8, 2>::ranges(ranges);
376    /// assert_eq!(*arr.len(), 6);
377    /// # }
378    /// ```
379    pub fn ranges(ranges: (isize, isize)) -> Self
380    where
381        T: num_traits::NumCast + num_traits::ToPrimitive + Default + Copy,
382    {
383        Self::range((ranges.0, ranges.1, 0))
384    }
385
386    /// Create a sized array with `start` and `end` values within `isize` range and a step value of range `usize`
387    ///
388    /// ## Note
389    /// - `end` will not be included while creating the array. Hence the array range is `start..=(end - 1)`
390    ///
391    /// ## Example
392    ///
393    /// ```
394    /// # use ndim::core::NdArray;
395    /// #
396    /// # fn main() {
397    /// let ranges: (isize, isize) = (-1, 5); // `arr` ranges from -1 to 4 w/o step
398    /// let step: usize = 2;
399    /// let arr = NdArray::<i8, 2>::ranges_with_step(ranges, step);
400    /// assert_eq!(*arr.len(), 3);
401    /// # }
402    /// ```
403    pub fn ranges_with_step(ranges: (isize, isize), step: usize) -> Self
404    where
405        T: num_traits::NumCast + num_traits::ToPrimitive,
406    {
407        Self::range((ranges.0, ranges.1, step))
408    }
409
410    /// Helper method in implementation to fill any `value` of size `X` (total size of array derived from shape)
411    ///
412    /// ## Note
413    /// This is a private method in the implementation and cannot (and should never) be used outside this `impl` block
414    ///
415    /// ## Example
416    ///
417    /// ```
418    /// # use ndim::core::NdArray;
419    /// #
420    /// # fn main() {
421    /// let shape: [usize; 2] = [3, 2];
422    /// let arr = NdArray::<u16, 2>::zeros(shape); // uses value(...)
423    /// # }
424    /// ```
425    fn values(val: T, shape: SizedArray<N>) -> Self {
426        let size: usize = Self::size_from_shape(&shape);
427
428        let mut vec: Vec<T> = vec![val; size];
429        let len: usize = vec.len();
430        let ptr: *mut T = vec[..].as_mut_ptr();
431        let strides: SizedArray<N> = Self::stride(&shape);
432        std::mem::forget(vec);
433
434        NdArray {
435            ptr,
436            len,
437            shape,
438            strides,
439        }
440    }
441
442    /// Create a sized array completely filled with numeral zero or `0`. Requires shape of size `N`
443    ///
444    /// ## Examples
445    ///
446    /// ```
447    /// # use ndim::core::NdArray;
448    /// #
449    /// # fn main() {
450    /// let shape: [usize; 2] = [3, 2];
451    /// let arr = NdArray::<u16, 2>::zeros(shape);
452    /// for i in 0..arr.shape()[0] {
453    ///     for j in 0..arr.shape()[1] {
454    ///         assert_eq!(arr[[i, j]], 0);
455    ///     }
456    /// }
457    /// # }
458    /// ```
459    pub fn zeros(shape: SizedArray<N>) -> Self
460    where
461        T: Default,
462    {
463        Self::values(T::default(), shape)
464    }
465
466    /// Create a sized array completely filled with numeral one or `1`. Requires shape of size `N`
467    ///
468    /// ## Examples
469    ///
470    /// ```
471    /// # use ndim::core::NdArray;
472    /// #
473    /// # fn main() {
474    /// let shape: [usize; 2] = [3, 2];
475    /// let arr = NdArray::<u16, 2>::ones(shape);
476    /// for i in 0..arr.shape()[0] {
477    ///     for j in 0..arr.shape()[1] {
478    ///         assert_eq!(arr[[i, j]], 1);
479    ///     }
480    /// }
481    /// # }
482    /// ```
483    pub fn ones(shape: SizedArray<N>) -> Self
484    where
485        T: num_traits::One,
486    {
487        Self::values(T::one(), shape)
488    }
489}
490
491/// Calculate the index using strides and the given index. Returns a value which can be used to access the memory of the 1-d sized array
492///
493/// ## Example
494///
495/// ```
496/// use ndim::core::NdArray;
497///
498/// fn main() {
499///     let shape: [usize; 2] = [2, 2];
500///     let mut arr = NdArray::<u16, 2>::zeros(shape);
501///     // mutable pointer is accessed and the value is changed pointing to [1, 1]
502///     // `get_index<..>(index: &.., strides: &..)` is used here to access the memory from the 1-d contiguous array
503///     arr[[1, 1]] = 12;
504///     assert_eq!(arr[[1, 1]], 12);
505/// }
506/// ```
507fn get_index<T, const N: usize>(index: &SizedArray<N>, strides: &SizedArray<N>) -> usize {
508    let mut idx: usize = 0;
509    for i in 0..N {
510        idx += index[i] * strides[i]
511    }
512
513    idx / strides[N - 1]
514}
515
516/// Use for indexing immutable NdArray
517///
518/// ## Note
519/// - Calls `get_index::<..>(index: &.., strides: &..)` to access the data from the contiguous sized 1-d array
520/// - `Panics` if the index is larger than the length of the sized array
521/// - Unsafe block accesses the value in the memory by calculating the offset from the pointer: `*mut T + idx`
522///
523/// ## Example
524///
525/// ```
526/// use ndim::core::NdArray;
527///
528/// fn main() {
529///     let shape: [usize; 2] = [2, 2];
530///     let mut arr = NdArray::<u16, 2>::zeros(shape);
531///     arr[[1, 1]] = 12; // mutable pointer is accessed and the value is changed pointing to [1, 1]
532/// }
533/// ```
534impl<T, const N: usize> Index<SizedArray<N>> for NdArray<T, N> {
535    type Output = T;
536
537    fn index(&self, index: SizedArray<N>) -> &Self::Output {
538        let idx = get_index::<T, N>(&index, &self.strides);
539        if idx >= self.len {
540            panic!("Index out of bounds")
541        }
542        unsafe { &*self.ptr.add(idx) }
543    }
544}
545
546/// Use for indexing mutable NdArray
547impl<T, const N: usize> IndexMut<SizedArray<N>> for NdArray<T, N> {
548    fn index_mut(&mut self, index: SizedArray<N>) -> &mut Self::Output {
549        let idx = get_index::<T, N>(&index, &self.strides);
550        if idx >= self.len {
551            panic!("Index out of bounds")
552        }
553        unsafe { &mut *self.ptr.add(idx) }
554    }
555}
556
557/// Drop *mut T when it goes out of scope
558///
559/// Manually dropping by getting a slice from the pointer. Total size allocated by the pointer is also taken to get this slice. Then, the drop is performed.
560///
561/// ## Note
562/// - Pointer is not dropped if null
563/// - Though drop occurs inside unsafe block, it takes the right size of the array. This does not mean that undefined behaviour cannot happen
564///
565/// ## Example
566///
567/// ```
568/// use ndim::core::NdArray;
569///
570/// fn main() {
571///     {
572///         let arr = NdArray::<u16, 2>::new();
573///         // `arr` as it goes out of scope here
574///         // Drop implementation is called
575///     }
576///     // `arr` is not accessible anymore
577/// }
578/// ```
579impl<T, const N: usize> Drop for NdArray<T, N> {
580    fn drop(&mut self) {
581        if !self.ptr.is_null() {
582            unsafe {
583                let slice: &mut [T] = std::slice::from_raw_parts_mut(self.ptr, self.len);
584                std::ptr::drop_in_place(slice);
585            }
586        }
587    }
588}
589
590//
591#[cfg(test)]
592mod core_ndim_t {
593    use crate::core::{Array, Array2, Array3, NdArray};
594
595    // Test for zeros creation in a 1-D sized array
596    // Try to access the value in the memory at location (x, y) and mutate it
597    #[test]
598    fn zeros_2dim_t() {
599        let shape: [usize; 2] = [2, 2];
600        let mut data: NdArray<u32, 2> = Array2::<u32>::zeros(shape);
601        assert_eq!(data[[1, 1]], 0);
602
603        data[[1, 1]] = 12;
604        assert_eq!(data[[1, 1]], 12);
605    }
606
607    // Test for ones creation in a 2-D sized array
608    // Try to access the value in the memory at location (x, y) and mutate it
609    #[test]
610    fn ones_2dim_t() {
611        let shape: [usize; 2] = [2, 2];
612        let mut data: NdArray<u32, 2> = Array2::<u32>::ones(shape);
613        assert_eq!(data[[1, 1]], 1);
614
615        data[[1, 1]] = 12;
616        assert_eq!(data[[1, 1]], 12);
617    }
618
619    // Test NdArray<T, N>::from(...) for a 3-D sized array of type u32
620    // Check if the memory set with shape is correct
621    #[test]
622    fn from_3dim_u32_t() {
623        let arr: [u32; 6] = [0, 1, 2, 3, 4, 5];
624        let ndim_arr: [[[i32; 3]; 2]; 1] = [[[0, 1, 2], [3, 4, 5]]];
625        let shape: [usize; 3] = [1, 2, 3];
626
627        let data: NdArray<u32, 3> = Array3::<u32>::from(&arr, shape);
628        let strides: [usize; 3] = [24, 12, 4];
629        assert_eq!(*data.strides(), strides);
630
631        let mut idx: usize = 0;
632        for i in 0..ndim_arr.len() {
633            for j in 0..ndim_arr[i].len() {
634                for k in 0..ndim_arr[i][j].len() {
635                    let index: [usize; 3] = [i, j, k] as [usize; 3];
636                    assert_eq!(data[index], arr[idx]);
637                    idx += 1;
638                }
639            }
640        }
641    }
642
643    // Test NdArray<T, N>::from(...) for a 3-D sized array of type i32
644    // Check if strides created are correct
645    // Check if the memory set with shape is correct
646    #[test]
647    fn from_3dim_i32_t() {
648        let arr: [i32; 6] = [0, -1, 2, -3, 4, -5];
649        let ndim_arr: [[i32; 2]; 3] = [[0, -1], [2, -3], [4, -5]];
650        let shape: [usize; 2] = [3, 2];
651
652        let data: NdArray<i32, 2> = Array2::<i32>::from(&arr, shape);
653        let strides: [usize; 2] = [8, 4];
654        assert_eq!(*data.strides(), strides);
655
656        let mut idx: usize = 0;
657        for i in 0..ndim_arr.len() {
658            for j in 0..ndim_arr[i].len() {
659                let index: [usize; 2] = [i, j] as [usize; 2];
660                assert_eq!(data[index], arr[idx]);
661                idx += 1;
662            }
663        }
664    }
665
666    // Test NdArray<T, N>::from(...) for a 3-D sized array of type f32
667    // Check if strides created are correct
668    // Check if the memory set with shape is correct
669    #[test]
670    fn from_3dim_f32_t() {
671        let arr: [f32; 6] = [0.0, -1.2, 2.1, -3.75, 4.004, -5.65];
672        let ndim_arr: [[f32; 2]; 3] = [[0.0, -1.2], [2.1, -3.75], [4.004, -5.65]];
673        let shape: [usize; 2] = [3, 2];
674
675        let data: NdArray<f32, 2> = Array2::<f32>::from(&arr, shape);
676        let strides: [usize; 2] = [8, 4];
677        assert_eq!(*data.strides(), strides);
678
679        let mut idx: usize = 0;
680        for i in 0..ndim_arr.len() {
681            for j in 0..ndim_arr[i].len() {
682                let index: [usize; 2] = [i, j] as [usize; 2];
683                assert_eq!(data[index], arr[idx]);
684                idx += 1;
685            }
686        }
687    }
688
689    // TODO T > u16: needs faster processing time
690    // Test NdArray<T, N>::arange(...) for a 1-D sized array
691    // Check if length and shape created are correct
692    // Check if the memory set with shape is correct
693    #[test]
694    fn arange_1dim_t() {
695        let range: usize = u16::MAX as usize;
696        let arr: Vec<u16> = (0..u16::MAX).collect::<Vec<u16>>();
697        let size: &usize = &range;
698
699        let data: NdArray<u16, 1> = Array::<u16>::arange(range);
700        assert_eq!(*data.len(), *size);
701        assert_eq!(*data.shape(), [*size]);
702
703        for i in 0..data.shape()[0] {
704            let index: [usize; 1] = [i] as [usize; 1];
705            assert_eq!(data[index], arr[i]);
706        }
707    }
708
709    // Test NdArray<T, N>::arange(...) for a 2-D sized array
710    // Check if length and shape created are correct
711    // Check if the memory set with shape is correct
712    #[test]
713    fn arange_2dim_t() {
714        let range: usize = u16::MAX as usize;
715        let arr: Vec<u16> = (0..u16::MAX).collect::<Vec<u16>>();
716        let size: &usize = &range;
717
718        let data: NdArray<u16, 2> = Array2::<u16>::arange(range);
719        assert_eq!(*data.len(), *size);
720        assert_eq!(*data.shape(), [1, *size]);
721
722        let mut idx: usize = 0;
723        for i in 0..data.shape()[0] {
724            for j in 0..data.shape()[1] {
725                let index: [usize; 2] = [i, j] as [usize; 2];
726                assert_eq!(data[index], arr[idx]);
727
728                idx += 1;
729            }
730        }
731    }
732
733    // Test NdArray<T, N>::arange(...) for a 3-D sized array
734    // Reshape the NdArray and check with the new shape
735    // Check if length created is correct
736    // Check if the memory set with shape is correct
737    #[test]
738    fn arange_3dim_t() {
739        let range: usize = u16::MAX as usize;
740        let arr: Vec<f32> = (0u16..u16::MAX)
741            .step_by(2)
742            .map(f32::from)
743            .collect::<Vec<f32>>();
744        let size: usize = if range % 2 == 0 {
745            range / 2
746        } else {
747            (range / 2) + 1
748        };
749
750        let mut data: NdArray<f32, 3> = Array3::<f32>::arange_with_step(range, 2);
751        let new_shape: [usize; 3] = [1, size / 128, 128]; // (1, 256, 128)
752        data.reshape(new_shape);
753
754        assert_eq!(*data.len(), size);
755        assert_eq!(*data.shape(), new_shape);
756
757        let mut idx: usize = 0;
758        for i in 0..data.shape()[0] {
759            for j in 0..data.shape()[1] {
760                for k in 0..data.shape()[2] {
761                    let index: [usize; 3] = [i, j, k] as [usize; 3];
762                    assert_eq!(data[index], arr[idx]);
763
764                    idx += 1;
765                }
766            }
767        }
768    }
769
770    // Test NdArray<T, N>::reshape(...) for a 2-D sized array
771    #[test]
772    fn reshape_2dim_t() {
773        let range: usize = u16::MAX as usize;
774        let size: &usize = &range;
775
776        let mut data: NdArray<u16, 2> = Array2::<u16>::arange(range);
777        assert_eq!(*data.len(), *size);
778        assert_eq!(*data.shape(), [1, *size]);
779
780        let mut new_shape: [usize; 2] = [*size / 5, 5];
781        data.reshape(new_shape);
782        assert_eq!(*data.shape(), new_shape);
783
784        new_shape = [*size / 15, 15];
785        data.reshape(new_shape);
786        assert_eq!(*data.shape(), new_shape);
787
788        new_shape = [*size / 257, 257];
789        data.reshape(new_shape);
790        assert_eq!(*data.shape(), new_shape);
791    }
792
793    // Test NdArray<T, N>::reshape(...) for a 3-D sized array
794    #[test]
795    fn reshape_3dim_t() {
796        let range: usize = u16::MAX as usize;
797        let size: &usize = &range;
798
799        let mut data: NdArray<u16, 3> = Array3::<u16>::arange(range);
800        assert_eq!(*data.len(), *size);
801        assert_eq!(*data.shape(), [1, 1, *size]);
802
803        let t_size: usize = std::mem::size_of::<u16>();
804        let mut new_shape: [usize; 3] = [3, 5, 4369];
805        let mut new_strides: [usize; 3] = [
806            new_shape[1] * new_shape[2] * t_size,
807            new_shape[2] * t_size,
808            t_size,
809        ];
810        data.reshape(new_shape);
811        assert_eq!(*data.shape(), new_shape);
812        assert_eq!(*data.strides(), new_strides);
813
814        new_shape = [1, 17, 3855];
815        new_strides = [
816            new_shape[1] * new_shape[2] * t_size,
817            new_shape[2] * t_size,
818            t_size,
819        ];
820        data.reshape(new_shape);
821        assert_eq!(*data.shape(), new_shape);
822        assert_eq!(*data.strides(), new_strides);
823    }
824}