From 117182c4a3a9980261b2894697c7ce86562c4847 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sat, 21 Dec 2024 14:25:28 -0800 Subject: [PATCH 1/4] Adds a fairly comprehensive ArrayLike trait --- src/arraylike.rs | 306 ++++++++++++++++++++ src/lib.rs | 2 + src/numeric/impl_num_traits.rs | 508 +++++++++++++++++++++++++++++++++ 3 files changed, 816 insertions(+) create mode 100644 src/arraylike.rs create mode 100644 src/numeric/impl_num_traits.rs diff --git a/src/arraylike.rs b/src/arraylike.rs new file mode 100644 index 000000000..bfeaa645e --- /dev/null +++ b/src/arraylike.rs @@ -0,0 +1,306 @@ +//! Traits for accepting multiple types as arrays. + +use crate::{ + aview0, + aview1, + aview_mut1, + ArrayBase, + ArrayRef, + ArrayViewMut, + CowArray, + Data, + DataMut, + Dimension, + Ix0, + Ix1, + ScalarOperand, +}; + +/// A trait for anything that can act like a multidimensional array. +/// +/// This trait provides a unified interface for interacting with arrays, scalars, slices, +/// and other types that conceptually act like arrays. It's designed to make your functions +/// more flexible by letting them handle a wide range of types without extra boilerplate. +/// +/// ## More Details +/// The key idea of `ArrayLike` is to bridge the gap between native ndarray types +/// (e.g., [`Array2`](crate::Array2), [`ArrayView`](crate::ArrayView)) and other +/// data structures like scalars or slices. It enables treating all these types +/// as "array-like" objects with a common set of operations. +/// +/// # Example +/// ``` +/// use ndarray::{array, Array, Array2, ArrayLike, DimMax}; +/// +/// fn multiply(left: &T, right: &G) -> Array>::Output> +/// where +/// T: ArrayLike, +/// G: ArrayLike, +/// T::Dim: DimMax, +/// A: Mul, +/// { +/// left.as_array() * right.as_array() +/// } +/// +/// let rows = array![[1], [2]]; +/// let cols = array![3, 4]; +/// assert_eq!(multiply(rows, col), array![[3, 4], [6, 8]]); +/// ``` +pub trait ArrayLike +{ + type Dim: Dimension; + type Elem; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim>; + + fn dim(&self) -> Self::Dim; + + fn as_elem(&self) -> Option<&Self::Elem>; +} + +pub trait ArrayLikeMut: ArrayLike +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim>; + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem>; +} + +impl ArrayLike for A +where A: ScalarOperand +{ + type Dim = Ix0; + type Elem = A; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + where Self::Elem: Clone + { + aview0(self).into() + } + + fn dim(&self) -> Self::Dim + { + Ix0() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + Some(self) + } +} + +impl ArrayLikeMut for A +where A: ScalarOperand +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + // SAFETY: The pointer will be non-null since it's a reference, + // and the view is tied to the lifetime of the mutable borrow + unsafe { ArrayViewMut::from_shape_ptr((), self as *mut Self::Elem) } + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + Some(self) + } +} + +impl ArrayLike for ArrayRef +where D: Dimension +{ + type Dim = D; + type Elem = A; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + { + self.view().into() + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + +impl ArrayLikeMut for ArrayRef +where D: Dimension +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + self.view_mut() + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.dim.size() == 1 { + self.first_mut() + } else { + None + } + } +} + +impl ArrayLike for ArrayBase +where + S: Data, + D: Dimension, +{ + type Dim = D; + type Elem = S::Elem; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + { + self.into() + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + +impl ArrayLikeMut for ArrayBase +where + S: DataMut, + D: Dimension, +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + self.view_mut() + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.dim.size() == 1 { + self.first_mut() + } else { + None + } + } +} + +impl ArrayLike for [A] +{ + type Dim = Ix1; + + type Elem = A; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + { + aview1(self).into() + } + + fn dim(&self) -> Self::Dim + { + Ix1(self.len()) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.len() == 1 { + Some(&self[0]) + } else { + None + } + } +} + +impl ArrayLikeMut for [A] +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + aview_mut1(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.len() == 1 { + Some(&mut self[0]) + } else { + None + } + } +} + +impl ArrayLike for Vec +{ + type Dim = Ix1; + + type Elem = A; + + fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + { + (&**self).as_array() + } + + fn dim(&self) -> Self::Dim + { + (&**self).dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + (&**self).as_elem() + } +} + +impl ArrayLikeMut for Vec +{ + fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + (&mut **self).as_array_mut() + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + (&mut **self).as_elem_mut() + } +} + +#[cfg(test)] +mod tests +{ + + use core::ops::Mul; + + use crate::{array, Array, ArrayLike, DimMax}; + + fn multiply(left: &T, right: &G) -> Array>::Output> + where + T: ArrayLike, + G: ArrayLike, + // Bounds to enable multiplication + T::Elem: Clone + Mul, + T::Dim: DimMax, + { + let left = &*left.as_array(); + let right = &*right.as_array(); + left * right + } + + #[test] + fn test_multiply() + { + let left = array![1, 2]; + let right = array![3, 4]; + assert_eq!(multiply(&left, &right), array![3, 8]); + assert_eq!(multiply(&left, &3), array![3, 6]); + } +} diff --git a/src/lib.rs b/src/lib.rs index b163f16a5..6f24fe0f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -207,6 +207,8 @@ mod low_level_util; mod zip; mod dimension; +mod arraylike; +pub use crate::arraylike::ArrayLike; pub use crate::zip::{FoldWhile, IntoNdProducer, NdProducer, Zip}; diff --git a/src/numeric/impl_num_traits.rs b/src/numeric/impl_num_traits.rs new file mode 100644 index 000000000..f54acaf0a --- /dev/null +++ b/src/numeric/impl_num_traits.rs @@ -0,0 +1,508 @@ +// //! Implementations to mimic the [`num`](https://docs.rs/num/0.4.3/num/) crate's capabilities. + +// use core::num::FpCategory; + +// use num_integer::{ExtendedGcd, Integer}; +// use num_traits::{Euclid, Float, Inv, Pow, Signed}; +// use paste::paste; + +// use crate::{arraylike::ArrayLike, dimension::broadcast::co_broadcast, Array, ArrayRef, DimMax, Dimension, Zip}; + +// /// Implement functions inside a generic scope that map another function to an array's elements. +// /// +// /// This is useful for implementing numeric functions succinctly. +// /// The macro takes three arguments: +// /// 1. A module identifier, indicating which module the mapping function is from +// /// 2. A literal, either "ref" to map by reference or "owned" to map by value +// /// 3. A parenthesized list of tuples, each one is (function_name, output_type) +// /// +// /// # Example +// /// ```no_run +// /// use num::Float; +// /// +// /// impl ArrayRef +// /// where +// /// A: Float + Clone, +// /// D: Dimension, +// /// { +// /// impl_singles!(Float, "owned", ((is_infinite, bool), (is_finite, bool))); +// /// } +// /// ``` +// /// expands to the following block: +// /// ```no_run +// /// use num::Float; +// /// +// /// impl ArrayRef +// /// where +// /// A: Float + Clone, +// /// D: Dimension, +// /// { +// /// fn is_infinite(&self) -> Array { +// /// self.mapv(Float::is_infinite) +// /// } +// /// +// /// fn is_finite(&self) -> Array { +// /// self.mapv(Float::is_finite) +// /// } +// /// } +// /// ``` +// macro_rules! impl_singles { +// ($mod:ident, "ref", ($(($fn:ident, $output:ty)),+)) => { +// $( +// #[must_use = "method returns a new array and does not mutate the original value"] +// fn $fn(&self) -> Array<$output, D> { +// self.map($mod::$fn) +// } +// )+ +// }; +// ($mod:ident, "owned", ($(($fn:ident, $output:ty)),+)) => { +// $( +// #[must_use = "method returns a new array and does not mutate the original value"] +// fn $fn(&self) -> Array<$output, D> { +// self.mapv($mod::$fn) +// } +// )+ +// }; +// } + +// /// Implement pairs of immutable and mutable functions inside a generic scope +// /// that map another function to an array's elements. +// /// +// /// This is useful for implementing numeric functions succinctly. +// /// The macro takes three arguments: +// /// 1. A module identifier, indicating which module the mapping function is from +// /// 2. A literal, either "ref" to map by reference or "owned" to map by value +// /// 3. A parenthesized list of function names from that module +// /// +// /// # Example +// /// ```no_run +// /// use num::Float; +// /// +// /// impl ArrayRef +// /// where +// /// A: Float + Clone, +// /// D: Dimension, +// /// { +// /// impl_pairs!(Float, "owned", (ceil)); +// /// } +// /// ``` +// /// expands to +// /// ```no_run +// /// use num::Float; +// /// +// /// impl ArrayRef +// /// where +// /// A: Float + Clone, +// /// D: Dimension, +// /// { +// /// fn ceil(&self) -> Array { +// /// self.mapv(Float::ceil) +// /// } +// /// +// /// fn ceil_assign(&mut self) { +// /// self.mapv_inplace(Float::ceil) +// /// } +// /// } +// /// ``` +// macro_rules! impl_pairs { +// ($mod:ident, "ref", ($($fn:ident),+)) => { +// impl_singles!($mod, "ref", ($(($fn, A)),+)); +// $( +// paste! { +// fn [<$fn _assign>](&mut self) { +// self.map_inplace(|v| *v = $mod::$fn(v)) +// } +// } +// )+ +// }; +// ($mod:ident, "owned", ($($fn:ident),+)) => { +// impl_singles!($mod, "owned", ($(($fn, A)),+)); +// $( +// paste! { +// fn [<$fn _assign>](&mut self) { +// self.mapv_inplace($mod::$fn) +// } +// } +// )+ +// }; +// } + +// /// Implement functions inside a generic scope that map another function to an array's elements, +// /// with an additional argument that can be a scalar or an array. +// /// +// /// This is useful for implementing numeric functions succinctly. +// /// The macro takes three arguments: +// /// 1. A module identifier, indicating which module the mapping function is from +// /// 2. A literal, either "ref" to map by reference or "owned" to map by value +// /// 3. A parenthesized list of (function_name, argument_name, output_type) +// /// +// /// This macro makes heavy use of [`ArrayLike`]; see its documentation for more details. +// /// +// /// # Example +// /// ```no_run +// /// use num::Integer; +// /// +// /// impl ArrayRef +// /// where +// /// A: Integer + Clone, +// /// D: Dimension, +// /// T: ArrayLike, +// /// { +// /// impl_broadcast_singles!(Integer, "ref", (is_multiple_of, other, bool)); +// /// } +// /// ``` +// /// expands to +// /// ```no_run +// /// use num::Integer; +// /// +// /// impl ArrayRef +// /// where +// /// A: Integer + Clone, +// /// D: Dimension, +// /// T: ArrayLike, +// /// { +// /// fn is_multiple_of(&self, other: &T) -> Array { +// /// Zip::from(self) +// /// .and(other.broadcast(self.raw_dim()).unwrap()) +// /// .map_collect(Integer::is_multiple_of) +// /// } +// /// } +// /// ``` +// /// +// /// # Panics +// /// Functions created by this macro will panic when the additional argument is an array +// /// that is not broadcastable-compatible (i.e., has an incompatible shape) with the +// /// `self` array. +// /// ``` +// macro_rules! impl_broadcast_singles { +// ($mod:ident, "ref", ($(($fn:ident, $arg:ident, $output:ty)),+)) => { +// $( +// #[must_use = "method returns a new array and does not mutate the original value"] +// fn $fn(&self, $arg: &T) -> Array<$output, >::Output> +// where +// T: ArrayLike, +// D: DimMax +// { +// let shape = co_broadcast::<_, _, >::Output>( +// &self.raw_dim(), &$arg.dim() +// ).unwrap(); +// Zip::from(self.broadcast(shape.clone()).unwrap()) +// .and($arg.broadcast(shape).unwrap()) +// .map_collect($mod::$fn) +// } +// )+ +// }; +// ($mod:ident, "owned", ($(($fn:ident, $arg:ident, $output:ty)),+)) => { +// $( +// #[must_use = "method returns a new array and does not mutate the original value"] +// fn $fn(&self, $arg: &T) -> Array<$output, >::Output> +// where +// T: ArrayLike, +// D: DimMax +// { +// let shape = co_broadcast::<_, _, >::Output>( +// &self.raw_dim(), &$arg.dim() +// ).unwrap(); +// Zip::from(self.broadcast(shape.clone()).unwrap()) +// .and($arg.broadcast(shape).unwrap()) +// .map_collect(|s, a| s.$fn(*a)) +// } +// )+ +// }; +// } + +// /// Implement pairs of immutable and mutable functions inside a generic scope that map +// /// another function to an array's elements, with an additional argument that can be a scalar or an array. +// /// +// /// This is useful for implementing numeric functions succinctly. +// /// The macro takes three arguments: +// /// 1. A module identifier, indicating which module the mapping function is from +// /// 2. A literal, either "ref" to map by reference or "owned" to map by value +// /// 3. A parenthesized list of (function_name, argument_name) +// /// +// /// This macro makes heavy use of [`ArrayLike`]; see its documentation for more details. +// /// +// /// # Example +// /// ```no_run +// /// use num::Integer; +// /// +// /// impl ArrayRef +// /// where +// /// A: Integer + Clone, +// /// D: Dimension, +// /// { +// /// impl_broadcast_pairs!(Integer, "ref", (div_mod, other)); +// /// } +// /// ``` +// /// expands to +// /// ```no_run +// /// use num::Integer; +// /// +// /// impl ArrayRef +// /// where +// /// A: Integer + Clone, +// /// D: Dimension, +// /// { +// /// fn div_mod(&self, other: &T) -> Array +// /// where T: ArrayLike +// /// { +// /// Zip::from(self) +// /// .and(other.broadcast(self.raw_dim()).unwrap()) +// /// .map_collect(Integer::is_multiple_of) +// /// } +// /// +// /// fn div_mod_assign(&mut self, other: &T) +// /// where T: ArrayLike +// /// { +// /// self.zip_mut_with(&other.broadcast(self.raw_dim()).unwrap(), |s, o| { +// /// *s = s.div_mod(o) +// /// }); +// /// } +// /// } +// /// ``` +// /// +// /// # Panics +// /// Functions created by this macro will panic when the additional argument is an array +// /// that is not broadcastable-compatible (i.e., has an incompatible shape) with the +// /// `self` array. +// /// ``` +// macro_rules! impl_broadcast_pairs { +// ($mod:ident, "ref", ($(($fn:ident, $arg:ident)),+)) => { +// impl_broadcast_singles!($mod, "ref", ($(($fn, $arg, A)),+)); +// $( +// paste! { +// fn [<$fn _assign>](&mut self, $arg: &T) +// where T: ArrayLike +// { +// self.zip_mut_with(&$arg.broadcast(self.raw_dim()).unwrap(), |s, o| { +// *s = s.$fn(o) +// }); +// } +// } +// )+ +// }; +// ($mod:ident, "owned", ($(($fn:ident, $arg:ident)),+)) => { +// impl_broadcast_singles!($mod, "owned", ($(($fn, $arg, A)),+)); +// $( +// paste! { +// fn [<$fn _assign>](&mut self, $arg: &T) +// where T: ArrayLike +// { +// self.zip_mut_with(&$arg.broadcast(self.raw_dim()).unwrap(), |s, o| { +// *s = s.$fn(*o) +// }); +// } +// } +// )+ +// }; +// } + +// /// Functions that forward to [`num_traits::Signed`] +// impl ArrayRef +// where +// A: Signed + Clone, +// D: Dimension, +// { +// impl_pairs!(Signed, "ref", (abs, signum)); +// impl_singles!(Signed, "ref", ((is_positive, bool), (is_negative, bool))); +// impl_broadcast_pairs!(Signed, "ref", ((abs_sub, other))); +// } + +// /// Functions that forward to [`num_traits::Pow`] +// impl ArrayRef +// where D: Dimension +// { +// fn pow(&self, rhs: &C) -> Array>::Output> +// where +// A: Pow + Clone, +// B: Clone, +// C: ArrayLike, +// D: DimMax, +// { +// let shape = co_broadcast::<_, _, >::Output>(&self.raw_dim(), &rhs.dim()).unwrap(); +// Zip::from(self.broadcast(shape.clone()).unwrap()) +// .and(rhs.broadcast(shape).unwrap()) +// .map_collect(|s, r| s.clone().pow(r.clone())) +// } + +// fn pow_assign(&mut self, rhs: &C) +// where +// A: Pow + Clone, +// B: Clone, +// C: ArrayLike, +// { +// self.zip_mut_with(&rhs.broadcast(self.raw_dim()).unwrap(), |s, r| *s = s.clone().pow(r.clone())); +// } +// } + +// /// Functions that forward to [`num_traits::Float`] +// impl ArrayRef +// where +// A: Float, +// D: Dimension, +// { +// impl_pairs!( +// Float, +// "owned", +// ( +// floor, ceil, round, trunc, fract, recip, sqrt, exp, exp2, ln, log2, log10, cbrt, sin, +// cos, tan, asin, acos, atan, exp_m1, ln_1p, sinh, cosh, tanh, asinh, acosh, atanh +// ) +// ); +// impl_singles!( +// Float, +// "owned", +// ( +// (is_nan, bool), +// (is_infinite, bool), +// (is_finite, bool), +// (is_normal, bool), +// (classify, FpCategory), +// (integer_decode, (u64, i16, i8)), +// (sin_cos, (A, A)) +// ) +// ); +// impl_broadcast_pairs!( +// Float, +// "owned", +// ( +// (powf, n), +// (log, base), +// (max, other), +// (min, other), +// (hypot, other), +// (atan2, other) +// ) +// ); + +// fn mul_add(&self, a: &B, b: &T) -> Array>::Output as DimMax>::Output> +// where +// B: ArrayLike, +// T: ArrayLike, +// D: DimMax, +// >::Output: DimMax, +// { +// let shape = co_broadcast::<_, _, >::Output>(&self.raw_dim(), &a.dim()).unwrap(); +// let shape: <>::Output as DimMax>::Output = co_broadcast(&shape, &b.dim()).unwrap(); +// Zip::from(self.broadcast(shape.clone()).unwrap()) +// .and(&a.broadcast(shape.clone()).unwrap()) +// .and(&b.broadcast(shape).unwrap()) +// .map_collect(|s, a, b| s.mul_add(*a, *b)) +// } + +// fn mul_add_assign(&mut self, a: &B, b: &C) +// where +// B: ArrayLike, +// C: ArrayLike, +// { +// let shape = self.raw_dim(); +// Zip::from(self) +// .and(&a.broadcast(shape.clone()).unwrap()) +// .and(&b.broadcast(shape).unwrap()) +// .map_collect(|s, a, b| s.mul_add(*a, *b)); +// } + +// fn powi(&self, n: &B) -> Array>::Output> +// where +// B: ArrayLike, +// D: DimMax, +// { +// let shape: >::Output = co_broadcast(&self.raw_dim(), &n.dim()).unwrap(); +// Zip::from(self.broadcast(shape.clone()).unwrap()) +// .and(&n.broadcast(shape).unwrap()) +// .map_collect(|s, n| s.powi(*n)) +// } + +// fn powi_assign(&mut self, n: &B) +// where B: ArrayLike +// { +// self.zip_mut_with(&n.broadcast(self.raw_dim()).unwrap(), |s, n| *s = s.powi(*n)); +// } +// } + +// /// Functions that forward to [`num_integer::Integer`] +// impl ArrayRef +// where +// A: Integer + Clone, +// D: Dimension, +// { +// impl_singles!(Integer, "ref", ((is_even, bool), (is_odd, bool))); + +// fn dec(&mut self) +// where Self: Clone +// { +// self.map_inplace(A::dec); +// } + +// fn inc(&mut self) +// where Self: Clone +// { +// self.map_inplace(A::inc); +// } + +// impl_broadcast_pairs!( +// Integer, +// "ref", +// ( +// (div_floor, other), +// (mod_floor, other), +// (gcd, other), +// (lcm, other), +// (div_ceil, other), +// (next_multiple_of, other), +// (prev_multiple_of, other) +// ) +// ); +// impl_broadcast_singles!( +// Integer, +// "ref", +// ( +// (is_multiple_of, other, bool), +// (div_rem, other, (A, A)), +// (gcd_lcm, other, (A, A)), +// (div_mod_floor, other, (A, A)), +// (extended_gcd, other, ExtendedGcd) +// ) +// ); + +// fn extended_gcd_lcm(&self, other: &B) -> Array<(ExtendedGcd, A), >::Output> +// where +// Self: Clone + Signed, +// B: ArrayLike, +// D: DimMax, +// { +// let shape: >::Output = co_broadcast(&self.raw_dim(), &other.dim()).unwrap(); +// Zip::from( +// self.broadcast(shape.clone()) +// .expect("Shape derived from co_broadcast should be ok"), +// ) +// .and( +// &other +// .broadcast(shape) +// .expect("Shape derived from co_broadcast should be ok"), +// ) +// .map_collect(|s, o| (s.extended_gcd(o), s.lcm(o))) +// } +// } + +// /// Functions that forward to [`num_traits::Euclid`] +// impl ArrayRef +// where +// A: Euclid + Clone, +// D: Dimension, +// { +// impl_broadcast_pairs!(Euclid, "ref", ((div_euclid, v), (rem_euclid, v))); +// impl_broadcast_singles!(Euclid, "ref", ((div_rem_euclid, v, (A, A)))); +// } + +// /// Functions that forward to [`num_traits::Inv`] +// impl ArrayRef +// where +// A: Inv + Clone, +// D: Dimension, +// { +// fn inv(&self) -> Array +// { +// self.mapv(Inv::inv) +// } +// } From 616a9e66b00b842cc825aa439a713139f9b5b7cc Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Mon, 23 Dec 2024 17:51:38 -0800 Subject: [PATCH 2/4] Change to View instead of Cow --- src/arraylike.rs | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/arraylike.rs b/src/arraylike.rs index bfeaa645e..1bef17e8e 100644 --- a/src/arraylike.rs +++ b/src/arraylike.rs @@ -6,8 +6,8 @@ use crate::{ aview_mut1, ArrayBase, ArrayRef, + ArrayView, ArrayViewMut, - CowArray, Data, DataMut, Dimension, @@ -51,7 +51,7 @@ pub trait ArrayLike type Dim: Dimension; type Elem; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim>; + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim>; fn dim(&self) -> Self::Dim; @@ -60,7 +60,7 @@ pub trait ArrayLike pub trait ArrayLikeMut: ArrayLike { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim>; + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim>; fn as_elem_mut(&mut self) -> Option<&mut Self::Elem>; } @@ -71,10 +71,10 @@ where A: ScalarOperand type Dim = Ix0; type Elem = A; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> where Self::Elem: Clone { - aview0(self).into() + aview0(self) } fn dim(&self) -> Self::Dim @@ -91,7 +91,7 @@ where A: ScalarOperand impl ArrayLikeMut for A where A: ScalarOperand { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { // SAFETY: The pointer will be non-null since it's a reference, // and the view is tied to the lifetime of the mutable borrow @@ -110,9 +110,9 @@ where D: Dimension type Dim = D; type Elem = A; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> { - self.view().into() + self.view() } fn dim(&self) -> Self::Dim @@ -133,7 +133,7 @@ where D: Dimension impl ArrayLikeMut for ArrayRef where D: Dimension { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { self.view_mut() } @@ -156,9 +156,9 @@ where type Dim = D; type Elem = S::Elem; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> { - self.into() + self.view() } fn dim(&self) -> Self::Dim @@ -181,7 +181,7 @@ where S: DataMut, D: Dimension, { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { self.view_mut() } @@ -202,9 +202,9 @@ impl ArrayLike for [A] type Elem = A; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> { - aview1(self).into() + aview1(self) } fn dim(&self) -> Self::Dim @@ -224,7 +224,7 @@ impl ArrayLike for [A] impl ArrayLikeMut for [A] { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { aview_mut1(self) } @@ -245,9 +245,9 @@ impl ArrayLike for Vec type Elem = A; - fn as_array(&self) -> CowArray<'_, Self::Elem, Self::Dim> + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> { - (&**self).as_array() + (&**self).view() } fn dim(&self) -> Self::Dim @@ -263,9 +263,9 @@ impl ArrayLike for Vec impl ArrayLikeMut for Vec { - fn as_array_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { - (&mut **self).as_array_mut() + (&mut **self).view_mut() } fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> @@ -282,16 +282,17 @@ mod tests use crate::{array, Array, ArrayLike, DimMax}; - fn multiply(left: &T, right: &G) -> Array>::Output> + fn multiply(left: &T, right: &G) -> Array>::Output> where T: ArrayLike, - G: ArrayLike, + G: ArrayLike, // Bounds to enable multiplication - T::Elem: Clone + Mul, + T::Elem: Clone + Mul, + G::Elem: Clone, T::Dim: DimMax, { - let left = &*left.as_array(); - let right = &*right.as_array(); + let left = &*left.view(); + let right = &*right.view(); left * right } From e3c1313688d494c16edb6bbe5e1e106a91910e7e Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Wed, 25 Dec 2024 17:53:42 -0800 Subject: [PATCH 3/4] Documenting ArrayLike(Mut) --- src/arraylike.rs | 571 ++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 2 +- 2 files changed, 547 insertions(+), 26 deletions(-) diff --git a/src/arraylike.rs b/src/arraylike.rs index 1bef17e8e..cf0c51df6 100644 --- a/src/arraylike.rs +++ b/src/arraylike.rs @@ -22,46 +22,161 @@ use crate::{ /// and other types that conceptually act like arrays. It's designed to make your functions /// more flexible by letting them handle a wide range of types without extra boilerplate. /// -/// ## More Details -/// The key idea of `ArrayLike` is to bridge the gap between native ndarray types -/// (e.g., [`Array2`](crate::Array2), [`ArrayView`](crate::ArrayView)) and other -/// data structures like scalars or slices. It enables treating all these types -/// as "array-like" objects with a common set of operations. +/// Like other parts of the `ndarray` crate, `ArrayLike` only works with scalars that implement +/// [`ScalarOperand`]. /// /// # Example /// ``` -/// use ndarray::{array, Array, Array2, ArrayLike, DimMax}; +/// use core::ops::Mul; +/// use ndarray::{array, Array, ArrayLike, DimMax}; /// -/// fn multiply(left: &T, right: &G) -> Array>::Output> +/// fn multiply(left: T, right: G) -> Array>::Output> /// where -/// T: ArrayLike, -/// G: ArrayLike, +/// T: ArrayLike, +/// G: ArrayLike, +/// // Bounds to enable multiplication +/// T::Elem: Clone + Mul, +/// G::Elem: Clone, /// T::Dim: DimMax, -/// A: Mul, /// { -/// left.as_array() * right.as_array() +/// &left.view() * &right.view() /// } /// -/// let rows = array![[1], [2]]; -/// let cols = array![3, 4]; -/// assert_eq!(multiply(rows, col), array![[3, 4], [6, 8]]); +/// let left = array![1, 2]; +/// let right = vec![3, 4]; +/// // Array-vector multiplication +/// assert_eq!(multiply(&left, &right), array![3, 8]); +/// // Array-scalar multiplication +/// assert_eq!(multiply(&left, 3), array![3, 6]); /// ``` +/// +/// # `ArrayLike` vs [`ArrayRef`] +/// Both `ArrayLike` and `ArrayRef` provide a kind of unifying abstraction for `ndarray`, +/// and both are useful for writing functions with `ndarray`. However, they should not be +/// used interchangeably. `ArrayLike` is ideal when you want to write generic functions +/// that work with anything that "looks" like a multidimensional array, even if it isn't +/// strictly an `ndarray` type. When you know that a given variable or argument will be an +/// `ndarray` type, use `ArrayRef` instead. pub trait ArrayLike { + /// The dimensionality of the underlying array-like data structure. type Dim: Dimension; + + /// The element type of the underlying array-like data structure. type Elem; + /// Get a read-only view of the underlying array-like data structure. + /// + /// This method should never re-allocate the underlying data. fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim>; + /// Get the shape and strides of the underlying array-like data structure. + /// + /// # Example fn dim(&self) -> Self::Dim; + /// If the underlying object only has one element, return it; otherwise return `None`. + /// + /// This method allows for optimizations when the `ArrayLike` value is actually a + /// scalar, in which case one can avoid allocations and aid the compiler by not + /// turning it into a full view. + /// + /// # Example + /// ```rust + /// use ndarray::{array, ArrayLike}; + /// + /// let arr = array![1, 2, 3]; + /// let arr_single = array![1]; + /// let scalar = 1; + /// + /// matches!(arr.as_elem(), None); + /// matches!(arr.as_elem(), Some(1)); + /// matches!(scalar.as_elem(), Some(1)); + /// ``` + /// + /// # For Implementors: + /// Array-like objects that can contain multiple elements are free to return `Some(_)` + /// if and only if a runtime check determines there is only one element in the container. fn as_elem(&self) -> Option<&Self::Elem>; } +/// A trait for mutable array-like objects. +/// +/// This extends [`ArrayLike`] by providing mutable access to the underlying data. +/// Use it when you need to modify the contents of an array-like object. +/// +/// ## More Details +/// `ArrayLikeMut` is designed for types that can provide mutable access to their elements. +/// For example, mutable slices and arrays implement this trait, but immutable views or +/// read-only data structures won't. +/// +/// # Examples +/// ``` +/// use core::ops::MulAssign; +/// use ndarray::{array, ArrayLike, ArrayLikeMut, DimMax}; +/// +/// fn multiply_assign(left: &mut T, right: &G) +/// where +/// T: ArrayLikeMut, +/// G: ArrayLike, +/// // Bounds to enable multiplication +/// T::Elem: Clone + MulAssign, +/// G::Elem: Clone, +/// // Ensure that the broadcast is still assignable to the left side +/// T::Dim: DimMax, +/// { +/// *left.view_mut() *= &right.view(); +/// } +/// +/// let mut left = array![1, 2]; +/// let right = array![3, 4]; +/// +/// multiply_assign(&mut left, &right); +/// assert_eq!(left, array![3, 8]); +/// +/// multiply_assign(&mut left, &2); +/// assert_eq!(left, array![6, 16]); +/// ``` pub trait ArrayLikeMut: ArrayLike { + /// Get a mutable view of the underlying array-like data structure. + /// + /// This method should never re-allocate the underlying data. fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim>; + /// If the underlying object only has one element, return a mutable reference; otherwise return `None`. + /// + /// This method allows for optimizations when the `ArrayLike` value is actually a + /// scalar, in which case one can avoid allocations and aid the compiler by not + /// turning it into a full view. + /// + /// # Example + /// ```rust + /// use ndarray::{array, ArrayLike, ArrayLikeMut}; + /// use num_traits::Zero; + /// + /// fn assign_sum(mut left: T, right: G) + /// where + /// T: ArrayLikeMut, + /// G: ArrayLike, + /// // Bounds to enable sum + /// T::Elem: Zero + Clone, + /// { + /// if let Some(e) = left.as_elem_mut() { + /// *e = right.view().sum(); + /// } + /// } + /// + /// + /// let arr = array![1, 2, 3]; + /// let mut arr_single = array![1]; + /// assign_sum(&mut arr_single, arr); + /// assert_eq!(arr_single[0], 6); + /// ``` + /// + /// # For Implementors: + /// Array-like objects that can contain multiple elements are free to return `Some(_)` + /// if and only if a runtime check determines there is only one element in the container. fn as_elem_mut(&mut self) -> Option<&mut Self::Elem>; } @@ -130,6 +245,58 @@ where D: Dimension } } +impl ArrayLike for &ArrayRef +where D: Dimension +{ + type Dim = D; + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + (*self).view() + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + +impl ArrayLike for &mut ArrayRef +where D: Dimension +{ + type Dim = D; + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + (**self).view() + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + impl ArrayLikeMut for ArrayRef where D: Dimension { @@ -148,6 +315,24 @@ where D: Dimension } } +impl ArrayLikeMut for &mut ArrayRef +where D: Dimension +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + ArrayRef::view_mut(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.dim.size() == 1 { + self.first_mut() + } else { + None + } + } +} + impl ArrayLike for ArrayBase where S: Data, @@ -158,7 +343,63 @@ where fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> { - self.view() + ArrayRef::view(self) + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + +impl ArrayLike for &ArrayBase +where + S: Data, + D: Dimension, +{ + type Dim = D; + type Elem = S::Elem; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + ArrayRef::view(self) + } + + fn dim(&self) -> Self::Dim + { + self.raw_dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.dim.size() == 1 { + self.first() + } else { + None + } + } +} + +impl ArrayLike for &mut ArrayBase +where + S: Data, + D: Dimension, +{ + type Dim = D; + type Elem = S::Elem; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + ArrayRef::view(self) } fn dim(&self) -> Self::Dim @@ -183,7 +424,27 @@ where { fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> { - self.view_mut() + ArrayRef::view_mut(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.dim.size() == 1 { + self.first_mut() + } else { + None + } + } +} + +impl ArrayLikeMut for &mut ArrayBase +where + S: DataMut, + D: Dimension, +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + ArrayRef::view_mut(self) } fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> @@ -222,6 +483,58 @@ impl ArrayLike for [A] } } +impl ArrayLike for &[A] +{ + type Dim = Ix1; + + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + aview1(self) + } + + fn dim(&self) -> Self::Dim + { + Ix1(self.len()) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.len() == 1 { + Some(&self[0]) + } else { + None + } + } +} + +impl ArrayLike for &mut [A] +{ + type Dim = Ix1; + + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + aview1(self) + } + + fn dim(&self) -> Self::Dim + { + Ix1(self.len()) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if self.len() == 1 { + Some(&self[0]) + } else { + None + } + } +} + impl ArrayLikeMut for [A] { fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> @@ -239,6 +552,23 @@ impl ArrayLikeMut for [A] } } +impl ArrayLikeMut for &mut [A] +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + aview_mut1(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if self.len() == 1 { + Some(&mut self[0]) + } else { + None + } + } +} + impl ArrayLike for Vec { type Dim = Ix1; @@ -261,6 +591,50 @@ impl ArrayLike for Vec } } +impl ArrayLike for &Vec +{ + type Dim = Ix1; + + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + (&**self).view() + } + + fn dim(&self) -> Self::Dim + { + (&**self).dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + (&**self).as_elem() + } +} + +impl ArrayLike for &mut Vec +{ + type Dim = Ix1; + + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + (&**self).view() + } + + fn dim(&self) -> Self::Dim + { + (&**self).dim() + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + (&**self).as_elem() + } +} + impl ArrayLikeMut for Vec { fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> @@ -274,26 +648,160 @@ impl ArrayLikeMut for Vec } } +impl ArrayLikeMut for &mut Vec +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + (&mut **self).view_mut() + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + (&mut **self).as_elem_mut() + } +} + +impl ArrayLike for [A; N] +{ + type Dim = Ix1; + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + ArrayView::from(self) + } + + fn dim(&self) -> Self::Dim + { + Ix1(N) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if N == 1 { + Some(&self[0]) + } else { + None + } + } +} + +impl ArrayLike for &[A; N] +{ + type Dim = Ix1; + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + ArrayView::from(self) + } + + fn dim(&self) -> Self::Dim + { + Ix1(N) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if N == 1 { + Some(&self[0]) + } else { + None + } + } +} + +impl ArrayLike for &mut [A; N] +{ + type Dim = Ix1; + type Elem = A; + + fn view(&self) -> ArrayView<'_, Self::Elem, Self::Dim> + { + ArrayView::from(self) + } + + fn dim(&self) -> Self::Dim + { + Ix1(N) + } + + fn as_elem(&self) -> Option<&Self::Elem> + { + if N == 1 { + Some(&self[0]) + } else { + None + } + } +} + +impl ArrayLikeMut for [A; N] +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + ArrayViewMut::from(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if N == 1 { + Some(&mut self[0]) + } else { + None + } + } +} + +impl ArrayLikeMut for &mut [A; N] +{ + fn view_mut(&mut self) -> ArrayViewMut<'_, Self::Elem, Self::Dim> + { + ArrayViewMut::from(self) + } + + fn as_elem_mut(&mut self) -> Option<&mut Self::Elem> + { + if N == 1 { + Some(&mut self[0]) + } else { + None + } + } +} + #[cfg(test)] mod tests { - - use core::ops::Mul; + use core::ops::{Mul, MulAssign}; use crate::{array, Array, ArrayLike, DimMax}; - fn multiply(left: &T, right: &G) -> Array>::Output> + use super::ArrayLikeMut; + + fn multiply(left: T, right: G) -> Array>::Output> where T: ArrayLike, - G: ArrayLike, + G: ArrayLike, // Bounds to enable multiplication - T::Elem: Clone + Mul, + T::Elem: Clone + Mul, G::Elem: Clone, T::Dim: DimMax, { - let left = &*left.view(); - let right = &*right.view(); - left * right + &left.view() * &right.view() + } + + fn multiply_assign(mut left: T, right: G) + where + T: ArrayLikeMut, + G: ArrayLike, + // Bounds to enable multiplication + T::Elem: Clone + MulAssign, + G::Elem: Clone, + // Ensure that the broadcast is still assignable to the left side + T::Dim: DimMax, + { + *left.view_mut() *= &right.view(); } #[test] @@ -302,6 +810,19 @@ mod tests let left = array![1, 2]; let right = array![3, 4]; assert_eq!(multiply(&left, &right), array![3, 8]); - assert_eq!(multiply(&left, &3), array![3, 6]); + assert_eq!(multiply(&left, 3), array![3, 6]); + } + + #[test] + fn test_multiply_assign() + { + let mut left = array![1, 2]; + let right = array![3, 4]; + + multiply_assign(&mut left, &right); + assert_eq!(left, array![3, 8]); + + multiply_assign(&mut left, 2); + assert_eq!(left, array![6, 16]); } } diff --git a/src/lib.rs b/src/lib.rs index 6f24fe0f5..a9f53c147 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -208,7 +208,7 @@ mod zip; mod dimension; mod arraylike; -pub use crate::arraylike::ArrayLike; +pub use crate::arraylike::{ArrayLike, ArrayLikeMut}; pub use crate::zip::{FoldWhile, IntoNdProducer, NdProducer, Zip}; From 761fbfe3ecb25e6c026e7d760c4a2366d4b4ff52 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Thu, 26 Dec 2024 15:54:38 -0700 Subject: [PATCH 4/4] Thought I'd already deleted this file --- src/numeric/impl_num_traits.rs | 508 --------------------------------- 1 file changed, 508 deletions(-) delete mode 100644 src/numeric/impl_num_traits.rs diff --git a/src/numeric/impl_num_traits.rs b/src/numeric/impl_num_traits.rs deleted file mode 100644 index f54acaf0a..000000000 --- a/src/numeric/impl_num_traits.rs +++ /dev/null @@ -1,508 +0,0 @@ -// //! Implementations to mimic the [`num`](https://docs.rs/num/0.4.3/num/) crate's capabilities. - -// use core::num::FpCategory; - -// use num_integer::{ExtendedGcd, Integer}; -// use num_traits::{Euclid, Float, Inv, Pow, Signed}; -// use paste::paste; - -// use crate::{arraylike::ArrayLike, dimension::broadcast::co_broadcast, Array, ArrayRef, DimMax, Dimension, Zip}; - -// /// Implement functions inside a generic scope that map another function to an array's elements. -// /// -// /// This is useful for implementing numeric functions succinctly. -// /// The macro takes three arguments: -// /// 1. A module identifier, indicating which module the mapping function is from -// /// 2. A literal, either "ref" to map by reference or "owned" to map by value -// /// 3. A parenthesized list of tuples, each one is (function_name, output_type) -// /// -// /// # Example -// /// ```no_run -// /// use num::Float; -// /// -// /// impl ArrayRef -// /// where -// /// A: Float + Clone, -// /// D: Dimension, -// /// { -// /// impl_singles!(Float, "owned", ((is_infinite, bool), (is_finite, bool))); -// /// } -// /// ``` -// /// expands to the following block: -// /// ```no_run -// /// use num::Float; -// /// -// /// impl ArrayRef -// /// where -// /// A: Float + Clone, -// /// D: Dimension, -// /// { -// /// fn is_infinite(&self) -> Array { -// /// self.mapv(Float::is_infinite) -// /// } -// /// -// /// fn is_finite(&self) -> Array { -// /// self.mapv(Float::is_finite) -// /// } -// /// } -// /// ``` -// macro_rules! impl_singles { -// ($mod:ident, "ref", ($(($fn:ident, $output:ty)),+)) => { -// $( -// #[must_use = "method returns a new array and does not mutate the original value"] -// fn $fn(&self) -> Array<$output, D> { -// self.map($mod::$fn) -// } -// )+ -// }; -// ($mod:ident, "owned", ($(($fn:ident, $output:ty)),+)) => { -// $( -// #[must_use = "method returns a new array and does not mutate the original value"] -// fn $fn(&self) -> Array<$output, D> { -// self.mapv($mod::$fn) -// } -// )+ -// }; -// } - -// /// Implement pairs of immutable and mutable functions inside a generic scope -// /// that map another function to an array's elements. -// /// -// /// This is useful for implementing numeric functions succinctly. -// /// The macro takes three arguments: -// /// 1. A module identifier, indicating which module the mapping function is from -// /// 2. A literal, either "ref" to map by reference or "owned" to map by value -// /// 3. A parenthesized list of function names from that module -// /// -// /// # Example -// /// ```no_run -// /// use num::Float; -// /// -// /// impl ArrayRef -// /// where -// /// A: Float + Clone, -// /// D: Dimension, -// /// { -// /// impl_pairs!(Float, "owned", (ceil)); -// /// } -// /// ``` -// /// expands to -// /// ```no_run -// /// use num::Float; -// /// -// /// impl ArrayRef -// /// where -// /// A: Float + Clone, -// /// D: Dimension, -// /// { -// /// fn ceil(&self) -> Array { -// /// self.mapv(Float::ceil) -// /// } -// /// -// /// fn ceil_assign(&mut self) { -// /// self.mapv_inplace(Float::ceil) -// /// } -// /// } -// /// ``` -// macro_rules! impl_pairs { -// ($mod:ident, "ref", ($($fn:ident),+)) => { -// impl_singles!($mod, "ref", ($(($fn, A)),+)); -// $( -// paste! { -// fn [<$fn _assign>](&mut self) { -// self.map_inplace(|v| *v = $mod::$fn(v)) -// } -// } -// )+ -// }; -// ($mod:ident, "owned", ($($fn:ident),+)) => { -// impl_singles!($mod, "owned", ($(($fn, A)),+)); -// $( -// paste! { -// fn [<$fn _assign>](&mut self) { -// self.mapv_inplace($mod::$fn) -// } -// } -// )+ -// }; -// } - -// /// Implement functions inside a generic scope that map another function to an array's elements, -// /// with an additional argument that can be a scalar or an array. -// /// -// /// This is useful for implementing numeric functions succinctly. -// /// The macro takes three arguments: -// /// 1. A module identifier, indicating which module the mapping function is from -// /// 2. A literal, either "ref" to map by reference or "owned" to map by value -// /// 3. A parenthesized list of (function_name, argument_name, output_type) -// /// -// /// This macro makes heavy use of [`ArrayLike`]; see its documentation for more details. -// /// -// /// # Example -// /// ```no_run -// /// use num::Integer; -// /// -// /// impl ArrayRef -// /// where -// /// A: Integer + Clone, -// /// D: Dimension, -// /// T: ArrayLike, -// /// { -// /// impl_broadcast_singles!(Integer, "ref", (is_multiple_of, other, bool)); -// /// } -// /// ``` -// /// expands to -// /// ```no_run -// /// use num::Integer; -// /// -// /// impl ArrayRef -// /// where -// /// A: Integer + Clone, -// /// D: Dimension, -// /// T: ArrayLike, -// /// { -// /// fn is_multiple_of(&self, other: &T) -> Array { -// /// Zip::from(self) -// /// .and(other.broadcast(self.raw_dim()).unwrap()) -// /// .map_collect(Integer::is_multiple_of) -// /// } -// /// } -// /// ``` -// /// -// /// # Panics -// /// Functions created by this macro will panic when the additional argument is an array -// /// that is not broadcastable-compatible (i.e., has an incompatible shape) with the -// /// `self` array. -// /// ``` -// macro_rules! impl_broadcast_singles { -// ($mod:ident, "ref", ($(($fn:ident, $arg:ident, $output:ty)),+)) => { -// $( -// #[must_use = "method returns a new array and does not mutate the original value"] -// fn $fn(&self, $arg: &T) -> Array<$output, >::Output> -// where -// T: ArrayLike, -// D: DimMax -// { -// let shape = co_broadcast::<_, _, >::Output>( -// &self.raw_dim(), &$arg.dim() -// ).unwrap(); -// Zip::from(self.broadcast(shape.clone()).unwrap()) -// .and($arg.broadcast(shape).unwrap()) -// .map_collect($mod::$fn) -// } -// )+ -// }; -// ($mod:ident, "owned", ($(($fn:ident, $arg:ident, $output:ty)),+)) => { -// $( -// #[must_use = "method returns a new array and does not mutate the original value"] -// fn $fn(&self, $arg: &T) -> Array<$output, >::Output> -// where -// T: ArrayLike, -// D: DimMax -// { -// let shape = co_broadcast::<_, _, >::Output>( -// &self.raw_dim(), &$arg.dim() -// ).unwrap(); -// Zip::from(self.broadcast(shape.clone()).unwrap()) -// .and($arg.broadcast(shape).unwrap()) -// .map_collect(|s, a| s.$fn(*a)) -// } -// )+ -// }; -// } - -// /// Implement pairs of immutable and mutable functions inside a generic scope that map -// /// another function to an array's elements, with an additional argument that can be a scalar or an array. -// /// -// /// This is useful for implementing numeric functions succinctly. -// /// The macro takes three arguments: -// /// 1. A module identifier, indicating which module the mapping function is from -// /// 2. A literal, either "ref" to map by reference or "owned" to map by value -// /// 3. A parenthesized list of (function_name, argument_name) -// /// -// /// This macro makes heavy use of [`ArrayLike`]; see its documentation for more details. -// /// -// /// # Example -// /// ```no_run -// /// use num::Integer; -// /// -// /// impl ArrayRef -// /// where -// /// A: Integer + Clone, -// /// D: Dimension, -// /// { -// /// impl_broadcast_pairs!(Integer, "ref", (div_mod, other)); -// /// } -// /// ``` -// /// expands to -// /// ```no_run -// /// use num::Integer; -// /// -// /// impl ArrayRef -// /// where -// /// A: Integer + Clone, -// /// D: Dimension, -// /// { -// /// fn div_mod(&self, other: &T) -> Array -// /// where T: ArrayLike -// /// { -// /// Zip::from(self) -// /// .and(other.broadcast(self.raw_dim()).unwrap()) -// /// .map_collect(Integer::is_multiple_of) -// /// } -// /// -// /// fn div_mod_assign(&mut self, other: &T) -// /// where T: ArrayLike -// /// { -// /// self.zip_mut_with(&other.broadcast(self.raw_dim()).unwrap(), |s, o| { -// /// *s = s.div_mod(o) -// /// }); -// /// } -// /// } -// /// ``` -// /// -// /// # Panics -// /// Functions created by this macro will panic when the additional argument is an array -// /// that is not broadcastable-compatible (i.e., has an incompatible shape) with the -// /// `self` array. -// /// ``` -// macro_rules! impl_broadcast_pairs { -// ($mod:ident, "ref", ($(($fn:ident, $arg:ident)),+)) => { -// impl_broadcast_singles!($mod, "ref", ($(($fn, $arg, A)),+)); -// $( -// paste! { -// fn [<$fn _assign>](&mut self, $arg: &T) -// where T: ArrayLike -// { -// self.zip_mut_with(&$arg.broadcast(self.raw_dim()).unwrap(), |s, o| { -// *s = s.$fn(o) -// }); -// } -// } -// )+ -// }; -// ($mod:ident, "owned", ($(($fn:ident, $arg:ident)),+)) => { -// impl_broadcast_singles!($mod, "owned", ($(($fn, $arg, A)),+)); -// $( -// paste! { -// fn [<$fn _assign>](&mut self, $arg: &T) -// where T: ArrayLike -// { -// self.zip_mut_with(&$arg.broadcast(self.raw_dim()).unwrap(), |s, o| { -// *s = s.$fn(*o) -// }); -// } -// } -// )+ -// }; -// } - -// /// Functions that forward to [`num_traits::Signed`] -// impl ArrayRef -// where -// A: Signed + Clone, -// D: Dimension, -// { -// impl_pairs!(Signed, "ref", (abs, signum)); -// impl_singles!(Signed, "ref", ((is_positive, bool), (is_negative, bool))); -// impl_broadcast_pairs!(Signed, "ref", ((abs_sub, other))); -// } - -// /// Functions that forward to [`num_traits::Pow`] -// impl ArrayRef -// where D: Dimension -// { -// fn pow(&self, rhs: &C) -> Array>::Output> -// where -// A: Pow + Clone, -// B: Clone, -// C: ArrayLike, -// D: DimMax, -// { -// let shape = co_broadcast::<_, _, >::Output>(&self.raw_dim(), &rhs.dim()).unwrap(); -// Zip::from(self.broadcast(shape.clone()).unwrap()) -// .and(rhs.broadcast(shape).unwrap()) -// .map_collect(|s, r| s.clone().pow(r.clone())) -// } - -// fn pow_assign(&mut self, rhs: &C) -// where -// A: Pow + Clone, -// B: Clone, -// C: ArrayLike, -// { -// self.zip_mut_with(&rhs.broadcast(self.raw_dim()).unwrap(), |s, r| *s = s.clone().pow(r.clone())); -// } -// } - -// /// Functions that forward to [`num_traits::Float`] -// impl ArrayRef -// where -// A: Float, -// D: Dimension, -// { -// impl_pairs!( -// Float, -// "owned", -// ( -// floor, ceil, round, trunc, fract, recip, sqrt, exp, exp2, ln, log2, log10, cbrt, sin, -// cos, tan, asin, acos, atan, exp_m1, ln_1p, sinh, cosh, tanh, asinh, acosh, atanh -// ) -// ); -// impl_singles!( -// Float, -// "owned", -// ( -// (is_nan, bool), -// (is_infinite, bool), -// (is_finite, bool), -// (is_normal, bool), -// (classify, FpCategory), -// (integer_decode, (u64, i16, i8)), -// (sin_cos, (A, A)) -// ) -// ); -// impl_broadcast_pairs!( -// Float, -// "owned", -// ( -// (powf, n), -// (log, base), -// (max, other), -// (min, other), -// (hypot, other), -// (atan2, other) -// ) -// ); - -// fn mul_add(&self, a: &B, b: &T) -> Array>::Output as DimMax>::Output> -// where -// B: ArrayLike, -// T: ArrayLike, -// D: DimMax, -// >::Output: DimMax, -// { -// let shape = co_broadcast::<_, _, >::Output>(&self.raw_dim(), &a.dim()).unwrap(); -// let shape: <>::Output as DimMax>::Output = co_broadcast(&shape, &b.dim()).unwrap(); -// Zip::from(self.broadcast(shape.clone()).unwrap()) -// .and(&a.broadcast(shape.clone()).unwrap()) -// .and(&b.broadcast(shape).unwrap()) -// .map_collect(|s, a, b| s.mul_add(*a, *b)) -// } - -// fn mul_add_assign(&mut self, a: &B, b: &C) -// where -// B: ArrayLike, -// C: ArrayLike, -// { -// let shape = self.raw_dim(); -// Zip::from(self) -// .and(&a.broadcast(shape.clone()).unwrap()) -// .and(&b.broadcast(shape).unwrap()) -// .map_collect(|s, a, b| s.mul_add(*a, *b)); -// } - -// fn powi(&self, n: &B) -> Array>::Output> -// where -// B: ArrayLike, -// D: DimMax, -// { -// let shape: >::Output = co_broadcast(&self.raw_dim(), &n.dim()).unwrap(); -// Zip::from(self.broadcast(shape.clone()).unwrap()) -// .and(&n.broadcast(shape).unwrap()) -// .map_collect(|s, n| s.powi(*n)) -// } - -// fn powi_assign(&mut self, n: &B) -// where B: ArrayLike -// { -// self.zip_mut_with(&n.broadcast(self.raw_dim()).unwrap(), |s, n| *s = s.powi(*n)); -// } -// } - -// /// Functions that forward to [`num_integer::Integer`] -// impl ArrayRef -// where -// A: Integer + Clone, -// D: Dimension, -// { -// impl_singles!(Integer, "ref", ((is_even, bool), (is_odd, bool))); - -// fn dec(&mut self) -// where Self: Clone -// { -// self.map_inplace(A::dec); -// } - -// fn inc(&mut self) -// where Self: Clone -// { -// self.map_inplace(A::inc); -// } - -// impl_broadcast_pairs!( -// Integer, -// "ref", -// ( -// (div_floor, other), -// (mod_floor, other), -// (gcd, other), -// (lcm, other), -// (div_ceil, other), -// (next_multiple_of, other), -// (prev_multiple_of, other) -// ) -// ); -// impl_broadcast_singles!( -// Integer, -// "ref", -// ( -// (is_multiple_of, other, bool), -// (div_rem, other, (A, A)), -// (gcd_lcm, other, (A, A)), -// (div_mod_floor, other, (A, A)), -// (extended_gcd, other, ExtendedGcd) -// ) -// ); - -// fn extended_gcd_lcm(&self, other: &B) -> Array<(ExtendedGcd, A), >::Output> -// where -// Self: Clone + Signed, -// B: ArrayLike, -// D: DimMax, -// { -// let shape: >::Output = co_broadcast(&self.raw_dim(), &other.dim()).unwrap(); -// Zip::from( -// self.broadcast(shape.clone()) -// .expect("Shape derived from co_broadcast should be ok"), -// ) -// .and( -// &other -// .broadcast(shape) -// .expect("Shape derived from co_broadcast should be ok"), -// ) -// .map_collect(|s, o| (s.extended_gcd(o), s.lcm(o))) -// } -// } - -// /// Functions that forward to [`num_traits::Euclid`] -// impl ArrayRef -// where -// A: Euclid + Clone, -// D: Dimension, -// { -// impl_broadcast_pairs!(Euclid, "ref", ((div_euclid, v), (rem_euclid, v))); -// impl_broadcast_singles!(Euclid, "ref", ((div_rem_euclid, v, (A, A)))); -// } - -// /// Functions that forward to [`num_traits::Inv`] -// impl ArrayRef -// where -// A: Inv + Clone, -// D: Dimension, -// { -// fn inv(&self) -> Array -// { -// self.mapv(Inv::inv) -// } -// }