Skip to main content

rlst/dense/
array.rs

1//! Basic Array type
2//!
3//! [Array] is the basic type for dense calculations in Rlst. The full definition
4//! `Array<ArrayImpl, NDIM>` represents a tensor with `NDIM` axes implemented through
5//! the implementeation type `ArrayImpl`.
6
7use std::ops::{Add, Sub};
8
9use aligned_vec::{AVec, CACHELINE_ALIGN, ConstAlign};
10use empty_axis::AxisPosition;
11use iterators::{
12    ArrayDefaultIteratorByRef, ArrayDefaultIteratorByValue, ArrayDefaultIteratorMut,
13    ArrayDiagIteratorByRef, ArrayDiagIteratorByValue, ArrayDiagIteratorMut, ColIterator,
14    ColIteratorMut, RowIterator, RowIteratorMut,
15};
16use itertools::izip;
17use num::{One, Zero, traits::MulAdd};
18use operators::{
19    addition::ArrayAddition, cast::ArrayCast, cmp_wise_division::CmpWiseDivision,
20    cmp_wise_product::CmpWiseProduct, coerce::CoerceArray, mul_add::MulAddImpl, negation::ArrayNeg,
21    reverse_axis::ReverseAxis, scalar_mult::ArrayScalarMult, subtraction::ArraySubtraction,
22    transpose::ArrayTranspose, unary_op::ArrayUnaryOperator,
23};
24use typenum::{Const, NonZero, PInt, ToUInt, U, Unsigned};
25
26use crate::base_types::{c32, c64};
27
28use crate::dense::array::iterators::{MultisliceIterator, MultisliceIteratorMut};
29use crate::{
30    ArrayFromContainerType, Linspace, MutableArrayImpl, RowMajorArrayFromContainerType,
31    ScalarAsArray,
32};
33
34use paste::paste;
35use rand::{Rng, SeedableRng};
36use rand_chacha::ChaCha8Rng;
37use rand_distr::{Distribution, StandardNormal, StandardUniform};
38use reference::{ArrayRef, ArrayRefMut};
39use slice::ArraySlice;
40use subview::ArraySubView;
41
42use crate::{
43    AsMatrixApply, AsMultiIndex, EvaluateArray, EvaluateRowMajorArray, Gemm, IsGreaterByOne,
44    IsGreaterZero, IsSmallerThan, Max, MemoryLayout, NumberDifference, NumberType, RandScalar,
45    RandomAccessByValue, RlstError, RlstResult, RlstScalar, SubtractEqual, TransMode,
46    dense::{
47        array::multislice::Multislice,
48        base_array::BaseArray,
49        data_container::{
50            AlignedVectorContainer, SliceContainer, SliceContainerMut, VectorContainer,
51        },
52        layout::check_multi_index_in_bounds,
53        strided_base_array::{StridedBaseArray, required_storage_len},
54    },
55    traits::{
56        accessors::{
57            RandomAccessByRef, RandomAccessMut, RawAccess, RawAccessMut,
58            UnsafeDisjointRandom1DAccessMut, UnsafeRandom1DAccessByRef,
59            UnsafeRandom1DAccessByValue, UnsafeRandomAccessByRef, UnsafeRandomAccessByValue,
60            UnsafeRandomAccessMut,
61        },
62        base_operations::{BaseItem, ResizeInPlace, Shape, Stride},
63        data_container::ContainerType,
64    },
65};
66
67use super::{data_container::ArrayContainer, layout::row_major_stride_from_shape};
68
69pub mod empty_axis;
70pub mod iterators;
71pub mod mult_into;
72pub mod operations;
73pub mod operators;
74// pub mod rank1_array;
75pub mod flattened;
76pub mod multislice;
77pub mod reference;
78pub mod shared_array;
79pub mod slice;
80pub mod subview;
81pub mod zero_dim;
82
83pub use zero_dim::ZeroDimArray;
84
85/// A heap-allocated, contiguous column-major array.
86///
87/// Construct one with [`DynArray::from_shape`], [`DynArray::from_shape_and_vec`], or
88/// [`crate::rlst_dynamic_array`]. Elements supplied as a flat vector use column-major order.
89/// This storage requires at least one axis; use [`ZeroDimArray`] for scalar results.
90pub type DynArray<Item, const NDIM: usize> = Array<BaseArray<VectorContainer<Item>, NDIM>, NDIM>;
91
92/// A contiguous column-major array with an explicitly aligned heap allocation.
93pub type AlignedDynArray<Item, const NDIM: usize, const ALIGNMENT: usize = CACHELINE_ALIGN> =
94    Array<BaseArray<AlignedVectorContainer<Item, ALIGNMENT>, NDIM>, NDIM>;
95
96/// A heap-allocated array with an explicit non-overlapping stride.
97///
98/// Logical iteration remains column-major even when the physical storage layout is not.
99pub type StridedDynArray<Item, const NDIM: usize> =
100    Array<StridedBaseArray<VectorContainer<Item>, NDIM>, NDIM>;
101
102/// A statically allocated array.
103///
104/// `N` is the number of elements to be reserved in the static
105/// container.
106pub type StaticArray<Item, const NDIM: usize, const N: usize> =
107    Array<BaseArray<ArrayContainer<Item, N>, NDIM>, NDIM>;
108
109/// A dynamically allocated array from a data slice.
110pub type SliceArray<'a, Item, const NDIM: usize> =
111    Array<BaseArray<SliceContainer<'a, Item>, NDIM>, NDIM>;
112
113/// A dynamically allocated array from a data slice with a given stride.
114pub type StridedSliceArray<'a, Item, const NDIM: usize> =
115    Array<StridedBaseArray<SliceContainer<'a, Item>, NDIM>, NDIM>;
116
117/// A mutable dynamically allocated array from a data slice.
118pub type SliceArrayMut<'a, Item, const NDIM: usize> =
119    Array<BaseArray<SliceContainerMut<'a, Item>, NDIM>, NDIM>;
120
121/// A mutable dynamically allocated array from a data slice with a given stride.
122pub type StridedSliceArrayMut<'a, Item, const NDIM: usize> =
123    Array<StridedBaseArray<SliceContainerMut<'a, Item>, NDIM>, NDIM>;
124
125/// Public array wrapper around a storage, view, or lazy-expression implementation.
126///
127/// `NDIM` is the number of axes. The implementation determines whether data is owned or
128/// borrowed, contiguous or strided, mutable, or computed lazily. Most users construct one of
129/// the aliases such as [`DynArray`] rather than naming the implementation type.
130pub struct Array<ArrayImpl, const NDIM: usize>(ArrayImpl);
131
132impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM> {
133    /// Wrap an array implementation.
134    ///
135    /// The implementation determines the supported ranks and access operations.
136    /// [`ZeroDimArray`] provides scalar-only storage for rank zero.
137    pub fn new(arr: ArrayImpl) -> Self {
138        Self(arr)
139    }
140
141    /// Return a reference to the implementation.
142    #[inline(always)]
143    pub fn imp(&self) -> &ArrayImpl {
144        &self.0
145    }
146
147    /// Return a mutable reference to the implementation.
148    #[inline(always)]
149    pub fn imp_mut(&mut self) -> &mut ArrayImpl {
150        &mut self.0
151    }
152
153    /// Extract the inner implementation.
154    #[inline(always)]
155    pub fn into_imp(self) -> ArrayImpl {
156        self.0
157    }
158
159    /// Wrap a shared borrow in an array value.
160    ///
161    /// This is useful with operators and views that consume their operands. The returned value
162    /// does not copy data and cannot outlive `self`.
163    #[inline(always)]
164    pub fn r(&self) -> Array<ArrayRef<'_, ArrayImpl, NDIM>, NDIM> {
165        Array::new(ArrayRef::new(self))
166    }
167
168    /// Wrap an exclusive borrow in an array value.
169    ///
170    /// The returned value does not copy data and cannot outlive the mutable borrow of `self`.
171    #[inline(always)]
172    pub fn r_mut(&mut self) -> Array<ArrayRefMut<'_, ArrayImpl, NDIM>, NDIM> {
173        Array::new(ArrayRefMut::new(self))
174    }
175}
176
177impl<ArrayImpl, const NDIM: usize> BaseItem for Array<ArrayImpl, NDIM>
178where
179    ArrayImpl: BaseItem,
180{
181    type Item = ArrayImpl::Item;
182}
183
184impl<ArrayImpl: Shape<NDIM>, const NDIM: usize> Array<ArrayImpl, NDIM> {
185    /// Return the number of elements in the array.
186    #[inline(always)]
187    pub fn number_of_elements(&self) -> usize {
188        self.0.shape().iter().product()
189    }
190}
191
192impl<Item: Copy + Default, const NDIM: usize> DynArray<Item, NDIM> {
193    /// Allocate a default-initialized column-major array with `shape`.
194    ///
195    /// A zero extent is permitted and produces an empty array.
196    #[inline(always)]
197    pub fn from_shape(shape: [usize; NDIM]) -> Self {
198        let size = shape.iter().product();
199        Self::new(BaseArray::new(VectorContainer::new(size), shape))
200    }
201
202    /// Create a column-major array from `data` without copying the vector.
203    ///
204    /// Flat element `i` corresponds to the column-major logical index `i`.
205    ///
206    /// # Panics
207    ///
208    /// Panics if `data.len()` differs from the product of `shape`.
209    #[inline(always)]
210    pub fn from_shape_and_vec(shape: [usize; NDIM], data: Vec<Item>) -> Self {
211        assert_eq!(
212            data.len(),
213            shape.iter().product::<usize>(),
214            "Data length does not match the shape: {} != {}",
215            data.len(),
216            shape.iter().product::<usize>()
217        );
218        Self::new(BaseArray::new(VectorContainer::from_vec(data), shape))
219    }
220}
221
222impl<Item: Copy + Default, const NDIM: usize, const ALIGNMENT: usize>
223    AlignedDynArray<Item, NDIM, ALIGNMENT>
224{
225    /// Allocate a default-initialized column-major array with `shape` and `ALIGNMENT`.
226    ///
227    /// `ALIGNMENT` must satisfy the allocator requirements documented by `aligned-vec`.
228    #[inline(always)]
229    pub fn from_shape(shape: [usize; NDIM]) -> Self {
230        let size = shape.iter().product();
231        Self::new(BaseArray::new(AlignedVectorContainer::new(size), shape))
232    }
233
234    /// Create an aligned column-major array from an aligned vector without copying it.
235    ///
236    /// # Panics
237    ///
238    /// Panics if `data.len()` differs from the product of `shape`.
239    #[inline(always)]
240    pub fn from_shape_and_avec(
241        shape: [usize; NDIM],
242        data: AVec<Item, ConstAlign<ALIGNMENT>>,
243    ) -> Self {
244        assert_eq!(
245            data.len(),
246            shape.iter().product::<usize>(),
247            "Data length does not match the shape: {} != {}",
248            data.len(),
249            shape.iter().product::<usize>()
250        );
251        Self::new(BaseArray::new(
252            AlignedVectorContainer::from_avec(data),
253            shape,
254        ))
255    }
256}
257
258impl<Item: Copy + Default> DynArray<Item, 2> {
259    /// Create a new dense matrix with shape `shape` filled with values from the iterator `iter`.
260    pub fn from_iter_aij<Iter: Iterator<Item = ([usize; 2], Item)>>(
261        shape: [usize; 2],
262        iter: Iter,
263    ) -> DynArray<Item, 2> {
264        let mut out = DynArray::<Item, 2>::from_shape(shape);
265
266        for (index, item) in iter {
267            out[index] = item;
268        }
269
270        out
271    }
272}
273
274impl<Item: Copy + Default, const ALIGNMENT: usize> AlignedDynArray<Item, 2, ALIGNMENT> {
275    /// Create a new dense matrix with shape `shape` filled with values from the iterator `iter`.
276    pub fn from_iter_aij<Iter: Iterator<Item = ([usize; 2], Item)>>(
277        shape: [usize; 2],
278        iter: Iter,
279    ) -> AlignedDynArray<Item, 2, ALIGNMENT> {
280        let mut out = AlignedDynArray::<Item, 2, ALIGNMENT>::from_shape(shape);
281
282        for (index, item) in iter {
283            out[index] = item;
284        }
285
286        out
287    }
288}
289
290impl<Item: Copy + Default> From<&[Item]> for DynArray<Item, 1> {
291    fn from(value: &[Item]) -> Self {
292        DynArray::<Item, 1>::from_shape_and_vec([value.len()], value.to_vec())
293    }
294}
295
296impl<Item: Copy + Default, const N: usize> From<[Item; N]> for StaticArray<Item, 1, N> {
297    fn from(value: [Item; N]) -> Self {
298        StaticArray::new(BaseArray::new(value.into(), [N]))
299    }
300}
301
302impl<Item: Copy + Default> From<Vec<Item>> for DynArray<Item, 1> {
303    fn from(value: Vec<Item>) -> Self {
304        DynArray::<Item, 1>::from_shape_and_vec([value.len()], value)
305    }
306}
307
308impl<Item: Copy + Default> From<Vec<Vec<Item>>> for DynArray<Item, 2> {
309    fn from(value: Vec<Vec<Item>>) -> Self {
310        let nrows = value.len();
311        if nrows == 0 {
312            empty_array()
313        } else {
314            let ncols = value.first().unwrap().len();
315            // Check that all columns have identical length
316
317            for row in value.iter() {
318                assert_eq!(ncols, row.len(), "All rows must have equal length.");
319            }
320
321            // Now create the matrix and fill with values.
322
323            let mut out = DynArray::<Item, 2>::from_shape([nrows, ncols]);
324
325            for (row_index, row) in value.iter().enumerate() {
326                for (col_index, &elem) in row.iter().enumerate() {
327                    out[[row_index, col_index]] = elem;
328                }
329            }
330
331            out
332        }
333    }
334}
335
336impl<Item: Copy + Default, const NDIM: usize> StridedDynArray<Item, NDIM> {
337    /// Allocate an array with an explicit physical `stride`.
338    ///
339    /// The allocation spans through the greatest reachable storage offset. Strides must map
340    /// every logical index to a distinct storage element.
341    ///
342    /// # Panics
343    ///
344    /// Panics if two computed logical offsets are equal. Shape/stride arithmetic uses ordinary
345    /// `usize` operations, so callers must provide metadata whose products, offsets, and storage
346    /// span are representable.
347    #[inline(always)]
348    pub fn from_shape_and_stride(shape: [usize; NDIM], stride: [usize; NDIM]) -> Self {
349        let size = required_storage_len(shape, stride);
350        Self::new(StridedBaseArray::new(
351            VectorContainer::new(size),
352            shape,
353            stride,
354        ))
355    }
356}
357
358impl<Item: Copy + Default, const NDIM: usize>
359    Array<StridedBaseArray<VectorContainer<Item>, NDIM>, NDIM>
360{
361    /// Create a new heap allocated row-major array.
362    #[inline(always)]
363    pub fn row_major(shape: [usize; NDIM]) -> Self {
364        let size = shape.iter().product();
365        Self::new(StridedBaseArray::new(
366            VectorContainer::new(size),
367            shape,
368            row_major_stride_from_shape(shape),
369        ))
370    }
371}
372
373impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
374where
375    ArrayImpl: Shape<NDIM>,
376{
377    /// Return the shape of an array.
378    ///
379    /// # Traits:
380    /// - [Shape]
381    #[inline(always)]
382    pub fn shape(&self) -> [usize; NDIM] {
383        self.0.shape()
384    }
385}
386
387impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
388where
389    ArrayImpl: Shape<NDIM>,
390{
391    /// Return the length of an array.
392    ///
393    /// For more than one dimension the length is the number of elements.
394    /// # Traits:
395    /// - [Shape]
396    #[inline(always)]
397    pub fn len(&self) -> usize {
398        self.0.len()
399    }
400
401    /// Return true if the array is empty.
402    ///
403    /// This is equivalent to at least one dimension being zero.
404    /// # Traits:
405    /// - [Shape]
406    #[inline(always)]
407    pub fn is_empty(&self) -> bool {
408        self.len() == 0
409    }
410}
411
412impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
413where
414    ArrayImpl: UnsafeRandomAccessByValue<NDIM>,
415{
416    /// Get an element by value.
417    ///
418    /// # Safety
419    /// `multi_index` must be in range.
420    ///
421    /// # Traits
422    /// - [UnsafeRandomAccessByValue]
423    #[inline(always)]
424    pub unsafe fn get_value_unchecked(&self, multi_index: [usize; NDIM]) -> ArrayImpl::Item {
425        unsafe { self.0.get_value_unchecked(multi_index) }
426    }
427}
428
429impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
430where
431    ArrayImpl: UnsafeRandomAccessByRef<NDIM>,
432{
433    /// Get an element by reference.
434    ///
435    /// # Safety
436    /// `multi_index` must be in range.
437    ///
438    /// # Traits
439    /// - [UnsafeRandomAccessByRef]
440    #[inline(always)]
441    pub unsafe fn get_unchecked(&self, multi_index: [usize; NDIM]) -> &ArrayImpl::Item {
442        unsafe { self.0.get_unchecked(multi_index) }
443    }
444}
445
446impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
447where
448    ArrayImpl: UnsafeRandomAccessMut<NDIM>,
449{
450    /// Get an element by mutable reference.
451    ///
452    /// # Safety
453    /// `multi_index` must be in range.
454    ///
455    /// # Traits
456    /// - [UnsafeRandomAccessMut]
457    #[inline(always)]
458    pub unsafe fn get_unchecked_mut(&mut self, multi_index: [usize; NDIM]) -> &mut ArrayImpl::Item {
459        unsafe { self.0.get_unchecked_mut(multi_index) }
460    }
461}
462
463impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
464where
465    ArrayImpl: RandomAccessByValue<NDIM>,
466{
467    /// Get an element by value.
468    ///
469    /// # Traits
470    /// - [RandomAccessByValue]
471    #[inline(always)]
472    pub fn get_value(&self, multi_index: [usize; NDIM]) -> Option<ArrayImpl::Item> {
473        self.0.get_value(multi_index)
474    }
475}
476
477impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
478where
479    ArrayImpl: RandomAccessByRef<NDIM>,
480{
481    /// Get an element by reference.
482    ///
483    /// # Traits
484    /// - [RandomAccessByRef]
485    #[inline(always)]
486    pub fn get(&self, multi_index: [usize; NDIM]) -> Option<&ArrayImpl::Item> {
487        self.0.get(multi_index)
488    }
489}
490
491impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
492where
493    ArrayImpl: RandomAccessMut<NDIM>,
494{
495    /// Get an element by mutable reference.
496    ///
497    /// # Traits
498    /// - [RandomAccessMut]
499    #[inline(always)]
500    pub fn get_mut(&mut self, multi_index: [usize; NDIM]) -> Option<&mut ArrayImpl::Item> {
501        self.0.get_mut(multi_index)
502    }
503}
504
505impl<ArrayImpl, const NDIM: usize> std::ops::Index<[usize; NDIM]> for Array<ArrayImpl, NDIM>
506where
507    ArrayImpl: RandomAccessByRef<NDIM>,
508{
509    type Output = ArrayImpl::Item;
510    #[inline(always)]
511    fn index(&self, index: [usize; NDIM]) -> &Self::Output {
512        self.0.get(index).unwrap()
513    }
514}
515
516impl<ArrayImpl, const NDIM: usize> std::ops::IndexMut<[usize; NDIM]> for Array<ArrayImpl, NDIM>
517where
518    Array<ArrayImpl, NDIM>: std::ops::Index<[usize; NDIM], Output = ArrayImpl::Item>,
519    ArrayImpl: RandomAccessMut<NDIM>,
520{
521    #[inline(always)]
522    fn index_mut(&mut self, index: [usize; NDIM]) -> &mut ArrayImpl::Item {
523        self.0.get_mut(index).unwrap()
524    }
525}
526
527impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
528where
529    ArrayImpl: RawAccess,
530{
531    /// Return the complete backing storage when it is exposed as one contiguous slice.
532    ///
533    /// The slice follows physical storage order, which may differ from logical column-major
534    /// iteration for a row-major array. Views and lazy expressions can return `None`.
535    #[inline(always)]
536    pub fn data(&self) -> Option<&[ArrayImpl::Item]> {
537        self.0.data()
538    }
539}
540
541impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
542where
543    ArrayImpl: RawAccessMut,
544{
545    /// Return the complete mutable backing storage when exposed as one contiguous slice.
546    ///
547    /// The slice follows physical storage order. Views and lazy expressions can return `None`.
548    #[inline(always)]
549    pub fn data_mut(&mut self) -> Option<&mut [ArrayImpl::Item]> {
550        self.0.data_mut()
551    }
552}
553
554impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
555where
556    ArrayImpl: Stride<NDIM>,
557{
558    /// Return the stride.
559    ///
560    /// # Traits
561    /// - [Stride]
562    #[inline(always)]
563    pub fn stride(&self) -> [usize; NDIM] {
564        self.0.stride()
565    }
566}
567
568impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
569where
570    ArrayImpl: Stride<NDIM> + Shape<NDIM>,
571{
572    /// Return the memory layout
573    ///
574    /// The possible memory layouts are defined in [MemoryLayout].
575    #[inline(always)]
576    pub fn memory_layout(&self) -> MemoryLayout {
577        self.0.memory_layout()
578    }
579
580    /// Return true of the memory layout is contiguous, otherwise false.
581    ///
582    /// A memory layout is contiguous if it is either row or column major.
583    pub fn is_contiguous(&self) -> bool {
584        self.0.is_contiguous()
585    }
586}
587
588impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
589where
590    ArrayImpl: ResizeInPlace<NDIM>,
591{
592    /// Resize the array to the new `shape`.
593    ///
594    /// The content of the array will be lost upon resizing.
595    ///
596    /// # Traits
597    /// - [ResizeInPlace]
598    pub fn resize_in_place(&mut self, shape: [usize; NDIM]) {
599        self.0.resize_in_place(shape)
600    }
601}
602
603impl<Item, ArrayImpl, const NDIM: usize> EvaluateArray<Item, NDIM> for Array<ArrayImpl, NDIM>
604where
605    Item: Copy + Default,
606    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM> + ContainerType,
607{
608    type OutputImpl =
609        <<ArrayImpl as ContainerType>::Type as ArrayFromContainerType>::OutputImpl<Item, NDIM>;
610
611    fn eval(&self) -> Array<Self::OutputImpl, NDIM> {
612        let mut out = <<ArrayImpl as ContainerType>::Type as ArrayFromContainerType>::new_arr::<
613            Item,
614            NDIM,
615        >(self.shape());
616
617        out.fill_from(self);
618
619        out
620    }
621}
622
623impl<Item, ArrayImpl, const NDIM: usize> EvaluateRowMajorArray<Item, NDIM>
624    for Array<ArrayImpl, NDIM>
625where
626    Item: Copy + Default,
627    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM> + ContainerType,
628{
629    type OutputImpl =
630        <<ArrayImpl as ContainerType>::Type as RowMajorArrayFromContainerType>::OutputImpl<
631            Item,
632            NDIM,
633        >;
634
635    fn eval_row_major(&self) -> Array<Self::OutputImpl, NDIM> {
636        let mut out =
637            <<ArrayImpl as ContainerType>::Type as RowMajorArrayFromContainerType>::new_row_major_arr::<
638                Item,
639                NDIM,
640            >(self.shape());
641
642        out.fill_from(self);
643
644        out
645    }
646}
647
648/// Create an empty array of given type and dimension.
649///
650/// Empty arrays serve as convenient containers for input into functions that
651/// resize an array before filling it with data.
652pub fn empty_array<Item, const NDIM: usize>() -> Array<BaseArray<VectorContainer<Item>, NDIM>, NDIM>
653where
654    Item: Copy + Default,
655{
656    let shape = [0; NDIM];
657    let container = VectorContainer::new(0);
658    Array::new(BaseArray::new(container, shape))
659}
660
661impl<'a, Item, const NDIM: usize> SliceArray<'a, Item, NDIM>
662where
663    Item: Copy + Default,
664{
665    /// Create a new array from `slice` with a given `shape`.
666    pub fn from_shape(slice: &'a [Item], shape: [usize; NDIM]) -> Self {
667        assert_eq!(
668            slice.len(),
669            shape.iter().product::<usize>(),
670            "Slice length does not match the shape: {} != {}",
671            slice.len(),
672            shape.iter().product::<usize>()
673        );
674        Array::new(BaseArray::new(SliceContainer::new(slice), shape))
675    }
676}
677
678impl<'a, Item, const NDIM: usize> SliceArrayMut<'a, Item, NDIM>
679where
680    Item: Copy + Default,
681{
682    /// Create a new array from mutable `slice` with a given `shape`.
683    pub fn from_shape(slice: &'a mut [Item], shape: [usize; NDIM]) -> Self {
684        assert_eq!(
685            slice.len(),
686            shape.iter().product::<usize>(),
687            "Slice length does not match the shape: {} != {}",
688            slice.len(),
689            shape.iter().product::<usize>()
690        );
691        Array::new(BaseArray::new(SliceContainerMut::new(slice), shape))
692    }
693}
694
695impl<'a, Item, const NDIM: usize> StridedSliceArray<'a, Item, NDIM>
696where
697    Item: Copy + Default,
698{
699    /// Create a new array from `slice` with a given `shape` and `stride`.
700    pub fn from_shape_and_stride(
701        slice: &'a [Item],
702        shape: [usize; NDIM],
703        stride: [usize; NDIM],
704    ) -> Self {
705        Array::new(StridedBaseArray::new(
706            SliceContainer::new(slice),
707            shape,
708            stride,
709        ))
710    }
711}
712
713impl<'a, Item, const NDIM: usize> StridedSliceArrayMut<'a, Item, NDIM>
714where
715    Item: Copy + Default,
716{
717    /// Create a new array from `slice` with a given `shape` and `stride`.
718    pub fn from_shape_and_stride(
719        slice: &'a mut [Item],
720        shape: [usize; NDIM],
721        stride: [usize; NDIM],
722    ) -> Self {
723        Array::new(StridedBaseArray::new(
724            SliceContainerMut::new(slice),
725            shape,
726            stride,
727        ))
728    }
729}
730
731impl<ArrayImpl: Shape<NDIM>, const NDIM: usize> std::fmt::Debug for Array<ArrayImpl, NDIM> {
732    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
733        let shape = self.shape();
734        write!(f, "Array[").unwrap();
735        for item in shape {
736            write!(f, "{item},").unwrap();
737        }
738        write!(f, "]")
739    }
740}
741
742impl<Item, const NDIM: usize> DynArray<Item, NDIM>
743where
744    Item: Copy + Default,
745{
746    /// Create a new array and fill with values from `arr`.
747    pub fn new_from<ArrayImpl>(arr: &Array<ArrayImpl, NDIM>) -> Self
748    where
749        ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
750    {
751        let mut output = empty_array::<Item, NDIM>();
752        output.fill_from_resize(arr);
753        output
754    }
755}
756
757impl<Item, const NDIM: usize> StridedDynArray<Item, NDIM>
758where
759    Item: Copy + Default,
760{
761    /// Create a new strided array with `stride` and fill with values from `arr`.
762    pub fn new_from<ArrayImpl>(stride: [usize; NDIM], arr: &Array<ArrayImpl, NDIM>) -> Self
763    where
764        ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
765    {
766        let mut output = StridedDynArray::from_shape_and_stride(arr.shape(), stride);
767        output.fill_from(arr);
768        output
769    }
770
771    /// Create a new row-major array from existing array `arr`.
772    pub fn row_major_from<ArrayImpl>(arr: &Array<ArrayImpl, NDIM>) -> Self
773    where
774        ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
775    {
776        StridedDynArray::new_from(row_major_stride_from_shape(arr.shape()), arr)
777    }
778}
779
780impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
781where
782    ArrayImpl: UnsafeRandom1DAccessByValue + Shape<NDIM>,
783{
784    /// Iterate through the array by value.
785    ///
786    /// The iterator always proceeds in column-major order.
787    ///
788    /// # Traits
789    /// - [UnsafeRandom1DAccessByValue]
790    /// - [Shape]
791    #[inline(always)]
792    pub fn iter_value(&self) -> ArrayDefaultIteratorByValue<'_, ArrayImpl, NDIM> {
793        ArrayDefaultIteratorByValue::new(self)
794    }
795}
796
797impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
798where
799    ArrayImpl: UnsafeRandom1DAccessByRef + Shape<NDIM>,
800{
801    /// Iterate through the array by reference.
802    ///
803    /// The iterator always proceeds in column-major order.
804    ///
805    /// # Traits
806    /// - [UnsafeRandom1DAccessByRef]
807    /// - [Shape]
808    #[inline(always)]
809    pub fn iter_ref(&self) -> ArrayDefaultIteratorByRef<'_, ArrayImpl, NDIM> {
810        ArrayDefaultIteratorByRef::new(self)
811    }
812}
813
814impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
815where
816    ArrayImpl: UnsafeDisjointRandom1DAccessMut + Shape<NDIM>,
817{
818    /// Iterate through the array by mutable reference.
819    ///
820    /// The iterator always proceeds in column-major order.
821    ///
822    /// # Traits
823    /// - [UnsafeDisjointRandom1DAccessMut]
824    /// - [Shape]
825    #[inline(always)]
826    pub fn iter_mut(&mut self) -> ArrayDefaultIteratorMut<'_, ArrayImpl, NDIM> {
827        ArrayDefaultIteratorMut::new(self)
828    }
829}
830
831impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
832where
833    ArrayImpl: UnsafeRandomAccessByValue<NDIM, Item = Item> + Shape<NDIM>,
834{
835    /// Iterate through the diagonal of the array by value.
836    ///
837    /// # Traits
838    /// - [UnsafeRandomAccessByValue]
839    /// - [Shape]
840    #[inline(always)]
841    pub fn diag_iter_value(&self) -> ArrayDiagIteratorByValue<'_, ArrayImpl, NDIM> {
842        ArrayDiagIteratorByValue::new(self)
843    }
844}
845
846impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
847where
848    ArrayImpl: UnsafeRandomAccessByRef<NDIM, Item = Item> + Shape<NDIM>,
849{
850    /// Iterate through the diagonal of the array by reference.
851    ///
852    /// # Traits
853    /// - [UnsafeRandomAccessByRef]
854    /// - [Shape]
855    #[inline(always)]
856    pub fn diag_iter_ref(&self) -> ArrayDiagIteratorByRef<'_, ArrayImpl, NDIM> {
857        ArrayDiagIteratorByRef::new(self)
858    }
859}
860
861impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
862where
863    ArrayImpl: UnsafeRandomAccessMut<NDIM, Item = Item> + Shape<NDIM>,
864{
865    /// Iterate through the diagonal of the array by mutable reference.
866    ///
867    /// # Traits
868    /// - [UnsafeRandomAccessByRef]
869    /// - [Shape]
870    #[inline(always)]
871    pub fn diag_iter_mut(&mut self) -> ArrayDiagIteratorMut<'_, ArrayImpl, NDIM> {
872        ArrayDiagIteratorMut::new(self)
873    }
874}
875
876impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
877where
878    ArrayImpl: UnsafeDisjointRandom1DAccessMut<Item = Item> + Shape<NDIM>,
879{
880    /// Copy values from another array in logical column-major order.
881    ///
882    /// # Panics
883    ///
884    /// Panics if the shapes differ.
885    #[inline(always)]
886    pub fn fill_from<ArrayImplOther>(&mut self, other: &Array<ArrayImplOther, NDIM>)
887    where
888        ArrayImplOther: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
889    {
890        assert_eq!(self.shape(), other.shape());
891
892        for (item, other_item) in self.iter_mut().zip(other.iter_value()) {
893            *item = other_item;
894        }
895    }
896}
897
898impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
899where
900    Const<NDIM>: ToUInt,
901    <Const<NDIM> as ToUInt>::Output: Unsigned + NonZero,
902    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item>
903        + UnsafeRandomAccessByValue<NDIM, Item = Item>
904        + Shape<NDIM>,
905{
906    /// Create a new multislice iterator.
907    ///
908    /// Given an array `arr` with dimensions `[2, 3, 4, 5]`. Calling
909    /// `self.multislice_iter` with `axes = [0, 2]` returns an iterator
910    /// over values of the type `(indices, slice)`, where indices is a
911    /// [2; usize] array taking all possible axis indices in axes `0` and `2`,
912    /// and `slice` is the output of `arr.multislice([0, 2], [indices])`.
913    pub fn multislice_iter<const SDIM: usize, const OUTDIM: usize>(
914        &self,
915        axes: [usize; SDIM],
916    ) -> MultisliceIterator<'_, Item, ArrayImpl, NDIM, SDIM, OUTDIM>
917    where
918        Const<SDIM>: ToUInt,
919        Const<OUTDIM>: ToUInt,
920        <Const<SDIM> as ToUInt>::Output: Unsigned + NonZero,
921        <Const<OUTDIM> as ToUInt>::Output: Unsigned + NonZero,
922        NumberType<SDIM>: IsSmallerThan<NDIM>,
923        PInt<U<OUTDIM>>: Add<PInt<U<SDIM>>, Output = PInt<U<NDIM>>>,
924        PInt<U<NDIM>>: Sub<PInt<U<SDIM>>>,
925    {
926        MultisliceIterator::new(self, axes)
927    }
928}
929
930impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
931where
932    Const<NDIM>: ToUInt,
933    <Const<NDIM> as ToUInt>::Output: Unsigned + NonZero,
934    ArrayImpl: UnsafeDisjointRandom1DAccessMut<Item = Item>
935        + UnsafeRandomAccessMut<NDIM, Item = Item>
936        + Shape<NDIM>,
937{
938    /// Create a new mutable multislice iterator.
939    ///
940    /// Given an array `arr` with dimensions `[2, 3, 4, 5]`. Calling
941    /// `self.multislice_iter_mut` with `axes = [0, 2]` returns an iterator
942    /// over values of the type `(indices, slice)`, where indices is a
943    /// [2; usize] array taking all possible axis indices in axes `0` and `2`,
944    /// and `slice` is the output of `arr.multislice([0, 2], [indices])`.
945    pub fn multislice_iter_mut<const SDIM: usize, const OUTDIM: usize>(
946        &mut self,
947        axes: [usize; SDIM],
948    ) -> MultisliceIteratorMut<'_, Item, ArrayImpl, NDIM, SDIM, OUTDIM>
949    where
950        Const<SDIM>: ToUInt,
951        Const<OUTDIM>: ToUInt,
952        <Const<SDIM> as ToUInt>::Output: Unsigned + NonZero,
953        <Const<OUTDIM> as ToUInt>::Output: Unsigned + NonZero,
954        NumberType<SDIM>: IsSmallerThan<NDIM>,
955        PInt<U<OUTDIM>>: Add<PInt<U<SDIM>>, Output = PInt<U<NDIM>>>,
956        PInt<U<NDIM>>: Sub<PInt<U<SDIM>>>,
957    {
958        MultisliceIteratorMut::new(self, axes)
959    }
960}
961
962impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
963where
964    ArrayImpl: UnsafeDisjointRandom1DAccessMut + Shape<NDIM>,
965{
966    /// Fill elements in logical column-major order from an iterator.
967    ///
968    /// Filling stops when either the array or iterator is exhausted. Existing trailing array
969    /// elements are unchanged when the iterator is short; excess iterator values are ignored.
970    #[inline(always)]
971    pub fn fill_from_iter<Iter>(&mut self, iter: Iter)
972    where
973        Iter: Iterator<Item = ArrayImpl::Item>,
974    {
975        for (item, other_item) in izip!(self.iter_mut(), iter) {
976            *item = other_item;
977        }
978    }
979}
980
981impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
982where
983    ArrayImpl: ResizeInPlace<NDIM> + Shape<NDIM> + UnsafeDisjointRandom1DAccessMut,
984{
985    /// Fill from another array and resize if necessary.
986    ///
987    /// This method is especially useful together with [empty_array].
988    ///
989    /// #Traits
990    /// - [UnsafeDisjointRandom1DAccessMut]
991    /// - [Shape]
992    #[inline(always)]
993    pub fn fill_from_resize<ArrayImplOther>(&mut self, other: &Array<ArrayImplOther, NDIM>)
994    where
995        ArrayImplOther: Shape<NDIM> + UnsafeRandom1DAccessByValue<Item = ArrayImpl::Item>,
996    {
997        self.resize_in_place(other.shape());
998        self.fill_from(other);
999    }
1000}
1001
1002impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1003where
1004    ArrayImpl: UnsafeDisjointRandom1DAccessMut + Shape<NDIM>,
1005{
1006    /// Fill array with a given value.
1007    ///
1008    /// # Traits
1009    /// - [UnsafeDisjointRandom1DAccessMut]
1010    /// - [Shape]
1011    #[inline(always)]
1012    pub fn fill_with_value(&mut self, value: ArrayImpl::Item) {
1013        for item in self.iter_mut() {
1014            *item = value;
1015        }
1016    }
1017}
1018
1019impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1020where
1021    ArrayImpl: UnsafeDisjointRandom1DAccessMut + Shape<NDIM>,
1022{
1023    /// Set the elements of the array to zero.
1024    pub fn set_zero(&mut self) {
1025        for item in self.iter_mut() {
1026            *item = Default::default();
1027        }
1028    }
1029}
1030
1031impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1032where
1033    ArrayImpl: UnsafeDisjointRandom1DAccessMut + UnsafeRandomAccessMut<NDIM> + Shape<NDIM>,
1034    ArrayImpl::Item: One,
1035{
1036    /// Set all off-diagonal elements to zero and the diagonal to one.
1037    pub fn set_identity(&mut self) {
1038        self.set_zero();
1039        self.diag_iter_mut().for_each(|elem| *elem = One::one());
1040    }
1041}
1042
1043impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1044where
1045    ArrayImpl: UnsafeRandomAccessByValue<NDIM> + Shape<NDIM>,
1046    ArrayImpl::Item: std::iter::Sum,
1047{
1048    /// Compute the sum of the diagonal values of the array.
1049    ///
1050    /// Note: The Item type must support the trait [std::iter::Sum].
1051    ///
1052    /// # Traits
1053    /// - [UnsafeRandomAccessByValue]
1054    /// - [Shape]
1055    #[inline(always)]
1056    pub fn trace(&self) -> ArrayImpl::Item {
1057        self.diag_iter_value().sum()
1058    }
1059}
1060
1061impl<Item, ArrayImpl> Array<ArrayImpl, 1>
1062where
1063    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<1>,
1064    ArrayImpl::Item: Conj<Output = Item>
1065        + std::ops::Mul<Output = ArrayImpl::Item>
1066        + std::ops::Add<Output = Item>,
1067{
1068    /// Compute the inner product of two 1d arrays.
1069    ///
1070    /// The elements of `other` are taken as conjugate.
1071    ///
1072    /// Returns `None` if the arrays are empty.
1073    ///
1074    /// Note: The Item type must support the traits [Conj], [std::iter::Sum] and [std::ops::Mul].
1075    ///
1076    /// # Traits
1077    /// - [UnsafeRandom1DAccessByValue]
1078    /// - [Shape]
1079    #[inline(always)]
1080    pub fn inner<ArrayImplOther>(&self, other: &Array<ArrayImplOther, 1>) -> Option<Item>
1081    where
1082        ArrayImplOther: UnsafeRandom1DAccessByValue<Item = Item> + Shape<1>,
1083    {
1084        assert_eq!(self.shape(), other.shape());
1085        izip!(self.iter_value(), other.iter_value())
1086            .map(|(x, y)| x * y.conj())
1087            .reduce(std::ops::Add::add)
1088    }
1089}
1090
1091impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1092where
1093    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
1094    Item: Abs,
1095    <Item as Abs>::Output: Max<Output = <Item as Abs>::Output>,
1096{
1097    /// Compute the maximum absolute value over all elements.
1098    ///
1099    /// Note: The item type must support [Abs] and the output of [Abs]
1100    /// must support [Max].
1101    ///
1102    /// The function returns `None` if the array is empty.
1103    ///
1104    /// # Traits
1105    /// - [UnsafeRandom1DAccessByValue]
1106    /// - [Shape]
1107    #[inline(always)]
1108    pub fn max_abs(&self) -> Option<<Item as Abs>::Output> {
1109        self.iter_value().map(|elem| elem.abs()).reduce(Max::max)
1110    }
1111}
1112
1113impl<Item, ArrayImpl> Array<ArrayImpl, 1>
1114where
1115    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<1>,
1116    Item: Abs,
1117    <Item as Abs>::Output: std::ops::Add<Output = <Item as Abs>::Output>,
1118{
1119    /// Compute the 1-norm of a 1d array.
1120    ///
1121    /// The function returns `None` if the array is empty.
1122    ///
1123    /// Note: The item type must support [Abs] and the output of [Abs] must
1124    /// support [std::ops::Add].
1125    ///
1126    ///
1127    /// # Traits
1128    /// - [UnsafeRandom1DAccessByValue]
1129    /// - [Shape]
1130    #[inline(always)]
1131    pub fn norm_1(&self) -> Option<<Item as Abs>::Output> {
1132        self.iter_value()
1133            .map(|elem| elem.abs())
1134            .reduce(std::ops::Add::add)
1135    }
1136}
1137
1138impl<Item, ArrayImpl> Array<ArrayImpl, 1>
1139where
1140    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<1>,
1141    Item: AbsSquare,
1142    <Item as AbsSquare>::Output: Sqrt<Output = <Item as AbsSquare>::Output>
1143        + std::ops::Add<Output = <Item as AbsSquare>::Output>,
1144{
1145    /// Compute the 2-norm of a 1d array.
1146    ///
1147    /// Note: The item type must support [AbsSquare]. The output of [AbsSquare]
1148    /// must support [std::ops::Add] and [Sqrt].
1149    ///
1150    /// # Traits
1151    /// - [UnsafeRandom1DAccessByValue]
1152    /// - [Shape]
1153    #[inline(always)]
1154    pub fn norm_2(&self) -> Option<<Item as AbsSquare>::Output> {
1155        self.iter_value()
1156            .map(|elem| elem.abs_square())
1157            .reduce(std::ops::Add::add)
1158            .map(|elem| elem.sqrt())
1159    }
1160}
1161
1162impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1163where
1164    ArrayImpl: UnsafeRandom1DAccessByValue<Item = Item> + Shape<NDIM>,
1165    Item: AbsSquare,
1166    <Item as AbsSquare>::Output: Sqrt<Output = <Item as AbsSquare>::Output>
1167        + std::ops::Add<Output = <Item as AbsSquare>::Output>
1168        + Copy
1169        + Default,
1170{
1171    /// Compute the Frobenius-Norm of a nd array.
1172    ///
1173    /// Note: The item type must support [AbsSquare]. The output of [AbsSquare]
1174    /// must support [std::ops::Add], [Sqrt], [Copy], and [Default].
1175    ///
1176    /// # Traits
1177    /// - [UnsafeRandom1DAccessByValue]
1178    /// - [Shape]
1179    #[inline(always)]
1180    pub fn norm_fro(&self) -> Option<<Item as AbsSquare>::Output> {
1181        self.r()
1182            .abs_square()
1183            .iter_value()
1184            .reduce(std::ops::Add::add)
1185            .map(|elem| elem.sqrt())
1186    }
1187}
1188
1189impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1190where
1191    ArrayImpl: UnsafeRandom1DAccessByValue + Shape<NDIM>,
1192{
1193    /// Convert an array into the new item type T.
1194    ///
1195    /// Note: It is required that `ArrayImpl::Item: Into<T>`.
1196    ///
1197    /// # Traits
1198    /// - [UnsafeRandom1DAccessByValue]
1199    /// - [Shape]
1200    #[allow(clippy::type_complexity)]
1201    pub fn into_type<T>(
1202        self,
1203    ) -> Array<
1204        ArrayUnaryOperator<ArrayImpl::Item, T, ArrayImpl, fn(ArrayImpl::Item) -> T, NDIM>,
1205        NDIM,
1206    >
1207    where
1208        ArrayImpl::Item: Into<T>,
1209    {
1210        Array::new(ArrayUnaryOperator::new(self, |item| item.into()))
1211    }
1212}
1213
1214impl<ArrayImpl> Array<ArrayImpl, 2>
1215where
1216    ArrayImpl: Shape<2> + UnsafeRandomAccessByValue<2>,
1217{
1218    /// Return a column iterator for a 2d array.
1219    ///
1220    /// # Traits
1221    /// - [UnsafeRandomAccessByValue]
1222    /// - [Shape]
1223    pub fn col_iter(&self) -> ColIterator<'_, ArrayImpl, 2> {
1224        ColIterator::new(self)
1225    }
1226}
1227
1228impl<ArrayImpl> Array<ArrayImpl, 2>
1229where
1230    ArrayImpl: Shape<2> + UnsafeRandomAccessMut<2>,
1231{
1232    /// Return a mutable column iterator for a 2d array.
1233    ///
1234    /// # Traits
1235    /// - [UnsafeRandomAccessMut]
1236    /// - [Shape]
1237    pub fn col_iter_mut(&mut self) -> ColIteratorMut<'_, ArrayImpl, 2> {
1238        ColIteratorMut::new(self)
1239    }
1240}
1241
1242impl<ArrayImpl> Array<ArrayImpl, 2>
1243where
1244    ArrayImpl: Shape<2> + UnsafeRandomAccessByValue<2>,
1245{
1246    /// Return a row iterator for a 2d array.
1247    ///
1248    /// # Traits
1249    /// - [UnsafeRandomAccessByValue]
1250    /// - [Shape]
1251    pub fn row_iter(&self) -> RowIterator<'_, ArrayImpl, 2> {
1252        RowIterator::new(self)
1253    }
1254}
1255
1256impl<ArrayImpl> Array<ArrayImpl, 2>
1257where
1258    ArrayImpl: Shape<2> + UnsafeRandomAccessByValue<2>,
1259{
1260    /// Return a mutable row iterator for a 2d array.
1261    ///
1262    /// # Traits
1263    /// - [UnsafeRandomAccessMut]
1264    /// - [Shape]
1265    pub fn row_iter_mut(&mut self) -> RowIteratorMut<'_, ArrayImpl, 2> {
1266        RowIteratorMut::new(self)
1267    }
1268}
1269
1270impl<ArrayImpl> Array<ArrayImpl, 2>
1271where
1272    ArrayImpl: UnsafeRandom1DAccessByValue + Shape<2>,
1273{
1274    /// Return an iterator of the form `(i, j, data)`.
1275    ///
1276    /// Here, `i` is the row, `j` is the column, and `data` is the associated element
1277    /// returned by value.
1278    ///
1279    /// # Traits
1280    /// - [UnsafeRandom1DAccessByValue]
1281    /// - [Shape]
1282    pub fn iter_aij_value(&self) -> impl Iterator<Item = ([usize; 2], ArrayImpl::Item)> + '_ {
1283        let iter = ArrayDefaultIteratorByValue::new(self);
1284        AsMultiIndex::multi_index(std::iter::Iterator::enumerate(iter), self.shape())
1285    }
1286}
1287
1288impl<ArrayImpl> Array<ArrayImpl, 2>
1289where
1290    ArrayImpl: UnsafeRandom1DAccessByRef + Shape<2>,
1291{
1292    /// Return an iterator of the form `(i, j, &data)`.
1293    ///
1294    /// Here, `i` is the row, `j` is the column, and `&data` is the reference to the associated
1295    /// element.
1296    ///
1297    /// # Traits
1298    /// - [UnsafeRandom1DAccessByRef]
1299    /// - [Shape]
1300    pub fn iter_aij_ref(&self) -> impl Iterator<Item = ([usize; 2], &ArrayImpl::Item)> + '_ {
1301        let iter = ArrayDefaultIteratorByRef::new(self);
1302        AsMultiIndex::multi_index(std::iter::Iterator::enumerate(iter), self.shape())
1303    }
1304}
1305
1306impl<ArrayImpl> Array<ArrayImpl, 2>
1307where
1308    ArrayImpl: UnsafeDisjointRandom1DAccessMut + Shape<2>,
1309{
1310    /// Return an iterator of the form `(i, j, &mut data)`.
1311    ///
1312    /// Here, `i` is the row, `j` is the column, and `&data` is the mutable reference to the associated
1313    /// element.
1314    ///
1315    /// # Traits
1316    /// - [UnsafeRandom1DAccessByRef]
1317    /// - [Shape]
1318    pub fn iter_aij_mut(
1319        &mut self,
1320    ) -> impl Iterator<Item = ([usize; 2], &mut ArrayImpl::Item)> + '_ {
1321        let shape = self.shape();
1322        let iter = ArrayDefaultIteratorMut::new(self);
1323        AsMultiIndex::multi_index(std::iter::Iterator::enumerate(iter), shape)
1324    }
1325}
1326
1327impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM> {
1328    /// Cast array to type `T`.
1329    ///
1330    /// The cast is done through [num::cast::cast] and source and target types need to
1331    /// support casting through that function.
1332    pub fn cast<Target>(self) -> Array<ArrayCast<Target, ArrayImpl, NDIM>, NDIM> {
1333        Array::new(ArrayCast::new(self))
1334    }
1335}
1336
1337impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM> {
1338    /// Coerce the array to a specific dimension.
1339    ///
1340    /// This is useful to coerce from a generic dimension parameter to
1341    /// a specific number of dimensions.
1342    pub fn coerce_dim<const CDIM: usize>(
1343        self,
1344    ) -> RlstResult<Array<CoerceArray<ArrayImpl, NDIM, CDIM>, CDIM>> {
1345        if CDIM == NDIM {
1346            // If the dimensions and item types match return a CoerceArray
1347            Ok(Array::new(CoerceArray::new(self)))
1348        } else {
1349            // Otherwise, we need to coerce the array.
1350            Err(RlstError::GeneralError(
1351                format!("Cannot coerce array: dimensions do not match {CDIM} != {NDIM}.")
1352                    .to_string(),
1353            ))
1354        }
1355    }
1356}
1357
1358impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1359where
1360    ArrayImpl: Shape<NDIM>,
1361{
1362    /// Permute axes of an array.
1363    ///
1364    /// For a valid permutation, `permutation[output_axis]` identifies the corresponding source
1365    /// axis. For example, `[1, 0, 2]` swaps the first two axes.
1366    ///
1367    /// # Panics
1368    ///
1369    /// Panics if an entry is outside `0..NDIM` or if an axis occurs more than once.
1370    /// Every source axis must occur exactly once.
1371    pub fn permute_axes(
1372        self,
1373        permutation: [usize; NDIM],
1374    ) -> Array<ArrayTranspose<ArrayImpl, NDIM>, NDIM> {
1375        Array::new(ArrayTranspose::new(self, permutation))
1376    }
1377
1378    /// Transpose an array.
1379    ///
1380    /// The transpose of an n-dimensional array reverses the order of the axes.
1381    pub fn transpose(self) -> Array<ArrayTranspose<ArrayImpl, NDIM>, NDIM> {
1382        let mut permutation = [0; NDIM];
1383
1384        for (ind, p) in (0..NDIM).rev().enumerate() {
1385            permutation[ind] = p;
1386        }
1387
1388        self.permute_axes(permutation)
1389    }
1390}
1391
1392impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1393where
1394    Item: RandScalar + RlstScalar,
1395    ArrayImpl: UnsafeDisjointRandom1DAccessMut<Item = Item> + Shape<NDIM>,
1396    StandardNormal: Distribution<Item::Real>,
1397    StandardUniform: Distribution<Item::Real>,
1398{
1399    /// Fill an array with normally distributed random numbers.
1400    ///
1401    /// # Traits
1402    /// - [UnsafeDisjointRandom1DAccessMut]
1403    /// - [Shape]
1404    /// - Item: [RandScalar] + [RlstScalar]
1405    pub fn fill_from_standard_normal<R: Rng>(&mut self, rng: &mut R) {
1406        let dist = StandardNormal;
1407        self.iter_mut()
1408            .for_each(|val| *val = <Item>::random_scalar(rng, &dist));
1409    }
1410
1411    /// Fill an array with equally distributed random numbers.
1412    ///
1413    /// # Traits
1414    /// - [UnsafeDisjointRandom1DAccessMut]
1415    /// - [Shape]
1416    /// - Item: [RandScalar] + [RlstScalar]
1417    pub fn fill_from_equally_distributed<R: Rng>(&mut self, rng: &mut R) {
1418        let dist = StandardUniform;
1419        self.iter_mut()
1420            .for_each(|val| *val = <Item>::random_scalar(rng, &dist));
1421    }
1422
1423    /// Fill an array with equally distributed random numbers using a given `seed`.
1424    ///
1425    /// # Traits
1426    /// - [UnsafeDisjointRandom1DAccessMut]
1427    /// - [Shape]
1428    /// - Item: [RandScalar] + [RlstScalar]
1429    pub fn fill_from_seed_equally_distributed(&mut self, seed: usize) {
1430        let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
1431        let dist = StandardUniform;
1432        self.iter_mut()
1433            .for_each(|val| *val = <Item>::random_scalar(&mut rng, &dist));
1434    }
1435
1436    /// Fill an array with normally distributed random numbers using a given `seed`.
1437    ///
1438    /// # Traits
1439    /// - [UnsafeDisjointRandom1DAccessMut]
1440    /// - [Shape]
1441    /// - Item: [RandScalar] + [RlstScalar]
1442    pub fn fill_from_seed_normally_distributed(&mut self, seed: usize) {
1443        let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
1444        let dist = StandardNormal;
1445        self.iter_mut()
1446            .for_each(|val| *val = <Item>::random_scalar(&mut rng, &dist));
1447    }
1448}
1449
1450impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM> {
1451    /// Return a lazy view with one axis indexed in reverse.
1452    ///
1453    /// # Panics
1454    ///
1455    /// Panics if `axis >= NDIM`.
1456    pub fn reverse_axis(self, axis: usize) -> Array<ReverseAxis<ArrayImpl, NDIM>, NDIM> {
1457        assert!(axis < NDIM, "Axis out of bounds");
1458        Array::new(ReverseAxis::new(self, axis))
1459    }
1460}
1461
1462impl<ArrayImpl, const ADIM: usize> Array<ArrayImpl, ADIM>
1463where
1464    ArrayImpl: Shape<ADIM>,
1465{
1466    /// Create a slice from a given array.
1467    ///
1468    /// Consider an array `arr` with shape `[a0, a1, a2, a3, ...]`. The function call
1469    /// `arr.slice(2, 3)` returns a one dimension smaller array indexed by `[a0, a1, 3, a3, ...]`.
1470    /// Hence, the dimension `2` has been fixed to always have the value `3.`
1471    ///
1472    /// # Examples
1473    ///
1474    /// If `arr` is a matrix then the first column of the matrix is obtained from
1475    /// `arr.slice(1, 0)`, while the third row of the matrix is obtained from
1476    /// `arr.slice(0, 2)`.
1477    pub fn slice<const OUTDIM: usize>(
1478        self,
1479        axis: usize,
1480        index: usize,
1481    ) -> Array<ArraySlice<ArrayImpl, ADIM, OUTDIM>, OUTDIM>
1482    where
1483        NumberType<ADIM>: IsGreaterByOne<OUTDIM>,
1484        NumberType<OUTDIM>: IsGreaterZero,
1485    {
1486        Array::new(ArraySlice::new(self, [axis, index]))
1487    }
1488
1489    /// Create a multislice from a given array.
1490    ///
1491    /// A multislice works like [Array::slice] but allows the slicing along multiple dimensions at the same time.
1492    /// The `axes` array determines which axes are fixed and the `indices` array determines the value
1493    /// in each fixed axis. The dimension `SDIM` is the number of axes along which we slice. The parameter `OUTDIM`
1494    /// is the number of dimensions after slicing.
1495    pub fn multislice<const SDIM: usize, const OUTDIM: usize>(
1496        self,
1497        axes: [usize; SDIM],
1498        indices: [usize; SDIM],
1499    ) -> Array<Multislice<ArrayImpl, ADIM, SDIM, OUTDIM>, OUTDIM>
1500    where
1501        NumberType<SDIM>: IsSmallerThan<ADIM>,
1502        NumberDifference<ADIM, SDIM>: SubtractEqual<OUTDIM>,
1503    {
1504        Array::new(Multislice::new(self, axes, indices))
1505    }
1506}
1507
1508impl<ArrayImpl> Array<ArrayImpl, 2>
1509where
1510    ArrayImpl: Shape<2>,
1511{
1512    /// Return the row with index `row_index` from a two-dimensional array.
1513    pub fn row(self, row_index: usize) -> Array<ArraySlice<ArrayImpl, 2, 1>, 1> {
1514        self.slice::<1>(0, row_index)
1515    }
1516
1517    /// Return the column with index `col_index` from a two-dimensional array.
1518    pub fn col(self, col_index: usize) -> Array<ArraySlice<ArrayImpl, 2, 1>, 1> {
1519        self.slice::<1>(1, col_index)
1520    }
1521}
1522
1523impl<ArrayImpl: Shape<NDIM>, const NDIM: usize> Array<ArrayImpl, NDIM> {
1524    /// Move the array into a subview specified by an `offset` and `shape` of the subview.
1525    ///
1526    /// The `offset` is the starting index of the subview and the `shape` is the number of indices
1527    /// in each dimension of the subview.
1528    pub fn into_subview(
1529        self,
1530        offset: [usize; NDIM],
1531        shape: [usize; NDIM],
1532    ) -> Array<ArraySubView<ArrayImpl, NDIM>, NDIM> {
1533        Array::new(ArraySubView::new(self, offset, shape))
1534    }
1535}
1536
1537impl<Item, ArrayImpl1, ArrayImpl2, const NDIM: usize> MulAdd<Item, Array<ArrayImpl2, NDIM>>
1538    for Array<ArrayImpl1, NDIM>
1539where
1540    ArrayImpl1: BaseItem<Item = Item> + Shape<NDIM>,
1541    ArrayImpl2: BaseItem<Item = Item> + Shape<NDIM>,
1542    Item: MulAdd<Output = Item> + Copy,
1543{
1544    type Output = Array<MulAddImpl<ArrayImpl1, ArrayImpl2, Item, NDIM>, NDIM>;
1545    /// Compentwie form `self * a + b`, where `a` is a scalar and `b` is another array.
1546    /// The implementation depdends on the `MulAdd` trait from the `num` crate for the componets of
1547    /// the arrays.
1548    fn mul_add(
1549        self,
1550        a: Item,
1551        b: Array<ArrayImpl2, NDIM>,
1552    ) -> Array<MulAddImpl<ArrayImpl1, ArrayImpl2, Item, NDIM>, NDIM>
1553    where
1554        ArrayImpl1: BaseItem<Item = Item> + Shape<NDIM>,
1555        ArrayImpl2: BaseItem<Item = Item> + Shape<NDIM>,
1556        Item: MulAdd<Output = Item> + Copy,
1557    {
1558        Array::new(MulAddImpl::new(self, b, a))
1559    }
1560}
1561
1562impl<ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::Add<Array<ArrayImpl2, NDIM>>
1563    for Array<ArrayImpl1, NDIM>
1564where
1565    ArrayImpl1: Shape<NDIM>,
1566    ArrayImpl2: Shape<NDIM>,
1567{
1568    type Output = Array<ArrayAddition<ArrayImpl1, ArrayImpl2, NDIM>, NDIM>;
1569
1570    #[inline(always)]
1571    fn add(self, rhs: Array<ArrayImpl2, NDIM>) -> Self::Output {
1572        Array::new(ArrayAddition::new(self, rhs))
1573    }
1574}
1575
1576impl<Item, ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::AddAssign<Array<ArrayImpl2, NDIM>>
1577    for Array<ArrayImpl1, NDIM>
1578where
1579    ArrayImpl1: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1580    ArrayImpl2: Shape<NDIM> + UnsafeRandom1DAccessByValue<Item = Item>,
1581    Item: std::ops::AddAssign,
1582{
1583    #[inline(always)]
1584    fn add_assign(&mut self, rhs: Array<ArrayImpl2, NDIM>) {
1585        assert_eq!(self.shape(), rhs.shape());
1586        for (item1, item2) in izip!(self.iter_mut(), rhs.iter_value()) {
1587            *item1 += item2;
1588        }
1589    }
1590}
1591
1592impl<ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::Sub<Array<ArrayImpl2, NDIM>>
1593    for Array<ArrayImpl1, NDIM>
1594where
1595    ArrayImpl1: Shape<NDIM>,
1596    ArrayImpl2: Shape<NDIM>,
1597{
1598    type Output = Array<ArraySubtraction<ArrayImpl1, ArrayImpl2, NDIM>, NDIM>;
1599
1600    fn sub(self, rhs: Array<ArrayImpl2, NDIM>) -> Self::Output {
1601        Array::new(ArraySubtraction::new(self, rhs))
1602    }
1603}
1604
1605impl<Item, ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::SubAssign<Array<ArrayImpl2, NDIM>>
1606    for Array<ArrayImpl1, NDIM>
1607where
1608    ArrayImpl1: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1609    ArrayImpl2: Shape<NDIM> + UnsafeRandom1DAccessByValue<Item = Item>,
1610    Item: std::ops::SubAssign,
1611{
1612    #[inline(always)]
1613    fn sub_assign(&mut self, rhs: Array<ArrayImpl2, NDIM>) {
1614        assert_eq!(self.shape(), rhs.shape());
1615        for (item1, item2) in izip!(self.iter_mut(), rhs.iter_value()) {
1616            *item1 -= item2;
1617        }
1618    }
1619}
1620
1621impl<Item, ArrayImpl, const NDIM: usize> std::ops::Neg for Array<ArrayImpl, NDIM>
1622where
1623    ArrayImpl: BaseItem<Item = Item>,
1624    Item: std::ops::Neg<Output = Item>,
1625{
1626    type Output = Array<ArrayNeg<ArrayImpl, NDIM>, NDIM>;
1627
1628    fn neg(self) -> Self::Output {
1629        Array::new(ArrayNeg::new(self))
1630    }
1631}
1632
1633impl<ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::Div<Array<ArrayImpl2, NDIM>>
1634    for Array<ArrayImpl1, NDIM>
1635where
1636    ArrayImpl1: Shape<NDIM>,
1637    ArrayImpl2: Shape<NDIM>,
1638{
1639    type Output = Array<CmpWiseDivision<ArrayImpl1, ArrayImpl2, NDIM>, NDIM>;
1640
1641    fn div(self, rhs: Array<ArrayImpl2, NDIM>) -> Self::Output {
1642        Array::new(CmpWiseDivision::new(self, rhs))
1643    }
1644}
1645
1646impl<Item, ArrayImpl, const NDIM: usize> std::ops::Div<Item> for Array<ArrayImpl, NDIM>
1647where
1648    ArrayImpl: Shape<NDIM> + BaseItem<Item = Item>,
1649    Item: Recip<Output = Item> + std::ops::Div<Output = Item>,
1650{
1651    type Output = Array<ArrayScalarMult<Item, ArrayImpl, NDIM>, NDIM>;
1652
1653    fn div(self, rhs: Item) -> Self::Output {
1654        Array::new(ArrayScalarMult::new(rhs.recip(), self))
1655    }
1656}
1657
1658impl<Item, ArrayImpl, const NDIM: usize> std::ops::DivAssign<Item> for Array<ArrayImpl, NDIM>
1659where
1660    ArrayImpl: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1661    Item: std::ops::DivAssign + Copy,
1662{
1663    fn div_assign(&mut self, rhs: Item) {
1664        for item in self.iter_mut() {
1665            *item /= rhs;
1666        }
1667    }
1668}
1669
1670impl<ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::Mul<Array<ArrayImpl2, NDIM>>
1671    for Array<ArrayImpl1, NDIM>
1672where
1673    ArrayImpl1: Shape<NDIM>,
1674    ArrayImpl2: Shape<NDIM>,
1675{
1676    type Output = Array<CmpWiseProduct<ArrayImpl1, ArrayImpl2, NDIM>, NDIM>;
1677
1678    fn mul(self, rhs: Array<ArrayImpl2, NDIM>) -> Self::Output {
1679        Array::new(CmpWiseProduct::new(self, rhs))
1680    }
1681}
1682
1683impl<Item, ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::MulAssign<Array<ArrayImpl2, NDIM>>
1684    for Array<ArrayImpl1, NDIM>
1685where
1686    ArrayImpl1: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1687    ArrayImpl2: Shape<NDIM> + UnsafeRandom1DAccessByValue<Item = Item>,
1688    Item: std::ops::MulAssign,
1689{
1690    fn mul_assign(&mut self, rhs: Array<ArrayImpl2, NDIM>) {
1691        for (item1, item2) in izip!(self.iter_mut(), rhs.iter_value()) {
1692            *item1 *= item2;
1693        }
1694    }
1695}
1696
1697impl<Item, ArrayImpl1, ArrayImpl2, const NDIM: usize> std::ops::DivAssign<Array<ArrayImpl2, NDIM>>
1698    for Array<ArrayImpl1, NDIM>
1699where
1700    ArrayImpl1: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1701    ArrayImpl2: Shape<NDIM> + UnsafeRandom1DAccessByValue<Item = Item>,
1702    Item: std::ops::DivAssign,
1703{
1704    fn div_assign(&mut self, rhs: Array<ArrayImpl2, NDIM>) {
1705        for (item1, item2) in izip!(self.iter_mut(), rhs.iter_value()) {
1706            *item1 /= item2;
1707        }
1708    }
1709}
1710
1711macro_rules! impl_scalar_mult {
1712    ($ScalarType:ty) => {
1713        impl<ArrayImpl, const NDIM: usize> std::ops::Mul<Array<ArrayImpl, NDIM>> for $ScalarType {
1714            type Output = Array<ArrayScalarMult<$ScalarType, ArrayImpl, NDIM>, NDIM>;
1715
1716            fn mul(self, rhs: Array<ArrayImpl, NDIM>) -> Self::Output {
1717                Array::new(ArrayScalarMult::new(self, rhs))
1718            }
1719        }
1720
1721        impl<ArrayImpl, const NDIM: usize> std::ops::Mul<$ScalarType> for Array<ArrayImpl, NDIM>
1722        where
1723            ArrayImpl: Shape<NDIM> + BaseItem<Item = $ScalarType>,
1724        {
1725            type Output = Array<ArrayScalarMult<$ScalarType, ArrayImpl, NDIM>, NDIM>;
1726
1727            fn mul(self, rhs: $ScalarType) -> Self::Output {
1728                Array::new(ArrayScalarMult::new(rhs, self))
1729            }
1730        }
1731    };
1732}
1733
1734impl_scalar_mult!(f64);
1735impl_scalar_mult!(f32);
1736impl_scalar_mult!(c64);
1737impl_scalar_mult!(c32);
1738impl_scalar_mult!(usize);
1739impl_scalar_mult!(i8);
1740impl_scalar_mult!(i16);
1741impl_scalar_mult!(i32);
1742impl_scalar_mult!(i64);
1743impl_scalar_mult!(u8);
1744impl_scalar_mult!(u16);
1745impl_scalar_mult!(u32);
1746impl_scalar_mult!(u64);
1747
1748impl<Item, ArrayImpl, const NDIM: usize> std::ops::MulAssign<Item> for Array<ArrayImpl, NDIM>
1749where
1750    ArrayImpl: Shape<NDIM> + UnsafeDisjointRandom1DAccessMut<Item = Item>,
1751    Item: std::ops::MulAssign + Copy,
1752{
1753    fn mul_assign(&mut self, rhs: Item) {
1754        for item in self.iter_mut() {
1755            *item *= rhs;
1756        }
1757    }
1758}
1759
1760impl<ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM> {
1761    /// Create a new array by applying the unitary operator `op` to each element of `self`.
1762    pub fn unary_op<OpItem, OpTarget, Op: Fn(OpItem) -> OpTarget>(
1763        self,
1764        op: Op,
1765    ) -> Array<ArrayUnaryOperator<OpItem, OpTarget, ArrayImpl, Op, NDIM>, NDIM> {
1766        Array::new(ArrayUnaryOperator::new(self, op))
1767    }
1768}
1769
1770impl<Item: Default + Copy + std::ops::Mul<Output = Item>, ArrayImpl, const NDIM: usize>
1771    Array<ArrayImpl, NDIM>
1772where
1773    ArrayImpl: BaseItem<Item = Item>,
1774{
1775    /// Multiple the array with a given `scalar`.
1776    ///
1777    /// Note: The `Item` type must support [std::ops::Mul].
1778    ///
1779    pub fn scalar_mul(self, scalar: Item) -> Array<ArrayScalarMult<Item, ArrayImpl, NDIM>, NDIM> {
1780        Array::new(ArrayScalarMult::new(scalar, self))
1781    }
1782}
1783
1784macro_rules! impl_unary_op_trait {
1785    ($name:ident, $method_name:ident) => {
1786        paste! {
1787
1788        use crate::traits::number_traits::$name;
1789        impl<Item: $name, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1790            where
1791                ArrayImpl: BaseItem<Item = Item>,
1792            {
1793                #[inline(always)]
1794                #[doc = "Componentwise apply `" $method_name "` to the array."]
1795                pub fn $method_name(self) -> Array<ArrayUnaryOperator<Item, <Item as $name>::Output, ArrayImpl, fn(Item) -> <Item as $name>::Output, NDIM>, NDIM> {
1796                    self.unary_op(|x| x.$method_name())
1797                }
1798            }
1799        }
1800
1801    };
1802}
1803
1804impl_unary_op_trait!(Conj, conj);
1805impl_unary_op_trait!(Abs, abs);
1806impl_unary_op_trait!(Square, square);
1807impl_unary_op_trait!(AbsSquare, abs_square);
1808impl_unary_op_trait!(Sqrt, sqrt);
1809impl_unary_op_trait!(Exp, exp);
1810impl_unary_op_trait!(Ln, ln);
1811impl_unary_op_trait!(Recip, recip);
1812impl_unary_op_trait!(Sin, sin);
1813impl_unary_op_trait!(Cos, cos);
1814impl_unary_op_trait!(Tan, tan);
1815impl_unary_op_trait!(Asin, asin);
1816impl_unary_op_trait!(Acos, acos);
1817impl_unary_op_trait!(Atan, atan);
1818impl_unary_op_trait!(Sinh, sinh);
1819impl_unary_op_trait!(Cosh, cosh);
1820impl_unary_op_trait!(Tanh, tanh);
1821impl_unary_op_trait!(Asinh, asinh);
1822impl_unary_op_trait!(Acosh, acosh);
1823impl_unary_op_trait!(Atanh, atanh);
1824
1825impl<Item, ArrayImplX, ArrayImplY, ArrayImpl>
1826    AsMatrixApply<Array<ArrayImplX, 2>, Array<ArrayImplY, 2>> for Array<ArrayImpl, 2>
1827where
1828    Item: Gemm + Copy,
1829    ArrayImplX: RawAccess<Item = Item> + Stride<2> + Shape<2>,
1830    ArrayImplY: RawAccessMut<Item = Item> + Stride<2> + Shape<2>,
1831    ArrayImpl: RawAccess<Item = Item> + Stride<2> + Shape<2>,
1832{
1833    fn apply(
1834        &self,
1835        alpha: Self::Item,
1836        x: &Array<ArrayImplX, 2>,
1837        beta: Self::Item,
1838        y: &mut Array<ArrayImplY, 2>,
1839    ) {
1840        let stride_a = self.stride();
1841        let stride_x = x.stride();
1842        let stride_y = y.stride();
1843
1844        let shape_a = self.shape();
1845        let shape_x = x.shape();
1846        let shape_y = y.shape();
1847
1848        assert_eq!(
1849            shape_a[0], shape_y[0],
1850            "`y` has incompatible shape {shape_y:#?} with `self` {shape_a:#?}."
1851        );
1852
1853        assert_eq!(
1854            shape_a[1], shape_x[0],
1855            "`x` has incompatible shape {shape_x:#?} with `self` {shape_a:#?}."
1856        );
1857
1858        assert_eq!(
1859            shape_y[1], shape_x[1],
1860            "`x` has incompatible shape {shape_x:#?} with `y` {shape_y:#?}."
1861        );
1862
1863        let m = shape_a[0];
1864        let n = shape_x[1];
1865        let k = shape_a[1];
1866
1867        <Item as Gemm>::gemm(
1868            TransMode::NoTrans,
1869            TransMode::NoTrans,
1870            m,
1871            n,
1872            k,
1873            alpha,
1874            self.data().unwrap(),
1875            stride_a[0],
1876            stride_a[1],
1877            x.data().unwrap(),
1878            stride_x[0],
1879            stride_x[1],
1880            beta,
1881            y.data_mut().unwrap(),
1882            stride_y[0],
1883            stride_y[1],
1884        );
1885    }
1886}
1887
1888impl<Item, ArrayImplX, ArrayImplY, ArrayImpl>
1889    AsMatrixApply<Array<ArrayImplX, 1>, Array<ArrayImplY, 1>> for Array<ArrayImpl, 2>
1890where
1891    Item: Gemm + Copy,
1892    ArrayImplX: RawAccess<Item = Item> + Stride<1> + Shape<1>,
1893    ArrayImplY: RawAccessMut<Item = Item> + Stride<1> + Shape<1>,
1894    ArrayImpl: RawAccess<Item = Item> + Stride<2> + Shape<2>,
1895{
1896    fn apply(
1897        &self,
1898        alpha: Self::Item,
1899        x: &Array<ArrayImplX, 1>,
1900        beta: Self::Item,
1901        y: &mut Array<ArrayImplY, 1>,
1902    ) {
1903        self.apply(
1904            alpha,
1905            &x.r().insert_empty_axis::<2>(AxisPosition::Back),
1906            beta,
1907            &mut y.r_mut().insert_empty_axis::<2>(AxisPosition::Back),
1908        );
1909    }
1910}
1911
1912impl<Item, ArrayImpl> Array<ArrayImpl, 2>
1913where
1914    ArrayImpl: RawAccess<Item = Item> + Stride<2> + Shape<2>,
1915    Item: Gemm + Copy + Default + One + Zero,
1916{
1917    /// Compute the matrix-matrix product of `self` with `other`.
1918    pub fn dot<ArrayImplOther, const NDIM: usize>(
1919        &self,
1920        other: &Array<ArrayImplOther, NDIM>,
1921    ) -> DynArray<Item, NDIM>
1922    where
1923        ArrayImplOther: RawAccess<Item = Item> + Shape<NDIM> + Stride<NDIM>,
1924    {
1925        let a_shape = self.shape();
1926        let x_shape = other.shape();
1927
1928        let mut out = empty_array::<Item, NDIM>();
1929
1930        if NDIM == 1 {
1931            let mut out = out.r_mut().coerce_dim::<1>().unwrap();
1932            out.resize_in_place([a_shape[0]]);
1933
1934            self.apply(
1935                Item::one(),
1936                &other.r().coerce_dim::<1>().unwrap(),
1937                Item::zero(),
1938                &mut out,
1939            );
1940        } else if NDIM == 2 {
1941            let mut out = out.r_mut().coerce_dim::<2>().unwrap();
1942            out.resize_in_place([a_shape[0], x_shape[1]]);
1943
1944            self.apply(
1945                Item::one(),
1946                &other.r().coerce_dim::<2>().unwrap(),
1947                Item::zero(),
1948                &mut out,
1949            )
1950        } else {
1951            panic!("NDIM = {NDIM} not supported.");
1952        }
1953
1954        out
1955    }
1956}
1957
1958impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1959where
1960    ArrayImpl: UnsafeRandomAccessMut<NDIM, Item = Item>,
1961    Item: Copy,
1962{
1963    /// Swap the elements at the positions `pos1` and `pos2`
1964    ///
1965    /// # Safety
1966    ///
1967    /// `pos1` and `pos2` must not be out of bounds.
1968    #[inline(always)]
1969    pub unsafe fn swap_unsafe(&mut self, pos1: [usize; NDIM], pos2: [usize; NDIM]) {
1970        let elem1 = *unsafe { self.get_unchecked_mut(pos1) };
1971        let elem2 = *unsafe { self.get_unchecked_mut(pos2) };
1972
1973        *unsafe { self.get_unchecked_mut(pos1) } = elem2;
1974        *unsafe { self.get_unchecked_mut(pos2) } = elem1;
1975    }
1976}
1977
1978impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
1979where
1980    ArrayImpl: UnsafeRandomAccessMut<NDIM, Item = Item> + Shape<NDIM>,
1981    Item: Copy,
1982{
1983    /// Swap the elements at the positions `pos1` and `pos2`.
1984    ///
1985    /// Returns an error if either `pos1` or `pos2` is out of bounds.
1986    #[inline(always)]
1987    pub fn swap(&mut self, pos1: [usize; NDIM], pos2: [usize; NDIM]) -> RlstResult<()> {
1988        let shape = self.shape();
1989        if !check_multi_index_in_bounds(pos1, shape) {
1990            Err(RlstError::GeneralError(format!(
1991                "Index `pos1` = {:#?} out of bounds.",
1992                pos1
1993            )))
1994        } else if !check_multi_index_in_bounds(pos2, shape) {
1995            Err(RlstError::GeneralError(format!(
1996                "Index `pos2` = {:#?} out of bounds.",
1997                pos2
1998            )))
1999        } else {
2000            unsafe {
2001                self.swap_unsafe(pos1, pos2);
2002            }
2003            Ok(())
2004        }
2005    }
2006}
2007
2008impl<Item> ScalarAsArray for Item
2009where
2010    Item: Default + Copy,
2011{
2012    fn as_array<const NDIM: usize>(&self) -> StaticArray<Self, NDIM, 1> {
2013        let mut arr = Array::new(BaseArray::new(ArrayContainer::<Item, 1>::new(), [1; NDIM]));
2014        *unsafe { arr.get_unchecked_mut([0; NDIM]) } = *self;
2015        arr
2016    }
2017}
2018
2019impl<Item, const NDIM: usize> StaticArray<Item, NDIM, 1>
2020where
2021    Item: Copy + Default,
2022{
2023    /// Create an empty 1-element array of the given dimension.
2024    pub fn one_element_array() -> Self {
2025        Array::new(BaseArray::new(ArrayContainer::<Item, 1>::new(), [1; NDIM]))
2026    }
2027}
2028
2029macro_rules! impl_linspace_float {
2030    ($dtype:ty) => {
2031        impl<ArrayImpl> Linspace<$dtype> for Array<ArrayImpl, 1>
2032        where
2033            ArrayImpl: MutableArrayImpl<$dtype, 1>,
2034        {
2035            fn linspace(&mut self, a: $dtype, b: $dtype) {
2036                let n = self.len() - 1;
2037
2038                assert!(b > a, "`linspace` requires `{b} = b > a = {a} ");
2039                assert!(n > 1, "`linspace` requires `{n} = self.len() > 1");
2040
2041                let delta = (b - a) / (n as $dtype);
2042
2043                for (ind, elem) in self.iter_mut().enumerate().take(n) {
2044                    *elem = a + delta * ind as $dtype;
2045                }
2046
2047                self[[n]] = b;
2048            }
2049        }
2050    };
2051}
2052
2053impl_linspace_float!(f32);
2054impl_linspace_float!(f64);
2055
2056#[cfg(test)]
2057mod test {
2058
2059    use approx::assert_relative_eq;
2060
2061    use crate::assert_array_relative_eq;
2062
2063    use super::*;
2064
2065    #[test]
2066    pub fn test_add() {
2067        let a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2068        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2069        let expected: DynArray<_, 1> = vec![2.5, 4.3, 7.0].into();
2070
2071        crate::assert_array_relative_eq!((a.r() + b.r()).eval(), expected, 1E-10);
2072    }
2073
2074    #[test]
2075    pub fn test_add_assign() {
2076        let mut a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2077        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2078        let expected: DynArray<_, 1> = vec![2.5, 4.3, 7.0].into();
2079
2080        a += b;
2081
2082        crate::assert_array_relative_eq!(a, expected, 1E-10);
2083    }
2084
2085    #[test]
2086    pub fn test_sub() {
2087        let a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2088        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2089        let expected: DynArray<_, 1> = vec![-0.5, -0.3, -1.0].into();
2090
2091        crate::assert_array_relative_eq!((a.r() - b.r()).eval(), expected, 1E-10);
2092    }
2093
2094    #[test]
2095    pub fn test_sub_assign() {
2096        let mut a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2097        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2098        let expected: DynArray<_, 1> = vec![-0.5, -0.3, -1.0].into();
2099
2100        a -= b;
2101
2102        crate::assert_array_relative_eq!(a, expected, 1E-10);
2103    }
2104
2105    #[test]
2106    pub fn test_cmp_mul() {
2107        let a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2108        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2109        let expected: DynArray<_, 1> = vec![1.5, 4.6, 12.0].into();
2110
2111        crate::assert_array_relative_eq!(a.r() * b.r(), expected, 1E-10);
2112    }
2113
2114    #[test]
2115    pub fn test_cmp_mul_assign() {
2116        let mut a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2117        let b: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2118        let expected: DynArray<_, 1> = vec![1.5, 4.6, 12.0].into();
2119
2120        a *= b;
2121
2122        crate::assert_array_relative_eq!(a, expected, 1E-10);
2123    }
2124
2125    #[test]
2126    pub fn test_scalar_mul() {
2127        let a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2128        let expected: DynArray<_, 1> = vec![2.0, 4.0, 6.0].into();
2129
2130        crate::assert_array_relative_eq!(2.0_f64 * a.r(), expected, 1E-10);
2131    }
2132
2133    #[test]
2134    pub fn test_scalar_mul_assign() {
2135        let mut a: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2136        let expected: DynArray<_, 1> = vec![2.0, 4.0, 6.0].into();
2137
2138        a *= 2.0;
2139
2140        crate::assert_array_relative_eq!(a.r(), expected, 1E-10);
2141    }
2142
2143    #[test]
2144    pub fn test_cmp_div() {
2145        let a: DynArray<_, 1> = vec![1.5, 4.6, 12.0].into();
2146        let b: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2147        let expected: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2148
2149        crate::assert_array_relative_eq!(a.r() / b.r(), expected, 1E-10);
2150    }
2151
2152    #[test]
2153    pub fn test_cmp_div_assign() {
2154        let mut a: DynArray<_, 1> = vec![1.5, 4.6, 12.0].into();
2155        let b: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2156        let expected: DynArray<_, 1> = vec![1.5, 2.3, 4.0].into();
2157
2158        a /= b;
2159
2160        crate::assert_array_relative_eq!(a, expected, 1E-10);
2161    }
2162
2163    #[test]
2164    pub fn test_scalar_div() {
2165        let a: DynArray<_, 1> = vec![2.0, 4.0, 6.0].into();
2166        let expected: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2167
2168        crate::assert_array_relative_eq!(a.r() / 2.0, expected, 1E-10);
2169    }
2170
2171    #[test]
2172    pub fn test_scalar_div_assign() {
2173        let mut a: DynArray<_, 1> = vec![2.0, 4.0, 6.0].into();
2174        let expected: DynArray<_, 1> = vec![1.0, 2.0, 3.0].into();
2175
2176        a /= 2.0;
2177
2178        crate::assert_array_relative_eq!(a.r(), expected, 1E-10);
2179    }
2180
2181    #[test]
2182    pub fn test_into_subview() {
2183        let mut rng = ChaCha8Rng::seed_from_u64(0);
2184        let mut a = DynArray::<f64, 3>::from_shape([7, 5, 9]);
2185        a.fill_from_equally_distributed(&mut rng);
2186
2187        let view = a.r().into_subview([1, 0, 3], [2, 3, 4]);
2188
2189        assert_eq!(view[[1, 2, 3]], a[[2, 2, 6]])
2190    }
2191
2192    #[test]
2193    pub fn test_apply() {
2194        let mut rng = ChaCha8Rng::seed_from_u64(0);
2195
2196        let alpha = 1.5;
2197        let beta = 3.6;
2198
2199        let mut a = DynArray::<f64, 2>::from_shape([3, 2]);
2200        a.fill_from_equally_distributed(&mut rng);
2201
2202        let mut x = DynArray::<f64, 1>::from_shape([2]);
2203        x.fill_from_equally_distributed(&mut rng);
2204
2205        let mut y = DynArray::<f64, 1>::from_shape([3]);
2206        y.fill_from_equally_distributed(&mut rng);
2207        let y2 = y.eval();
2208
2209        a.apply(alpha, &x, beta, &mut y);
2210
2211        for (row_index, row) in a.row_iter().enumerate() {
2212            assert_relative_eq!(
2213                alpha * row.inner(&x).unwrap() + beta * y2[[row_index]],
2214                y[[row_index]],
2215                epsilon = 1E-10
2216            );
2217        }
2218
2219        let mut x = DynArray::<f64, 2>::from_shape([2, 5]);
2220        x.fill_from_equally_distributed(&mut rng);
2221
2222        let mut y = DynArray::<f64, 2>::from_shape([3, 5]);
2223        y.fill_from_equally_distributed(&mut rng);
2224        let y2 = y.eval();
2225
2226        a.apply(alpha, &x, beta, &mut y);
2227
2228        for (row_index, row) in a.row_iter().enumerate() {
2229            for (col_index, col) in x.col_iter().enumerate() {
2230                assert_relative_eq!(
2231                    alpha * row.inner(&col).unwrap() + beta * y2[[row_index, col_index]],
2232                    y[[row_index, col_index]],
2233                    epsilon = 1E-10
2234                );
2235            }
2236        }
2237    }
2238
2239    #[test]
2240    pub fn test_dot() {
2241        let mut rng = ChaCha8Rng::seed_from_u64(0);
2242
2243        let mut a = DynArray::<f64, 2>::from_shape([3, 2]);
2244        a.fill_from_equally_distributed(&mut rng);
2245
2246        let mut x = DynArray::<f64, 1>::from_shape([2]);
2247        x.fill_from_equally_distributed(&mut rng);
2248
2249        let mut y = DynArray::<f64, 1>::from_shape([3]);
2250
2251        a.apply(1.0, &x, 0.0, &mut y);
2252
2253        crate::assert_array_relative_eq!(y, a.dot(&x), 1E-10);
2254
2255        let mut x = DynArray::<f64, 2>::from_shape([2, 5]);
2256        x.fill_from_equally_distributed(&mut rng);
2257
2258        let mut y = DynArray::<f64, 2>::from_shape([3, 5]);
2259
2260        a.apply(1.0, &x, 0.0, &mut y);
2261
2262        crate::assert_array_relative_eq!(y, a.dot(&x), 1E-10);
2263    }
2264
2265    #[test]
2266    pub fn test_swap() {
2267        let mut a = DynArray::<usize, 1>::from_shape([2]);
2268        a[[0]] = 1;
2269        a[[1]] = 2;
2270
2271        a.swap([0], [1]).unwrap();
2272        assert_eq!(a[[0]], 2);
2273        assert_eq!(a[[1]], 1);
2274    }
2275
2276    #[test]
2277    pub fn test_multislice_iterator() {
2278        let mut arr = DynArray::<f64, _>::from_shape([2, 3, 4, 5]);
2279        arr.fill_from_seed_equally_distributed(0);
2280
2281        let mut count = 0;
2282        for (indices, slice) in arr.r().multislice_iter::<_, 2>([2, 3]) {
2283            assert_array_relative_eq!(slice, arr.r().multislice::<_, 2>([2, 3], indices), 1E-12);
2284            count += 1;
2285        }
2286        assert_eq!(count, 20);
2287    }
2288
2289    #[test]
2290    pub fn test_multislice_iterator_mut() {
2291        let mut arr = DynArray::<f64, _>::from_shape([2, 3, 4, 5]);
2292        arr.fill_from_seed_equally_distributed(0);
2293        let expected = arr.eval();
2294
2295        let mut count = 0;
2296        for (indices, slice) in arr.r_mut().multislice_iter_mut::<_, 2>([2, 3]) {
2297            assert_array_relative_eq!(
2298                slice,
2299                expected.r().multislice::<_, 2>([2, 3], indices),
2300                1E-12
2301            );
2302            count += 1;
2303        }
2304        assert_eq!(count, 20);
2305    }
2306}