diff --git a/src/lib.rs b/src/lib.rs index 2ab74aa..4d3b454 100644 --- a/src/lib.rs +++ b/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 = typenum::Const; -type AlgNum = nalgebra::Const; -type ArrLen = as generic_array::IntoArrayLength>::ArrayLength; +/// Nothing to see here +/// +/// Congratulations, you've found a cute seal pup! +/// +/// cute baby seal! +/// +/// 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}; +/// 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; +/// type TypenumMatrix = GenericMatrix; +/// type TypenumConstMatrix = GenericMatrix, 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 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 Conv for nalgebra::Const +where + typenum::Const: generic_array::IntoArrayLength, +{ + const SEAL: Seal = Seal(()); + + const NUM: usize = N; + + type Nalg = nalgebra::Const; + + type ArrLen = as generic_array::IntoArrayLength>::ArrayLength; + + fn new_nalg() -> Self::Nalg { + nalgebra::Const:: + } +} + +impl Conv for typenum::Const +where + typenum::Const: generic_array::IntoArrayLength, +{ + const SEAL: Seal = Seal(()); + + const NUM: usize = N; + + type Nalg = nalgebra::Const; + + type ArrLen = as generic_array::IntoArrayLength>::ArrayLength; + + fn new_nalg() -> Self::Nalg { + nalgebra::Const:: + } +} + +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 Conv for typenum::UInt +where + U: Conv, + U::Nalg: nalgebra::dimension::DimMul, + U::ArrLen: core::ops::Mul, + typenum::Prod: ArrayLength, +{ + const SEAL: Seal = Seal(()); + + const NUM: usize = 2 * U::NUM; + + type Nalg = >::Output; + + type ArrLen = typenum::operator_aliases::Prod; + + fn new_nalg() -> Self::Nalg { + Self::Nalg::from_usize(Self::NUM) + } +} + +impl Conv for typenum::UInt +where + typenum::UInt: Conv, + as Conv>::Nalg: nalgebra::dimension::DimAdd, + as Conv>::ArrLen: core::ops::Add, + typenum::Sum< as Conv>::ArrLen, typenum::U1>: ArrayLength, +{ + const SEAL: Seal = Seal(()); + + const NUM: usize = 1 + as Conv>::NUM; + + type Nalg = + < as Conv>::Nalg as nalgebra::dimension::DimAdd>::Output; + + type ArrLen = + typenum::operator_aliases::Sum< 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: Conv { + /// See [`Seal`] + const SEAL: Seal; + + /// Converts core array to [`GenericArray`] + fn array_to_generic(array: [E; N]) -> GenericArray; + + /// Converts [`GenericArray`] to core array + fn generic_to_array(generic: GenericArray) -> [E; N]; +} + +impl Corr for T +where + T: Conv, + typenum::Const: IntoArrayLength, +{ + const SEAL: Seal = Seal(()); + + #[inline] + fn array_to_generic(array: [E; N]) -> GenericArray { + GenericArray::from_array(array) + } + + #[inline] + fn generic_to_array(generic: GenericArray) -> [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: Conv { + /// See [`Seal`] + const SEAL: Seal; + + /// "forward" conversion + fn fwd(value: GenericArray) -> GenericArray; + + /// "backward" conversion + fn bwd(value: GenericArray) -> GenericArray; +} + +impl Comp for T +where + T: Conv, +{ + const SEAL: Seal = Seal(()); + + #[inline] + fn fwd(value: GenericArray) -> GenericArray::ArrLen> { + value + } + + #[inline] + fn bwd(value: GenericArray::ArrLen>) -> GenericArray { + value + } +} + +use generic_array::{functional::FunctionalSequence, ArrayLength, GenericArray, IntoArrayLength}; use nalgebra::{ - allocator::Allocator, ArrayStorage, Dim, IsContiguous, Matrix, Owned, RawStorage, ToTypenum, + 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( + pub GenericArray, C::ArrLen>, +); -/// A stack-allocated storage, of [`typenum`]-backed row-major two dimensional array -#[derive(Debug, Clone)] -pub struct GenericArrayStorage(GenericArray, C>) +impl Clone for GenericArrayStorage where - R: ArrayLength, - C: ArrayLength; + T: Clone, + GenericArray, C::ArrLen>: Clone, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} -impl Copy for GenericArrayStorage +impl Copy for GenericArrayStorage where T: Copy, - R: ArrayLength, - R::ArrayType: Copy, - C: ArrayLength, - C::ArrayType>: Copy, + ::ArrayType: Copy, + ::ArrayType>: Copy, { } +impl AsRef<[T]> for GenericArrayStorage { + fn as_ref(&self) -> &[T] { + GenericArray::slice_from_chunks(&self.0) + } +} + +impl AsMut<[T]> for GenericArrayStorage { + 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 RawStorage, AlgNum> - for GenericArrayStorage, ArrLen> -where - TNum: IntoArrayLength, - TNum: IntoArrayLength, - AlgNum: Dim, - AlgNum: Dim, -{ - type RStride = AlgNum<1>; +unsafe impl RawStorage for GenericArrayStorage { + type RStride = nalgebra::U1; - type CStride = AlgNum; + 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::::dangling().as_ptr() } else { - core::ptr::NonNull::::dangling().as_ptr().cast_const() + self.0.as_ptr().cast() } } - fn shape(&self) -> (nalgebra::Const, nalgebra::Const) { - (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( - , nalgebra::Const>>::ptr(self), - R * C, - ) + self.as_ref() } } #[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")] -unsafe impl - nalgebra::RawStorageMut, nalgebra::Const> - for GenericArrayStorage, ArrLen> -where - typenum::Const: IntoArrayLength, - typenum::Const: IntoArrayLength, - nalgebra::Const: Dim, - nalgebra::Const: Dim, +unsafe impl RawStorageMut + for GenericArrayStorage { fn ptr_mut(&mut self) -> *mut T { - self.0 - .first_mut() - .map_or(core::ptr::NonNull::::dangling().as_ptr(), |first| { - first.as_mut_ptr() - }) + if self.0.is_empty() { + core::ptr::NonNull::::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( - , nalgebra::Const>>::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 - nalgebra::Storage, nalgebra::Const> - for GenericArrayStorage, ArrLen> +unsafe impl Storage + for GenericArrayStorage where - T: Clone, - typenum::Const: IntoArrayLength, - typenum::Const: IntoArrayLength, - nalgebra::Const: Dim, - nalgebra::Const: Dim, + nalgebra::DefaultAllocator: nalgebra::allocator::Allocator, { - fn into_owned(self) -> Owned, nalgebra::Const> + fn into_owned(self) -> Owned where - nalgebra::DefaultAllocator: - nalgebra::allocator::Allocator, nalgebra::Const>, + nalgebra::DefaultAllocator: nalgebra::allocator::Allocator, { - let init: [[T; R]; C] = self.0.into_array().map(GenericArray::into_array); nalgebra::DefaultAllocator::allocate_from_iterator( - nalgebra::Const::, - nalgebra::Const::, - init.into_iter().flatten(), + R::new_nalg(), + C::new_nalg(), + self.0.into_iter().flatten(), ) } - fn clone_owned(&self) -> Owned, nalgebra::Const> + fn clone_owned(&self) -> Owned where - nalgebra::DefaultAllocator: - nalgebra::allocator::Allocator, nalgebra::Const>, + nalgebra::DefaultAllocator: nalgebra::allocator::Allocator, { self.clone().into_owned() } @@ -139,131 +322,127 @@ where } #[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")] -unsafe impl IsContiguous - for GenericArrayStorage +unsafe impl IsContiguous for GenericArrayStorage {} + +/// Alias to [`nalgebra::Matrix`], completely "hiding" `const usize`s away. See crate's documentation on how this is possible. +pub type GenericMatrix = + nalgebra::Matrix::Nalg, ::Nalg, GenericArrayStorage>; + +impl, C: Conv + Corr> + From<[[T; AR]; AC]> for GenericArrayStorage { + fn from(value: [[T; AR]; AC]) -> Self { + GenericArrayStorage(C::array_to_generic(value.map(R::array_to_generic))) + } } -/// Alias to [`nalgebra::Matrix`], completely "hinding" `const usize`s away. See crate's documentation on how this is possible. -pub type GenericMatrix = nalgebra::Matrix< - T, - R, - C, - GenericArrayStorage< - T, - <::Typenum as IntoArrayLength>::ArrayLength, - <::Typenum as IntoArrayLength>::ArrayLength, - >, ->; - -impl From<[[T; R]; C]> - for GenericArrayStorage< - T, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - > -where - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, +impl, C: Conv + Corr> + From> for [[T; AR]; AC] { - fn from(value: [[T; R]; C]) -> Self { - GenericArrayStorage(GenericArray::from_array( - value.map(GenericArray::from_array), - )) + fn from(value: GenericArrayStorage) -> Self { + C::generic_to_array(value.0).map(R::generic_to_array) } } -impl - From< - GenericArrayStorage< - T, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - >, - > for [[T; R]; C] -where - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, +/// [`GenericMatrix`]-conversion trait intended for core arrays and regular [`nalgebra`] matrices +pub trait GenericMatrixFromExt { + /// 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 into_generic_matrix(self) -> GenericMatrix; +} + +impl, C: Conv + Corr> + GenericMatrixFromExt for [[T; AR]; AC] { - fn from( - value: GenericArrayStorage< - T, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - < as ToTypenum>::Typenum as IntoArrayLength>::ArrayLength, - >, - ) -> Self { - value.0.into_array().map(GenericArray::into_array) + type T = T; + + fn into_generic_matrix(self) -> GenericMatrix { + GenericMatrix::from_data(self.into()) } } -/// Extension trait, providing convenience operations conversion operations so easier intertop with rest of the ecosystem. -pub trait GenericMatrixExt -where - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, +// Yes, I AM sorry for the `T: Clone` here. +impl + IsContiguous> + GenericMatrixFromExt for Matrix { - /// Creates [`GenericMatrix`] from core Rust array. - fn from_array(array: [[T; R]; C]) -> Self; + type T = T; - /// Creates [`GenericMatrix`] from regular `nalgebra` matrix, backed up by [`nalgebra::ArrayStorage`]. - fn from_regular( - regular: Matrix, AlgNum, nalgebra::ArrayStorage>, - ) -> Self; + fn into_generic_matrix(self) -> GenericMatrix { + 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; - /// Converts [`GenericMatrix`] into core Rust array. - fn into_array(self) -> [[T; R]; C]; + /// Type defining column count + type C: Conv; - /// Converts [`GenericMatrix`] into regular `nalgebra` matrix, backed up by [`nalgebra::ArrayStorage`]. - fn into_regular(self) -> Matrix, AlgNum, nalgebra::ArrayStorage>; + /// Changes type of [`GenericMatrix`] to a different row and column count descriptors. + fn conv, NewC: Conv + Comp>( + self, + ) -> GenericMatrix; + + /// Converts [`GenericMatrix`] into core array-backed regular [`nalgebra`] matrix + fn into_array_matrix( + self, + ) -> Matrix< + Self::T, + nalgebra::Const, + nalgebra::Const, + nalgebra::ArrayStorage, + > + where + Self::R: Corr, + Self::C: Corr; } -impl GenericMatrixExt - for GenericMatrix, AlgNum> -where - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, - AlgNum: ToTypenum, - TNum: IntoArrayLength, - as ToTypenum>::Typenum: - IntoArrayLength as IntoArrayLength>::ArrayLength>, -{ - fn from_array(array: [[T; R]; C]) -> Self { - Self::from_data(array.into()) - } +impl GenericMatrixExt for GenericMatrix { + type T = T; - fn from_regular( - regular: Matrix, AlgNum, nalgebra::ArrayStorage>, - ) -> Self { - Self::from_data(regular.data.0.into()) - } + type R = R; + + type C = C; - fn into_array(self) -> [[T; R]; C] { - self.data.into() + fn conv, NewC: Conv + Comp>( + self, + ) -> GenericMatrix { + 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 into_regular(self) -> Matrix, AlgNum, nalgebra::ArrayStorage> { - let array_storage: nalgebra::ArrayStorage = ArrayStorage(self.data.into()); - Matrix::from_data(array_storage) + fn into_array_matrix( + self, + ) -> Matrix< + Self::T, + nalgebra::Const, + nalgebra::Const, + nalgebra::ArrayStorage, + > + where + Self::R: Corr, + Self::C: Corr, + { + let data = self.data; + let array: [[Self::T; AR]; AC] = data.into(); + Matrix::from_data(ArrayStorage(array)) } } diff --git a/src/tests.rs b/src/tests.rs index 12512f2..aedcaa0 100644 --- a/src/tests.rs +++ b/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 = - 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 = - GenericMatrix::from_array([[1, 2, 5], [3, -4, 0]]); + [[1, 2, 5], [3, -4, 0]].into_generic_matrix(); - let regular_matrix: Matrix> = - 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) +}