Complete, hard-to-discritize rewrite of the crate
All in the hope to reduce bounds count.
This commit is contained in:
parent
6d20316802
commit
1d19e3d5f8
2 changed files with 434 additions and 213 deletions
557
src/lib.rs
557
src/lib.rs
|
|
@ -1,64 +1,268 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![no_std] // <-- yeah, in case you wondered - there you are, feel free to use it
|
||||
|
||||
type TNum<const N: usize> = typenum::Const<N>;
|
||||
type AlgNum<const N: usize> = nalgebra::Const<N>;
|
||||
type ArrLen<const N: usize> = <TNum<N> as generic_array::IntoArrayLength>::ArrayLength;
|
||||
/// Nothing to see here
|
||||
///
|
||||
/// Congratulations, you've found a cute seal pup!
|
||||
///
|
||||
/// <img src="https://www.startpage.com/av/proxy-image?piurl=https%3A%2F%2Fi.pinimg.com%2Foriginals%2Fb8%2F2f%2F61%2Fb82f6130b6a8d6afbefc07b08e1f8750.jpg&sp=1731807377Td567a79c382f033584e92b2a748b4f2c5e21b672386c3919dd66700457951351" alt="cute baby seal!" width="10%"/>
|
||||
///
|
||||
/// This tiny thing ensures that [`Conv`], [`Corr`] and [`Comp`] traits are not implemented outside of this crate. There's no strict reasoning for this now, but I might want to use some **unsafe** operations in these trait implementations in the future.
|
||||
#[derive(Debug)]
|
||||
pub struct Seal(());
|
||||
|
||||
use generic_array::{ArrayLength, GenericArray, IntoArrayLength};
|
||||
use nalgebra::{
|
||||
allocator::Allocator, ArrayStorage, Dim, IsContiguous, Matrix, Owned, RawStorage, ToTypenum,
|
||||
};
|
||||
/// Convenience trait, used to defining type **conv**ersions
|
||||
///
|
||||
/// Also, this is the only bound to [`GenericMatrix`] type alias, meaning that all of the following are valid [`GenericMatrix`]es:
|
||||
/// ```rust
|
||||
/// # use generic_array_storage::GenericMatrix;
|
||||
/// type NalgebraMatrix = GenericMatrix<i32, nalgebra::U3, nalgebra::U4>;
|
||||
/// type TypenumMatrix = GenericMatrix<i32, typenum::U3, typenum::U4>;
|
||||
/// type TypenumConstMatrix = GenericMatrix<i32, typenum::Const<3>, typenum::Const<3>>;
|
||||
/// // (nalgebra::Const are actually aliased by nalgebra::{U1, U2, ...})
|
||||
/// ```
|
||||
///
|
||||
/// If you need to convert between them, see [`Comp`].
|
||||
///
|
||||
/// This trait is sealed.
|
||||
pub trait Conv {
|
||||
/// See [`Seal`]
|
||||
const SEAL: Seal;
|
||||
|
||||
/// A stack-allocated storage, of [`typenum`]-backed row-major two dimensional array
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GenericArrayStorage<T, R, C>(GenericArray<GenericArray<T, R>, C>)
|
||||
/// A core `usize` corresponding to length/size
|
||||
const NUM: usize;
|
||||
|
||||
/// [`nalgebra`]-faced type (matrix dimension)
|
||||
type Nalg: Dim;
|
||||
|
||||
/// [`typenum`]/[`generic_array`]-faced type (generic array length)
|
||||
type ArrLen: ArrayLength;
|
||||
|
||||
/// Constructor method used in [`nalgebra`] implementations
|
||||
fn new_nalg() -> Self::Nalg;
|
||||
}
|
||||
|
||||
impl<const N: usize> Conv for nalgebra::Const<N>
|
||||
where
|
||||
R: ArrayLength,
|
||||
C: ArrayLength;
|
||||
typenum::Const<N>: generic_array::IntoArrayLength,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
impl<T, R, C> Copy for GenericArrayStorage<T, R, C>
|
||||
const NUM: usize = N;
|
||||
|
||||
type Nalg = nalgebra::Const<N>;
|
||||
|
||||
type ArrLen = <typenum::Const<N> as generic_array::IntoArrayLength>::ArrayLength;
|
||||
|
||||
fn new_nalg() -> Self::Nalg {
|
||||
nalgebra::Const::<N>
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Conv for typenum::Const<N>
|
||||
where
|
||||
typenum::Const<N>: generic_array::IntoArrayLength,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
const NUM: usize = N;
|
||||
|
||||
type Nalg = nalgebra::Const<N>;
|
||||
|
||||
type ArrLen = <typenum::Const<N> as generic_array::IntoArrayLength>::ArrayLength;
|
||||
|
||||
fn new_nalg() -> Self::Nalg {
|
||||
nalgebra::Const::<N>
|
||||
}
|
||||
}
|
||||
|
||||
impl Conv for typenum::UTerm {
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
const NUM: usize = 0;
|
||||
|
||||
type Nalg = nalgebra::Const<0>;
|
||||
|
||||
type ArrLen = Self;
|
||||
|
||||
fn new_nalg() -> Self::Nalg {
|
||||
nalgebra::Const::<0>
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Conv for typenum::UInt<U, B0>
|
||||
where
|
||||
U: Conv,
|
||||
U::Nalg: nalgebra::dimension::DimMul<nalgebra::U2>,
|
||||
U::ArrLen: core::ops::Mul<typenum::U2>,
|
||||
typenum::Prod<U::ArrLen, typenum::U2>: ArrayLength,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
const NUM: usize = 2 * U::NUM;
|
||||
|
||||
type Nalg = <U::Nalg as nalgebra::dimension::DimMul<nalgebra::U2>>::Output;
|
||||
|
||||
type ArrLen = typenum::operator_aliases::Prod<U::ArrLen, typenum::U2>;
|
||||
|
||||
fn new_nalg() -> Self::Nalg {
|
||||
Self::Nalg::from_usize(Self::NUM)
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Conv for typenum::UInt<U, B1>
|
||||
where
|
||||
typenum::UInt<U, B0>: Conv,
|
||||
<typenum::UInt<U, B0> as Conv>::Nalg: nalgebra::dimension::DimAdd<nalgebra::U1>,
|
||||
<typenum::UInt<U, B0> as Conv>::ArrLen: core::ops::Add<typenum::U1>,
|
||||
typenum::Sum<<typenum::UInt<U, B0> as Conv>::ArrLen, typenum::U1>: ArrayLength,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
const NUM: usize = 1 + <typenum::UInt<U, B0> as Conv>::NUM;
|
||||
|
||||
type Nalg =
|
||||
<<typenum::UInt<U, B0> as Conv>::Nalg as nalgebra::dimension::DimAdd<nalgebra::U1>>::Output;
|
||||
|
||||
type ArrLen =
|
||||
typenum::operator_aliases::Sum<<typenum::UInt<U, B0> as Conv>::ArrLen, typenum::U1>;
|
||||
|
||||
fn new_nalg() -> Self::Nalg {
|
||||
Self::Nalg::from_usize(Self::NUM)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait, used to signify that particular [`Conv`] implementor **corr**esponds to some integer. This has to be a separate trait, since integer correspondence is not something we always need, and the actual point of this crate is to get rid of such bounds. This trait is a way to reintroduce it, if you happen to need that.
|
||||
///
|
||||
/// Trait defines methods for core array to/from [`GenericArray`] conversion. Implementations are expected to be trivial, i.e. [`GenericArray`] method call or unsafe transmute backed by some explanation.
|
||||
///
|
||||
/// This trait is sealed.
|
||||
pub trait Corr<const N: usize>: Conv {
|
||||
/// See [`Seal`]
|
||||
const SEAL: Seal;
|
||||
|
||||
/// Converts core array to [`GenericArray`]
|
||||
fn array_to_generic<E>(array: [E; N]) -> GenericArray<E, Self::ArrLen>;
|
||||
|
||||
/// Converts [`GenericArray`] to core array
|
||||
fn generic_to_array<E>(generic: GenericArray<E, Self::ArrLen>) -> [E; N];
|
||||
}
|
||||
|
||||
impl<const N: usize, T> Corr<N> for T
|
||||
where
|
||||
T: Conv,
|
||||
typenum::Const<N>: IntoArrayLength<ArrayLength = T::ArrLen>,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
#[inline]
|
||||
fn array_to_generic<E>(array: [E; N]) -> GenericArray<E, Self::ArrLen> {
|
||||
GenericArray::from_array(array)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn generic_to_array<E>(generic: GenericArray<E, Self::ArrLen>) -> [E; N] {
|
||||
generic.into_array()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait, used to signify that a pair of [`Conv`] implementors are **comp**atible with each other, meaning that [`GenericArray`]s sized with them can be converted [`Comp::fwd`] and [`Comp::bwd`].
|
||||
///
|
||||
/// This trait is sealed.
|
||||
pub trait Comp<Other: Conv>: Conv {
|
||||
/// See [`Seal`]
|
||||
const SEAL: Seal;
|
||||
|
||||
/// "forward" conversion
|
||||
fn fwd<E>(value: GenericArray<E, Self::ArrLen>) -> GenericArray<E, Other::ArrLen>;
|
||||
|
||||
/// "backward" conversion
|
||||
fn bwd<E>(value: GenericArray<E, Other::ArrLen>) -> GenericArray<E, Self::ArrLen>;
|
||||
}
|
||||
|
||||
impl<T: Conv, Other: Conv> Comp<Other> for T
|
||||
where
|
||||
T: Conv<ArrLen = Other::ArrLen>,
|
||||
{
|
||||
const SEAL: Seal = Seal(());
|
||||
|
||||
#[inline]
|
||||
fn fwd<E>(value: GenericArray<E, Self::ArrLen>) -> GenericArray<E, <Other as Conv>::ArrLen> {
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bwd<E>(value: GenericArray<E, <Other as Conv>::ArrLen>) -> GenericArray<E, Self::ArrLen> {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
use generic_array::{functional::FunctionalSequence, ArrayLength, GenericArray, IntoArrayLength};
|
||||
use nalgebra::{
|
||||
allocator::Allocator, ArrayStorage, Dim, IsContiguous, Matrix, Owned, RawStorage,
|
||||
RawStorageMut, Scalar, Storage,
|
||||
};
|
||||
use typenum::{B0, B1};
|
||||
|
||||
/// A stack-allocated storage, of [`typenum`]-backed col-major two dimensional array
|
||||
///
|
||||
/// This struct is transparent and completely public, since it has nothing to hide! Note that [`GenericArray`] is transparent itself, so this struct effectively has the same layout as a two-dimensional array of the corresponding size.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct GenericArrayStorage<T, R: Conv, C: Conv>(
|
||||
pub GenericArray<GenericArray<T, R::ArrLen>, C::ArrLen>,
|
||||
);
|
||||
|
||||
impl<T, R: Conv, C: Conv> Clone for GenericArrayStorage<T, R, C>
|
||||
where
|
||||
T: Clone,
|
||||
GenericArray<GenericArray<T, R::ArrLen>, C::ArrLen>: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: Conv, C: Conv> Copy for GenericArrayStorage<T, R, C>
|
||||
where
|
||||
T: Copy,
|
||||
R: ArrayLength,
|
||||
R::ArrayType<T>: Copy,
|
||||
C: ArrayLength,
|
||||
C::ArrayType<GenericArray<T, R>>: Copy,
|
||||
<R::ArrLen as ArrayLength>::ArrayType<T>: Copy,
|
||||
<C::ArrLen as ArrayLength>::ArrayType<GenericArray<T, R::ArrLen>>: Copy,
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
|
||||
unsafe impl<const R: usize, const C: usize, T> RawStorage<T, AlgNum<R>, AlgNum<C>>
|
||||
for GenericArrayStorage<T, ArrLen<R>, ArrLen<C>>
|
||||
where
|
||||
TNum<R>: IntoArrayLength,
|
||||
TNum<C>: IntoArrayLength,
|
||||
AlgNum<R>: Dim,
|
||||
AlgNum<C>: Dim,
|
||||
{
|
||||
type RStride = AlgNum<1>;
|
||||
impl<T, R: Conv, C: Conv> AsRef<[T]> for GenericArrayStorage<T, R, C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
GenericArray::slice_from_chunks(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
type CStride = AlgNum<R>;
|
||||
impl<T, R: Conv, C: Conv> AsMut<[T]> for GenericArrayStorage<T, R, C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
GenericArray::slice_from_chunks_mut(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
|
||||
unsafe impl<T, R: Conv, C: Conv> RawStorage<T, R::Nalg, C::Nalg> for GenericArrayStorage<T, R, C> {
|
||||
type RStride = nalgebra::U1;
|
||||
|
||||
type CStride = R::Nalg;
|
||||
|
||||
fn ptr(&self) -> *const T {
|
||||
// SAFETY:
|
||||
// Trait doc does not state it, but I assume that pointer should be valid and non-null.
|
||||
//
|
||||
// So this actually returns cast of [`NonNull::dangling`], in case array contains no elements
|
||||
if let Some(first) = self.0.first() {
|
||||
// there is at least one element - grab it's pointer
|
||||
first.as_ptr()
|
||||
if self.0.is_empty() {
|
||||
core::ptr::NonNull::<T>::dangling().as_ptr()
|
||||
} else {
|
||||
core::ptr::NonNull::<T>::dangling().as_ptr().cast_const()
|
||||
self.0.as_ptr().cast()
|
||||
}
|
||||
}
|
||||
|
||||
fn shape(&self) -> (nalgebra::Const<R>, nalgebra::Const<C>) {
|
||||
(nalgebra::Const, nalgebra::Const)
|
||||
fn shape(&self) -> (R::Nalg, C::Nalg) {
|
||||
(R::new_nalg(), C::new_nalg())
|
||||
}
|
||||
|
||||
fn strides(&self) -> (Self::RStride, Self::CStride) {
|
||||
(nalgebra::Const, nalgebra::Const)
|
||||
(nalgebra::U1, R::new_nalg())
|
||||
}
|
||||
|
||||
fn is_contiguous(&self) -> bool {
|
||||
|
|
@ -66,69 +270,48 @@ where
|
|||
}
|
||||
|
||||
unsafe fn as_slice_unchecked(&self) -> &[T] {
|
||||
core::slice::from_raw_parts(
|
||||
<Self as nalgebra::RawStorage<T, nalgebra::Const<R>, nalgebra::Const<C>>>::ptr(self),
|
||||
R * C,
|
||||
)
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
|
||||
unsafe impl<const R: usize, const C: usize, T>
|
||||
nalgebra::RawStorageMut<T, nalgebra::Const<R>, nalgebra::Const<C>>
|
||||
for GenericArrayStorage<T, ArrLen<R>, ArrLen<C>>
|
||||
where
|
||||
typenum::Const<R>: IntoArrayLength,
|
||||
typenum::Const<C>: IntoArrayLength,
|
||||
nalgebra::Const<R>: Dim,
|
||||
nalgebra::Const<C>: Dim,
|
||||
unsafe impl<T, R: Conv, C: Conv> RawStorageMut<T, R::Nalg, C::Nalg>
|
||||
for GenericArrayStorage<T, R, C>
|
||||
{
|
||||
fn ptr_mut(&mut self) -> *mut T {
|
||||
self.0
|
||||
.first_mut()
|
||||
.map_or(core::ptr::NonNull::<T>::dangling().as_ptr(), |first| {
|
||||
first.as_mut_ptr()
|
||||
})
|
||||
if self.0.is_empty() {
|
||||
core::ptr::NonNull::<T>::dangling().as_ptr()
|
||||
} else {
|
||||
self.0.as_mut_ptr().cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
|
||||
core::slice::from_raw_parts_mut(
|
||||
<Self as nalgebra::RawStorageMut<T, nalgebra::Const<R>, nalgebra::Const<C>>>::ptr_mut(
|
||||
self,
|
||||
),
|
||||
R * C,
|
||||
)
|
||||
// SAFETY: see struct's doc - it's layout is guaranteed to be like that of a two-dimensional array
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
|
||||
unsafe impl<const R: usize, const C: usize, T: nalgebra::Scalar>
|
||||
nalgebra::Storage<T, nalgebra::Const<R>, nalgebra::Const<C>>
|
||||
for GenericArrayStorage<T, ArrLen<R>, ArrLen<C>>
|
||||
unsafe impl<T: Scalar, R: Conv, C: Conv> Storage<T, R::Nalg, C::Nalg>
|
||||
for GenericArrayStorage<T, R, C>
|
||||
where
|
||||
T: Clone,
|
||||
typenum::Const<R>: IntoArrayLength,
|
||||
typenum::Const<C>: IntoArrayLength,
|
||||
nalgebra::Const<R>: Dim,
|
||||
nalgebra::Const<C>: Dim,
|
||||
nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
|
||||
{
|
||||
fn into_owned(self) -> Owned<T, nalgebra::Const<R>, nalgebra::Const<C>>
|
||||
fn into_owned(self) -> Owned<T, R::Nalg, C::Nalg>
|
||||
where
|
||||
nalgebra::DefaultAllocator:
|
||||
nalgebra::allocator::Allocator<nalgebra::Const<R>, nalgebra::Const<C>>,
|
||||
nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
|
||||
{
|
||||
let init: [[T; R]; C] = self.0.into_array().map(GenericArray::into_array);
|
||||
nalgebra::DefaultAllocator::allocate_from_iterator(
|
||||
nalgebra::Const::<R>,
|
||||
nalgebra::Const::<C>,
|
||||
init.into_iter().flatten(),
|
||||
R::new_nalg(),
|
||||
C::new_nalg(),
|
||||
self.0.into_iter().flatten(),
|
||||
)
|
||||
}
|
||||
|
||||
fn clone_owned(&self) -> Owned<T, nalgebra::Const<R>, nalgebra::Const<C>>
|
||||
fn clone_owned(&self) -> Owned<T, R::Nalg, C::Nalg>
|
||||
where
|
||||
nalgebra::DefaultAllocator:
|
||||
nalgebra::allocator::Allocator<nalgebra::Const<R>, nalgebra::Const<C>>,
|
||||
nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
|
||||
{
|
||||
self.clone().into_owned()
|
||||
}
|
||||
|
|
@ -139,131 +322,127 @@ where
|
|||
}
|
||||
|
||||
#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
|
||||
unsafe impl<R: ArrayLength, C: ArrayLength, T: nalgebra::Scalar> IsContiguous
|
||||
for GenericArrayStorage<T, R, C>
|
||||
{
|
||||
}
|
||||
unsafe impl<R: Conv, C: Conv, T: nalgebra::Scalar> IsContiguous for GenericArrayStorage<T, R, C> {}
|
||||
|
||||
/// Alias to [`nalgebra::Matrix`], completely "hinding" `const usize`s away. See crate's documentation on how this is possible.
|
||||
pub type GenericMatrix<T, R, C> = nalgebra::Matrix<
|
||||
T,
|
||||
R,
|
||||
C,
|
||||
GenericArrayStorage<
|
||||
T,
|
||||
<<R as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
<<C as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
>,
|
||||
>;
|
||||
/// Alias to [`nalgebra::Matrix`], completely "hiding" `const usize`s away. See crate's documentation on how this is possible.
|
||||
pub type GenericMatrix<T, R, C> =
|
||||
nalgebra::Matrix<T, <R as Conv>::Nalg, <C as Conv>::Nalg, GenericArrayStorage<T, R, C>>;
|
||||
|
||||
impl<T, const R: usize, const C: usize> From<[[T; R]; C]>
|
||||
for GenericArrayStorage<
|
||||
T,
|
||||
<<AlgNum<R> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
<<AlgNum<C> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
>
|
||||
where
|
||||
AlgNum<R>: ToTypenum,
|
||||
TNum<R>: IntoArrayLength,
|
||||
<AlgNum<R> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<R> as IntoArrayLength>::ArrayLength>,
|
||||
AlgNum<C>: ToTypenum,
|
||||
TNum<C>: IntoArrayLength,
|
||||
<AlgNum<C> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<C> as IntoArrayLength>::ArrayLength>,
|
||||
impl<T, const AR: usize, const AC: usize, R: Conv + Corr<AR>, C: Conv + Corr<AC>>
|
||||
From<[[T; AR]; AC]> for GenericArrayStorage<T, R, C>
|
||||
{
|
||||
fn from(value: [[T; R]; C]) -> Self {
|
||||
GenericArrayStorage(GenericArray::from_array(
|
||||
value.map(GenericArray::from_array),
|
||||
))
|
||||
fn from(value: [[T; AR]; AC]) -> Self {
|
||||
GenericArrayStorage(C::array_to_generic(value.map(R::array_to_generic)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const R: usize, const C: usize>
|
||||
From<
|
||||
GenericArrayStorage<
|
||||
T,
|
||||
<<AlgNum<R> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
<<AlgNum<C> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
>,
|
||||
> for [[T; R]; C]
|
||||
where
|
||||
AlgNum<R>: ToTypenum,
|
||||
TNum<R>: IntoArrayLength,
|
||||
<AlgNum<R> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<R> as IntoArrayLength>::ArrayLength>,
|
||||
AlgNum<C>: ToTypenum,
|
||||
TNum<C>: IntoArrayLength,
|
||||
<AlgNum<C> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<C> as IntoArrayLength>::ArrayLength>,
|
||||
impl<T, const AR: usize, const AC: usize, R: Conv + Corr<AR>, C: Conv + Corr<AC>>
|
||||
From<GenericArrayStorage<T, R, C>> for [[T; AR]; AC]
|
||||
{
|
||||
fn from(
|
||||
value: GenericArrayStorage<
|
||||
T,
|
||||
<<AlgNum<R> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
<<AlgNum<C> as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength,
|
||||
>,
|
||||
) -> Self {
|
||||
value.0.into_array().map(GenericArray::into_array)
|
||||
fn from(value: GenericArrayStorage<T, R, C>) -> Self {
|
||||
C::generic_to_array(value.0).map(R::generic_to_array)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait, providing convenience operations conversion operations so easier intertop with rest of the ecosystem.
|
||||
pub trait GenericMatrixExt<T, const R: usize, const C: usize>
|
||||
where
|
||||
AlgNum<R>: ToTypenum,
|
||||
TNum<R>: IntoArrayLength,
|
||||
<AlgNum<R> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<R> as IntoArrayLength>::ArrayLength>,
|
||||
AlgNum<C>: ToTypenum,
|
||||
TNum<C>: IntoArrayLength,
|
||||
<AlgNum<C> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<C> as IntoArrayLength>::ArrayLength>,
|
||||
{
|
||||
/// [`GenericMatrix`]-conversion trait intended for core arrays and regular [`nalgebra`] matrices
|
||||
pub trait GenericMatrixFromExt<R: Conv, C: Conv> {
|
||||
/// Type of the elements.
|
||||
///
|
||||
/// This an associated type for the simple reason that is can be such.
|
||||
type T;
|
||||
|
||||
/// Creates [`GenericMatrix`] from core Rust array.
|
||||
fn from_array(array: [[T; R]; C]) -> Self;
|
||||
|
||||
/// Creates [`GenericMatrix`] from regular `nalgebra` matrix, backed up by [`nalgebra::ArrayStorage`].
|
||||
fn from_regular(
|
||||
regular: Matrix<T, AlgNum<R>, AlgNum<C>, nalgebra::ArrayStorage<T, R, C>>,
|
||||
) -> Self;
|
||||
|
||||
/// Converts [`GenericMatrix`] into core Rust array.
|
||||
fn into_array(self) -> [[T; R]; C];
|
||||
|
||||
/// Converts [`GenericMatrix`] into regular `nalgebra` matrix, backed up by [`nalgebra::ArrayStorage`].
|
||||
fn into_regular(self) -> Matrix<T, AlgNum<R>, AlgNum<C>, nalgebra::ArrayStorage<T, R, C>>;
|
||||
fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C>;
|
||||
}
|
||||
|
||||
impl<T, const R: usize, const C: usize> GenericMatrixExt<T, R, C>
|
||||
for GenericMatrix<T, AlgNum<R>, AlgNum<C>>
|
||||
where
|
||||
AlgNum<R>: ToTypenum,
|
||||
TNum<R>: IntoArrayLength,
|
||||
<AlgNum<R> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<R> as IntoArrayLength>::ArrayLength>,
|
||||
AlgNum<C>: ToTypenum,
|
||||
TNum<C>: IntoArrayLength,
|
||||
<AlgNum<C> as ToTypenum>::Typenum:
|
||||
IntoArrayLength<ArrayLength = <TNum<C> as IntoArrayLength>::ArrayLength>,
|
||||
impl<T, const AR: usize, const AC: usize, R: Conv + Corr<AR>, C: Conv + Corr<AC>>
|
||||
GenericMatrixFromExt<R, C> for [[T; AR]; AC]
|
||||
{
|
||||
fn from_array(array: [[T; R]; C]) -> Self {
|
||||
Self::from_data(array.into())
|
||||
type T = T;
|
||||
|
||||
fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C> {
|
||||
GenericMatrix::from_data(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, I AM sorry for the `T: Clone` here.
|
||||
impl<T: Clone, R: Conv, C: Conv, S: RawStorage<T, R::Nalg, C::Nalg> + IsContiguous>
|
||||
GenericMatrixFromExt<R, C> for Matrix<T, R::Nalg, C::Nalg, S>
|
||||
{
|
||||
type T = T;
|
||||
|
||||
fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C> {
|
||||
let (rows, rest) = GenericArray::<_, R::ArrLen>::chunks_from_slice(self.as_slice());
|
||||
debug_assert!(rest.is_empty(), "Should be no leftover");
|
||||
let arr = GenericArray::<_, C::ArrLen>::from_slice(rows);
|
||||
let storage = GenericArrayStorage(arr.clone());
|
||||
GenericMatrix::from_data(storage)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait defining [`GenericMatrix`] conversions.
|
||||
pub trait GenericMatrixExt {
|
||||
/// Type of the elements.
|
||||
///
|
||||
/// This an associated type for the simple reason that is can be such.
|
||||
type T;
|
||||
|
||||
/// Type defining rows count
|
||||
type R: Conv;
|
||||
|
||||
/// Type defining column count
|
||||
type C: Conv;
|
||||
|
||||
/// Changes type of [`GenericMatrix`] to a different row and column count descriptors.
|
||||
fn conv<NewR: Conv + Comp<Self::R>, NewC: Conv + Comp<Self::C>>(
|
||||
self,
|
||||
) -> GenericMatrix<Self::T, NewR, NewC>;
|
||||
|
||||
/// Converts [`GenericMatrix`] into core array-backed regular [`nalgebra`] matrix
|
||||
fn into_array_matrix<const AR: usize, const AC: usize>(
|
||||
self,
|
||||
) -> Matrix<
|
||||
Self::T,
|
||||
nalgebra::Const<AR>,
|
||||
nalgebra::Const<AC>,
|
||||
nalgebra::ArrayStorage<Self::T, AR, AC>,
|
||||
>
|
||||
where
|
||||
Self::R: Corr<AR>,
|
||||
Self::C: Corr<AC>;
|
||||
}
|
||||
|
||||
impl<T, R: Conv, C: Conv> GenericMatrixExt for GenericMatrix<T, R, C> {
|
||||
type T = T;
|
||||
|
||||
type R = R;
|
||||
|
||||
type C = C;
|
||||
|
||||
fn conv<NewR: Conv + Comp<Self::R>, NewC: Conv + Comp<Self::C>>(
|
||||
self,
|
||||
) -> GenericMatrix<Self::T, NewR, NewC> {
|
||||
let data = self.data.0;
|
||||
let mapped_cols = data.map(NewR::bwd);
|
||||
let full_mapped = NewC::bwd(mapped_cols);
|
||||
GenericMatrix::from_data(GenericArrayStorage(full_mapped))
|
||||
}
|
||||
|
||||
fn from_regular(
|
||||
regular: Matrix<T, AlgNum<R>, AlgNum<C>, nalgebra::ArrayStorage<T, R, C>>,
|
||||
) -> Self {
|
||||
Self::from_data(regular.data.0.into())
|
||||
}
|
||||
|
||||
fn into_array(self) -> [[T; R]; C] {
|
||||
self.data.into()
|
||||
}
|
||||
|
||||
fn into_regular(self) -> Matrix<T, AlgNum<R>, AlgNum<C>, nalgebra::ArrayStorage<T, R, C>> {
|
||||
let array_storage: nalgebra::ArrayStorage<T, R, C> = ArrayStorage(self.data.into());
|
||||
Matrix::from_data(array_storage)
|
||||
fn into_array_matrix<const AR: usize, const AC: usize>(
|
||||
self,
|
||||
) -> Matrix<
|
||||
Self::T,
|
||||
nalgebra::Const<AR>,
|
||||
nalgebra::Const<AC>,
|
||||
nalgebra::ArrayStorage<Self::T, AR, AC>,
|
||||
>
|
||||
where
|
||||
Self::R: Corr<AR>,
|
||||
Self::C: Corr<AC>,
|
||||
{
|
||||
let data = self.data;
|
||||
let array: [[Self::T; AR]; AC] = data.into();
|
||||
Matrix::from_data(ArrayStorage(array))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
90
src/tests.rs
90
src/tests.rs
|
|
@ -1,6 +1,4 @@
|
|||
use nalgebra::{ArrayStorage, Matrix};
|
||||
|
||||
use super::{GenericMatrix, GenericMatrixExt};
|
||||
use crate::{GenericMatrix, GenericMatrixExt, GenericMatrixFromExt};
|
||||
|
||||
#[test]
|
||||
fn from_regular() {
|
||||
|
|
@ -10,7 +8,7 @@ fn from_regular() {
|
|||
];
|
||||
|
||||
let generic_matrix: GenericMatrix<i32, nalgebra::U2, nalgebra::U3> =
|
||||
GenericMatrix::from_regular(regular_matrix);
|
||||
regular_matrix.into_generic_matrix();
|
||||
|
||||
for i in 0..2 {
|
||||
for j in 0..3 {
|
||||
|
|
@ -27,10 +25,9 @@ fn from_regular() {
|
|||
#[test]
|
||||
fn into_regular() {
|
||||
let generic_matrix: GenericMatrix<i32, nalgebra::U3, nalgebra::U2> =
|
||||
GenericMatrix::from_array([[1, 2, 5], [3, -4, 0]]);
|
||||
[[1, 2, 5], [3, -4, 0]].into_generic_matrix();
|
||||
|
||||
let regular_matrix: Matrix<i32, nalgebra::U3, nalgebra::U2, ArrayStorage<i32, 3, 2>> =
|
||||
generic_matrix.into_regular();
|
||||
let regular_matrix = generic_matrix.into_array_matrix::<3, 2>();
|
||||
|
||||
for i in 0..3 {
|
||||
for j in 0..2 {
|
||||
|
|
@ -46,10 +43,11 @@ fn into_regular() {
|
|||
|
||||
#[test]
|
||||
fn transposition() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, nalgebra::U2, nalgebra::U3> = nalgebra::matrix![
|
||||
1, 2, 4;
|
||||
5, -7, 0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
let a_tr = a.transpose();
|
||||
|
||||
|
|
@ -62,20 +60,22 @@ fn transposition() {
|
|||
|
||||
#[test]
|
||||
fn to_owned() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, typenum::Const<2>, typenum::Const<3>> = nalgebra::matrix![
|
||||
1, 2, 4;
|
||||
5, -7, 0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
assert_eq!(a.clone_owned(), nalgebra::matrix![1, 2, 4; 5, -7, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, typenum::U2, typenum::U3> = nalgebra::matrix![
|
||||
1, 2, 4;
|
||||
5, -7, 0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
let sum = a + a;
|
||||
|
||||
|
|
@ -84,10 +84,11 @@ fn addition() {
|
|||
|
||||
#[test]
|
||||
fn subtraction() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, nalgebra::Const<2>, nalgebra::Const<3>> = nalgebra::matrix![
|
||||
1, 2, 4;
|
||||
5, -7, 0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
let sum = a - a;
|
||||
|
||||
|
|
@ -96,10 +97,11 @@ fn subtraction() {
|
|||
|
||||
#[test]
|
||||
fn multiplication() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, nalgebra::Const<2>, nalgebra::Const<3>> = nalgebra::matrix![
|
||||
1, 2, 4;
|
||||
5, -7, 0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
let sum = a * a.transpose();
|
||||
|
||||
|
|
@ -109,10 +111,11 @@ fn multiplication() {
|
|||
#[allow(clippy::float_cmp)]
|
||||
#[test]
|
||||
fn determinant() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
let a: GenericMatrix<_, typenum::U2, typenum::U2> = nalgebra::matrix![
|
||||
1.0, 3.0;
|
||||
-4.0, 4.0;
|
||||
]);
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
assert_eq!(a.determinant(), 16.0);
|
||||
}
|
||||
|
|
@ -120,13 +123,52 @@ fn determinant() {
|
|||
#[allow(clippy::float_cmp)]
|
||||
#[test]
|
||||
fn inversion() {
|
||||
let a = GenericMatrix::from_regular(nalgebra::matrix![
|
||||
1.0, 2.0;
|
||||
5.0, -7.0;
|
||||
]);
|
||||
let a: GenericMatrix<_, typenum::U2, typenum::U2> = nalgebra::matrix![
|
||||
1.0, 3.0;
|
||||
-4.0, 4.0;
|
||||
]
|
||||
.into_generic_matrix();
|
||||
|
||||
assert_eq!(
|
||||
a.try_inverse().expect("Should be able to inverse"),
|
||||
nalgebra::matrix![-7.0, -5.0; -2.0, 1.0].transpose() / -17.0
|
||||
nalgebra::matrix![4.0, 4.0; -3.0, 1.0].transpose() / 16.0
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(
|
||||
unused_variables,
|
||||
unused_assignments,
|
||||
reason = "Test is the compilation"
|
||||
)]
|
||||
#[test]
|
||||
fn comp() {
|
||||
let data = [[1, 2, 3], [-4, 5, -0], [-232, 343, 232_111]];
|
||||
|
||||
let mut typenum_matrix: GenericMatrix<_, typenum::U3, typenum::U3> = data.into_generic_matrix();
|
||||
let mut nalgebra_matrix: GenericMatrix<_, nalgebra::U3, nalgebra::U3> =
|
||||
data.into_generic_matrix();
|
||||
let mut typenum_const_matrix: GenericMatrix<_, typenum::Const<3>, typenum::Const<3>> =
|
||||
data.into_generic_matrix();
|
||||
// nalgebra's const and alias types are identical
|
||||
|
||||
// note, that none of them can be directly assigned to each other:
|
||||
// typenum_matrix = nalgebra_matrix;
|
||||
// typenum_const_matrix = typenum_matrix;
|
||||
// nalgebra_matrix = typenum_const_matrix;
|
||||
// (all of the above lines do fail)
|
||||
|
||||
// but assignments will succeed, once conversion is used:
|
||||
typenum_matrix = nalgebra_matrix.conv();
|
||||
typenum_const_matrix = typenum_matrix.conv();
|
||||
nalgebra_matrix = typenum_const_matrix.conv();
|
||||
|
||||
// of there's matrix of different dimensions,
|
||||
let some_different_matrix: GenericMatrix<_, typenum::U1, typenum::U3> =
|
||||
nalgebra::matrix![1, 2, 3].into_generic_matrix();
|
||||
|
||||
// all of the assignments fail:
|
||||
// typenum_matrix = some_different_matrix;
|
||||
// nalgebra_matrix = some_different_matrix;
|
||||
// typenum_const_matrix = some_different_matrix;
|
||||
// (all of the above lines do fail)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue