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 = ⦥
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 = ⦥
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 = ⦥
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 = ⦥
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}