nalgebra-macros-0.2.2/Cargo.toml0000644000000022640000000000100120740ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "nalgebra-macros" version = "0.2.2" authors = [ "Andreas Longva", "Sébastien Crozet ", ] description = "Procedural macros for nalgebra" homepage = "https://nalgebra.org" documentation = "https://www.nalgebra.org/docs" readme = "README.md" keywords = [ "linear", "algebra", "matrix", "vector", "math", ] categories = [ "science", "mathematics", ] license = "Apache-2.0" repository = "https://github.com/dimforge/nalgebra" resolver = "2" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["full"] [dev-dependencies.nalgebra] version = "0.32" nalgebra-macros-0.2.2/Cargo.toml.orig000064400000000000000000000012520072674642500156010ustar 00000000000000[package] name = "nalgebra-macros" version = "0.2.2" authors = ["Andreas Longva", "Sébastien Crozet "] edition = "2018" description = "Procedural macros for nalgebra" documentation = "https://www.nalgebra.org/docs" homepage = "https://nalgebra.org" repository = "https://github.com/dimforge/nalgebra" readme = "../README.md" categories = ["science", "mathematics"] keywords = ["linear", "algebra", "matrix", "vector", "math"] license = "Apache-2.0" [lib] proc-macro = true [dependencies] syn = { version = "2.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] nalgebra = { version = "0.32", path = ".." } nalgebra-macros-0.2.2/LICENSE000064400000000000000000000000120072674642500137100ustar 00000000000000../LICENSEnalgebra-macros-0.2.2/README.md000064400000000000000000000016020072674642500141700ustar 00000000000000

crates.io

crates.io

Users guide | Documentation

-----

Linear algebra library for the Rust programming language.

----- nalgebra-macros-0.2.2/src/lib.rs000064400000000000000000000214630072674642500146230ustar 00000000000000//! Macros for `nalgebra`. //! //! This crate is not intended for direct consumption. Instead, the macros are re-exported by //! `nalgebra` if the `macros` feature is enabled (enabled by default). #![deny( nonstandard_style, unused, missing_docs, rust_2018_idioms, rust_2018_compatibility, future_incompatible, missing_copy_implementations, missing_debug_implementations, clippy::all )] mod matrix_vector_impl; mod stack_impl; use matrix_vector_impl::{Matrix, Vector}; use crate::matrix_vector_impl::{dmatrix_impl, dvector_impl, matrix_impl, vector_impl}; use proc_macro::TokenStream; use quote::quote; use stack_impl::stack_impl; use syn::parse_macro_input; /// Construct a fixed-size matrix directly from data. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// This macro facilitates easy construction of matrices when the entries of the matrix are known /// (either as constants or expressions). This macro produces an instance of `SMatrix`. This means /// that the data of the matrix is stored on the stack, and its dimensions are fixed at /// compile-time. If you want to construct a dynamic matrix, use [`dmatrix!`] instead. /// /// `matrix!` is intended to be both the simplest and most efficient way to construct (small) /// matrices, and can also be used in *const fn* contexts. /// /// The syntax is MATLAB-like. Column elements are separated by a comma (`,`), and a semi-colon /// (`;`) designates that a new row begins. /// /// # Examples /// ``` /// use nalgebra::matrix; /// /// // Produces a Matrix3<_> == SMatrix<_, 3, 3> /// let a = matrix![1, 2, 3; /// 4, 5, 6; /// 7, 8, 9]; /// ``` /// /// You can construct matrices with arbitrary expressions for its elements: /// /// ``` /// use nalgebra::{matrix, Matrix2}; /// let theta = 0.45f64; /// /// let r = matrix![theta.cos(), - theta.sin(); /// theta.sin(), theta.cos()]; /// ``` #[proc_macro] pub fn matrix(stream: TokenStream) -> TokenStream { matrix_impl(stream) } /// Construct a dynamic matrix directly from data. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// The syntax is exactly the same as for [`matrix!`], but instead of producing instances of /// `SMatrix`, it produces instances of `DMatrix`. At the moment it is not usable /// in `const fn` contexts. /// /// # Example /// ``` /// use nalgebra::dmatrix; /// /// // Produces a DMatrix<_> /// let a = dmatrix![1, 2, 3; /// 4, 5, 6; /// 7, 8, 9]; /// ``` #[proc_macro] pub fn dmatrix(stream: TokenStream) -> TokenStream { dmatrix_impl(stream) } /// Construct a fixed-size column vector directly from data. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// Similarly to [`matrix!`], this macro facilitates easy construction of fixed-size vectors. /// However, whereas the [`matrix!`] macro expects each row to be separated by a semi-colon, /// the syntax of this macro is instead similar to `vec!`, in that the elements of the vector /// are simply listed consecutively. /// /// `vector!` is intended to be the most readable and performant way of constructing small, /// fixed-size vectors, and it is usable in `const fn` contexts. /// /// # Example /// ``` /// use nalgebra::vector; /// /// // Produces a Vector3<_> == SVector<_, 3> /// let v = vector![1, 2, 3]; /// ``` #[proc_macro] pub fn vector(stream: TokenStream) -> TokenStream { vector_impl(stream) } /// Construct a dynamic column vector directly from data. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// The syntax is exactly the same as for [`vector!`], but instead of producing instances of /// `SVector`, it produces instances of `DVector`. At the moment it is not usable /// in `const fn` contexts. /// /// # Example /// ``` /// use nalgebra::dvector; /// /// // Produces a DVector<_> /// let v = dvector![1, 2, 3]; /// ``` #[proc_macro] pub fn dvector(stream: TokenStream) -> TokenStream { dvector_impl(stream) } /// Construct a fixed-size point directly from data. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// Similarly to [`vector!`], this macro facilitates easy construction of points. /// /// `point!` is intended to be the most readable and performant way of constructing small, /// points, and it is usable in `const fn` contexts. /// /// # Example /// ``` /// use nalgebra::point; /// /// // Produces a Point3<_> /// let v = point![1, 2, 3]; /// ``` #[proc_macro] pub fn point(stream: TokenStream) -> TokenStream { let vector = parse_macro_input!(stream as Vector); let len = vector.len(); let array_tokens = vector.to_array_tokens(); let output = quote! { nalgebra::Point::<_, #len> { coords: nalgebra::SVector::<_, #len> ::from_array_storage(nalgebra::ArrayStorage([#array_tokens])) } }; proc_macro::TokenStream::from(output) } /// Construct a new matrix by stacking matrices in a block matrix. /// /// **Note: Requires the `macros` feature to be enabled (enabled by default)**. /// /// This macro facilitates the construction of /// [block matrices](https://en.wikipedia.org/wiki/Block_matrix) /// by stacking blocks (matrices) using the same MATLAB-like syntax as the [`matrix!`] and /// [`dmatrix!`] macros: /// /// ```rust /// # use nalgebra::stack; /// # /// # fn main() { /// # let [a, b, c, d] = std::array::from_fn(|_| nalgebra::Matrix1::new(0)); /// // a, b, c and d are matrices /// let block_matrix = stack![ a, b; /// c, d ]; /// # } /// ``` /// /// The resulting matrix is stack-allocated if the dimension of each block row and column /// can be determined at compile-time, otherwise it is heap-allocated. /// This is the case if, for every row, there is at least one matrix with a fixed number of rows, /// and, for every column, there is at least one matrix with a fixed number of columns. /// /// [`stack!`] also supports special syntax to indicate zero blocks in a matrix: /// /// ```rust /// # use nalgebra::stack; /// # /// # fn main() { /// # let [a, b, c, d] = std::array::from_fn(|_| nalgebra::Matrix1::new(0)); /// // a and d are matrices /// let block_matrix = stack![ a, 0; /// 0, d ]; /// # } /// ``` /// Here, the `0` literal indicates a zero matrix of implicitly defined size. /// In order to infer the size of the zero blocks, there must be at least one matrix /// in every row and column of the matrix. /// In other words, no row or column can consist entirely of implicit zero blocks. /// /// # Panics /// /// Panics if dimensions are inconsistent and it cannot be determined at compile-time. /// /// # Examples /// /// ``` /// use nalgebra::{matrix, SMatrix, stack}; /// /// let a = matrix![1, 2; /// 3, 4]; /// let b = matrix![5, 6; /// 7, 8]; /// let c = matrix![9, 10]; /// /// let block_matrix = stack![ a, b; /// c, 0 ]; /// /// assert_eq!(block_matrix, matrix![1, 2, 5, 6; /// 3, 4, 7, 8; /// 9, 10, 0, 0]); /// /// // Verify that the resulting block matrix is stack-allocated /// let _: SMatrix<_, 3, 4> = block_matrix; /// ``` /// /// The example above shows how stacking stack-allocated matrices results in a stack-allocated /// block matrix. If all row and column dimensions can not be determined at compile-time, /// the result is instead a dynamically allocated matrix: /// /// ``` /// use nalgebra::{dmatrix, DMatrix, Dyn, matrix, OMatrix, SMatrix, stack, U3}; /// /// # let a = matrix![1, 2; 3, 4]; let c = matrix![9, 10]; /// // a and c as before, but b is a dynamic matrix this time /// let b = dmatrix![5, 6; /// 7, 8]; /// /// // In this case, the number of rows can be statically inferred to be 3 (U3), /// // but the number of columns cannot, hence it is dynamic /// let block_matrix: OMatrix<_, U3, Dyn> = stack![ a, b; /// c, 0 ]; /// /// // If necessary, a fully dynamic matrix (DMatrix) can be obtained by reshaping /// let dyn_block_matrix: DMatrix<_> = block_matrix.reshape_generic(Dyn(3), Dyn(4)); /// ``` /// Note that explicitly annotating the types of `block_matrix` and `dyn_block_matrix` is /// only made for illustrative purposes, and is not generally necessary. /// #[proc_macro] pub fn stack(stream: TokenStream) -> TokenStream { let matrix = parse_macro_input!(stream as Matrix); proc_macro::TokenStream::from(stack_impl(matrix).unwrap_or_else(syn::Error::into_compile_error)) } nalgebra-macros-0.2.2/src/matrix_vector_impl.rs000064400000000000000000000143630072674642500177650ustar 00000000000000use proc_macro::TokenStream; use quote::{quote, ToTokens, TokenStreamExt}; use std::ops::Index; use syn::parse::{Error, Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::Expr; use syn::{parse_macro_input, Token}; use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree}; use proc_macro2::{Group, Punct}; /// A matrix of expressions pub struct Matrix { // Represent the matrix data in row-major format data: Vec, nrows: usize, ncols: usize, } impl Index<(usize, usize)> for Matrix { type Output = Expr; fn index(&self, (row, col): (usize, usize)) -> &Self::Output { let linear_idx = self.ncols * row + col; &self.data[linear_idx] } } impl Matrix { pub fn nrows(&self) -> usize { self.nrows } pub fn ncols(&self) -> usize { self.ncols } /// Produces a stream of tokens representing this matrix as a column-major nested array. pub fn to_col_major_nested_array_tokens(&self) -> TokenStream2 { let mut result = TokenStream2::new(); for j in 0..self.ncols() { let mut col = TokenStream2::new(); let col_iter = (0..self.nrows()).map(|i| &self[(i, j)]); col.append_separated(col_iter, Punct::new(',', Spacing::Alone)); result.append(Group::new(Delimiter::Bracket, col)); result.append(Punct::new(',', Spacing::Alone)); } TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, result))) } /// Produces a stream of tokens representing this matrix as a column-major flat array /// (suitable for representing e.g. a `DMatrix`). pub fn to_col_major_flat_array_tokens(&self) -> TokenStream2 { let mut data = TokenStream2::new(); for j in 0..self.ncols() { for i in 0..self.nrows() { self[(i, j)].to_tokens(&mut data); data.append(Punct::new(',', Spacing::Alone)); } } TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data))) } } type MatrixRowSyntax = Punctuated; impl Parse for Matrix { fn parse(input: ParseStream<'_>) -> syn::Result { let mut data = Vec::new(); let mut ncols = None; let mut nrows = 0; while !input.is_empty() { let row = MatrixRowSyntax::parse_separated_nonempty(input)?; let row_span = row.span(); if let Some(ncols) = ncols { if row.len() != ncols { let error_msg = format!( "Unexpected number of entries in row {}. Expected {}, found {} entries.", nrows, ncols, row.len() ); return Err(Error::new(row_span, error_msg)); } } else { ncols = Some(row.len()); } data.extend(row.into_iter()); nrows += 1; // We've just read a row, so if there are more tokens, there must be a semi-colon, // otherwise the input is malformed if !input.is_empty() { input.parse::()?; } } Ok(Self { data, nrows, ncols: ncols.unwrap_or(0), }) } } pub struct Vector { elements: Vec, } impl Vector { pub fn to_array_tokens(&self) -> TokenStream2 { let mut data = TokenStream2::new(); data.append_separated(&self.elements, Punct::new(',', Spacing::Alone)); TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data))) } pub fn len(&self) -> usize { self.elements.len() } } impl Parse for Vector { fn parse(input: ParseStream<'_>) -> syn::Result { // The syntax of a vector is just the syntax of a single matrix row if input.is_empty() { Ok(Self { elements: Vec::new(), }) } else { let elements = MatrixRowSyntax::parse_terminated(input)? .into_iter() .collect(); Ok(Self { elements }) } } } pub fn matrix_impl(stream: TokenStream) -> TokenStream { let matrix = parse_macro_input!(stream as Matrix); let row_dim = matrix.nrows(); let col_dim = matrix.ncols(); let array_tokens = matrix.to_col_major_nested_array_tokens(); // TODO: Use quote_spanned instead?? let output = quote! { nalgebra::SMatrix::<_, #row_dim, #col_dim> ::from_array_storage(nalgebra::ArrayStorage(#array_tokens)) }; proc_macro::TokenStream::from(output) } pub fn dmatrix_impl(stream: TokenStream) -> TokenStream { let matrix = parse_macro_input!(stream as Matrix); let row_dim = matrix.nrows(); let col_dim = matrix.ncols(); let array_tokens = matrix.to_col_major_flat_array_tokens(); // TODO: Use quote_spanned instead?? let output = quote! { nalgebra::DMatrix::<_> ::from_vec_storage(nalgebra::VecStorage::new( nalgebra::Dyn(#row_dim), nalgebra::Dyn(#col_dim), vec!#array_tokens)) }; proc_macro::TokenStream::from(output) } pub fn vector_impl(stream: TokenStream) -> TokenStream { let vector = parse_macro_input!(stream as Vector); let len = vector.len(); let array_tokens = vector.to_array_tokens(); let output = quote! { nalgebra::SVector::<_, #len> ::from_array_storage(nalgebra::ArrayStorage([#array_tokens])) }; proc_macro::TokenStream::from(output) } pub fn dvector_impl(stream: TokenStream) -> TokenStream { let vector = parse_macro_input!(stream as Vector); let len = vector.len(); let array_tokens = vector.to_array_tokens(); let output = quote! { nalgebra::DVector::<_> ::from_vec_storage(nalgebra::VecStorage::new( nalgebra::Dyn(#len), nalgebra::Const::<1>, vec!#array_tokens)) }; proc_macro::TokenStream::from(output) } nalgebra-macros-0.2.2/src/stack_impl.rs000064400000000000000000000355640072674642500162120ustar 00000000000000use crate::Matrix; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, quote_spanned}; use syn::spanned::Spanned; use syn::{Error, Expr, Lit}; #[allow(clippy::too_many_lines)] pub fn stack_impl(matrix: Matrix) -> syn::Result { // The prefix is used to construct variable names // that are extremely unlikely to collide with variable names used in e.g. expressions // by the user. Although we could use a long, pseudo-random string, this makes the generated // code very painful to parse, so we settle for something more semantic that is still // very unlikely to collide let prefix = "___na"; let n_block_rows = matrix.nrows(); let n_block_cols = matrix.ncols(); let mut output = quote! {}; // First assign data and shape for each matrix entry to variables // (this is important so that we, for example, don't evaluate an expression more than once) for i in 0..n_block_rows { for j in 0..n_block_cols { let expr = &matrix[(i, j)]; if !is_literal_zero(expr) { let ident_block = format_ident!("{prefix}_stack_{i}_{j}_block"); let ident_shape = format_ident!("{prefix}_stack_{i}_{j}_shape"); output.extend(std::iter::once(quote_spanned! {expr.span()=> let ref #ident_block = #expr; let #ident_shape = #ident_block.shape_generic(); })); } } } // Determine the number of rows (dimension) in each block row, // and write out variables that define block row dimensions and offsets into the // output matrix for i in 0..n_block_rows { // The dimension of the block row is the result of trying to unify the row shape of // all blocks in the block row let dim = (0 ..n_block_cols) .filter_map(|j| { let expr = &matrix[(i, j)]; if !is_literal_zero(expr) { let mut ident_shape = format_ident!("{prefix}_stack_{i}_{j}_shape"); ident_shape.set_span(ident_shape.span().located_at(expr.span())); Some(quote_spanned!{expr.span()=> #ident_shape.0 }) } else { None } }).reduce(|a, b| { let expect_msg = format!("All blocks in block row {i} must have the same number of rows"); quote_spanned!{b.span()=> >::representative(#a, #b) .expect(#expect_msg) } }).ok_or(Error::new(Span::call_site(), format!("Block row {i} cannot consist entirely of implicit zero blocks.")))?; let dim_ident = format_ident!("{prefix}_stack_row_{i}_dim"); let offset_ident = format_ident!("{prefix}_stack_row_{i}_offset"); let offset = if i == 0 { quote! { 0 } } else { let prev_offset_ident = format_ident!("{prefix}_stack_row_{}_offset", i - 1); let prev_dim_ident = format_ident!("{prefix}_stack_row_{}_dim", i - 1); quote! { #prev_offset_ident + <_ as nalgebra::Dim>::value(&#prev_dim_ident) } }; output.extend(std::iter::once(quote! { let #dim_ident = #dim; let #offset_ident = #offset; })); } // Do the same thing for the block columns for j in 0..n_block_cols { let dim = (0 ..n_block_rows) .filter_map(|i| { let expr = &matrix[(i, j)]; if !is_literal_zero(expr) { let mut ident_shape = format_ident!("{prefix}_stack_{i}_{j}_shape"); ident_shape.set_span(ident_shape.span().located_at(expr.span())); Some(quote_spanned!{expr.span()=> #ident_shape.1 }) } else { None } }).reduce(|a, b| { let expect_msg = format!("All blocks in block column {j} must have the same number of columns"); quote_spanned!{b.span()=> >::representative(#a, #b) .expect(#expect_msg) } }).ok_or(Error::new(Span::call_site(), format!("Block column {j} cannot consist entirely of implicit zero blocks.")))?; let dim_ident = format_ident!("{prefix}_stack_col_{j}_dim"); let offset_ident = format_ident!("{prefix}_stack_col_{j}_offset"); let offset = if j == 0 { quote! { 0 } } else { let prev_offset_ident = format_ident!("{prefix}_stack_col_{}_offset", j - 1); let prev_dim_ident = format_ident!("{prefix}_stack_col_{}_dim", j - 1); quote! { #prev_offset_ident + <_ as nalgebra::Dim>::value(&#prev_dim_ident) } }; output.extend(std::iter::once(quote! { let #dim_ident = #dim; let #offset_ident = #offset; })); } // Determine number of rows and cols in output matrix, // by adding together dimensions of all block rows/cols let num_rows = (0..n_block_rows) .map(|i| { let ident = format_ident!("{prefix}_stack_row_{i}_dim"); quote! { #ident } }) .reduce(|a, b| { quote! { <_ as nalgebra::DimAdd<_>>::add(#a, #b) } }) .unwrap_or(quote! { nalgebra::dimension::U0 }); let num_cols = (0..n_block_cols) .map(|j| { let ident = format_ident!("{prefix}_stack_col_{j}_dim"); quote! { #ident } }) .reduce(|a, b| { quote! { <_ as nalgebra::DimAdd<_>>::add(#a, #b) } }) .unwrap_or(quote! { nalgebra::dimension::U0 }); // It should be possible to use `uninitialized_generic` here instead // however that would mean that the macro needs to generate unsafe code // which does not seem like a great idea. output.extend(std::iter::once(quote! { let mut matrix = nalgebra::Matrix::zeros_generic(#num_rows, #num_cols); })); for i in 0..n_block_rows { for j in 0..n_block_cols { let row_dim = format_ident!("{prefix}_stack_row_{i}_dim"); let col_dim = format_ident!("{prefix}_stack_col_{j}_dim"); let row_offset = format_ident!("{prefix}_stack_row_{i}_offset"); let col_offset = format_ident!("{prefix}_stack_col_{j}_offset"); let expr = &matrix[(i, j)]; if !is_literal_zero(expr) { let expr_ident = format_ident!("{prefix}_stack_{i}_{j}_block"); output.extend(std::iter::once(quote! { let start = (#row_offset, #col_offset); let shape = (#row_dim, #col_dim); let input_view = #expr_ident.generic_view((0, 0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); })); } } } Ok(quote! { { #output matrix } }) } fn is_literal_zero(expr: &Expr) -> bool { matches!(expr, Expr::Lit(syn::ExprLit { lit: Lit::Int(integer_literal), .. }) if integer_literal.base10_digits() == "0") } #[cfg(test)] mod tests { use crate::stack_impl::stack_impl; use crate::Matrix; use quote::quote; #[test] fn stack_simple_generation() { let input: Matrix = syn::parse_quote![ a, 0; 0, b; ]; let result = stack_impl(input).unwrap(); let expected = quote! {{ let ref ___na_stack_0_0_block = a; let ___na_stack_0_0_shape = ___na_stack_0_0_block.shape_generic(); let ref ___na_stack_1_1_block = b; let ___na_stack_1_1_shape = ___na_stack_1_1_block.shape_generic(); let ___na_stack_row_0_dim = ___na_stack_0_0_shape.0; let ___na_stack_row_0_offset = 0; let ___na_stack_row_1_dim = ___na_stack_1_1_shape.0; let ___na_stack_row_1_offset = ___na_stack_row_0_offset + <_ as nalgebra::Dim>::value(&___na_stack_row_0_dim); let ___na_stack_col_0_dim = ___na_stack_0_0_shape.1; let ___na_stack_col_0_offset = 0; let ___na_stack_col_1_dim = ___na_stack_1_1_shape.1; let ___na_stack_col_1_offset = ___na_stack_col_0_offset + <_ as nalgebra::Dim>::value(&___na_stack_col_0_dim); let mut matrix = nalgebra::Matrix::zeros_generic( <_ as nalgebra::DimAdd<_>>::add(___na_stack_row_0_dim, ___na_stack_row_1_dim), <_ as nalgebra::DimAdd<_>>::add(___na_stack_col_0_dim, ___na_stack_col_1_dim) ); let start = (___na_stack_row_0_offset, ___na_stack_col_0_offset); let shape = (___na_stack_row_0_dim, ___na_stack_col_0_dim); let input_view = ___na_stack_0_0_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); let start = (___na_stack_row_1_offset, ___na_stack_col_1_offset); let shape = (___na_stack_row_1_dim, ___na_stack_col_1_dim); let input_view = ___na_stack_1_1_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); matrix }}; assert_eq!(format!("{result}"), format!("{}", expected)); } #[test] fn stack_complex_generation() { let input: Matrix = syn::parse_quote![ a, 0, b; 0, c, d; e, 0, 0; ]; let result = stack_impl(input).unwrap(); let expected = quote! {{ let ref ___na_stack_0_0_block = a; let ___na_stack_0_0_shape = ___na_stack_0_0_block.shape_generic(); let ref ___na_stack_0_2_block = b; let ___na_stack_0_2_shape = ___na_stack_0_2_block.shape_generic(); let ref ___na_stack_1_1_block = c; let ___na_stack_1_1_shape = ___na_stack_1_1_block.shape_generic(); let ref ___na_stack_1_2_block = d; let ___na_stack_1_2_shape = ___na_stack_1_2_block.shape_generic(); let ref ___na_stack_2_0_block = e; let ___na_stack_2_0_shape = ___na_stack_2_0_block.shape_generic(); let ___na_stack_row_0_dim = < nalgebra :: constraint :: ShapeConstraint as nalgebra :: constraint :: SameNumberOfRows < _ , _ >> :: representative (___na_stack_0_0_shape . 0 , ___na_stack_0_2_shape . 0) . expect ("All blocks in block row 0 must have the same number of rows") ; let ___na_stack_row_0_offset = 0; let ___na_stack_row_1_dim = < nalgebra :: constraint :: ShapeConstraint as nalgebra :: constraint :: SameNumberOfRows < _ , _ >> :: representative (___na_stack_1_1_shape . 0 , ___na_stack_1_2_shape . 0) . expect ("All blocks in block row 1 must have the same number of rows") ; let ___na_stack_row_1_offset = ___na_stack_row_0_offset + <_ as nalgebra::Dim>::value(&___na_stack_row_0_dim); let ___na_stack_row_2_dim = ___na_stack_2_0_shape.0; let ___na_stack_row_2_offset = ___na_stack_row_1_offset + <_ as nalgebra::Dim>::value(&___na_stack_row_1_dim); let ___na_stack_col_0_dim = < nalgebra :: constraint :: ShapeConstraint as nalgebra :: constraint :: SameNumberOfColumns < _ , _ >> :: representative (___na_stack_0_0_shape . 1 , ___na_stack_2_0_shape . 1) . expect ("All blocks in block column 0 must have the same number of columns") ; let ___na_stack_col_0_offset = 0; let ___na_stack_col_1_dim = ___na_stack_1_1_shape.1; let ___na_stack_col_1_offset = ___na_stack_col_0_offset + <_ as nalgebra::Dim>::value(&___na_stack_col_0_dim); let ___na_stack_col_2_dim = < nalgebra :: constraint :: ShapeConstraint as nalgebra :: constraint :: SameNumberOfColumns < _ , _ >> :: representative (___na_stack_0_2_shape . 1 , ___na_stack_1_2_shape . 1) . expect ("All blocks in block column 2 must have the same number of columns") ; let ___na_stack_col_2_offset = ___na_stack_col_1_offset + <_ as nalgebra::Dim>::value(&___na_stack_col_1_dim); let mut matrix = nalgebra::Matrix::zeros_generic( <_ as nalgebra::DimAdd<_>>::add( <_ as nalgebra::DimAdd<_>>::add(___na_stack_row_0_dim, ___na_stack_row_1_dim), ___na_stack_row_2_dim ), <_ as nalgebra::DimAdd<_>>::add( <_ as nalgebra::DimAdd<_>>::add(___na_stack_col_0_dim, ___na_stack_col_1_dim), ___na_stack_col_2_dim ) ); let start = (___na_stack_row_0_offset, ___na_stack_col_0_offset); let shape = (___na_stack_row_0_dim, ___na_stack_col_0_dim); let input_view = ___na_stack_0_0_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); let start = (___na_stack_row_0_offset, ___na_stack_col_2_offset); let shape = (___na_stack_row_0_dim, ___na_stack_col_2_dim); let input_view = ___na_stack_0_2_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); let start = (___na_stack_row_1_offset, ___na_stack_col_1_offset); let shape = (___na_stack_row_1_dim, ___na_stack_col_1_dim); let input_view = ___na_stack_1_1_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); let start = (___na_stack_row_1_offset, ___na_stack_col_2_offset); let shape = (___na_stack_row_1_dim, ___na_stack_col_2_dim); let input_view = ___na_stack_1_2_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); let start = (___na_stack_row_2_offset, ___na_stack_col_0_offset); let shape = (___na_stack_row_2_dim, ___na_stack_col_0_dim); let input_view = ___na_stack_2_0_block.generic_view((0,0), shape); let mut output_view = matrix.generic_view_mut(start, shape); output_view.copy_from(&input_view); matrix }}; assert_eq!(format!("{result}"), format!("{}", expected)); } }