commit
23ac85e896
6
.github/workflows/nalgebra-ci-build.yml
vendored
6
.github/workflows/nalgebra-ci-build.yml
vendored
@ -69,6 +69,12 @@ jobs:
|
||||
- name: test nalgebra-sparse (slow tests)
|
||||
# Unfortunately, the "slow-tests" take so much time that we need to run them with --release
|
||||
run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,slow-tests slow
|
||||
test-nalgebra-macros:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: test nalgebra-macros
|
||||
run: cargo test -p nalgebra-macros
|
||||
build-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
# env:
|
||||
|
@ -22,7 +22,7 @@ name = "nalgebra"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
default = [ "std", "macros" ]
|
||||
std = [ "matrixmultiply", "simba/std" ]
|
||||
sparse = [ ]
|
||||
debug = [ "approx/num-complex", "rand" ]
|
||||
@ -32,6 +32,7 @@ compare = [ "matrixcompare-core" ]
|
||||
libm = [ "simba/libm" ]
|
||||
libm-force = [ "simba/libm_force" ]
|
||||
no_unsound_assume_init = [ ]
|
||||
macros = [ "nalgebra-macros" ]
|
||||
|
||||
# Conversion
|
||||
convert-mint = [ "mint" ]
|
||||
@ -60,6 +61,7 @@ proptest-support = [ "proptest" ]
|
||||
slow-tests = []
|
||||
|
||||
[dependencies]
|
||||
nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true }
|
||||
typenum = "1.12"
|
||||
rand-package = { package = "rand", version = "0.8", optional = true, default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
@ -92,7 +94,7 @@ matrixcompare = "0.3.0"
|
||||
itertools = "0.10"
|
||||
|
||||
[workspace]
|
||||
members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse" ]
|
||||
members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros" ]
|
||||
resolver = "2"
|
||||
|
||||
[[example]]
|
||||
@ -109,4 +111,4 @@ lto = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
# Enable certain features when building docs for docs.rs
|
||||
features = [ "proptest-support", "compare" ]
|
||||
features = [ "proptest-support", "compare", "macros" ]
|
||||
|
25
nalgebra-macros/Cargo.toml
Normal file
25
nalgebra-macros/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "nalgebra-macros"
|
||||
version = "0.1.0"
|
||||
authors = [ "Andreas Longva", "Sébastien Crozet <developer@crozet.re>" ]
|
||||
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="1.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nalgebra = { version = "0.26.1", path = ".." }
|
||||
trybuild = "1.0.42"
|
282
nalgebra-macros/src/lib.rs
Normal file
282
nalgebra-macros/src/lib.rs
Normal file
@ -0,0 +1,282 @@
|
||||
//! 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).
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::parse::{Error, Parse, ParseStream, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::Expr;
|
||||
use syn::{parse_macro_input, Token};
|
||||
|
||||
use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree};
|
||||
use proc_macro2::{Group, Punct};
|
||||
|
||||
struct Matrix {
|
||||
// Represent the matrix as a row-major vector of vectors of expressions
|
||||
rows: Vec<Vec<Expr>>,
|
||||
ncols: usize,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
fn nrows(&self) -> usize {
|
||||
self.rows.len()
|
||||
}
|
||||
|
||||
fn ncols(&self) -> usize {
|
||||
self.ncols
|
||||
}
|
||||
|
||||
/// Produces a stream of tokens representing this matrix as a column-major nested array.
|
||||
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(move |i| &self.rows[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`).
|
||||
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.rows[i][j].to_tokens(&mut data);
|
||||
data.append(Punct::new(',', Spacing::Alone));
|
||||
}
|
||||
}
|
||||
TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data)))
|
||||
}
|
||||
}
|
||||
|
||||
type MatrixRowSyntax = Punctuated<Expr, Token![,]>;
|
||||
|
||||
impl Parse for Matrix {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut rows = Vec::new();
|
||||
let mut ncols = None;
|
||||
|
||||
while !input.is_empty() {
|
||||
let row_span = input.span();
|
||||
let row = MatrixRowSyntax::parse_separated_nonempty(input)?;
|
||||
|
||||
if let Some(ncols) = ncols {
|
||||
if row.len() != ncols {
|
||||
let row_idx = rows.len();
|
||||
let error_msg = format!(
|
||||
"Unexpected number of entries in row {}. Expected {}, found {} entries.",
|
||||
row_idx,
|
||||
ncols,
|
||||
row.len()
|
||||
);
|
||||
return Err(Error::new(row_span, error_msg));
|
||||
}
|
||||
} else {
|
||||
ncols = Some(row.len());
|
||||
}
|
||||
rows.push(row.into_iter().collect());
|
||||
|
||||
// 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::<Token![;]>()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
rows,
|
||||
ncols: ncols.unwrap_or(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a fixed-size matrix directly from data.
|
||||
///
|
||||
/// **Note: Requires the `macro` 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Construct a dynamic matrix directly from data.
|
||||
///
|
||||
/// **Note: Requires the `macro` 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.
|
||||
///
|
||||
/// ```
|
||||
/// 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 {
|
||||
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::Dynamic::new(#row_dim),
|
||||
nalgebra::Dynamic::new(#col_dim),
|
||||
vec!#array_tokens))
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(output)
|
||||
}
|
||||
|
||||
struct Vector {
|
||||
elements: Vec<Expr>,
|
||||
}
|
||||
|
||||
impl Vector {
|
||||
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)))
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Vector {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
// 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_separated_nonempty(input)?
|
||||
.into_iter()
|
||||
.collect();
|
||||
Ok(Self { elements })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a fixed-size column vector directly from data.
|
||||
///
|
||||
/// **Note: Requires the `macro` 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.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// use nalgebra::vector;
|
||||
///
|
||||
/// // Produces a Vector3<_> == SVector<_, 3>
|
||||
/// let v = vector![1, 2, 3];
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn vector(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)
|
||||
}
|
||||
|
||||
/// Construct a dynamic column vector directly from data.
|
||||
///
|
||||
/// **Note: Requires the `macro` 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.
|
||||
///
|
||||
/// ```
|
||||
/// use nalgebra::dvector;
|
||||
///
|
||||
/// // Produces a DVector<_>
|
||||
/// let v = dvector![1, 2, 3];
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn dvector(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::Dynamic::new(#len),
|
||||
nalgebra::Const::<1>,
|
||||
vec!#array_tokens))
|
||||
};
|
||||
proc_macro::TokenStream::from(output)
|
||||
}
|
258
nalgebra-macros/tests/tests.rs
Normal file
258
nalgebra-macros/tests/tests.rs
Normal file
@ -0,0 +1,258 @@
|
||||
use nalgebra::{
|
||||
DMatrix, DVector, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2, Matrix2x1, Matrix2x3, Matrix2x4,
|
||||
Matrix3, Matrix3x1, Matrix3x2, Matrix3x4, Matrix4, Matrix4x1, Matrix4x2, Matrix4x3, SMatrix,
|
||||
SVector, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6,
|
||||
};
|
||||
use nalgebra_macros::{dmatrix, dvector, matrix, vector};
|
||||
|
||||
fn check_statically_same_type<T>(_: &T, _: &T) {}
|
||||
|
||||
/// Wrapper for `assert_eq` that also asserts that the types are the same
|
||||
macro_rules! assert_eq_and_type {
|
||||
($left:expr, $right:expr $(,)?) => {
|
||||
check_statically_same_type(&$left, &$right);
|
||||
assert_eq!($left, $right);
|
||||
};
|
||||
}
|
||||
|
||||
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn matrix_small_dims_exhaustive() {
|
||||
// 0x0
|
||||
assert_eq_and_type!(matrix![], SMatrix::<i32, 0, 0>::zeros());
|
||||
|
||||
// 1xN
|
||||
assert_eq_and_type!(matrix![1], SMatrix::<i32, 1, 1>::new(1));
|
||||
assert_eq_and_type!(matrix![1, 2], Matrix1x2::new(1, 2));
|
||||
assert_eq_and_type!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3));
|
||||
assert_eq_and_type!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4));
|
||||
|
||||
// 2xN
|
||||
assert_eq_and_type!(matrix![1; 2], Matrix2x1::new(1, 2));
|
||||
assert_eq_and_type!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4));
|
||||
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6));
|
||||
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
|
||||
// 3xN
|
||||
assert_eq_and_type!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3));
|
||||
assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6));
|
||||
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12],
|
||||
Matrix3x4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
|
||||
|
||||
// 4xN
|
||||
assert_eq_and_type!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4));
|
||||
assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12],
|
||||
Matrix4x3::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
|
||||
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16],
|
||||
Matrix4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_const_fn() {
|
||||
// Ensure that matrix! can be used in const contexts
|
||||
const _: SMatrix<i32, 0, 0> = matrix![];
|
||||
const _: SMatrix<i32, 1, 2> = matrix![1, 2];
|
||||
const _: SMatrix<i32, 2, 3> = matrix![1, 2, 3; 4, 5, 6];
|
||||
}
|
||||
|
||||
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn dmatrix_small_dims_exhaustive() {
|
||||
// 0x0
|
||||
assert_eq_and_type!(dmatrix![], DMatrix::<i32>::zeros(0, 0));
|
||||
|
||||
// 1xN
|
||||
assert_eq_and_type!(dmatrix![1], DMatrix::from_row_slice(1, 1, &[1]));
|
||||
assert_eq_and_type!(dmatrix![1, 2], DMatrix::from_row_slice(1, 2, &[1, 2]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3], DMatrix::from_row_slice(1, 3, &[1, 2, 3]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3, 4], DMatrix::from_row_slice(1, 4, &[1, 2, 3, 4]));
|
||||
|
||||
// 2xN
|
||||
assert_eq_and_type!(dmatrix![1; 2], DMatrix::from_row_slice(2, 1, &[1, 2]));
|
||||
assert_eq_and_type!(dmatrix![1, 2; 3, 4], DMatrix::from_row_slice(2, 2, &[1, 2, 3, 4]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6], DMatrix::from_row_slice(2, 3, &[1, 2, 3, 4, 5, 6]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], DMatrix::from_row_slice(2, 4, &[1, 2, 3, 4, 5, 6, 7, 8]));
|
||||
|
||||
// 3xN
|
||||
assert_eq_and_type!(dmatrix![1; 2; 3], DMatrix::from_row_slice(3, 1, &[1, 2, 3]));
|
||||
assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6], DMatrix::from_row_slice(3, 2, &[1, 2, 3, 4, 5, 6]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], DMatrix::from_row_slice(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12],
|
||||
DMatrix::from_row_slice(3, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
|
||||
|
||||
// 4xN
|
||||
assert_eq_and_type!(dmatrix![1; 2; 3; 4], DMatrix::from_row_slice(4, 1, &[1, 2, 3, 4]));
|
||||
assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], DMatrix::from_row_slice(4, 2, &[1, 2, 3, 4, 5, 6, 7, 8]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12],
|
||||
DMatrix::from_row_slice(4, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
|
||||
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16],
|
||||
DMatrix::from_row_slice(4, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));
|
||||
}
|
||||
|
||||
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn vector_small_dims_exhaustive() {
|
||||
assert_eq_and_type!(vector![], SVector::<i32, 0>::zeros());
|
||||
assert_eq_and_type!(vector![1], Vector1::<i32>::new(1));
|
||||
assert_eq_and_type!(vector![1, 2], Vector2::new(1, 2));
|
||||
assert_eq_and_type!(vector![1, 2, 3], Vector3::new(1, 2, 3));
|
||||
assert_eq_and_type!(vector![1, 2, 3, 4], Vector4::new(1, 2, 3, 4));
|
||||
assert_eq_and_type!(vector![1, 2, 3, 4, 5], Vector5::new(1, 2, 3, 4, 5));
|
||||
assert_eq_and_type!(vector![1, 2, 3, 4, 5, 6], Vector6::new(1, 2, 3, 4, 5, 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vector_const_fn() {
|
||||
// Ensure that vector! can be used in const contexts
|
||||
const _: SVector<i32, 0> = vector![];
|
||||
const _: Vector1<i32> = vector![1];
|
||||
const _: Vector2<i32> = vector![1, 2];
|
||||
const _: Vector6<i32> = vector![1, 2, 3, 4, 5, 6];
|
||||
}
|
||||
|
||||
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn dvector_small_dims_exhaustive() {
|
||||
assert_eq_and_type!(dvector![], DVector::<i32>::zeros(0));
|
||||
assert_eq_and_type!(dvector![1], DVector::from_column_slice(&[1]));
|
||||
assert_eq_and_type!(dvector![1, 2], DVector::from_column_slice(&[1, 2]));
|
||||
assert_eq_and_type!(dvector![1, 2, 3], DVector::from_column_slice(&[1, 2, 3]));
|
||||
assert_eq_and_type!(dvector![1, 2, 3, 4], DVector::from_column_slice(&[1, 2, 3, 4]));
|
||||
assert_eq_and_type!(dvector![1, 2, 3, 4, 5], DVector::from_column_slice(&[1, 2, 3, 4, 5]));
|
||||
assert_eq_and_type!(dvector![1, 2, 3, 4, 5, 6], DVector::from_column_slice(&[1, 2, 3, 4, 5, 6]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_trybuild_tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
// Verify error message when we give a matrix with mismatched dimensions
|
||||
t.compile_fail("tests/trybuild/matrix_mismatched_dimensions.rs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dmatrix_trybuild_tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
// Verify error message when we give a matrix with mismatched dimensions
|
||||
t.compile_fail("tests/trybuild/dmatrix_mismatched_dimensions.rs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_builtin_types() {
|
||||
// Check that matrix! compiles for all built-in types
|
||||
const _: SMatrix<i8, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<i16, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<i32, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<i64, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<isize, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<u8, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<u16, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<u32, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<u64, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<usize, 2, 2> = matrix![0, 1; 2, 3];
|
||||
const _: SMatrix<f32, 2, 2> = matrix![0.0, 1.0; 2.0, 3.0];
|
||||
const _: SMatrix<f64, 2, 2> = matrix![0.0, 1.0; 2.0, 3.0];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vector_builtin_types() {
|
||||
// Check that vector! compiles for all built-in types
|
||||
const _: SVector<i8, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<i16, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<i32, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<i64, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<isize, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<u8, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<u16, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<u32, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<u64, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<usize, 4> = vector![0, 1, 2, 3];
|
||||
const _: SVector<f32, 4> = vector![0.0, 1.0, 2.0, 3.0];
|
||||
const _: SVector<f64, 4> = vector![0.0, 1.0, 2.0, 3.0];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dmatrix_builtin_types() {
|
||||
// Check that dmatrix! compiles for all built-in types
|
||||
let _: DMatrix<i8> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<i16> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<i32> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<i64> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<isize> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<u8> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<u16> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<u32> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<u64> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<usize> = dmatrix![0, 1; 2, 3];
|
||||
let _: DMatrix<f32> = dmatrix![0.0, 1.0; 2.0, 3.0];
|
||||
let _: DMatrix<f64> = dmatrix![0.0, 1.0; 2.0, 3.0];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dvector_builtin_types() {
|
||||
// Check that dvector! compiles for all built-in types
|
||||
let _: DVector<i8> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<i16> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<i32> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<i64> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<isize> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<u8> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<u16> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<u32> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<u64> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<usize> = dvector![0, 1, 2, 3];
|
||||
let _: DVector<f32> = dvector![0.0, 1.0, 2.0, 3.0];
|
||||
let _: DVector<f64> = dvector![0.0, 1.0, 2.0, 3.0];
|
||||
}
|
||||
|
||||
/// Black box function that's just used for testing macros with function call expressions.
|
||||
fn f<T>(x: T) -> T {
|
||||
x
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn matrix_arbitrary_expressions() {
|
||||
// Test that matrix! supports arbitrary expressions for its elements
|
||||
let a = matrix![1 + 2 , 2 * 3;
|
||||
4 * f(5 + 6), 7 - 8 * 9];
|
||||
let a_expected = Matrix2::new(1 + 2 , 2 * 3,
|
||||
4 * f(5 + 6), 7 - 8 * 9);
|
||||
assert_eq_and_type!(a, a_expected);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn dmatrix_arbitrary_expressions() {
|
||||
// Test that dmatrix! supports arbitrary expressions for its elements
|
||||
let a = dmatrix![1 + 2 , 2 * 3;
|
||||
4 * f(5 + 6), 7 - 8 * 9];
|
||||
let a_expected = DMatrix::from_row_slice(2, 2, &[1 + 2 , 2 * 3,
|
||||
4 * f(5 + 6), 7 - 8 * 9]);
|
||||
assert_eq_and_type!(a, a_expected);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn vector_arbitrary_expressions() {
|
||||
// Test that vector! supports arbitrary expressions for its elements
|
||||
let a = vector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9];
|
||||
let a_expected = Vector4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9);
|
||||
assert_eq_and_type!(a, a_expected);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn dvector_arbitrary_expressions() {
|
||||
// Test that dvector! supports arbitrary expressions for its elements
|
||||
let a = dvector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9];
|
||||
let a_expected = DVector::from_column_slice(&[1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]);
|
||||
assert_eq_and_type!(a, a_expected);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
use nalgebra_macros::dmatrix;
|
||||
|
||||
fn main() {
|
||||
dmatrix![1, 2, 3;
|
||||
4, 5];
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
error: Unexpected number of entries in row 1. Expected 3, found 2 entries.
|
||||
--> $DIR/dmatrix_mismatched_dimensions.rs:5:13
|
||||
|
|
||||
5 | 4, 5];
|
||||
| ^
|
@ -0,0 +1,6 @@
|
||||
use nalgebra_macros::matrix;
|
||||
|
||||
fn main() {
|
||||
matrix![1, 2, 3;
|
||||
4, 5];
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
error: Unexpected number of entries in row 1. Expected 3, found 2 entries.
|
||||
--> $DIR/matrix_mismatched_dimensions.rs:5:13
|
||||
|
|
||||
5 | 4, 5];
|
||||
| ^
|
@ -29,7 +29,10 @@ use crate::base::storage::{
|
||||
ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut,
|
||||
};
|
||||
use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit};
|
||||
use crate::SimdComplexField;
|
||||
use crate::{ArrayStorage, SMatrix, SimdComplexField};
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use crate::{DMatrix, DVector, Dynamic, VecStorage};
|
||||
|
||||
/// A square matrix.
|
||||
pub type SquareMatrix<T, D, S> = Matrix<T, D, D, S>;
|
||||
@ -317,6 +320,49 @@ impl<T, R, C, S> Matrix<T, R, C, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const R: usize, const C: usize> SMatrix<T, R, C> {
|
||||
/// Creates a new statically-allocated matrix from the given [ArrayStorage].
|
||||
///
|
||||
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||
/// work in `const fn` contexts.
|
||||
#[inline(always)]
|
||||
pub const fn from_array_storage(storage: ArrayStorage<T, R, C>) -> Self {
|
||||
// This is sound because the row and column types are exactly the same as that of the
|
||||
// storage, so there can be no mismatch
|
||||
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
|
||||
// `from_data` const fn compatible
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<T> DMatrix<T> {
|
||||
/// Creates a new heap-allocated matrix from the given [VecStorage].
|
||||
///
|
||||
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||
/// work in `const fn` contexts.
|
||||
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, Dynamic>) -> Self {
|
||||
// This is sound because the dimensions of the matrix and the storage are guaranteed
|
||||
// to be the same
|
||||
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
|
||||
// `from_data` const fn compatible
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<T> DVector<T> {
|
||||
/// Creates a new heap-allocated matrix from the given [VecStorage].
|
||||
///
|
||||
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||
/// work in `const fn` contexts.
|
||||
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, U1>) -> Self {
|
||||
// This is sound because the dimensions of the matrix and the storage are guaranteed
|
||||
// to be the same
|
||||
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||
/// Creates a new matrix with the given data.
|
||||
#[inline(always)]
|
||||
|
@ -136,6 +136,9 @@ pub use crate::sparse::*;
|
||||
)]
|
||||
pub use base as core;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
pub use nalgebra_macros::{dmatrix, dvector, matrix, vector};
|
||||
|
||||
use simba::scalar::SupersetOf;
|
||||
use std::cmp::{self, Ordering, PartialOrd};
|
||||
|
||||
|
11
tests/core/macros.rs
Normal file
11
tests/core/macros.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use nalgebra::{dmatrix, dvector, matrix, vector};
|
||||
|
||||
#[test]
|
||||
fn sanity_test() {
|
||||
// The macros are already tested in `nalgebra-macros`. Here we just test that they compile fine.
|
||||
|
||||
let _ = matrix![1, 2, 3; 4, 5, 6];
|
||||
let _ = dmatrix![1, 2, 3; 4, 5, 6];
|
||||
let _ = vector![1, 2, 3, 4, 5, 6];
|
||||
let _ = dvector![1, 2, 3, 4, 5, 6];
|
||||
}
|
@ -16,3 +16,6 @@ mod matrixcompare;
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
pub mod helper;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod macros;
|
||||
|
11
tests/lib.rs
11
tests/lib.rs
@ -1,7 +1,12 @@
|
||||
#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand")))]
|
||||
#[cfg(not(all(
|
||||
feature = "debug",
|
||||
feature = "compare",
|
||||
feature = "rand",
|
||||
feature = "macros"
|
||||
)))]
|
||||
compile_error!(
|
||||
"Please enable the `debug`, `compare`, and `rand` features in order to compile and run the tests.
|
||||
Example: `cargo test --features debug,compare,rand`"
|
||||
"Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests.
|
||||
Example: `cargo test --features debug,compare,rand,macros`"
|
||||
);
|
||||
|
||||
#[cfg(feature = "abomonation-serialize")]
|
||||
|
Loading…
Reference in New Issue
Block a user