diff --git a/CHANGELOG.md b/CHANGELOG.md index 2783416b..04ea1c34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ documented here. This project adheres to [Semantic Versioning](https://semver.org/). +## [0.28.0] +### Added +- Implement `Hash` for `Transform`. +- Implement `Borrow` and `BorrowMut` for contiguous slices. + +### Modified +- The `OPoint` type has been added. It takes the dimension number as a type-level integer (e.g. `Const<3>`) instead + of a const-generic. The type `Point` is now an alias for `OPoint`. This changes doesn't affect any + of the existing code using `Point`. However, it will allow the use `OPoint` in a generic context where the dimension + cannot be easily expressed as a const-generic (because of the current limitation of const-generics in Rust). +- Several clippy warnings were fixed. This results in some method signature changes (e.g. taking `self` instead of `&self`) + but this should not have any practical infulances on existing codebase. +- The `Point::new` constructors are no longer const-fn. This is due to some limitations in const-fn + not allowing custom trait-bounds. Use the `point!` macro instead to build points in const environments. +- `Dynamic::new` and `Unit::new_unchecked` are now const-fn. +- Methods returning `Result<(), ()>` now return `bool` instead. + +### Fixed +- Fixed a potential unsoundess issue when converting a mutable slice to a `&mut[T]`. + +## [0.27.1] +### Fixed +- Fixed a bug in the conversion from `glam::Vec2` or `glam::DVec2` to `Isometry2`. + ## [0.27.0] This removes the `convert-glam` and `convert-glam-unchecked` optional features. Instead, this adds the `convert-glam013`, `convert-glam014`, and `convert-glam015` optional features for @@ -34,7 +58,7 @@ conversions targeting the versions 0.13, 0.14, and 0.15 of `glam`. Fix a regression introduced in 0.26.0 preventing `DVector` from being serialized with `serde`. ## [0.26.0] -This releases integrates `min-const-generics` to nalgebra. See +This release integrates `min-const-generics` to nalgebra. See [our blog post](https://www.dimforge.com/blog/2021/04/12/integrating-const-generics-to-nalgebra) for details about this release. @@ -74,7 +98,7 @@ for details about this release. ## [0.25.3] ### Added -- The `Vector::simd_cap_magnitude` method to cap the magnitude of the a vector with +- The `Vector::simd_cap_magnitude` method to cap the magnitude of the vector with SIMD components. ## [0.25.2] @@ -105,7 +129,7 @@ This updates all the dependencies of nalgebra to their latest version, including ### New crate: nalgebra-sparse Alongside this release of `nalgebra`, we are releasing `nalgebra-sparse`: a crate dedicated to sparse matrix -computation with `nalgebra`. The `sparse` module of `nalgebra`itself still exists for backward compatibility +computation with `nalgebra`. The `sparse` module of `nalgebra`itself still exists for backward compatibility, but it will be deprecated soon in favor of the `nalgebra-sparse` crate. ### Added @@ -121,12 +145,12 @@ but it will be deprecated soon in favor of the `nalgebra-sparse` crate. ## [0.24.0] ### Added -* The `DualQuaternion` type. It is still work-in-progress but the basics are here: +* The `DualQuaternion` type. It is still work-in-progress, but the basics are here: creation from its real and dual part, multiplication of two dual quaternions, and normalization. ### Removed -* There is no blanket `impl PartialEq for Unit` any more. Instead, it is +* There is no blanket `impl PartialEq for Unit` anymore. Instead, it is implemented specifically for `UnitComplex`, `UnitQuaternion` and `Unit`. ## [0.23.2] @@ -153,7 +177,7 @@ In this release we improved the documentation of the matrix and vector types by: and `Vector.apply(f)`. * The `Quaternion::from([N; 4])` conversion to build a quaternion from an array of four elements. * The `Isometry::from(Translation)` conversion to build an isometry from a translation. - * The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit>` with its i-th component set to 1.0 and the + * The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit>` with its i-th component set to 1.0, and the others set to zero. * The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear interpolation for the translational part, and spherical interpolation for the rotational part. @@ -162,7 +186,7 @@ In this release we improved the documentation of the matrix and vector types by: ## [0.22.0] In this release, we are using the new version 0.2 of simba. One major change of that version is that the -use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point +use of `libm` is now opt-in when building targeting `no-std` environment. If you are using floating-point operations with nalgebra in a `no-std` environment, you will need to enable the new `libm` feature of nalgebra for your code to compile again. @@ -170,7 +194,7 @@ of nalgebra for your code to compile again. * The `libm` feature that enables `libm` when building for `no-std` environment. * The `libm-force` feature that enables `libm` even when building for a not `no-std` environment. * `Cholesky::new_unchecked` which build a Cholesky decomposition without checking that its input is - positive-definite. It can be use with SIMD types. + positive-definite. It can be used with SIMD types. * The `Default` trait is now implemented for matrices, and quaternions. They are all filled with zeros, except for `UnitQuaternion` which is initialized with the identity. * Matrix exponential `matrix.exp()`. @@ -341,7 +365,7 @@ library (i.e. it supports `#![no_std]`). See the corresponding [documentation](h * Add methods `.rotation_between_axis(...)` and `.scaled_rotation_between_axis(...)` to `UnitComplex` to compute the rotation matrix between two 2D **unit** vectors. * Add methods `.axis_angle()` to `UnitComplex` and `UnitQuaternion` in order to retrieve both the - unit rotation axis and the rotation angle simultaneously. + unit rotation axis, and the rotation angle simultaneously. * Add functions to construct a random matrix with a user-defined distribution: `::from_distribution(...)`. ## [0.14.0] @@ -362,7 +386,7 @@ library (i.e. it supports `#![no_std]`). See the corresponding [documentation](h the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`. * `.iamin()` that returns the index of the vector entry with - smallest absolute value. + the smallest absolute value. * The `mint` feature that can be enabled in order to allow conversions from and to types of the [mint](https://crates.io/crates/mint) crate. * Aliases for matrix and vector slices. Their are named by adding `Slice` @@ -400,7 +424,7 @@ This adds support for serialization using the * The alias `MatrixNM` is now deprecated. Use `MatrixMN` instead (we reordered M and N to be in alphabetical order). * In-place componentwise multiplication and division - `.component_mul_mut(...)` and `.component_div_mut(...)` have bee deprecated + `.component_mul_mut(...)` and `.component_div_mut(...)` have been deprecated for a future renaming. Use `.component_mul_assign(...)` and `.component_div_assign(...)` instead. @@ -502,7 +526,7 @@ This version is a major rewrite of the library. Major changes are: All other mathematical traits, except `Axpy` have been removed from **nalgebra**. * Methods are now preferred to free functions because they do not require any - trait to be used any more. + trait to be used anymore. * Most algebraic entities can be parametrized by type-level integers to specify their dimensions. Using `Dynamic` instead of a type-level integer indicates that the dimension known at run-time only. @@ -578,7 +602,7 @@ only: * The free functions `::prepend_rotation`, `::append_rotation`, `::append_rotation_wrt_center`, `::append_rotation_wrt_point`, `::append_transformation`, and `::append_translation ` have been removed. - Instead create the rotation or translation object explicitly and use + Instead, create the rotation or translation object explicitly and use multiplication to compose it with anything else. * The free function `::outer` has been removed. Use column-vector × @@ -604,7 +628,7 @@ Binary operations are now allowed between references as well. For example ### Modified Removed unused parameters to methods from the `ApproxEq` trait. Those were -required before rust 1.0 to help type inference. The are not needed any more +required before rust 1.0 to help type inference. They are not needed any more since it now allowed to write for a type `T` that implements `ApproxEq`: `::approx_epsilon()`. This replaces the old form: `ApproxEq::approx_epsilon(None::)`. @@ -623,7 +647,7 @@ since it now allowed to write for a type `T` that implements `ApproxEq`: `UnitQuaternion::from_axisangle`. The new `::new` method now requires a not-normalized quaternion. -Methods names starting with `new_with_` now start with `from_`. This is more +Method names starting with `new_with_` now start with `from_`. This is more idiomatic in Rust. The `Norm` trait now uses an associated type instead of a type parameter. @@ -654,8 +678,8 @@ crate for vectors, rotations and points. To enable them, activate the ## [0.8.0] ### Modified - * Almost everything (types, methods, and traits) now use full names instead - of abbreviations (e.g. `Vec3` becomes `Vector3`). Most changes are abvious. + * Almost everything (types, methods, and traits) now use fulls names instead + of abbreviations (e.g. `Vec3` becomes `Vector3`). Most changes are obvious. Note however that: - `::sqnorm` becomes `::norm_squared`. - `::sqdist` becomes `::distance_squared`. @@ -689,11 +713,11 @@ you [there](https://users.nphysics.org)! ### Removed * Removed zero-sized elements `Vector0`, `Point0`. - * Removed 4-dimensional transformations `Rotation4` and `Isometry4` (which had an implementation to incomplete to be useful). + * Removed 4-dimensional transformations `Rotation4` and `Isometry4` (which had an implementation too incomplete to be useful). ### Modified * Vectors are now multipliable with isometries. This will result into a pure rotation (this is how - vectors differ from point semantically: they design directions so they are not translatable). + vectors differ from point semantically: they design directions, so they are not translatable). * `{Isometry3, Rotation3}::look_at` reimplemented and renamed to `::look_at_rh` and `::look_at_lh` to agree with the computer graphics community (in particular, the GLM library). Use the `::look_at_rh` variant to build a view matrix that diff --git a/Cargo.toml b/Cargo.toml index 5c2ad286..8f4c7876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.27.0" +version = "0.28.0" authors = [ "Sébastien Crozet " ] description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices." diff --git a/examples/cargo/Cargo.toml b/examples/cargo/Cargo.toml index e0dd983c..9020b0ec 100644 --- a/examples/cargo/Cargo.toml +++ b/examples/cargo/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" authors = [ "You" ] [dependencies] -nalgebra = "0.27.0" +nalgebra = "0.28.0" [[bin]] name = "example" diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml index f97536d0..bebacab8 100644 --- a/nalgebra-glm/Cargo.toml +++ b/nalgebra-glm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-glm" -version = "0.13.0" +version = "0.14.0" authors = ["sebcrozet "] description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library." @@ -27,4 +27,4 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ] num-traits = { version = "0.2", default-features = false } approx = { version = "0.5", default-features = false } simba = { version = "0.5", default-features = false } -nalgebra = { path = "..", version = "0.27", default-features = false } +nalgebra = { path = "..", version = "0.28", default-features = false } diff --git a/nalgebra-glm/src/lib.rs b/nalgebra-glm/src/lib.rs index df578a80..391391f4 100644 --- a/nalgebra-glm/src/lib.rs +++ b/nalgebra-glm/src/lib.rs @@ -21,7 +21,7 @@ **nalgebra-glm** using the module prefix `glm::`. For example you will write `glm::rotate(...)` instead of the more verbose `nalgebra_glm::rotate(...)`: - ```rust + ``` extern crate nalgebra_glm as glm; ``` diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml index c368a3c6..86825a37 100644 --- a/nalgebra-lapack/Cargo.toml +++ b/nalgebra-lapack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-lapack" -version = "0.18.0" +version = "0.19.0" authors = [ "Sébastien Crozet ", "Andrew Straw " ] description = "Matrix decompositions using nalgebra matrices and Lapack bindings." @@ -29,7 +29,7 @@ accelerate = ["lapack-src/accelerate"] intel-mkl = ["lapack-src/intel-mkl"] [dependencies] -nalgebra = { version = "0.27", path = ".." } +nalgebra = { version = "0.28", path = ".." } num-traits = "0.2" num-complex = { version = "0.4", default-features = false } simba = "0.5" @@ -39,7 +39,7 @@ lapack-src = { version = "0.8", default-features = false } # clippy = "*" [dev-dependencies] -nalgebra = { version = "0.27", features = [ "arbitrary", "rand" ], path = ".." } +nalgebra = { version = "0.28", features = [ "arbitrary", "rand" ], path = ".." } proptest = { version = "1", default-features = false, features = ["std"] } quickcheck = "1" approx = "0.5" diff --git a/nalgebra-lapack/src/cholesky.rs b/nalgebra-lapack/src/cholesky.rs index fbbf6fd4..bc3515a5 100644 --- a/nalgebra-lapack/src/cholesky.rs +++ b/nalgebra-lapack/src/cholesky.rs @@ -80,6 +80,7 @@ where } /// Retrieves the lower-triangular factor of the cholesky decomposition. + #[must_use] pub fn l(&self) -> OMatrix { let mut res = self.l.clone(); res.fill_upper_triangle(Zero::zero(), 1); @@ -91,6 +92,7 @@ where /// /// This is an allocation-less version of `self.l()`. The values of the strict upper-triangular /// part are garbage and should be ignored by further computations. + #[must_use] pub fn l_dirty(&self) -> &OMatrix { &self.l } diff --git a/nalgebra-lapack/src/eigen.rs b/nalgebra-lapack/src/eigen.rs index cbe1555a..1bca79a5 100644 --- a/nalgebra-lapack/src/eigen.rs +++ b/nalgebra-lapack/src/eigen.rs @@ -302,6 +302,7 @@ where /// The determinant of the decomposed matrix. #[inline] + #[must_use] pub fn determinant(&self) -> T { let mut det = T::one(); for e in self.eigenvalues.iter() { diff --git a/nalgebra-lapack/src/hessenberg.rs b/nalgebra-lapack/src/hessenberg.rs index b025a8bf..c5765022 100644 --- a/nalgebra-lapack/src/hessenberg.rs +++ b/nalgebra-lapack/src/hessenberg.rs @@ -89,6 +89,7 @@ where /// Computes the hessenberg matrix of this decomposition. #[inline] + #[must_use] pub fn h(&self) -> OMatrix { let mut h = self.h.clone_owned(); h.fill_lower_triangle(T::zero(), 2); @@ -109,6 +110,7 @@ where /// Computes the unitary matrix `Q` of this decomposition. #[inline] + #[must_use] pub fn q(&self) -> OMatrix { let n = self.h.nrows() as i32; let mut q = self.h.clone_owned(); diff --git a/nalgebra-lapack/src/lib.rs b/nalgebra-lapack/src/lib.rs index 39965a59..9a027772 100644 --- a/nalgebra-lapack/src/lib.rs +++ b/nalgebra-lapack/src/lib.rs @@ -30,7 +30,7 @@ //! the system installation of netlib without LAPACKE (note the E) or //! CBLAS: //! -//! ```.ignore +//! ```ignore //! sudo apt-get install gfortran libblas3gf liblapack3gf //! export CARGO_FEATURE_SYSTEM_NETLIB=1 //! export CARGO_FEATURE_EXCLUDE_LAPACKE=1 @@ -44,7 +44,7 @@ //! //! On macOS, do this to use Apple's Accelerate framework: //! -//! ```.ignore +//! ```ignore //! export CARGO_FEATURES="--no-default-features --features accelerate" //! cargo build ${CARGO_FEATURES} //! ``` diff --git a/nalgebra-lapack/src/lu.rs b/nalgebra-lapack/src/lu.rs index 5ca5f143..2130fc7e 100644 --- a/nalgebra-lapack/src/lu.rs +++ b/nalgebra-lapack/src/lu.rs @@ -85,6 +85,7 @@ where /// Gets the lower-triangular matrix part of the decomposition. #[inline] + #[must_use] pub fn l(&self) -> OMatrix> { let (nrows, ncols) = self.lu.data.shape(); let mut res = self.lu.columns_generic(0, nrows.min(ncols)).into_owned(); @@ -97,6 +98,7 @@ where /// Gets the upper-triangular matrix part of the decomposition. #[inline] + #[must_use] pub fn u(&self) -> OMatrix, C> { let (nrows, ncols) = self.lu.data.shape(); let mut res = self.lu.rows_generic(0, nrows.min(ncols)).into_owned(); @@ -111,6 +113,7 @@ where /// Computing the permutation matrix explicitly is costly and usually not necessary. /// To permute rows of a matrix or vector, use the method `self.permute(...)` instead. #[inline] + #[must_use] pub fn p(&self) -> OMatrix { let (dim, _) = self.lu.data.shape(); let mut id = Matrix::identity_generic(dim, dim); @@ -124,6 +127,7 @@ where /// Gets the LAPACK permutation indices. #[inline] + #[must_use] pub fn permutation_indices(&self) -> &OVector> { &self.p } diff --git a/nalgebra-lapack/src/qr.rs b/nalgebra-lapack/src/qr.rs index b58c3f35..7b2d5df6 100644 --- a/nalgebra-lapack/src/qr.rs +++ b/nalgebra-lapack/src/qr.rs @@ -92,6 +92,7 @@ where /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] + #[must_use] pub fn r(&self) -> OMatrix, C> { let (nrows, ncols) = self.qr.data.shape(); self.qr.rows_generic(0, nrows.min(ncols)).upper_triangle() @@ -117,6 +118,7 @@ where /// Computes the orthogonal matrix `Q` of this decomposition. #[inline] + #[must_use] pub fn q(&self) -> OMatrix> { let (nrows, ncols) = self.qr.data.shape(); let min_nrows_ncols = nrows.min(ncols); diff --git a/nalgebra-lapack/src/schur.rs b/nalgebra-lapack/src/schur.rs index a850f01d..3bee2635 100644 --- a/nalgebra-lapack/src/schur.rs +++ b/nalgebra-lapack/src/schur.rs @@ -138,6 +138,7 @@ where /// Computes the real eigenvalues of the decomposed matrix. /// /// Return `None` if some eigenvalues are complex. + #[must_use] pub fn eigenvalues(&self) -> Option> { if self.im.iter().all(|e| e.is_zero()) { Some(self.re.clone()) @@ -147,6 +148,7 @@ where } /// Computes the complex eigenvalues of the decomposed matrix. + #[must_use] pub fn complex_eigenvalues(&self) -> OVector, D> where DefaultAllocator: Allocator, D>, diff --git a/nalgebra-lapack/src/svd.rs b/nalgebra-lapack/src/svd.rs index f6b907b6..3357e621 100644 --- a/nalgebra-lapack/src/svd.rs +++ b/nalgebra-lapack/src/svd.rs @@ -175,6 +175,7 @@ macro_rules! svd_impl( /// /// All singular value below epsilon will be set to zero instead of being inverted. #[inline] + #[must_use] pub fn pseudo_inverse(&self, epsilon: $t) -> OMatrix<$t, C, R> { let nrows = self.u.data.shape().0; let ncols = self.vt.data.shape().1; @@ -207,6 +208,7 @@ macro_rules! svd_impl( /// This is the number of singular values that are not too small (i.e. greater than /// the given `epsilon`). #[inline] + #[must_use] pub fn rank(&self, epsilon: $t) -> usize { let mut i = 0; diff --git a/nalgebra-lapack/src/symmetric_eigen.rs b/nalgebra-lapack/src/symmetric_eigen.rs index 0cd6bfc1..d276437e 100644 --- a/nalgebra-lapack/src/symmetric_eigen.rs +++ b/nalgebra-lapack/src/symmetric_eigen.rs @@ -138,6 +138,7 @@ where /// The determinant of the decomposed matrix. #[inline] + #[must_use] pub fn determinant(&self) -> T { let mut det = T::one(); for e in self.eigenvalues.iter() { @@ -150,6 +151,7 @@ where /// Rebuild the original matrix. /// /// This is useful if some of the eigenvalues have been manually modified. + #[must_use] pub fn recompose(&self) -> OMatrix { let mut u_t = self.eigenvectors.clone(); for i in 0..self.eigenvalues.len() { diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml index 59eb5ba1..490950bc 100644 --- a/nalgebra-macros/Cargo.toml +++ b/nalgebra-macros/Cargo.toml @@ -21,5 +21,5 @@ quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] -nalgebra = { version = "0.27.0", path = ".." } +nalgebra = { version = "0.28.0", path = ".." } trybuild = "1.0.42" diff --git a/nalgebra-sparse/Cargo.toml b/nalgebra-sparse/Cargo.toml index 4af053c2..09b6ad73 100644 --- a/nalgebra-sparse/Cargo.toml +++ b/nalgebra-sparse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-sparse" -version = "0.3.0" +version = "0.4.0" authors = [ "Andreas Longva", "Sébastien Crozet " ] edition = "2018" description = "Sparse matrix computation based on nalgebra." @@ -20,7 +20,7 @@ compare = [ "matrixcompare-core" ] slow-tests = [] [dependencies] -nalgebra = { version="0.27", path = "../" } +nalgebra = { version="0.28", path = "../" } num-traits = { version = "0.2", default-features = false } proptest = { version = "1.0", optional = true } matrixcompare-core = { version = "0.1.0", optional = true } @@ -28,7 +28,7 @@ matrixcompare-core = { version = "0.1.0", optional = true } [dev-dependencies] itertools = "0.10" matrixcompare = { version = "0.3.0", features = [ "proptest-support" ] } -nalgebra = { version="0.27", path = "../", features = ["compare"] } +nalgebra = { version="0.28", path = "../", features = ["compare"] } [package.metadata.docs.rs] # Enable certain features when building docs for docs.rs diff --git a/nalgebra-sparse/src/convert/mod.rs b/nalgebra-sparse/src/convert/mod.rs index 77388b22..93fa5a8b 100644 --- a/nalgebra-sparse/src/convert/mod.rs +++ b/nalgebra-sparse/src/convert/mod.rs @@ -7,7 +7,7 @@ //! The following example illustrates how to convert between matrix formats with the `From` //! implementations. //! -//! ```rust +//! ``` //! use nalgebra_sparse::{csr::CsrMatrix, csc::CscMatrix, coo::CooMatrix}; //! use nalgebra::DMatrix; //! diff --git a/nalgebra-sparse/src/coo.rs b/nalgebra-sparse/src/coo.rs index 364083a9..679dbdb2 100644 --- a/nalgebra-sparse/src/coo.rs +++ b/nalgebra-sparse/src/coo.rs @@ -20,7 +20,7 @@ use crate::SparseFormatError; /// /// # Examples /// -/// ```rust +/// ``` /// use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix, csc::CscMatrix}; /// /// // Initialize a matrix with all zeros (no explicitly stored entries). @@ -45,6 +45,43 @@ pub struct CooMatrix { values: Vec, } +impl CooMatrix { + /// Pushes a dense matrix into the sparse one. + /// + /// This adds the dense matrix `m` starting at the `r`th row and `c`th column + /// to the matrix. + /// + /// Panics + /// ------ + /// + /// Panics if any part of the dense matrix is out of bounds of the sparse matrix + /// when inserted at `(r, c)`. + #[inline] + pub fn push_matrix>( + &mut self, + r: usize, + c: usize, + m: &na::Matrix, + ) { + let block_nrows = m.nrows(); + let block_ncols = m.ncols(); + let max_row_with_block = r + block_nrows - 1; + let max_col_with_block = c + block_ncols - 1; + assert!(max_row_with_block < self.nrows); + assert!(max_col_with_block < self.ncols); + + self.reserve(block_ncols * block_nrows); + + for (col_idx, col) in m.column_iter().enumerate() { + for (row_idx, v) in col.iter().enumerate() { + self.row_indices.push(r + row_idx); + self.col_indices.push(c + col_idx); + self.values.push(v.clone()); + } + } + } +} + impl CooMatrix { /// Construct a zero COO matrix of the given dimensions. /// @@ -173,12 +210,14 @@ impl CooMatrix { /// The number of rows in the matrix. #[inline] + #[must_use] pub fn nrows(&self) -> usize { self.nrows } /// The number of columns in the matrix. #[inline] + #[must_use] pub fn ncols(&self) -> usize { self.ncols } @@ -189,21 +228,25 @@ impl CooMatrix { /// entries, then it may have a different number of non-zeros as reported by `nnz()` compared /// to its CSR representation. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.values.len() } /// The row indices of the explicitly stored entries. + #[must_use] pub fn row_indices(&self) -> &[usize] { &self.row_indices } /// The column indices of the explicitly stored entries. + #[must_use] pub fn col_indices(&self) -> &[usize] { &self.col_indices } /// The values of the explicitly stored entries. + #[must_use] pub fn values(&self) -> &[T] { &self.values } diff --git a/nalgebra-sparse/src/cs.rs b/nalgebra-sparse/src/cs.rs index d6f9b229..cde0a3e2 100644 --- a/nalgebra-sparse/src/cs.rs +++ b/nalgebra-sparse/src/cs.rs @@ -32,11 +32,13 @@ impl CsMatrix { } #[inline] + #[must_use] pub fn pattern(&self) -> &SparsityPattern { &self.sparsity_pattern } #[inline] + #[must_use] pub fn values(&self) -> &[T] { &self.values } @@ -48,6 +50,7 @@ impl CsMatrix { /// Returns the raw data represented as a tuple `(major_offsets, minor_indices, values)`. #[inline] + #[must_use] pub fn cs_data(&self) -> (&[usize], &[usize], &[T]) { let pattern = self.pattern(); ( @@ -88,6 +91,7 @@ impl CsMatrix { /// Internal method for simplifying access to a lane's data #[inline] + #[must_use] pub fn get_index_range(&self, row_index: usize) -> Option> { let row_begin = *self.sparsity_pattern.major_offsets().get(row_index)?; let row_end = *self.sparsity_pattern.major_offsets().get(row_index + 1)?; @@ -111,6 +115,7 @@ impl CsMatrix { /// Returns an entry for the given major/minor indices, or `None` if the indices are out /// of bounds. + #[must_use] pub fn get_entry(&self, major_index: usize, minor_index: usize) -> Option> { let row_range = self.get_index_range(major_index)?; let (_, minor_indices, values) = self.cs_data(); @@ -139,6 +144,7 @@ impl CsMatrix { get_mut_entry_from_slices(minor_dim, minor_indices, values, minor_index) } + #[must_use] pub fn get_lane(&self, index: usize) -> Option> { let range = self.get_index_range(index)?; let (_, minor_indices, values) = self.cs_data(); @@ -150,6 +156,7 @@ impl CsMatrix { } #[inline] + #[must_use] pub fn get_lane_mut(&mut self, index: usize) -> Option> { let range = self.get_index_range(index)?; let minor_dim = self.pattern().minor_dim(); @@ -172,6 +179,7 @@ impl CsMatrix { } #[inline] + #[must_use] pub fn filter

(&self, predicate: P) -> Self where T: Clone, @@ -207,6 +215,7 @@ impl CsMatrix { } /// Returns the diagonal of the matrix as a sparse matrix. + #[must_use] pub fn diagonal_as_matrix(&self) -> Self where T: Clone, @@ -372,26 +381,31 @@ macro_rules! impl_cs_lane_common_methods { ($name:ty) => { impl<'a, T> $name { #[inline] + #[must_use] pub fn minor_dim(&self) -> usize { self.minor_dim } #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.minor_indices.len() } #[inline] + #[must_use] pub fn minor_indices(&self) -> &[usize] { self.minor_indices } #[inline] + #[must_use] pub fn values(&self) -> &[T] { self.values } #[inline] + #[must_use] pub fn get_entry(&self, global_col_index: usize) -> Option> { get_entry_from_slices( self.minor_dim, @@ -416,6 +430,7 @@ impl<'a, T> CsLaneMut<'a, T> { (self.minor_indices, self.values) } + #[must_use] pub fn get_entry_mut(&mut self, global_minor_index: usize) -> Option> { get_mut_entry_from_slices( self.minor_dim, diff --git a/nalgebra-sparse/src/csc.rs b/nalgebra-sparse/src/csc.rs index 6a88c1d5..15e0746c 100644 --- a/nalgebra-sparse/src/csc.rs +++ b/nalgebra-sparse/src/csc.rs @@ -19,7 +19,7 @@ use std::slice::{Iter, IterMut}; /// /// # Usage /// -/// ```rust +/// ``` /// use nalgebra_sparse::csc::CscMatrix; /// use nalgebra::{DMatrix, Matrix3x4}; /// use matrixcompare::assert_matrix_eq; @@ -97,7 +97,7 @@ use std::slice::{Iter, IterMut}; /// represents the matrix in a column-by-column fashion. The entries associated with column `j` are /// determined as follows: /// -/// ```rust +/// ``` /// # let col_offsets: Vec = vec![0, 0]; /// # let row_indices: Vec = vec![]; /// # let values: Vec = vec![]; @@ -192,12 +192,14 @@ impl CscMatrix { /// The number of rows in the matrix. #[inline] + #[must_use] pub fn nrows(&self) -> usize { self.cs.pattern().minor_dim() } /// The number of columns in the matrix. #[inline] + #[must_use] pub fn ncols(&self) -> usize { self.cs.pattern().major_dim() } @@ -208,24 +210,28 @@ impl CscMatrix { /// number of algebraically zero entries in the matrix. Explicitly stored entries can still /// be zero. Corresponds to the number of entries in the sparsity pattern. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.pattern().nnz() } /// The column offsets defining part of the CSC format. #[inline] + #[must_use] pub fn col_offsets(&self) -> &[usize] { self.pattern().major_offsets() } /// The row indices defining part of the CSC format. #[inline] + #[must_use] pub fn row_indices(&self) -> &[usize] { self.pattern().minor_indices() } /// The non-zero values defining part of the CSC format. #[inline] + #[must_use] pub fn values(&self) -> &[T] { self.cs.values() } @@ -298,6 +304,7 @@ impl CscMatrix { /// ------ /// Panics if column index is out of bounds. #[inline] + #[must_use] pub fn col(&self, index: usize) -> CscCol { self.get_col(index).expect("Row index must be in bounds") } @@ -315,12 +322,14 @@ impl CscMatrix { /// Return the column at the given column index, or `None` if out of bounds. #[inline] + #[must_use] pub fn get_col(&self, index: usize) -> Option> { self.cs.get_lane(index).map(|lane| CscCol { lane }) } /// Mutable column access for the given column index, or `None` if out of bounds. #[inline] + #[must_use] pub fn get_col_mut(&mut self, index: usize) -> Option> { self.cs.get_lane_mut(index).map(|lane| CscColMut { lane }) } @@ -381,6 +390,7 @@ impl CscMatrix { } /// Returns a reference to the underlying sparsity pattern. + #[must_use] pub fn pattern(&self) -> &SparsityPattern { self.cs.pattern() } @@ -397,6 +407,7 @@ impl CscMatrix { /// /// Each call to this function incurs the cost of a binary search among the explicitly /// stored row entries for the given column. + #[must_use] pub fn get_entry(&self, row_index: usize, col_index: usize) -> Option> { self.cs.get_entry(col_index, row_index) } @@ -422,6 +433,7 @@ impl CscMatrix { /// Panics /// ------ /// Panics if `row_index` or `col_index` is out of bounds. + #[must_use] pub fn index_entry(&self, row_index: usize, col_index: usize) -> SparseEntry { self.get_entry(row_index, col_index) .expect("Out of bounds matrix indices encountered") @@ -441,6 +453,7 @@ impl CscMatrix { } /// Returns a triplet of slices `(col_offsets, row_indices, values)` that make up the CSC data. + #[must_use] pub fn csc_data(&self) -> (&[usize], &[usize], &[T]) { self.cs.cs_data() } @@ -453,6 +466,7 @@ impl CscMatrix { /// Creates a sparse matrix that contains only the explicit entries decided by the /// given predicate. + #[must_use] pub fn filter

(&self, predicate: P) -> Self where T: Clone, @@ -470,6 +484,7 @@ impl CscMatrix { /// Returns a new matrix representing the upper triangular part of this matrix. /// /// The result includes the diagonal of the matrix. + #[must_use] pub fn upper_triangle(&self) -> Self where T: Clone, @@ -480,6 +495,7 @@ impl CscMatrix { /// Returns a new matrix representing the lower triangular part of this matrix. /// /// The result includes the diagonal of the matrix. + #[must_use] pub fn lower_triangle(&self) -> Self where T: Clone, @@ -488,6 +504,7 @@ impl CscMatrix { } /// Returns the diagonal of the matrix as a sparse matrix. + #[must_use] pub fn diagonal_as_csc(&self) -> Self where T: Clone, @@ -498,6 +515,7 @@ impl CscMatrix { } /// Compute the transpose of the matrix. + #[must_use] pub fn transpose(&self) -> CscMatrix where T: Scalar, @@ -617,24 +635,28 @@ macro_rules! impl_csc_col_common_methods { impl<'a, T> $name { /// The number of global rows in the column. #[inline] + #[must_use] pub fn nrows(&self) -> usize { self.lane.minor_dim() } /// The number of non-zeros in this column. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.lane.nnz() } /// The row indices corresponding to explicitly stored entries in this column. #[inline] + #[must_use] pub fn row_indices(&self) -> &[usize] { self.lane.minor_indices() } /// The values corresponding to explicitly stored entries in this column. #[inline] + #[must_use] pub fn values(&self) -> &[T] { self.lane.values() } @@ -643,6 +665,7 @@ macro_rules! impl_csc_col_common_methods { /// /// Each call to this function incurs the cost of a binary search among the explicitly /// stored row entries. + #[must_use] pub fn get_entry(&self, global_row_index: usize) -> Option> { self.lane.get_entry(global_row_index) } @@ -669,6 +692,7 @@ impl<'a, T> CscColMut<'a, T> { } /// Returns a mutable entry for the given global row index. + #[must_use] pub fn get_entry_mut(&mut self, global_row_index: usize) -> Option> { self.lane.get_entry_mut(global_row_index) } diff --git a/nalgebra-sparse/src/csr.rs b/nalgebra-sparse/src/csr.rs index ded189eb..4c65908b 100644 --- a/nalgebra-sparse/src/csr.rs +++ b/nalgebra-sparse/src/csr.rs @@ -19,7 +19,7 @@ use std::slice::{Iter, IterMut}; /// /// # Usage /// -/// ```rust +/// ``` /// use nalgebra_sparse::csr::CsrMatrix; /// use nalgebra::{DMatrix, Matrix3x4}; /// use matrixcompare::assert_matrix_eq; @@ -97,7 +97,7 @@ use std::slice::{Iter, IterMut}; /// represents the matrix in a row-by-row fashion. The entries associated with row `i` are /// determined as follows: /// -/// ```rust +/// ``` /// # let row_offsets: Vec = vec![0, 0]; /// # let col_indices: Vec = vec![]; /// # let values: Vec = vec![]; @@ -192,12 +192,14 @@ impl CsrMatrix { /// The number of rows in the matrix. #[inline] + #[must_use] pub fn nrows(&self) -> usize { self.cs.pattern().major_dim() } /// The number of columns in the matrix. #[inline] + #[must_use] pub fn ncols(&self) -> usize { self.cs.pattern().minor_dim() } @@ -208,12 +210,14 @@ impl CsrMatrix { /// number of algebraically zero entries in the matrix. Explicitly stored entries can still /// be zero. Corresponds to the number of entries in the sparsity pattern. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.cs.pattern().nnz() } /// The row offsets defining part of the CSR format. #[inline] + #[must_use] pub fn row_offsets(&self) -> &[usize] { let (offsets, _, _) = self.cs.cs_data(); offsets @@ -221,6 +225,7 @@ impl CsrMatrix { /// The column indices defining part of the CSR format. #[inline] + #[must_use] pub fn col_indices(&self) -> &[usize] { let (_, indices, _) = self.cs.cs_data(); indices @@ -228,6 +233,7 @@ impl CsrMatrix { /// The non-zero values defining part of the CSR format. #[inline] + #[must_use] pub fn values(&self) -> &[T] { self.cs.values() } @@ -300,6 +306,7 @@ impl CsrMatrix { /// ------ /// Panics if row index is out of bounds. #[inline] + #[must_use] pub fn row(&self, index: usize) -> CsrRow { self.get_row(index).expect("Row index must be in bounds") } @@ -317,12 +324,14 @@ impl CsrMatrix { /// Return the row at the given row index, or `None` if out of bounds. #[inline] + #[must_use] pub fn get_row(&self, index: usize) -> Option> { self.cs.get_lane(index).map(|lane| CsrRow { lane }) } /// Mutable row access for the given row index, or `None` if out of bounds. #[inline] + #[must_use] pub fn get_row_mut(&mut self, index: usize) -> Option> { self.cs.get_lane_mut(index).map(|lane| CsrRowMut { lane }) } @@ -383,6 +392,7 @@ impl CsrMatrix { } /// Returns a reference to the underlying sparsity pattern. + #[must_use] pub fn pattern(&self) -> &SparsityPattern { self.cs.pattern() } @@ -399,6 +409,7 @@ impl CsrMatrix { /// /// Each call to this function incurs the cost of a binary search among the explicitly /// stored column entries for the given row. + #[must_use] pub fn get_entry(&self, row_index: usize, col_index: usize) -> Option> { self.cs.get_entry(row_index, col_index) } @@ -424,6 +435,7 @@ impl CsrMatrix { /// Panics /// ------ /// Panics if `row_index` or `col_index` is out of bounds. + #[must_use] pub fn index_entry(&self, row_index: usize, col_index: usize) -> SparseEntry { self.get_entry(row_index, col_index) .expect("Out of bounds matrix indices encountered") @@ -443,6 +455,7 @@ impl CsrMatrix { } /// Returns a triplet of slices `(row_offsets, col_indices, values)` that make up the CSR data. + #[must_use] pub fn csr_data(&self) -> (&[usize], &[usize], &[T]) { self.cs.cs_data() } @@ -455,6 +468,7 @@ impl CsrMatrix { /// Creates a sparse matrix that contains only the explicit entries decided by the /// given predicate. + #[must_use] pub fn filter

(&self, predicate: P) -> Self where T: Clone, @@ -470,6 +484,7 @@ impl CsrMatrix { /// Returns a new matrix representing the upper triangular part of this matrix. /// /// The result includes the diagonal of the matrix. + #[must_use] pub fn upper_triangle(&self) -> Self where T: Clone, @@ -480,6 +495,7 @@ impl CsrMatrix { /// Returns a new matrix representing the lower triangular part of this matrix. /// /// The result includes the diagonal of the matrix. + #[must_use] pub fn lower_triangle(&self) -> Self where T: Clone, @@ -488,6 +504,7 @@ impl CsrMatrix { } /// Returns the diagonal of the matrix as a sparse matrix. + #[must_use] pub fn diagonal_as_csr(&self) -> Self where T: Clone, @@ -498,6 +515,7 @@ impl CsrMatrix { } /// Compute the transpose of the matrix. + #[must_use] pub fn transpose(&self) -> CsrMatrix where T: Scalar, @@ -617,24 +635,28 @@ macro_rules! impl_csr_row_common_methods { impl<'a, T> $name { /// The number of global columns in the row. #[inline] + #[must_use] pub fn ncols(&self) -> usize { self.lane.minor_dim() } /// The number of non-zeros in this row. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.lane.nnz() } /// The column indices corresponding to explicitly stored entries in this row. #[inline] + #[must_use] pub fn col_indices(&self) -> &[usize] { self.lane.minor_indices() } /// The values corresponding to explicitly stored entries in this row. #[inline] + #[must_use] pub fn values(&self) -> &[T] { self.lane.values() } @@ -644,6 +666,7 @@ macro_rules! impl_csr_row_common_methods { /// Each call to this function incurs the cost of a binary search among the explicitly /// stored column entries. #[inline] + #[must_use] pub fn get_entry(&self, global_col_index: usize) -> Option> { self.lane.get_entry(global_col_index) } @@ -673,6 +696,7 @@ impl<'a, T> CsrRowMut<'a, T> { /// Returns a mutable entry for the given global column index. #[inline] + #[must_use] pub fn get_entry_mut(&mut self, global_col_index: usize) -> Option> { self.lane.get_entry_mut(global_col_index) } diff --git a/nalgebra-sparse/src/factorization/cholesky.rs b/nalgebra-sparse/src/factorization/cholesky.rs index a18761c9..0acc428d 100644 --- a/nalgebra-sparse/src/factorization/cholesky.rs +++ b/nalgebra-sparse/src/factorization/cholesky.rs @@ -42,6 +42,7 @@ impl CscSymbolicCholesky { } /// The pattern of the Cholesky factor `L`. + #[must_use] pub fn l_pattern(&self) -> &SparsityPattern { &self.l_pattern } @@ -171,6 +172,7 @@ impl CscCholesky { } /// Returns a reference to the Cholesky factor `L`. + #[must_use] pub fn l(&self) -> &CscMatrix { &self.l_factor } @@ -260,6 +262,7 @@ impl CscCholesky { /// # Panics /// /// Panics if `B` is not square. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve<'a>(&'a self, b: impl Into>) -> DMatrix { let b = b.into(); let mut output = b.clone_owned(); diff --git a/nalgebra-sparse/src/lib.rs b/nalgebra-sparse/src/lib.rs index 4b96717c..d50d8e15 100644 --- a/nalgebra-sparse/src/lib.rs +++ b/nalgebra-sparse/src/lib.rs @@ -73,7 +73,7 @@ //! //! # Example: COO -> CSR -> matrix-vector product //! -//! ```rust +//! ``` //! use nalgebra_sparse::{coo::CooMatrix, csr::CsrMatrix}; //! use nalgebra::{DMatrix, DVector}; //! use matrixcompare::assert_matrix_eq; @@ -93,6 +93,9 @@ //! coo.push(1, 2, 1.3); //! coo.push(2, 2, 4.1); //! +//! // ... or add entire dense matrices like so: +//! // coo.push_matrix(0, 0, &dense); +//! //! // The simplest way to construct a CSR matrix is to first construct a COO matrix, and //! // then convert it to CSR. The `From` trait is implemented for conversions between different //! // sparse matrix types. @@ -170,6 +173,7 @@ pub struct SparseFormatError { impl SparseFormatError { /// The type of error. + #[must_use] pub fn kind(&self) -> &SparseFormatErrorKind { &self.kind } diff --git a/nalgebra-sparse/src/ops/mod.rs b/nalgebra-sparse/src/ops/mod.rs index a6a21fbc..d7e6d432 100644 --- a/nalgebra-sparse/src/ops/mod.rs +++ b/nalgebra-sparse/src/ops/mod.rs @@ -90,7 +90,7 @@ //! `C <- 3.0 * C + 2.0 * A^T * B`, where `A`, `B`, `C` are matrices and `A^T` is the transpose //! of `A`. The simplest way to write this is: //! -//! ```rust +//! ``` //! # use nalgebra_sparse::csr::CsrMatrix; //! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10); //! # let mut c = CsrMatrix::identity(10); @@ -109,7 +109,7 @@ //! //! An alternative way to implement this expression (here using CSR matrices) is: //! -//! ```rust +//! ``` //! # use nalgebra_sparse::csr::CsrMatrix; //! # let a = CsrMatrix::identity(10); let b = CsrMatrix::identity(10); //! # let mut c = CsrMatrix::identity(10); @@ -140,11 +140,13 @@ pub enum Op { impl Op { /// Returns a reference to the inner value that the operation applies to. + #[must_use] pub fn inner_ref(&self) -> &T { self.as_ref().into_inner() } /// Returns an `Op` applied to a reference of the inner value. + #[must_use] pub fn as_ref(&self) -> Op<&T> { match self { Op::NoOp(obj) => Op::NoOp(&obj), diff --git a/nalgebra-sparse/src/ops/serial/mod.rs b/nalgebra-sparse/src/ops/serial/mod.rs index 82285e86..4b0cc904 100644 --- a/nalgebra-sparse/src/ops/serial/mod.rs +++ b/nalgebra-sparse/src/ops/serial/mod.rs @@ -96,11 +96,13 @@ impl OperationError { } /// The operation error kind. + #[must_use] pub fn kind(&self) -> &OperationErrorKind { &self.error_kind } /// The underlying error message. + #[must_use] pub fn message(&self) -> &str { self.message.as_str() } diff --git a/nalgebra-sparse/src/pattern.rs b/nalgebra-sparse/src/pattern.rs index 08552d6a..2e490285 100644 --- a/nalgebra-sparse/src/pattern.rs +++ b/nalgebra-sparse/src/pattern.rs @@ -60,18 +60,21 @@ impl SparsityPattern { /// The offsets for the major dimension. #[inline] + #[must_use] pub fn major_offsets(&self) -> &[usize] { &self.major_offsets } /// The indices for the minor dimension. #[inline] + #[must_use] pub fn minor_indices(&self) -> &[usize] { &self.minor_indices } /// The number of major lanes in the pattern. #[inline] + #[must_use] pub fn major_dim(&self) -> usize { assert!(self.major_offsets.len() > 0); self.major_offsets.len() - 1 @@ -79,12 +82,14 @@ impl SparsityPattern { /// The number of minor lanes in the pattern. #[inline] + #[must_use] pub fn minor_dim(&self) -> usize { self.minor_dim } /// The number of "non-zeros", i.e. explicitly stored entries in the pattern. #[inline] + #[must_use] pub fn nnz(&self) -> usize { self.minor_indices.len() } @@ -96,12 +101,14 @@ impl SparsityPattern { /// /// Panics if `major_index` is out of bounds. #[inline] + #[must_use] pub fn lane(&self, major_index: usize) -> &[usize] { self.get_lane(major_index).unwrap() } /// Get the lane at the given index, or `None` if out of bounds. #[inline] + #[must_use] pub fn get_lane(&self, major_index: usize) -> Option<&[usize]> { let offset_begin = *self.major_offsets().get(major_index)?; let offset_end = *self.major_offsets().get(major_index + 1)?; @@ -197,6 +204,7 @@ impl SparsityPattern { /// assert_eq!(entries, vec![(0, 0), (0, 2), (1, 1), (2, 0)]); /// ``` /// + #[must_use] pub fn entries(&self) -> SparsityPatternIter { SparsityPatternIter::from_pattern(self) } @@ -228,6 +236,7 @@ impl SparsityPattern { /// /// This is analogous to matrix transposition, i.e. an entry `(i, j)` becomes `(j, i)` in the /// new pattern. + #[must_use] pub fn transpose(&self) -> Self { // By using unit () values, we can use the same routines as for CSR/CSC matrices let values = vec![(); self.nnz()]; diff --git a/nalgebra-sparse/tests/unit_tests/coo.rs b/nalgebra-sparse/tests/unit_tests/coo.rs index c9fa1778..c70c5f97 100644 --- a/nalgebra-sparse/tests/unit_tests/coo.rs +++ b/nalgebra-sparse/tests/unit_tests/coo.rs @@ -252,3 +252,95 @@ fn coo_push_out_of_bounds_entries() { assert_panics!(coo.clone().push(3, 2, 1)); } } + +#[test] +fn coo_push_matrix_valid_entries() { + let mut coo = CooMatrix::new(3, 3); + + // Works with static + { + // new is row-major... + let inserted = nalgebra::SMatrix::::new(1, 2, 3, 4); + coo.push_matrix(1, 1, &inserted); + + // insert happens column-major, so expect transposition when read this way + assert_eq!( + coo.triplet_iter().collect::>(), + vec![(1, 1, &1), (2, 1, &3), (1, 2, &2), (2, 2, &4)] + ); + } + + // Works with owned dynamic + { + let inserted = nalgebra::DMatrix::::repeat(1, 2, 5); + coo.push_matrix(0, 0, &inserted); + + assert_eq!( + coo.triplet_iter().collect::>(), + vec![ + (1, 1, &1), + (2, 1, &3), + (1, 2, &2), + (2, 2, &4), + (0, 0, &5), + (0, 1, &5) + ] + ); + } + + // Works with sliced + { + let source = nalgebra::SMatrix::::new(6, 7, 8, 9); + let sliced = source.fixed_slice::<2, 1>(0, 0); + coo.push_matrix(1, 0, &sliced); + + assert_eq!( + coo.triplet_iter().collect::>(), + vec![ + (1, 1, &1), + (2, 1, &3), + (1, 2, &2), + (2, 2, &4), + (0, 0, &5), + (0, 1, &5), + (1, 0, &6), + (2, 0, &8) + ] + ); + } +} + +#[test] +fn coo_push_matrix_out_of_bounds_entries() { + // 0x0 + { + let inserted = nalgebra::SMatrix::::new(1); + assert_panics!(CooMatrix::new(0, 0).push_matrix(0, 0, &inserted)); + } + // 0x1 + { + let inserted = nalgebra::SMatrix::::new(1); + assert_panics!(CooMatrix::new(1, 0).push_matrix(0, 0, &inserted)); + } + // 1x0 + { + let inserted = nalgebra::SMatrix::::new(1); + assert_panics!(CooMatrix::new(0, 1).push_matrix(0, 0, &inserted)); + } + + // 3x3 exceeds col-dim + { + let inserted = nalgebra::SMatrix::::repeat(1); + assert_panics!(CooMatrix::new(3, 3).push_matrix(0, 2, &inserted)); + } + // 3x3 exceeds row-dim + { + let inserted = nalgebra::SMatrix::::repeat(1); + assert_panics!(CooMatrix::new(3, 3).push_matrix(2, 0, &inserted)); + } + // 3x3 exceeds row-dim and row-dim + { + let inserted = nalgebra::SMatrix::::repeat(1); + assert_panics!(CooMatrix::new(3, 3).push_matrix(2, 2, &inserted)); + } +} diff --git a/src/base/allocator.rs b/src/base/allocator.rs index 062ffbc2..64871635 100644 --- a/src/base/allocator.rs +++ b/src/base/allocator.rs @@ -40,6 +40,7 @@ pub trait Reallocator: /// Reallocates a buffer of shape `(RTo, CTo)`, possibly reusing a previously allocated buffer /// `buf`. Data stored by `buf` are linearly copied to the output: /// + /// # Safety /// * The copy is performed as if both were just arrays (without a matrix structure). /// * If `buf` is larger than the output size, then extra elements of `buf` are truncated. /// * If `buf` is smaller than the output size, then extra elements of the output are left diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index 6d681ed5..643bc631 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -101,8 +101,8 @@ where } #[inline] - fn as_slice(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.ptr(), R * C) } + unsafe fn as_slice_unchecked(&self) -> &[T] { + std::slice::from_raw_parts(self.ptr(), R * C) } } @@ -118,8 +118,8 @@ where } #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) } + unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { + std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) } } @@ -286,11 +286,7 @@ where unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { for element in self.as_mut_slice() { let temp = bytes; - bytes = if let Some(remainder) = element.exhume(temp) { - remainder - } else { - return None; - } + bytes = element.exhume(temp)? } Some(bytes) } @@ -327,7 +323,7 @@ mod rkyv_impl { for ArrayStorage { fn serialize(&self, serializer: &mut S) -> Result { - Ok(self.0.serialize(serializer)?) + self.0.serialize(serializer) } } diff --git a/src/base/blas.rs b/src/base/blas.rs index 05705f17..b705c6c1 100644 --- a/src/base/blas.rs +++ b/src/base/blas.rs @@ -193,6 +193,7 @@ where /// ``` /// #[inline] + #[must_use] pub fn dot(&self, rhs: &Matrix) -> T where SB: Storage, @@ -221,6 +222,7 @@ where /// assert_ne!(vec1.dotc(&vec2), vec1.dot(&vec2)); /// ``` #[inline] + #[must_use] pub fn dotc(&self, rhs: &Matrix) -> T where T: SimdComplexField, @@ -248,6 +250,7 @@ where /// assert_eq!(mat1.tr_dot(&mat2), 9.1); /// ``` #[inline] + #[must_use] pub fn tr_dot(&self, rhs: &Matrix) -> T where SB: Storage, @@ -275,6 +278,7 @@ where } } +#[allow(clippy::too_many_arguments)] fn array_axcpy( y: &mut [T], a: T, @@ -331,6 +335,7 @@ where /// assert_eq!(vec1, Vector3::new(6.0, 12.0, 18.0)); /// ``` #[inline] + #[allow(clippy::many_single_char_names)] pub fn axcpy(&mut self, a: T, x: &Vector, c: T, b: T) where SB: Storage, @@ -341,13 +346,17 @@ where let rstride1 = self.strides().0; let rstride2 = x.strides().0; - let y = self.data.as_mut_slice(); - let x = x.data.as_slice(); + unsafe { + // SAFETY: the conversion to slices is OK because we access the + // elements taking the strides into account. + let y = self.data.as_mut_slice_unchecked(); + let x = x.data.as_slice_unchecked(); - if !b.is_zero() { - array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len()); - } else { - array_axc(y, a, x, c, rstride1, rstride2, x.len()); + if !b.is_zero() { + array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len()); + } else { + array_axc(y, a, x, c, rstride1, rstride2, x.len()); + } } } @@ -1379,12 +1388,12 @@ where { work.gemv(T::one(), mid, &rhs.column(0), T::zero()); self.column_mut(0) - .gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone()); + .gemv_tr(alpha.inlined_clone(), rhs, work, beta.inlined_clone()); for j in 1..rhs.ncols() { work.gemv(T::one(), mid, &rhs.column(j), T::zero()); self.column_mut(j) - .gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone()); + .gemv_tr(alpha.inlined_clone(), rhs, work, beta.inlined_clone()); } } diff --git a/src/base/cg.rs b/src/base/cg.rs index 2b62524b..742824c7 100644 --- a/src/base/cg.rs +++ b/src/base/cg.rs @@ -386,7 +386,7 @@ impl, DimNameDiff::::name()), ) - .tr_dot(&shift); + .tr_dot(shift); let post_translation = self.generic_slice( (0, 0), (DimNameDiff::::name(), DimNameDiff::::name()), @@ -423,7 +423,7 @@ where (D::dim() - 1, 0), (Const::<1>, DimNameDiff::::name()), ); - let n = normalizer.tr_dot(&v); + let n = normalizer.tr_dot(v); if !n.is_zero() { return transform * (v / n); diff --git a/src/base/componentwise.rs b/src/base/componentwise.rs index 2ba49b2a..02b2cae6 100644 --- a/src/base/componentwise.rs +++ b/src/base/componentwise.rs @@ -28,6 +28,7 @@ impl> Matrix { /// assert_eq!(a.abs(), Matrix2::new(0.0, 1.0, 2.0, 3.0)) /// ``` #[inline] + #[must_use] pub fn abs(&self) -> OMatrix where T: Signed, @@ -49,6 +50,7 @@ macro_rules! component_binop_impl( ($($binop: ident, $binop_mut: ident, $binop_assign: ident, $cmpy: ident, $Trait: ident . $op: ident . $op_assign: ident, $desc:expr, $desc_cmpy:expr, $desc_mut:expr);* $(;)*) => {$( #[doc = $desc] #[inline] + #[must_use] pub fn $binop(&self, rhs: &Matrix) -> MatrixComponentOp where T: $Trait, R2: Dim, C2: Dim, @@ -251,6 +253,7 @@ impl> Matrix /// assert_eq!(u.inf(&v), expected) /// ``` #[inline] + #[must_use] pub fn inf(&self, other: &Self) -> OMatrix where T: SimdPartialOrd, @@ -271,6 +274,7 @@ impl> Matrix /// assert_eq!(u.sup(&v), expected) /// ``` #[inline] + #[must_use] pub fn sup(&self, other: &Self) -> OMatrix where T: SimdPartialOrd, @@ -291,6 +295,7 @@ impl> Matrix /// assert_eq!(u.inf_sup(&v), expected) /// ``` #[inline] + #[must_use] pub fn inf_sup(&self, other: &Self) -> (OMatrix, OMatrix) where T: SimdPartialOrd, diff --git a/src/base/construction.rs b/src/base/construction.rs index e3e56b53..d5ecc7c1 100644 --- a/src/base/construction.rs +++ b/src/base/construction.rs @@ -53,7 +53,10 @@ impl OMatrix where DefaultAllocator: Allocator, { - /// Creates a new uninitialized matrix. If the matrix has a compile-time dimension, this panics + /// Creates a new uninitialized matrix. + /// + /// # Safety + /// If the matrix has a compile-time dimension, this panics /// if `nrows != R::to_usize()` or `ncols != C::to_usize()`. #[inline] pub unsafe fn new_uninitialized_generic(nrows: R, ncols: C) -> mem::MaybeUninit { @@ -827,7 +830,7 @@ where Standard: Distribution, { #[inline] - fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> OMatrix { + fn sample(&self, rng: &mut G) -> OMatrix { let nrows = R::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10)); let ncols = C::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10)); @@ -864,7 +867,7 @@ where { /// Generate a uniformly distributed random unit vector. #[inline] - fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Unit> { + fn sample(&self, rng: &mut G) -> Unit> { Unit::new_normalize(OVector::from_distribution_generic( D::name(), Const::<1>, @@ -906,6 +909,7 @@ macro_rules! componentwise_constructors_impl( impl Matrix, Const<$C>, ArrayStorage> { /// Initializes this matrix from its components. #[inline] + #[allow(clippy::too_many_arguments)] pub const fn new($($($args: T),*),*) -> Self { unsafe { Self::from_data_statically_unchecked( diff --git a/src/base/construction_slice.rs b/src/base/construction_slice.rs index 43f3692b..7094bdca 100644 --- a/src/base/construction_slice.rs +++ b/src/base/construction_slice.rs @@ -10,6 +10,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> { /// Creates, without bound-checking, a matrix slice from an array and with dimensions and strides specified by generic types instances. /// + /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dynamic::new()`. #[inline] @@ -59,6 +60,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> impl<'a, T: Scalar, R: Dim, C: Dim> MatrixSlice<'a, T, R, C> { /// Creates, without bound-checking, a matrix slice from an array and with dimensions specified by generic types instances. /// + /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dynamic::new()`. #[inline] @@ -146,6 +148,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> { /// Creates, without bound-checking, a mutable matrix slice from an array and with dimensions and strides specified by generic types instances. /// + /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dynamic::new()`. #[inline] @@ -217,6 +220,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> impl<'a, T: Scalar, R: Dim, C: Dim> MatrixSliceMutMN<'a, T, R, C> { /// Creates, without bound-checking, a mutable matrix slice from an array and with dimensions specified by generic types instances. /// + /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dynamic::new()`. #[inline] diff --git a/src/base/conversion.rs b/src/base/conversion.rs index 532ab8ed..8ede11ca 100644 --- a/src/base/conversion.rs +++ b/src/base/conversion.rs @@ -1,8 +1,8 @@ #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use simba::scalar::{SubsetOf, SupersetOf}; +use std::borrow::{Borrow, BorrowMut}; use std::convert::{AsMut, AsRef, From, Into}; -use std::mem; use simba::simd::{PrimitiveSimdValue, SimdValue}; @@ -110,11 +110,11 @@ impl From<[T; D]> for SVector { } } -impl Into<[T; D]> for SVector { +impl From> for [T; D] { #[inline] - fn into(self) -> [T; D] { + fn from(vec: SVector) -> Self { // TODO: unfortunately, we must clone because we can move out of an array. - self.data.0[0].clone() + vec.data.0[0].clone() } } @@ -128,13 +128,13 @@ where } } -impl Into<[T; D]> for RowSVector +impl From> for [T; D] where Const: IsNotStaticOne, { #[inline] - fn into(self) -> [T; D] { - self.transpose().into() + fn from(vec: RowSVector) -> [T; D] { + vec.transpose().into() } } @@ -146,7 +146,7 @@ macro_rules! impl_from_into_asref_1D( #[inline] fn as_ref(&self) -> &[T; $SZ] { unsafe { - mem::transmute(self.data.ptr()) + &*(self.data.ptr() as *const [T; $SZ]) } } } @@ -157,7 +157,7 @@ macro_rules! impl_from_into_asref_1D( #[inline] fn as_mut(&mut self) -> &mut [T; $SZ] { unsafe { - mem::transmute(self.data.ptr_mut()) + &mut *(self.data.ptr_mut() as *mut [T; $SZ]) } } } @@ -186,39 +186,54 @@ impl From<[[T; R]; C]> for SMatrix Into<[[T; R]; C]> for SMatrix { +impl From> for [[T; R]; C] { #[inline] - fn into(self) -> [[T; R]; C] { - self.data.0 + fn from(vec: SMatrix) -> Self { + vec.data.0 } } -macro_rules! impl_from_into_asref_2D( - ($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$( - impl AsRef<[[T; $SZRows]; $SZCols]> for Matrix +macro_rules! impl_from_into_asref_borrow_2D( + + //does the impls on one case for either AsRef/AsMut and Borrow/BorrowMut + ( + ($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr); + $Ref:ident.$ref:ident(), $Mut:ident.$mut:ident() + ) => { + impl $Ref<[[T; $SZRows]; $SZCols]> for Matrix where S: ContiguousStorage { #[inline] - fn as_ref(&self) -> &[[T; $SZRows]; $SZCols] { + fn $ref(&self) -> &[[T; $SZRows]; $SZCols] { unsafe { - mem::transmute(self.data.ptr()) + &*(self.data.ptr() as *const [[T; $SZRows]; $SZCols]) } } } - impl AsMut<[[T; $SZRows]; $SZCols]> for Matrix + impl $Mut<[[T; $SZRows]; $SZCols]> for Matrix where S: ContiguousStorageMut { #[inline] - fn as_mut(&mut self) -> &mut [[T; $SZRows]; $SZCols] { + fn $mut(&mut self) -> &mut [[T; $SZRows]; $SZCols] { unsafe { - mem::transmute(self.data.ptr_mut()) + &mut *(self.data.ptr_mut() as *mut [[T; $SZRows]; $SZCols]) } } } + }; + + //collects the mappings from typenum pairs to consts + ($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$( + impl_from_into_asref_borrow_2D!( + ($NRows, $NCols) => ($SZRows, $SZCols); AsRef.as_ref(), AsMut.as_mut() + ); + impl_from_into_asref_borrow_2D!( + ($NRows, $NCols) => ($SZRows, $SZCols); Borrow.borrow(), BorrowMut.borrow_mut() + ); )*} ); // Implement for matrices with shape 2x2 .. 6x6. -impl_from_into_asref_2D!( +impl_from_into_asref_borrow_2D!( (U2, U2) => (2, 2); (U2, U3) => (2, 3); (U2, U4) => (2, 4); (U2, U5) => (2, 5); (U2, U6) => (2, 6); (U3, U2) => (3, 2); (U3, U3) => (3, 3); (U3, U4) => (3, 4); (U3, U5) => (3, 5); (U3, U6) => (3, 6); (U4, U2) => (4, 2); (U4, U3) => (4, 3); (U4, U4) => (4, 4); (U4, U5) => (4, 5); (U4, U6) => (4, 6); @@ -427,21 +442,21 @@ impl<'a, T: Scalar> From> for DVector { } } -impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorage> Into<&'a [T]> - for &'a Matrix +impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorage> + From<&'a Matrix> for &'a [T] { #[inline] - fn into(self) -> &'a [T] { - self.as_slice() + fn from(matrix: &'a Matrix) -> Self { + matrix.as_slice() } } -impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorageMut> Into<&'a mut [T]> - for &'a mut Matrix +impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: ContiguousStorageMut> + From<&'a mut Matrix> for &'a mut [T] { #[inline] - fn into(self) -> &'a mut [T] { - self.as_mut_slice() + fn from(matrix: &'a mut Matrix) -> Self { + matrix.as_mut_slice() } } @@ -452,6 +467,12 @@ impl<'a, T: Scalar + Copy> From<&'a [T]> for DVectorSlice<'a, T> { } } +impl<'a, T: Scalar> From> for &'a [T] { + fn from(vec: DVectorSlice<'a, T>) -> &'a [T] { + vec.data.into_slice() + } +} + impl<'a, T: Scalar + Copy> From<&'a mut [T]> for DVectorSliceMut<'a, T> { #[inline] fn from(slice: &'a mut [T]) -> Self { @@ -459,6 +480,12 @@ impl<'a, T: Scalar + Copy> From<&'a mut [T]> for DVectorSliceMut<'a, T> { } } +impl<'a, T: Scalar> From> for &'a mut [T] { + fn from(vec: DVectorSliceMut<'a, T>) -> &'a mut [T] { + vec.data.into_slice_mut() + } +} + impl From<[OMatrix; 2]> for OMatrix where diff --git a/src/base/coordinates.rs b/src/base/coordinates.rs index bf69b09e..be05d3e5 100644 --- a/src/base/coordinates.rs +++ b/src/base/coordinates.rs @@ -4,7 +4,6 @@ //! components using their names. For example, if `v` is a 3D vector, one can write `v.z` instead //! of `v[2]`. -use std::mem; use std::ops::{Deref, DerefMut}; use crate::base::dimension::{U1, U2, U3, U4, U5, U6}; @@ -38,7 +37,7 @@ macro_rules! deref_impl( #[inline] fn deref(&self) -> &Self::Target { - unsafe { mem::transmute(self.data.ptr()) } + unsafe { &*(self.data.ptr() as *const Self::Target) } } } @@ -46,7 +45,7 @@ macro_rules! deref_impl( where S: ContiguousStorageMut { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { mem::transmute(self.data.ptr_mut()) } + unsafe { &mut *(self.data.ptr_mut() as *mut Self::Target) } } } } diff --git a/src/base/default_allocator.rs b/src/base/default_allocator.rs index a9f994fe..4bfa11a8 100644 --- a/src/base/default_allocator.rs +++ b/src/base/default_allocator.rs @@ -16,7 +16,7 @@ use crate::base::array_storage::ArrayStorage; #[cfg(any(feature = "alloc", feature = "std"))] use crate::base::dimension::Dynamic; use crate::base::dimension::{Dim, DimName}; -use crate::base::storage::{Storage, StorageMut}; +use crate::base::storage::{ContiguousStorageMut, Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::vec_storage::VecStorage; use crate::base::Scalar; diff --git a/src/base/dimension.rs b/src/base/dimension.rs index 2b5ccec3..8573dd59 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -20,7 +20,7 @@ pub struct Dynamic { impl Dynamic { /// A dynamic size equal to `value`. #[inline] - pub fn new(value: usize) -> Self { + pub const fn new(value: usize) -> Self { Self { value } } } diff --git a/src/base/edition.rs b/src/base/edition.rs index 17c08c98..f403f9d3 100644 --- a/src/base/edition.rs +++ b/src/base/edition.rs @@ -11,13 +11,14 @@ use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, Shap #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::dimension::Dynamic; use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1}; -use crate::base::storage::{ReshapableStorage, Storage, StorageMut}; +use crate::base::storage::{ContiguousStorageMut, ReshapableStorage, Storage, StorageMut}; use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector}; /// # Rows and columns extraction impl> Matrix { /// Extracts the upper triangular part of this matrix (including the diagonal). #[inline] + #[must_use] pub fn upper_triangle(&self) -> OMatrix where DefaultAllocator: Allocator, @@ -30,6 +31,7 @@ impl> Matrix { /// Extracts the lower triangular part of this matrix (including the diagonal). #[inline] + #[must_use] pub fn lower_triangle(&self) -> OMatrix where DefaultAllocator: Allocator, @@ -42,6 +44,7 @@ impl> Matrix { /// Creates a new matrix by extracting the given set of rows from `self`. #[cfg(any(feature = "std", feature = "alloc"))] + #[must_use] pub fn select_rows<'a, I>(&self, irows: I) -> OMatrix where I: IntoIterator, @@ -78,6 +81,7 @@ impl> Matrix { /// Creates a new matrix by extracting the given set of columns from `self`. #[cfg(any(feature = "std", feature = "alloc"))] + #[must_use] pub fn select_columns<'a, I>(&self, icols: I) -> OMatrix where I: IntoIterator, @@ -583,6 +587,7 @@ impl> Matrix { /// Inserts `ninsert.value()` columns starting at the `i-th` place of this matrix. /// + /// # Safety /// The added column values are not initialized. #[inline] pub unsafe fn insert_columns_generic_uninitialized( @@ -664,6 +669,7 @@ impl> Matrix { /// Inserts `ninsert.value()` rows at the `i-th` place of this matrix. /// + /// # Safety /// The added rows values are not initialized. /// This is the generic implementation of `.insert_rows(...)` and /// `.insert_fixed_rows(...)` which have nicer API interfaces. diff --git a/src/base/indexing.rs b/src/base/indexing.rs index 3e0c085e..5107035c 100644 --- a/src/base/indexing.rs +++ b/src/base/indexing.rs @@ -44,7 +44,7 @@ impl DimRange for usize { #[test] fn dimrange_usize() { assert_eq!(DimRange::contained_by(&0, Const::<0>), false); - assert_eq!(DimRange::contained_by(&0, Const::<1>), true); + assert!(DimRange::contained_by(&0, Const::<1>)); } impl DimRange for ops::Range { @@ -68,24 +68,23 @@ impl DimRange for ops::Range { #[test] fn dimrange_range_usize() { - use std::usize::MAX; assert_eq!(DimRange::contained_by(&(0..0), Const::<0>), false); assert_eq!(DimRange::contained_by(&(0..1), Const::<0>), false); - assert_eq!(DimRange::contained_by(&(0..1), Const::<1>), true); + assert!(DimRange::contained_by(&(0..1), Const::<1>)); + assert!(DimRange::contained_by( + &((usize::MAX - 1)..usize::MAX), + Dynamic::new(usize::MAX) + )); assert_eq!( - DimRange::contained_by(&((MAX - 1)..MAX), Dynamic::new(MAX)), - true - ); - assert_eq!( - DimRange::length(&((MAX - 1)..MAX), Dynamic::new(MAX)), + DimRange::length(&((usize::MAX - 1)..usize::MAX), Dynamic::new(usize::MAX)), Dynamic::new(1) ); assert_eq!( - DimRange::length(&(MAX..(MAX - 1)), Dynamic::new(MAX)), + DimRange::length(&(usize::MAX..(usize::MAX - 1)), Dynamic::new(usize::MAX)), Dynamic::new(0) ); assert_eq!( - DimRange::length(&(MAX..MAX), Dynamic::new(MAX)), + DimRange::length(&(usize::MAX..usize::MAX), Dynamic::new(usize::MAX)), Dynamic::new(0) ); } @@ -111,20 +110,19 @@ impl DimRange for ops::RangeFrom { #[test] fn dimrange_rangefrom_usize() { - use std::usize::MAX; assert_eq!(DimRange::contained_by(&(0..), Const::<0>), false); assert_eq!(DimRange::contained_by(&(0..), Const::<0>), false); - assert_eq!(DimRange::contained_by(&(0..), Const::<1>), true); + assert!(DimRange::contained_by(&(0..), Const::<1>)); + assert!(DimRange::contained_by( + &((usize::MAX - 1)..), + Dynamic::new(usize::MAX) + )); assert_eq!( - DimRange::contained_by(&((MAX - 1)..), Dynamic::new(MAX)), - true - ); - assert_eq!( - DimRange::length(&((MAX - 1)..), Dynamic::new(MAX)), + DimRange::length(&((usize::MAX - 1)..), Dynamic::new(usize::MAX)), Dynamic::new(1) ); assert_eq!( - DimRange::length(&(MAX..), Dynamic::new(MAX)), + DimRange::length(&(usize::MAX..), Dynamic::new(usize::MAX)), Dynamic::new(0) ); } @@ -177,7 +175,7 @@ impl DimRange for ops::RangeFull { #[test] fn dimrange_rangefull() { - assert_eq!(DimRange::contained_by(&(..), Const::<0>), true); + assert!(DimRange::contained_by(&(..), Const::<0>)); assert_eq!(DimRange::length(&(..), Const::<1>), Const::<1>); } @@ -206,32 +204,31 @@ impl DimRange for ops::RangeInclusive { #[test] fn dimrange_rangeinclusive_usize() { - use std::usize::MAX; assert_eq!(DimRange::contained_by(&(0..=0), Const::<0>), false); - assert_eq!(DimRange::contained_by(&(0..=0), Const::<1>), true); + assert!(DimRange::contained_by(&(0..=0), Const::<1>)); assert_eq!( - DimRange::contained_by(&(MAX..=MAX), Dynamic::new(MAX)), + DimRange::contained_by(&(usize::MAX..=usize::MAX), Dynamic::new(usize::MAX)), false ); assert_eq!( - DimRange::contained_by(&((MAX - 1)..=MAX), Dynamic::new(MAX)), + DimRange::contained_by(&((usize::MAX - 1)..=usize::MAX), Dynamic::new(usize::MAX)), false ); - assert_eq!( - DimRange::contained_by(&((MAX - 1)..=(MAX - 1)), Dynamic::new(MAX)), - true - ); + assert!(DimRange::contained_by( + &((usize::MAX - 1)..=(usize::MAX - 1)), + Dynamic::new(usize::MAX) + )); assert_eq!(DimRange::length(&(0..=0), Const::<1>), Dynamic::new(1)); assert_eq!( - DimRange::length(&((MAX - 1)..=MAX), Dynamic::new(MAX)), + DimRange::length(&((usize::MAX - 1)..=usize::MAX), Dynamic::new(usize::MAX)), Dynamic::new(2) ); assert_eq!( - DimRange::length(&(MAX..=(MAX - 1)), Dynamic::new(MAX)), + DimRange::length(&(usize::MAX..=(usize::MAX - 1)), Dynamic::new(usize::MAX)), Dynamic::new(0) ); assert_eq!( - DimRange::length(&(MAX..=MAX), Dynamic::new(MAX)), + DimRange::length(&(usize::MAX..=usize::MAX), Dynamic::new(usize::MAX)), Dynamic::new(1) ); } @@ -257,21 +254,20 @@ impl DimRange for ops::RangeTo { #[test] fn dimrange_rangeto_usize() { - use std::usize::MAX; - assert_eq!(DimRange::contained_by(&(..0), Const::<0>), true); + assert!(DimRange::contained_by(&(..0), Const::<0>)); assert_eq!(DimRange::contained_by(&(..1), Const::<0>), false); - assert_eq!(DimRange::contained_by(&(..0), Const::<1>), true); + assert!(DimRange::contained_by(&(..0), Const::<1>)); + assert!(DimRange::contained_by( + &(..(usize::MAX - 1)), + Dynamic::new(usize::MAX) + )); assert_eq!( - DimRange::contained_by(&(..(MAX - 1)), Dynamic::new(MAX)), - true + DimRange::length(&(..(usize::MAX - 1)), Dynamic::new(usize::MAX)), + Dynamic::new(usize::MAX - 1) ); assert_eq!( - DimRange::length(&(..(MAX - 1)), Dynamic::new(MAX)), - Dynamic::new(MAX - 1) - ); - assert_eq!( - DimRange::length(&(..MAX), Dynamic::new(MAX)), - Dynamic::new(MAX) + DimRange::length(&(..usize::MAX), Dynamic::new(usize::MAX)), + Dynamic::new(usize::MAX) ); } @@ -296,21 +292,20 @@ impl DimRange for ops::RangeToInclusive { #[test] fn dimrange_rangetoinclusive_usize() { - use std::usize::MAX; assert_eq!(DimRange::contained_by(&(..=0), Const::<0>), false); assert_eq!(DimRange::contained_by(&(..=1), Const::<0>), false); - assert_eq!(DimRange::contained_by(&(..=0), Const::<1>), true); + assert!(DimRange::contained_by(&(..=0), Const::<1>)); assert_eq!( - DimRange::contained_by(&(..=(MAX)), Dynamic::new(MAX)), + DimRange::contained_by(&(..=(usize::MAX)), Dynamic::new(usize::MAX)), false ); + assert!(DimRange::contained_by( + &(..=(usize::MAX - 1)), + Dynamic::new(usize::MAX) + )); assert_eq!( - DimRange::contained_by(&(..=(MAX - 1)), Dynamic::new(MAX)), - true - ); - assert_eq!( - DimRange::length(&(..=(MAX - 1)), Dynamic::new(MAX)), - Dynamic::new(MAX) + DimRange::length(&(..=(usize::MAX - 1)), Dynamic::new(usize::MAX)), + Dynamic::new(usize::MAX) ); } @@ -485,6 +480,7 @@ impl> Matrix { /// Produces a view of the data at the given index, or /// `None` if the index is out of bounds. #[inline] + #[must_use] pub fn get<'a, I>(&'a self, index: I) -> Option where I: MatrixIndex<'a, T, R, C, S>, @@ -495,6 +491,7 @@ impl> Matrix { /// Produces a mutable view of the data at the given index, or /// `None` if the index is out of bounds. #[inline] + #[must_use] pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option where S: StorageMut, @@ -506,6 +503,7 @@ impl> Matrix { /// Produces a view of the data at the given index, or /// panics if the index is out of bounds. #[inline] + #[must_use] pub fn index<'a, I>(&'a self, index: I) -> I::Output where I: MatrixIndex<'a, T, R, C, S>, @@ -527,6 +525,7 @@ impl> Matrix { /// Produces a view of the data at the given index, without doing /// any bounds checking. #[inline] + #[must_use] pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Output where I: MatrixIndex<'a, T, R, C, S>, @@ -537,6 +536,7 @@ impl> Matrix { /// Returns a mutable view of the data at the given index, without doing /// any bounds checking. #[inline] + #[must_use] pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut where S: StorageMut, diff --git a/src/base/interpolation.rs b/src/base/interpolation.rs index c008743a..d5661e40 100644 --- a/src/base/interpolation.rs +++ b/src/base/interpolation.rs @@ -20,6 +20,7 @@ impl>(&self, rhs: &Vector, t: T) -> OVector where DefaultAllocator: Allocator, @@ -45,6 +46,7 @@ impl>(&self, rhs: &Vector, t: T) -> OVector where T: RealField, @@ -72,6 +74,7 @@ impl> Unit> { /// /// assert_eq!(v, v2); /// ``` + #[must_use] pub fn slerp>( &self, rhs: &Unit>, @@ -89,6 +92,7 @@ impl> Unit> { /// /// Returns `None` if the two vectors are almost collinear and with opposite direction /// (in this case, there is an infinity of possible results). + #[must_use] pub fn try_slerp>( &self, rhs: &Unit>, diff --git a/src/base/iter.rs b/src/base/iter.rs index 1592299d..0e13e4d3 100644 --- a/src/base/iter.rs +++ b/src/base/iter.rs @@ -96,6 +96,10 @@ macro_rules! iterator { let stride = self.strides.0.value(); self.ptr = self.ptr.add(stride); } + + // We want either `& *last` or `&mut *last` here, depending + // on the mutability of `$Ref`. + #[allow(clippy::transmute_ptr_to_ref)] Some(mem::transmute(old)) } } @@ -139,13 +143,13 @@ macro_rules! iterator { let inner_remaining = self.size % inner_size; // Compute pointer to last element - let last = self.ptr.offset( - (outer_remaining * outer_stride + inner_remaining * inner_stride) - as isize, - ); + let last = self + .ptr + .add((outer_remaining * outer_stride + inner_remaining * inner_stride)); // We want either `& *last` or `&mut *last` here, depending // on the mutability of `$Ref`. + #[allow(clippy::transmute_ptr_to_ref)] Some(mem::transmute(last)) } } diff --git a/src/base/matrix.rs b/src/base/matrix.rs index bc9942be..319e8eb9 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -336,7 +336,7 @@ mod rkyv_impl { for Matrix { fn serialize(&self, serializer: &mut _S) -> Result { - Ok(self.data.serialize(serializer)?) + self.data.serialize(serializer) } } @@ -441,6 +441,7 @@ impl> Matrix { /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.shape(), (3, 4)); #[inline] + #[must_use] pub fn shape(&self) -> (usize, usize) { let (nrows, ncols) = self.data.shape(); (nrows.value(), ncols.value()) @@ -455,6 +456,7 @@ impl> Matrix { /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.nrows(), 3); #[inline] + #[must_use] pub fn nrows(&self) -> usize { self.shape().0 } @@ -468,6 +470,7 @@ impl> Matrix { /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.ncols(), 4); #[inline] + #[must_use] pub fn ncols(&self) -> usize { self.shape().1 } @@ -483,6 +486,7 @@ impl> Matrix { /// // The column strides is the number of steps (here 2) multiplied by the corresponding dimension. /// assert_eq!(mat.strides(), (1, 10)); #[inline] + #[must_use] pub fn strides(&self) -> (usize, usize) { let (srows, scols) = self.data.strides(); (srows.value(), scols.value()) @@ -501,6 +505,7 @@ impl> Matrix { /// assert_eq!(m[i], m[3]); /// ``` #[inline] + #[must_use] pub fn vector_to_matrix_index(&self, i: usize) -> (usize, usize) { let (nrows, ncols) = self.shape(); @@ -529,6 +534,7 @@ impl> Matrix { /// assert_eq!(unsafe { *ptr }, m[0]); /// ``` #[inline] + #[must_use] pub fn as_ptr(&self) -> *const T { self.data.ptr() } @@ -537,6 +543,7 @@ impl> Matrix { /// /// See `relative_eq` from the `RelativeEq` trait for more details. #[inline] + #[must_use] pub fn relative_eq( &self, other: &Matrix, @@ -559,6 +566,8 @@ impl> Matrix { /// Tests whether `self` and `rhs` are exactly equal. #[inline] + #[must_use] + #[allow(clippy::should_implement_trait)] pub fn eq(&self, other: &Matrix) -> bool where T: PartialEq, @@ -609,6 +618,7 @@ impl> Matrix { /// Clones this matrix to one that owns its data. #[inline] + #[must_use] pub fn clone_owned(&self) -> OMatrix where DefaultAllocator: Allocator, @@ -619,6 +629,7 @@ impl> Matrix { /// Clones this matrix into one that owns its data. The actual type of the result depends on /// matrix storage combination rules for addition. #[inline] + #[must_use] pub fn clone_owned_sum(&self) -> MatrixSum where R2: Dim, @@ -692,6 +703,7 @@ impl> Matrix { impl> Matrix { /// Returns a matrix containing the result of `f` applied to each of its entries. #[inline] + #[must_use] pub fn map T2>(&self, mut f: F) -> OMatrix where DefaultAllocator: Allocator, @@ -738,6 +750,7 @@ impl> Matrix { /// - If the matrix has has least one component, then `init_f` is called with the first component /// to compute the initial value. Folding then continues on all the remaining components of the matrix. #[inline] + #[must_use] pub fn fold_with( &self, init_f: impl FnOnce(Option<&T>) -> T2, @@ -751,6 +764,7 @@ impl> Matrix { /// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`, /// `f` also gets passed the row and column index, i.e. `f(row, col, value)`. #[inline] + #[must_use] pub fn map_with_location T2>( &self, mut f: F, @@ -778,6 +792,7 @@ impl> Matrix { /// Returns a matrix containing the result of `f` applied to each entries of `self` and /// `rhs`. #[inline] + #[must_use] pub fn zip_map(&self, rhs: &Matrix, mut f: F) -> OMatrix where T2: Scalar, @@ -813,6 +828,7 @@ impl> Matrix { /// Returns a matrix containing the result of `f` applied to each entries of `self` and /// `b`, and `c`. #[inline] + #[must_use] pub fn zip_zip_map( &self, b: &Matrix, @@ -860,6 +876,7 @@ impl> Matrix { /// Folds a function `f` on each entry of `self`. #[inline] + #[must_use] pub fn fold(&self, init: Acc, mut f: impl FnMut(Acc, T) -> Acc) -> Acc { let (nrows, ncols) = self.data.shape(); @@ -879,6 +896,7 @@ impl> Matrix { /// Folds a function `f` on each pairs of entries from `self` and `rhs`. #[inline] + #[must_use] pub fn zip_fold( &self, rhs: &Matrix, @@ -1238,6 +1256,7 @@ impl> Matrix { impl> Vector { /// Gets a reference to the i-th element of this column vector without bound checking. #[inline] + #[must_use] pub unsafe fn vget_unchecked(&self, i: usize) -> &T { debug_assert!(i < self.nrows(), "Vector index out of bounds."); let i = i * self.strides().0; @@ -1248,6 +1267,7 @@ impl> Vector { impl> Vector { /// Gets a mutable reference to the i-th element of this column vector without bound checking. #[inline] + #[must_use] pub unsafe fn vget_unchecked_mut(&mut self, i: usize) -> &mut T { debug_assert!(i < self.nrows(), "Vector index out of bounds."); let i = i * self.strides().0; @@ -1258,6 +1278,7 @@ impl> Vector { impl> Matrix { /// Extracts a slice containing the entire matrix entries ordered column-by-columns. #[inline] + #[must_use] pub fn as_slice(&self) -> &[T] { self.data.as_slice() } @@ -1266,6 +1287,7 @@ impl> Matrix> Matrix { /// Extracts a mutable slice containing the entire matrix entries ordered column-by-columns. #[inline] + #[must_use] pub fn as_mut_slice(&mut self) -> &mut [T] { self.data.as_mut_slice() } @@ -1446,6 +1468,7 @@ impl> Matrix { impl> SquareMatrix { /// The diagonal of this matrix. #[inline] + #[must_use] pub fn diagonal(&self) -> OVector where DefaultAllocator: Allocator, @@ -1457,6 +1480,7 @@ impl> SquareMatrix { /// /// This is a more efficient version of `self.diagonal().map(f)` since this /// allocates only once. + #[must_use] pub fn map_diagonal(&self, mut f: impl FnMut(T) -> T2) -> OVector where DefaultAllocator: Allocator, @@ -1481,6 +1505,7 @@ impl> SquareMatrix { /// Computes a trace of a square matrix, i.e., the sum of its diagonal elements. #[inline] + #[must_use] pub fn trace(&self) -> T where T: Scalar + Zero + ClosedAdd, @@ -1504,6 +1529,7 @@ impl> SquareMatrix { impl> SquareMatrix { /// The symmetric part of `self`, i.e., `0.5 * (self + self.transpose())`. #[inline] + #[must_use] pub fn symmetric_part(&self) -> OMatrix where DefaultAllocator: Allocator, @@ -1520,6 +1546,7 @@ impl> SquareMatrix { /// The hermitian part of `self`, i.e., `0.5 * (self + self.adjoint())`. #[inline] + #[must_use] pub fn hermitian_part(&self) -> OMatrix where DefaultAllocator: Allocator, @@ -1542,6 +1569,7 @@ impl + IsNotStaticOne, S: Storage /// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and /// and setting the diagonal element to `1`. #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, DimSum> where DefaultAllocator: Allocator, DimSum>, @@ -1553,7 +1581,7 @@ impl + IsNotStaticOne, S: Storage let dim = DimSum::::from_usize(self.nrows() + 1); let mut res = OMatrix::identity_generic(dim, dim); res.generic_slice_mut::((0, 0), self.data.shape()) - .copy_from(&self); + .copy_from(self); res } } @@ -1562,6 +1590,7 @@ impl, S: Storage> Vector { /// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its /// coordinates. #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OVector> where DefaultAllocator: Allocator>, @@ -1589,6 +1618,7 @@ impl, S: Storage> Vector { impl, S: Storage> Vector { /// Constructs a new vector of higher dimension by appending `element` to the end of `self`. #[inline] + #[must_use] pub fn push(&self, element: T) -> OVector> where DefaultAllocator: Allocator>, @@ -1789,7 +1819,6 @@ macro_rules! impl_fmt { where T: Scalar + $trait, S: Storage, - DefaultAllocator: Allocator, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature = "std")] @@ -1807,20 +1836,17 @@ macro_rules! impl_fmt { 4 } - let (nrows, ncols) = self.data.shape(); + let (nrows, ncols) = self.shape(); - if nrows.value() == 0 || ncols.value() == 0 { + if nrows == 0 || ncols == 0 { return write!(f, "[ ]"); } let mut max_length = 0; - let mut lengths: OMatrix = Matrix::zeros_generic(nrows, ncols); - let (nrows, ncols) = self.shape(); for i in 0..nrows { for j in 0..ncols { - lengths[(i, j)] = val_width(&self[(i, j)], f); - max_length = crate::max(max_length, lengths[(i, j)]); + max_length = crate::max(max_length, val_width(&self[(i, j)], f)); } } @@ -1837,7 +1863,7 @@ macro_rules! impl_fmt { for i in 0..nrows { write!(f, " │")?; for j in 0..ncols { - let number_length = lengths[(i, j)] + 1; + let number_length = val_width(&self[(i, j)], f) + 1; let pad = max_length_with_space - number_length; write!(f, " {:>thepad$}", "", thepad = pad)?; match f.precision() { @@ -1870,19 +1896,29 @@ impl_fmt!(fmt::UpperHex, "{:X}", "{:1$X}"); impl_fmt!(fmt::Binary, "{:b}", "{:.1$b}"); impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}"); -#[test] -fn lower_exp() { - let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.); - assert_eq!( - format!("{:e}", test), - r" +#[cfg(test)] +mod tests { + #[test] + fn empty_display() { + let vec: Vec = Vec::new(); + let dvector = crate::DVector::from_vec(vec); + assert_eq!(format!("{}", dvector), "[ ]") + } + + #[test] + fn lower_exp() { + let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.); + assert_eq!( + format!("{:e}", test), + r" ┌ ┐ │ 1e6 2e5 │ │ 2e-5 1e0 │ └ ┘ " - ) + ) + } } /// # Cross product @@ -1891,6 +1927,7 @@ impl(&self, b: &Matrix) -> T where R2: Dim, @@ -1920,6 +1957,7 @@ impl(&self, b: &Matrix) -> MatrixCross where R2: Dim, @@ -1993,6 +2031,7 @@ impl> Vector { /// Computes the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`. #[inline] + #[must_use] pub fn cross_matrix(&self) -> OMatrix { OMatrix::::new( T::zero(), @@ -2011,6 +2050,7 @@ impl> Vector { impl> Matrix { /// The smallest angle between two vectors. #[inline] + #[must_use] pub fn angle(&self, other: &Matrix) -> T::SimdRealField where SB: Storage, diff --git a/src/base/matrix_slice.rs b/src/base/matrix_slice.rs index 6b03c7b2..96ebe59c 100644 --- a/src/base/matrix_slice.rs +++ b/src/base/matrix_slice.rs @@ -1,5 +1,5 @@ use std::marker::PhantomData; -use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; +use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; use std::slice; use crate::base::allocator::Allocator; @@ -77,6 +77,23 @@ macro_rules! slice_storage_impl( $T::from_raw_parts(storage.$get_addr(start.0, start.1), shape, strides) } } + + impl <'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> + $T<'a, T, R, C, RStride, CStride> + where + Self: ContiguousStorage + { + /// Extracts the original slice from this storage + pub fn into_slice(self) -> &'a [T] { + let (nrows, ncols) = self.shape(); + if nrows.value() != 0 && ncols.value() != 0 { + let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); + unsafe { slice::from_raw_parts(self.ptr, sz + 1) } + } else { + unsafe { slice::from_raw_parts(self.ptr, 0) } + } + } + } } ); @@ -108,6 +125,23 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone } } +impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> + SliceStorageMut<'a, T, R, C, RStride, CStride> +where + Self: ContiguousStorageMut, +{ + /// Extracts the original slice from this storage + pub fn into_slice_mut(self) -> &'a mut [T] { + let (nrows, ncols) = self.shape(); + if nrows.value() != 0 && ncols.value() != 0 { + let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); + unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) } + } else { + unsafe { slice::from_raw_parts_mut(self.ptr, 0) } + } + } +} + macro_rules! storage_impl( ($($T: ident),* $(,)*) => {$( unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Storage @@ -162,14 +196,14 @@ macro_rules! storage_impl( } #[inline] - fn as_slice(&self) -> &[T] { + unsafe fn as_slice_unchecked(&self) -> &[T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); - unsafe { slice::from_raw_parts(self.ptr, sz + 1) } + slice::from_raw_parts(self.ptr, sz + 1) } else { - unsafe { slice::from_raw_parts(self.ptr, 0) } + slice::from_raw_parts(self.ptr, 0) } } } @@ -187,13 +221,13 @@ unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu } #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { + unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); - unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) } + slice::from_raw_parts_mut(self.ptr, sz + 1) } else { - unsafe { slice::from_raw_parts_mut(self.ptr, 0) } + slice::from_raw_parts_mut(self.ptr, 0) } } } @@ -806,12 +840,32 @@ impl SliceRange for RangeFull { } } +impl SliceRange for RangeInclusive { + type Size = Dynamic; + + #[inline(always)] + fn begin(&self, _: D) -> usize { + *self.start() + } + + #[inline(always)] + fn end(&self, _: D) -> usize { + *self.end() + 1 + } + + #[inline(always)] + fn size(&self, _: D) -> Self::Size { + Dynamic::new(*self.end() + 1 - *self.start()) + } +} + // TODO: see how much of this overlaps with the general indexing // methods from indexing.rs. impl> Matrix { /// Slices a sub-matrix containing the rows indexed by the range `rows` and the columns indexed /// by the range `cols`. #[inline] + #[must_use] pub fn slice_range( &self, rows: RowRange, @@ -830,6 +884,7 @@ impl> Matrix { /// Slice containing all the rows indexed by the range `rows`. #[inline] + #[must_use] pub fn rows_range>( &self, rows: RowRange, @@ -839,6 +894,7 @@ impl> Matrix { /// Slice containing all the columns indexed by the range `rows`. #[inline] + #[must_use] pub fn columns_range>( &self, cols: ColRange, diff --git a/src/base/min_max.rs b/src/base/min_max.rs index d532d87a..83e62d10 100644 --- a/src/base/min_max.rs +++ b/src/base/min_max.rs @@ -13,6 +13,7 @@ impl> Matrix { /// assert_eq!(Vector3::new(-1.0, -2.0, -3.0).amax(), 3.0); /// ``` #[inline] + #[must_use] pub fn amax(&self) -> T where T: Zero + SimdSigned + SimdPartialOrd, @@ -33,6 +34,7 @@ impl> Matrix { /// Complex::new(1.0, 3.0)).camax(), 5.0); /// ``` #[inline] + #[must_use] pub fn camax(&self) -> T::SimdRealField where T: SimdComplexField, @@ -52,6 +54,7 @@ impl> Matrix { /// assert_eq!(Vector3::new(5u32, 2, 3).max(), 5); /// ``` #[inline] + #[must_use] pub fn max(&self) -> T where T: SimdPartialOrd + Zero, @@ -70,6 +73,7 @@ impl> Matrix { /// assert_eq!(Vector3::new(10.0, 2.0, 30.0).amin(), 2.0); /// ``` #[inline] + #[must_use] pub fn amin(&self) -> T where T: Zero + SimdPartialOrd + SimdSigned, @@ -90,6 +94,7 @@ impl> Matrix { /// Complex::new(1.0, 3.0)).camin(), 3.0); /// ``` #[inline] + #[must_use] pub fn camin(&self) -> T::SimdRealField where T: SimdComplexField, @@ -112,6 +117,7 @@ impl> Matrix { /// assert_eq!(Vector3::new(5u32, 2, 3).min(), 2); /// ``` #[inline] + #[must_use] pub fn min(&self) -> T where T: SimdPartialOrd + Zero, @@ -136,6 +142,7 @@ impl> Matrix { /// assert_eq!(mat.icamax_full(), (1, 0)); /// ``` #[inline] + #[must_use] pub fn icamax_full(&self) -> (usize, usize) where T: ComplexField, @@ -172,6 +179,7 @@ impl> Matri /// assert_eq!(mat.iamax_full(), (1, 2)); /// ``` #[inline] + #[must_use] pub fn iamax_full(&self) -> (usize, usize) { assert!(!self.is_empty(), "The input matrix must not be empty."); @@ -209,6 +217,7 @@ impl> Vector { /// assert_eq!(vec.icamax(), 2); /// ``` #[inline] + #[must_use] pub fn icamax(&self) -> usize where T: ComplexField, @@ -240,6 +249,7 @@ impl> Vector { /// assert_eq!(vec.argmax(), (2, 13)); /// ``` #[inline] + #[must_use] pub fn argmax(&self) -> (usize, T) where T: PartialOrd, @@ -271,6 +281,7 @@ impl> Vector { /// assert_eq!(vec.imax(), 2); /// ``` #[inline] + #[must_use] pub fn imax(&self) -> usize where T: PartialOrd, @@ -288,6 +299,7 @@ impl> Vector { /// assert_eq!(vec.iamax(), 1); /// ``` #[inline] + #[must_use] pub fn iamax(&self) -> usize where T: PartialOrd + Signed, @@ -319,6 +331,7 @@ impl> Vector { /// assert_eq!(vec.argmin(), (1, -15)); /// ``` #[inline] + #[must_use] pub fn argmin(&self) -> (usize, T) where T: PartialOrd, @@ -350,6 +363,7 @@ impl> Vector { /// assert_eq!(vec.imin(), 1); /// ``` #[inline] + #[must_use] pub fn imin(&self) -> usize where T: PartialOrd, @@ -367,6 +381,7 @@ impl> Vector { /// assert_eq!(vec.iamin(), 0); /// ``` #[inline] + #[must_use] pub fn iamin(&self) -> usize where T: PartialOrd + Signed, diff --git a/src/base/norm.rs b/src/base/norm.rs index 251bf661..09e11f7e 100644 --- a/src/base/norm.rs +++ b/src/base/norm.rs @@ -158,6 +158,7 @@ impl Norm for UniformNorm { impl> Matrix { /// The squared L2 norm of this vector. #[inline] + #[must_use] pub fn norm_squared(&self) -> T::SimdRealField where T: SimdComplexField, @@ -176,6 +177,7 @@ impl> Matrix { /// /// Use `.apply_norm` to apply a custom norm. #[inline] + #[must_use] pub fn norm(&self) -> T::SimdRealField where T: SimdComplexField, @@ -187,6 +189,7 @@ impl> Matrix { /// /// Use `.apply_metric_distance` to apply a custom norm. #[inline] + #[must_use] pub fn metric_distance(&self, rhs: &Matrix) -> T::SimdRealField where T: SimdComplexField, @@ -211,6 +214,7 @@ impl> Matrix { /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm()); /// ``` #[inline] + #[must_use] pub fn apply_norm(&self, norm: &impl Norm) -> T::SimdRealField where T: SimdComplexField, @@ -233,6 +237,7 @@ impl> Matrix { /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm()); /// ``` #[inline] + #[must_use] pub fn apply_metric_distance( &self, rhs: &Matrix, @@ -254,6 +259,7 @@ impl> Matrix { /// /// This function is simply implemented as a call to `norm()` #[inline] + #[must_use] pub fn magnitude(&self) -> T::SimdRealField where T: SimdComplexField, @@ -267,6 +273,7 @@ impl> Matrix { /// /// This function is simply implemented as a call to `norm_squared()` #[inline] + #[must_use] pub fn magnitude_squared(&self) -> T::SimdRealField where T: SimdComplexField, @@ -298,6 +305,7 @@ impl> Matrix { /// The Lp norm of this matrix. #[inline] + #[must_use] pub fn lp_norm(&self, p: i32) -> T::SimdRealField where T: SimdComplexField, @@ -341,6 +349,7 @@ impl> Matrix { /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`. #[inline] + #[must_use] pub fn cap_magnitude(&self, max: T::RealField) -> OMatrix where T: ComplexField, @@ -357,6 +366,7 @@ impl> Matrix { /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`. #[inline] + #[must_use] pub fn simd_cap_magnitude(&self, max: T::SimdRealField) -> OMatrix where T: SimdComplexField, diff --git a/src/base/ops.rs b/src/base/ops.rs index 8917efb1..852f6490 100644 --- a/src/base/ops.rs +++ b/src/base/ops.rs @@ -158,20 +158,17 @@ macro_rules! componentwise_binop_impl( // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? - if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() { - let arr1 = self.data.as_slice(); - let arr2 = rhs.data.as_slice(); - let out = out.data.as_mut_slice(); - for i in 0 .. arr1.len() { - unsafe { + unsafe { + if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() { + let arr1 = self.data.as_slice_unchecked(); + let arr2 = rhs.data.as_slice_unchecked(); + let out = out.data.as_mut_slice_unchecked(); + for i in 0 .. arr1.len() { *out.get_unchecked_mut(i) = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone()); } - } - } - else { - for j in 0 .. self.ncols() { - for i in 0 .. self.nrows() { - unsafe { + } else { + for j in 0 .. self.ncols() { + for i in 0 .. self.nrows() { let val = self.get_unchecked((i, j)).inlined_clone().$method(rhs.get_unchecked((i, j)).inlined_clone()); *out.get_unchecked_mut((i, j)) = val; } @@ -191,19 +188,17 @@ macro_rules! componentwise_binop_impl( // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? - if self.data.is_contiguous() && rhs.data.is_contiguous() { - let arr1 = self.data.as_mut_slice(); - let arr2 = rhs.data.as_slice(); - for i in 0 .. arr2.len() { - unsafe { + unsafe { + if self.data.is_contiguous() && rhs.data.is_contiguous() { + let arr1 = self.data.as_mut_slice_unchecked(); + let arr2 = rhs.data.as_slice_unchecked(); + + for i in 0 .. arr2.len() { arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone()); } - } - } - else { - for j in 0 .. rhs.ncols() { - for i in 0 .. rhs.nrows() { - unsafe { + } else { + for j in 0 .. rhs.ncols() { + for i in 0 .. rhs.nrows() { self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone()) } } @@ -221,20 +216,18 @@ macro_rules! componentwise_binop_impl( // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? - if self.data.is_contiguous() && rhs.data.is_contiguous() { - let arr1 = self.data.as_slice(); - let arr2 = rhs.data.as_mut_slice(); - for i in 0 .. arr1.len() { - unsafe { + unsafe { + if self.data.is_contiguous() && rhs.data.is_contiguous() { + let arr1 = self.data.as_slice_unchecked(); + let arr2 = rhs.data.as_mut_slice_unchecked(); + + for i in 0 .. arr1.len() { let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone()); *arr2.get_unchecked_mut(i) = res; } - } - } - else { - for j in 0 .. self.ncols() { - for i in 0 .. self.nrows() { - unsafe { + } else { + for j in 0 .. self.ncols() { + for i in 0 .. self.nrows() { let r = rhs.get_unchecked_mut((i, j)); *r = self.get_unchecked((i, j)).inlined_clone().$method(r.inlined_clone()) } @@ -536,7 +529,7 @@ macro_rules! left_scalar_mul_impl( // for rhs in res.iter_mut() { for rhs in res.as_mut_slice().iter_mut() { - *rhs = self * *rhs + *rhs *= self } res @@ -676,6 +669,7 @@ where { /// Equivalent to `self.transpose() * rhs`. #[inline] + #[must_use] pub fn tr_mul(&self, rhs: &Matrix) -> OMatrix where SB: Storage, @@ -692,6 +686,7 @@ where /// Equivalent to `self.adjoint() * rhs`. #[inline] + #[must_use] pub fn ad_mul(&self, rhs: &Matrix) -> OMatrix where T: SimdComplexField, @@ -801,6 +796,7 @@ where /// The kronecker product of two matrices (aka. tensor product of the corresponding linear /// maps). + #[must_use] pub fn kronecker( &self, rhs: &Matrix, diff --git a/src/base/properties.rs b/src/base/properties.rs index 1cbff719..9e250119 100644 --- a/src/base/properties.rs +++ b/src/base/properties.rs @@ -20,6 +20,7 @@ impl> Matrix { /// assert_eq!(mat.len(), 12); /// ``` #[inline] + #[must_use] pub fn len(&self) -> usize { let (nrows, ncols) = self.shape(); nrows * ncols @@ -35,12 +36,14 @@ impl> Matrix { /// assert!(!mat.is_empty()); /// ``` #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Indicates if this is a square matrix. #[inline] + #[must_use] pub fn is_square(&self) -> bool { let (nrows, ncols) = self.shape(); nrows == ncols @@ -52,6 +55,7 @@ impl> Matrix { /// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)` /// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero. #[inline] + #[must_use] pub fn is_identity(&self, eps: T::Epsilon) -> bool where T: Zero + One + RelativeEq, @@ -112,6 +116,7 @@ impl> Matrix { /// In this definition `Id` is approximately equal to the identity matrix with a relative error /// equal to `eps`. #[inline] + #[must_use] pub fn is_orthogonal(&self, eps: T::Epsilon) -> bool where T: Zero + One + ClosedAdd + ClosedMul + RelativeEq, @@ -129,6 +134,7 @@ where { /// Checks that this matrix is orthogonal and has a determinant equal to 1. #[inline] + #[must_use] pub fn is_special_orthogonal(&self, eps: T) -> bool where D: DimMin, @@ -139,6 +145,7 @@ where /// Returns `true` if this matrix is invertible. #[inline] + #[must_use] pub fn is_invertible(&self) -> bool { // TODO: improve this? self.clone_owned().try_inverse().is_some() diff --git a/src/base/statistics.rs b/src/base/statistics.rs index 8f6c77ce..59d78482 100644 --- a/src/base/statistics.rs +++ b/src/base/statistics.rs @@ -9,6 +9,7 @@ impl> Matrix { /// Returns a row vector where each element is the result of the application of `f` on the /// corresponding column of the original matrix. #[inline] + #[must_use] pub fn compress_rows( &self, f: impl Fn(VectorSlice) -> T, @@ -35,6 +36,7 @@ impl> Matrix { /// /// This is the same as `self.compress_rows(f).transpose()`. #[inline] + #[must_use] pub fn compress_rows_tr( &self, f: impl Fn(VectorSlice) -> T, @@ -58,6 +60,7 @@ impl> Matrix { /// Returns a column vector resulting from the folding of `f` on each column of this matrix. #[inline] + #[must_use] pub fn compress_columns( &self, init: OVector, @@ -95,6 +98,7 @@ impl> Matrix { /// assert_eq!(m.sum(), 21.0); /// ``` #[inline] + #[must_use] pub fn sum(&self) -> T where T: ClosedAdd + Zero, @@ -120,6 +124,7 @@ impl> Matrix { /// assert_eq!(mint.row_sum(), RowVector2::new(9,12)); /// ``` #[inline] + #[must_use] pub fn row_sum(&self) -> RowOVector where T: ClosedAdd + Zero, @@ -144,6 +149,7 @@ impl> Matrix { /// assert_eq!(mint.row_sum_tr(), Vector2::new(9,12)); /// ``` #[inline] + #[must_use] pub fn row_sum_tr(&self) -> OVector where T: ClosedAdd + Zero, @@ -168,6 +174,7 @@ impl> Matrix { /// assert_eq!(mint.column_sum(), Vector3::new(3,7,11)); /// ``` #[inline] + #[must_use] pub fn column_sum(&self) -> OVector where T: ClosedAdd + Zero, @@ -197,6 +204,7 @@ impl> Matrix { /// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8); /// ``` #[inline] + #[must_use] pub fn variance(&self) -> T where T: Field + SupersetOf, @@ -226,6 +234,7 @@ impl> Matrix { /// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25)); /// ``` #[inline] + #[must_use] pub fn row_variance(&self) -> RowOVector where T: Field + SupersetOf, @@ -246,6 +255,7 @@ impl> Matrix { /// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25)); /// ``` #[inline] + #[must_use] pub fn row_variance_tr(&self) -> OVector where T: Field + SupersetOf, @@ -267,6 +277,7 @@ impl> Matrix { /// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8); /// ``` #[inline] + #[must_use] pub fn column_variance(&self) -> OVector where T: Field + SupersetOf, @@ -306,6 +317,7 @@ impl> Matrix { /// assert_eq!(m.mean(), 3.5); /// ``` #[inline] + #[must_use] pub fn mean(&self) -> T where T: Field + SupersetOf, @@ -331,6 +343,7 @@ impl> Matrix { /// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5)); /// ``` #[inline] + #[must_use] pub fn row_mean(&self) -> RowOVector where T: Field + SupersetOf, @@ -351,6 +364,7 @@ impl> Matrix { /// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5)); /// ``` #[inline] + #[must_use] pub fn row_mean_tr(&self) -> OVector where T: Field + SupersetOf, @@ -371,6 +385,7 @@ impl> Matrix { /// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0)); /// ``` #[inline] + #[must_use] pub fn column_mean(&self) -> OVector where T: Field + SupersetOf, diff --git a/src/base/storage.rs b/src/base/storage.rs index 0238b36c..a750904f 100644 --- a/src/base/storage.rs +++ b/src/base/storage.rs @@ -1,7 +1,7 @@ //! Abstract definition of a matrix data storage. use std::fmt::Debug; -use std::mem; +use std::ptr; use crate::base::allocator::{Allocator, SameShapeC, SameShapeR}; use crate::base::default_allocator::DefaultAllocator; @@ -58,7 +58,7 @@ pub unsafe trait Storage: Debug + Sized { /// Compute the index corresponding to the irow-th row and icol-th column of this matrix. The /// index must be such that the following holds: /// - /// ```.ignore + /// ```ignore /// let lindex = self.linear_index(irow, icol); /// assert!(*self.get_unchecked(irow, icol) == *self.get_unchecked_linear(lindex)) /// ``` @@ -70,36 +70,57 @@ pub unsafe trait Storage: Debug + Sized { } /// Gets the address of the i-th matrix component without performing bound-checking. + /// + /// # Safety + /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] - unsafe fn get_address_unchecked_linear(&self, i: usize) -> *const T { + fn get_address_unchecked_linear(&self, i: usize) -> *const T { self.ptr().wrapping_add(i) } /// Gets the address of the i-th matrix component without performing bound-checking. + /// + /// # Safety + /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] - unsafe fn get_address_unchecked(&self, irow: usize, icol: usize) -> *const T { + fn get_address_unchecked(&self, irow: usize, icol: usize) -> *const T { self.get_address_unchecked_linear(self.linear_index(irow, icol)) } /// Retrieves a reference to the i-th element without bound-checking. + /// + /// # Safety + /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked_linear(&self, i: usize) -> &T { &*self.get_address_unchecked_linear(i) } /// Retrieves a reference to the i-th element without bound-checking. + /// + /// # Safety + /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked(&self, irow: usize, icol: usize) -> &T { self.get_unchecked_linear(self.linear_index(irow, icol)) } /// Indicates whether this data buffer stores its elements contiguously. + /// + /// # Safety + /// This function must not return `true` if the underlying storage is not contiguous, + /// or undefined behaviour will occur. fn is_contiguous(&self) -> bool; /// Retrieves the data buffer as a contiguous slice. /// + /// # Safety /// The matrix components may not be stored in a contiguous way, depending on the strides. - fn as_slice(&self) -> &[T]; + /// This method is unsafe because this can yield to invalid aliasing when called on some pairs + /// of matrix slices originating from the same matrix with strides. + /// + /// Call the safe alternative `matrix.as_slice()` instead. + unsafe fn as_slice_unchecked(&self) -> &[T]; /// Builds a matrix data storage that does not contain any reference. fn into_owned(self) -> Owned @@ -122,39 +143,57 @@ pub unsafe trait StorageMut: Storage { fn ptr_mut(&mut self) -> *mut T; /// Gets the mutable address of the i-th matrix component without performing bound-checking. + /// + /// # Safety + /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] - unsafe fn get_address_unchecked_linear_mut(&mut self, i: usize) -> *mut T { + fn get_address_unchecked_linear_mut(&mut self, i: usize) -> *mut T { self.ptr_mut().wrapping_add(i) } /// Gets the mutable address of the i-th matrix component without performing bound-checking. + /// + /// # Safety + /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] - unsafe fn get_address_unchecked_mut(&mut self, irow: usize, icol: usize) -> *mut T { + fn get_address_unchecked_mut(&mut self, irow: usize, icol: usize) -> *mut T { let lid = self.linear_index(irow, icol); self.get_address_unchecked_linear_mut(lid) } /// Retrieves a mutable reference to the i-th element without bound-checking. + /// + /// # Safety + /// If the index is out of bounds, the method will cause undefined behavior. unsafe fn get_unchecked_linear_mut(&mut self, i: usize) -> &mut T { &mut *self.get_address_unchecked_linear_mut(i) } /// Retrieves a mutable reference to the element at `(irow, icol)` without bound-checking. + /// + /// # Safety + /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked_mut(&mut self, irow: usize, icol: usize) -> &mut T { &mut *self.get_address_unchecked_mut(irow, icol) } /// Swaps two elements using their linear index without bound-checking. + /// + /// # Safety + /// If the indices are out of bounds, the method will cause undefined behavior. #[inline] unsafe fn swap_unchecked_linear(&mut self, i1: usize, i2: usize) { let a = self.get_address_unchecked_linear_mut(i1); let b = self.get_address_unchecked_linear_mut(i2); - mem::swap(&mut *a, &mut *b); + ptr::swap(a, b); } /// Swaps two elements without bound-checking. + /// + /// # Safety + /// If the indices are out of bounds, the method will cause undefined behavior. #[inline] unsafe fn swap_unchecked(&mut self, row_col1: (usize, usize), row_col2: (usize, usize)) { let lid1 = self.linear_index(row_col1.0, row_col1.1); @@ -165,8 +204,13 @@ pub unsafe trait StorageMut: Storage { /// Retrieves the mutable data buffer as a contiguous slice. /// - /// Matrix components may not be contiguous, depending on its strides. - fn as_mut_slice(&mut self) -> &mut [T]; + /// Matrix components may not be contiguous, depending on its strides. + /// + /// # Safety + /// The matrix components may not be stored in a contiguous way, depending on the strides. + /// This method is unsafe because this can yield to invalid aliasing when called on some pairs + /// of matrix slices originating from the same matrix with strides. + unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T]; } /// A matrix storage that is stored contiguously in memory. @@ -177,6 +221,12 @@ pub unsafe trait StorageMut: Storage { pub unsafe trait ContiguousStorage: Storage { + /// Converts this data storage to a contiguous slice. + fn as_slice(&self) -> &[T] { + // SAFETY: this is safe because this trait guarantees the fact + // that the data is stored contiguously. + unsafe { self.as_slice_unchecked() } + } } /// A mutable matrix storage that is stored contiguously in memory. @@ -187,6 +237,12 @@ pub unsafe trait ContiguousStorage: pub unsafe trait ContiguousStorageMut: ContiguousStorage + StorageMut { + /// Converts this data storage to a contiguous mutable slice. + fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: this is safe because this trait guarantees the fact + // that the data is stored contiguously. + unsafe { self.as_mut_slice_unchecked() } + } } /// A matrix storage that can be reshaped in-place. diff --git a/src/base/swizzle.rs b/src/base/swizzle.rs index 1e2f0e4c..25d6375f 100644 --- a/src/base/swizzle.rs +++ b/src/base/swizzle.rs @@ -8,6 +8,7 @@ macro_rules! impl_swizzle { $( /// Builds a new vector from components of `self`. #[inline] + #[must_use] pub fn $name(&self) -> $Result where D::Typenum: Cmp { $Result::new($(self[$i].inlined_clone()),*) diff --git a/src/base/unit.rs b/src/base/unit.rs index b5a4096f..a6ca33f3 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -1,6 +1,5 @@ #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; -use std::mem; use std::ops::Deref; #[cfg(feature = "serde-serialize-no-std")] @@ -96,7 +95,7 @@ mod rkyv_impl { impl, S: Fallible + ?Sized> Serialize for Unit { fn serialize(&self, serializer: &mut S) -> Result { - Ok(self.value.serialize(serializer)?) + self.value.serialize(serializer) } } @@ -222,14 +221,14 @@ impl Unit { impl Unit { /// Wraps the given value, assuming it is already normalized. #[inline] - pub fn new_unchecked(value: T) -> Self { + pub const fn new_unchecked(value: T) -> Self { Unit { value } } /// Wraps the given reference, assuming it is already normalized. #[inline] - pub fn from_ref_unchecked<'a>(value: &'a T) -> &'a Self { - unsafe { mem::transmute(value) } + pub fn from_ref_unchecked(value: &T) -> &Self { + unsafe { &*(value as *const T as *const Self) } } /// Retrieves the underlying value. @@ -332,7 +331,7 @@ impl Deref for Unit { #[inline] fn deref(&self) -> &T { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Self as *const T) } } } diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index 8b2d2ef2..be567094 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -95,12 +95,14 @@ impl VecStorage { /// The underlying data storage. #[inline] + #[must_use] pub fn as_vec(&self) -> &Vec { &self.data } /// The underlying mutable data storage. /// + /// # Safety /// This is unsafe because this may cause UB if the size of the vector is changed /// by the user. #[inline] @@ -110,6 +112,7 @@ impl VecStorage { /// Resizes the underlying mutable data storage and unwraps it. /// + /// # Safety /// If `sz` is larger than the current size, additional elements are uninitialized. /// If `sz` is smaller than the current size, additional elements are truncated. #[inline] @@ -129,20 +132,22 @@ impl VecStorage { /// The number of elements on the underlying vector. #[inline] + #[must_use] pub fn len(&self) -> usize { self.data.len() } /// Returns true if the underlying vector contains no elements. #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } } -impl Into> for VecStorage { - fn into(self) -> Vec { - self.data +impl From> for Vec { + fn from(vec: VecStorage) -> Self { + vec.data } } @@ -196,7 +201,7 @@ where } #[inline] - fn as_slice(&self) -> &[T] { + unsafe fn as_slice_unchecked(&self) -> &[T] { &self.data } } @@ -245,7 +250,7 @@ where } #[inline] - fn as_slice(&self) -> &[T] { + unsafe fn as_slice_unchecked(&self) -> &[T] { &self.data } } @@ -265,7 +270,7 @@ where } #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { + unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { &mut self.data[..] } } @@ -326,7 +331,7 @@ where } #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { + unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { &mut self.data[..] } } diff --git a/src/geometry/dual_quaternion.rs b/src/geometry/dual_quaternion.rs index b6b127ce..01ea9dcc 100644 --- a/src/geometry/dual_quaternion.rs +++ b/src/geometry/dual_quaternion.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + use crate::{ Isometry3, Matrix4, Normed, OVector, Point3, Quaternion, Scalar, SimdRealField, Translation3, Unit, UnitQuaternion, Vector3, Zero, U8, @@ -35,14 +38,23 @@ use simba::scalar::{ClosedNeg, RealField}; /// If a feature that you need is missing, feel free to open an issue or a PR. /// See https://github.com/dimforge/nalgebra/issues/487 #[repr(C)] -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct DualQuaternion { +#[derive(Debug, Copy, Clone)] +pub struct DualQuaternion { /// The real component of the quaternion pub real: Quaternion, /// The dual component of the quaternion pub dual: Quaternion, } +impl Eq for DualQuaternion {} + +impl PartialEq for DualQuaternion { + #[inline] + fn eq(&self, right: &Self) -> bool { + self.real == right.real && self.dual == right.dual + } +} + impl Default for DualQuaternion { fn default() -> Self { Self { @@ -232,6 +244,7 @@ where /// )); /// ``` #[inline] + #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Self { self * (T::one() - t) + other * t } @@ -271,8 +284,8 @@ where } impl DualQuaternion { - fn to_vector(&self) -> OVector { - self.as_ref().clone().into() + fn to_vector(self) -> OVector { + (*self.as_ref()).into() } } @@ -381,6 +394,7 @@ where /// )); /// ``` #[inline] + #[must_use] pub fn dual_quaternion(&self) -> &DualQuaternion { self.as_ref() } @@ -463,7 +477,6 @@ where /// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6); /// ``` #[inline] - #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse_mut(&mut self) { let quat = self.as_mut_unchecked(); quat.real = Unit::new_unchecked(quat.real).inverse().into_inner(); @@ -486,6 +499,7 @@ where /// assert_relative_eq!(dq_to * dq1, dq2, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn isometry_to(&self, other: &Self) -> Self { other / self } @@ -518,6 +532,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn lerp(&self, other: &Self, t: T) -> DualQuaternion { self.as_ref().lerp(other.as_ref(), t) } @@ -546,6 +561,7 @@ where /// ), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn nlerp(&self, other: &Self, t: T) -> Self { let mut res = self.lerp(other, t); let _ = res.normalize_mut(); @@ -581,6 +597,7 @@ where /// ); /// assert_relative_eq!(dq.translation().vector.y, 3.0, epsilon = 1.0e-6); #[inline] + #[must_use] pub fn sclerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -600,6 +617,7 @@ where /// * `epsilon`: the value below which the sinus of the angle separating both quaternion /// must be to return `None`. #[inline] + #[must_use] pub fn try_sclerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, @@ -612,9 +630,9 @@ where let other = { let dot_product = self.as_ref().real.coords.dot(&other.as_ref().real.coords); if dot_product < T::zero() { - -other.clone() + -*other } else { - other.clone() + *other } }; @@ -667,6 +685,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn rotation(&self) -> UnitQuaternion { Unit::new_unchecked(self.as_ref().real) } @@ -686,6 +705,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn translation(&self) -> Translation3 { let two = T::one() + T::one(); Translation3::from( @@ -712,7 +732,8 @@ where /// assert_relative_eq!(iso.translation.vector, translation, epsilon = 1.0e-6); /// ``` #[inline] - pub fn to_isometry(&self) -> Isometry3 { + #[must_use] + pub fn to_isometry(self) -> Isometry3 { Isometry3::from_parts(self.translation(), self.rotation()) } @@ -735,6 +756,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point3) -> Point3 { self * pt } @@ -758,6 +780,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &Vector3) -> Vector3 { self * v } @@ -781,6 +804,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point3) -> Point3 { self.inverse() * pt } @@ -805,6 +829,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse() * v } @@ -830,6 +855,7 @@ where /// ); /// ``` #[inline] + #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } @@ -857,7 +883,8 @@ where /// assert_relative_eq!(dq.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] - pub fn to_homogeneous(&self) -> Matrix4 { + #[must_use] + pub fn to_homogeneous(self) -> Matrix4 { self.to_isometry().to_homogeneous() } } diff --git a/src/geometry/dual_quaternion_construction.rs b/src/geometry/dual_quaternion_construction.rs index be274cf4..ea4c7ee2 100644 --- a/src/geometry/dual_quaternion_construction.rs +++ b/src/geometry/dual_quaternion_construction.rs @@ -186,9 +186,9 @@ where pub fn from_parts(translation: Translation3, rotation: UnitQuaternion) -> Self { let half: T = crate::convert(0.5f64); UnitDualQuaternion::new_unchecked(DualQuaternion { - real: rotation.clone().into_inner(), + real: rotation.into_inner(), dual: Quaternion::from_parts(T::zero(), translation.vector) - * rotation.clone().into_inner() + * rotation.into_inner() * half, }) } diff --git a/src/geometry/dual_quaternion_ops.rs b/src/geometry/dual_quaternion_ops.rs index 46bc22b5..2a1527ec 100644 --- a/src/geometry/dual_quaternion_ops.rs +++ b/src/geometry/dual_quaternion_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + /* * This file provides: * @@ -49,7 +52,6 @@ use crate::{ DualQuaternion, Isometry3, Point, Point3, Quaternion, SimdRealField, Translation3, Unit, UnitDualQuaternion, UnitQuaternion, Vector, Vector3, U3, }; -use std::mem; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; @@ -57,14 +59,14 @@ use std::ops::{ impl AsRef<[T; 8]> for DualQuaternion { #[inline] fn as_ref(&self) -> &[T; 8] { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Self as *const [T; 8]) } } } impl AsMut<[T; 8]> for DualQuaternion { #[inline] fn as_mut(&mut self) -> &mut [T; 8] { - unsafe { mem::transmute(self) } + unsafe { &mut *(self as *mut Self as *mut [T; 8]) } } } @@ -564,7 +566,7 @@ dual_quaternion_op_impl!( (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; - self * UnitDualQuaternion::::from_parts(rhs.clone(), UnitQuaternion::identity()); + self * UnitDualQuaternion::::from_parts(*rhs, UnitQuaternion::identity()); 'a, 'b); dual_quaternion_op_impl!( @@ -580,7 +582,7 @@ dual_quaternion_op_impl!( (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; - self * UnitDualQuaternion::::from_parts(rhs.clone(), UnitQuaternion::identity()); + self * UnitDualQuaternion::::from_parts(*rhs, UnitQuaternion::identity()); 'b); dual_quaternion_op_impl!( @@ -632,7 +634,7 @@ dual_quaternion_op_impl!( (U3, U1), (U4, U1); self: &'b Translation3, rhs: &'a UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; - UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) * rhs; + UnitDualQuaternion::::from_parts(*self, UnitQuaternion::identity()) * rhs; 'a, 'b); dual_quaternion_op_impl!( @@ -640,7 +642,7 @@ dual_quaternion_op_impl!( (U3, U1), (U4, U1); self: &'a Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; - UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) * rhs; + UnitDualQuaternion::::from_parts(*self, UnitQuaternion::identity()) * rhs; 'a); dual_quaternion_op_impl!( @@ -664,7 +666,7 @@ dual_quaternion_op_impl!( (U3, U1), (U4, U1); self: &'b Translation3, rhs: &'a UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; - UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) / rhs; + UnitDualQuaternion::::from_parts(*self, UnitQuaternion::identity()) / rhs; 'a, 'b); dual_quaternion_op_impl!( @@ -672,7 +674,7 @@ dual_quaternion_op_impl!( (U3, U1), (U4, U1); self: &'a Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; - UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) / rhs; + UnitDualQuaternion::::from_parts(*self, UnitQuaternion::identity()) / rhs; 'a); dual_quaternion_op_impl!( @@ -860,7 +862,7 @@ dual_quaternion_op_impl!( Output = Point3 => U3, U1; { let two: T = crate::convert(2.0f64); - let q_point = Quaternion::from_parts(T::zero(), rhs.coords.clone()); + let q_point = Quaternion::from_parts(T::zero(), rhs.coords); Point::from( ((self.as_ref().real * q_point + self.as_ref().dual * two) * self.as_ref().real.conjugate()) .vector() @@ -1115,7 +1117,7 @@ dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitQuaternion; - *self *= rhs.clone(); 'b); + *self *= *rhs; 'b); // UnitDualQuaternion ÷= UnitQuaternion dual_quaternion_op_impl!( @@ -1151,7 +1153,7 @@ dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b Translation3; - *self *= rhs.clone(); 'b); + *self *= *rhs; 'b); // UnitDualQuaternion ÷= Translation3 dual_quaternion_op_impl!( diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index d307c393..333468b3 100755 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -60,15 +60,17 @@ use crate::geometry::{AbstractRotation, Point, Translation}; feature = "serde-serialize-no-std", serde(bound(serialize = "R: Serialize, DefaultAllocator: Allocator>, - Owned>: Serialize")) + Owned>: Serialize, + T: Scalar")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "R: Deserialize<'de>, DefaultAllocator: Allocator>, - Owned>: Deserialize<'de>")) + Owned>: Deserialize<'de>, + T: Scalar")) )] -pub struct Isometry { +pub struct Isometry { /// The pure rotational part of this isometry. pub rotation: R, /// The pure translational part of this isometry. @@ -267,9 +269,10 @@ where /// assert_eq!(iso1.inverse() * iso2, iso1.inv_mul(&iso2)); /// ``` #[inline] + #[must_use] pub fn inv_mul(&self, rhs: &Isometry) -> Self { let inv_rot1 = self.rotation.inverse(); - let tr_12 = rhs.translation.vector.clone() - self.translation.vector.clone(); + let tr_12 = rhs.translation.vector - self.translation.vector; Isometry::from_parts( inv_rot1.transform_vector(&tr_12).into(), inv_rot1 * rhs.rotation.clone(), @@ -384,6 +387,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, 2.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } @@ -407,6 +411,7 @@ where /// assert_relative_eq!(transformed_point, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } @@ -429,9 +434,10 @@ where /// assert_relative_eq!(transformed_point, Point3::new(0.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.rotation - .inverse_transform_point(&(pt - &self.translation.vector)) + .inverse_transform_point(&(pt - self.translation.vector)) } /// Transform the given vector by the inverse of this isometry, ignoring the @@ -453,6 +459,7 @@ where /// assert_relative_eq!(transformed_point, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.rotation.inverse_transform_vector(v) } @@ -476,6 +483,7 @@ where /// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.rotation.inverse_transform_unit_vector(v) } @@ -505,6 +513,7 @@ impl Isometry { /// assert_relative_eq!(iso.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, @@ -536,6 +545,7 @@ impl Isometry { /// assert_relative_eq!(iso.to_matrix(), expected, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn to_matrix(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, diff --git a/src/geometry/isometry_construction.rs b/src/geometry/isometry_construction.rs index e13bde29..39a1d763 100644 --- a/src/geometry/isometry_construction.rs +++ b/src/geometry/isometry_construction.rs @@ -86,7 +86,7 @@ where Standard: Distribution + Distribution, { #[inline] - fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Isometry { + fn sample(&self, rng: &mut G) -> Isometry { Isometry::from_parts(rng.gen(), rng.gen()) } } diff --git a/src/geometry/isometry_conversion.rs b/src/geometry/isometry_conversion.rs index 75c0d98d..627ea3ee 100644 --- a/src/geometry/isometry_conversion.rs +++ b/src/geometry/isometry_conversion.rs @@ -239,7 +239,7 @@ where { #[inline] fn from(arr: [Isometry; 2]) -> Self { - let tra = Translation::from([arr[0].translation.clone(), arr[1].translation.clone()]); + let tra = Translation::from([arr[0].translation, arr[1].translation]); let rot = R::from([arr[0].rotation, arr[0].rotation]); Self::from_parts(tra, rot) @@ -258,10 +258,10 @@ where #[inline] fn from(arr: [Isometry; 4]) -> Self { let tra = Translation::from([ - arr[0].translation.clone(), - arr[1].translation.clone(), - arr[2].translation.clone(), - arr[3].translation.clone(), + arr[0].translation, + arr[1].translation, + arr[2].translation, + arr[3].translation, ]); let rot = R::from([ arr[0].rotation, @@ -286,14 +286,14 @@ where #[inline] fn from(arr: [Isometry; 8]) -> Self { let tra = Translation::from([ - arr[0].translation.clone(), - arr[1].translation.clone(), - arr[2].translation.clone(), - arr[3].translation.clone(), - arr[4].translation.clone(), - arr[5].translation.clone(), - arr[6].translation.clone(), - arr[7].translation.clone(), + arr[0].translation, + arr[1].translation, + arr[2].translation, + arr[3].translation, + arr[4].translation, + arr[5].translation, + arr[6].translation, + arr[7].translation, ]); let rot = R::from([ arr[0].rotation, @@ -322,22 +322,22 @@ where #[inline] fn from(arr: [Isometry; 16]) -> Self { let tra = Translation::from([ - arr[0].translation.clone(), - arr[1].translation.clone(), - arr[2].translation.clone(), - arr[3].translation.clone(), - arr[4].translation.clone(), - arr[5].translation.clone(), - arr[6].translation.clone(), - arr[7].translation.clone(), - arr[8].translation.clone(), - arr[9].translation.clone(), - arr[10].translation.clone(), - arr[11].translation.clone(), - arr[12].translation.clone(), - arr[13].translation.clone(), - arr[14].translation.clone(), - arr[15].translation.clone(), + arr[0].translation, + arr[1].translation, + arr[2].translation, + arr[3].translation, + arr[4].translation, + arr[5].translation, + arr[6].translation, + arr[7].translation, + arr[8].translation, + arr[9].translation, + arr[10].translation, + arr[11].translation, + arr[12].translation, + arr[13].translation, + arr[14].translation, + arr[15].translation, ]); let rot = R::from([ arr[0].rotation, diff --git a/src/geometry/isometry_interpolation.rs b/src/geometry/isometry_interpolation.rs index 2ee5461a..356dbdad 100644 --- a/src/geometry/isometry_interpolation.rs +++ b/src/geometry/isometry_interpolation.rs @@ -26,6 +26,7 @@ impl Isometry3 { /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -59,6 +60,7 @@ impl Isometry3 { /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, @@ -94,6 +96,7 @@ impl IsometryMatrix3 { /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -127,6 +130,7 @@ impl IsometryMatrix3 { /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, @@ -163,6 +167,7 @@ impl Isometry2 { /// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] + #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -199,6 +204,7 @@ impl IsometryMatrix2 { /// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] + #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, diff --git a/src/geometry/isometry_ops.rs b/src/geometry/isometry_ops.rs index 2e6477b5..5cf5ec35 100644 --- a/src/geometry/isometry_ops.rs +++ b/src/geometry/isometry_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; @@ -198,7 +201,7 @@ md_assign_impl_all!( const D; for; where; self: Isometry, D>, rhs: Rotation; [val] => self.rotation *= rhs; - [ref] => self.rotation *= rhs.clone(); + [ref] => self.rotation *= *rhs; ); md_assign_impl_all!( @@ -365,9 +368,9 @@ isometry_from_composition_impl_all!( D; self: Rotation, right: Translation, Output = Isometry, D>; [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); - [ref val] => Isometry::from_parts(Translation::from(self * right.vector), self.clone()); + [ref val] => Isometry::from_parts(Translation::from(self * right.vector), *self); [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); - [ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), self.clone()); + [ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), *self); ); // UnitQuaternion × Translation @@ -389,9 +392,9 @@ isometry_from_composition_impl_all!( self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); // TODO: do not clone. - [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); + [ref val] => Isometry::from_parts(self.translation, self.rotation * rhs); + [val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); ); // Rotation × Isometry @@ -416,9 +419,9 @@ isometry_from_composition_impl_all!( self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); // TODO: do not clone. - [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); + [ref val] => Isometry::from_parts(self.translation, self.rotation / rhs); + [val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); ); // Rotation ÷ Isometry @@ -441,9 +444,9 @@ isometry_from_composition_impl_all!( self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation * rhs); // TODO: do not clone. + [ref val] => Isometry::from_parts(self.translation, self.rotation * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation * *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); ); // UnitQuaternion × Isometry @@ -468,9 +471,9 @@ isometry_from_composition_impl_all!( self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation / rhs); // TODO: do not clone. + [ref val] => Isometry::from_parts(self.translation, self.rotation / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation / *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); ); // UnitQuaternion ÷ Isometry @@ -492,9 +495,9 @@ isometry_from_composition_impl_all!( D; self: Translation, right: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self, right); - [ref val] => Isometry::from_parts(self.clone(), right); - [val ref] => Isometry::from_parts(self, right.clone()); - [ref ref] => Isometry::from_parts(self.clone(), right.clone()); + [ref val] => Isometry::from_parts(*self, right); + [val ref] => Isometry::from_parts(self, *right); + [ref ref] => Isometry::from_parts(*self, *right); ); // Translation × UnitQuaternion @@ -503,9 +506,9 @@ isometry_from_composition_impl_all!( ; self: Translation, right: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self, right); - [ref val] => Isometry::from_parts(self.clone(), right); + [ref val] => Isometry::from_parts(*self, right); [val ref] => Isometry::from_parts(self, *right); - [ref ref] => Isometry::from_parts(self.clone(), *right); + [ref ref] => Isometry::from_parts(*self, *right); ); // Isometry × UnitComplex @@ -515,9 +518,9 @@ isometry_from_composition_impl_all!( self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation * rhs); // TODO: do not clone. + [ref val] => Isometry::from_parts(self.translation, self.rotation * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation * *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation * *rhs); ); // Isometry ÷ UnitComplex @@ -527,7 +530,7 @@ isometry_from_composition_impl_all!( self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); - [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation / rhs); // TODO: do not clone. + [ref val] => Isometry::from_parts(self.translation, self.rotation / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); - [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation / *rhs); + [ref ref] => Isometry::from_parts(self.translation, self.rotation / *rhs); ); diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index d6136cec..e9546cdd 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -8,7 +8,6 @@ use rand::{ #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; -use std::mem; use simba::scalar::RealField; @@ -19,7 +18,7 @@ use crate::base::{Matrix4, Vector, Vector3}; use crate::geometry::{Point3, Projective3}; /// A 3D orthographic projection stored as a homogeneous 4x4 matrix. -pub struct Orthographic3 { +pub struct Orthographic3 { matrix: Matrix4, } @@ -188,6 +187,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity()); /// ``` #[inline] + #[must_use] pub fn inverse(&self) -> Matrix4 { let mut res = self.to_homogeneous(); @@ -221,7 +221,8 @@ impl Orthographic3 { /// assert_eq!(proj.to_homogeneous(), expected); /// ``` #[inline] - pub fn to_homogeneous(&self) -> Matrix4 { + #[must_use] + pub fn to_homogeneous(self) -> Matrix4 { self.matrix } @@ -240,6 +241,7 @@ impl Orthographic3 { /// assert_eq!(*proj.as_matrix(), expected); /// ``` #[inline] + #[must_use] pub fn as_matrix(&self) -> &Matrix4 { &self.matrix } @@ -253,8 +255,9 @@ impl Orthographic3 { /// assert_eq!(proj.as_projective().to_homogeneous(), proj.to_homogeneous()); /// ``` #[inline] + #[must_use] pub fn as_projective(&self) -> &Projective3 { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Orthographic3 as *const Projective3) } } /// This transformation seen as a `Projective3`. @@ -266,7 +269,8 @@ impl Orthographic3 { /// assert_eq!(proj.to_projective().to_homogeneous(), proj.to_homogeneous()); /// ``` #[inline] - pub fn to_projective(&self) -> Projective3 { + #[must_use] + pub fn to_projective(self) -> Projective3 { Projective3::from_matrix_unchecked(self.matrix) } @@ -310,6 +314,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.left(), 10.0, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn left(&self) -> T { (-T::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)] } @@ -326,6 +331,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.right(), 1.0, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn right(&self) -> T { (T::one() - self.matrix[(0, 3)]) / self.matrix[(0, 0)] } @@ -342,6 +348,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.bottom(), 20.0, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn bottom(&self) -> T { (-T::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)] } @@ -358,6 +365,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.top(), 2.0, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn top(&self) -> T { (T::one() - self.matrix[(1, 3)]) / self.matrix[(1, 1)] } @@ -374,6 +382,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.znear(), 1000.0, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn znear(&self) -> T { (T::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)] } @@ -390,6 +399,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.zfar(), 0.1, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn zfar(&self) -> T { (-T::one() + self.matrix[(2, 3)]) / self.matrix[(2, 2)] } @@ -422,6 +432,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0)); /// ``` #[inline] + #[must_use] pub fn project_point(&self, p: &Point3) -> Point3 { Point3::new( self.matrix[(0, 0)] * p[0] + self.matrix[(0, 3)], @@ -457,6 +468,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.unproject_point(&p8), Point3::new(10.0, 20.0, -1000.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn unproject_point(&self, p: &Point3) -> Point3 { Point3::new( (p[0] - self.matrix[(0, 3)]) / self.matrix[(0, 0)], @@ -485,6 +497,7 @@ impl Orthographic3 { /// assert_relative_eq!(proj.project_vector(&v3), Vector3::z() * -2.0 / 999.9); /// ``` #[inline] + #[must_use] pub fn project_vector(&self, p: &Vector) -> Vector3 where SB: Storage, diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index fac8fa99..ba8368a2 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -9,18 +9,17 @@ use rand::{ #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; -use std::mem; use simba::scalar::RealField; use crate::base::dimension::U3; use crate::base::storage::Storage; -use crate::base::{Matrix4, Scalar, Vector, Vector3}; +use crate::base::{Matrix4, Vector, Vector3}; use crate::geometry::{Point3, Projective3}; /// A 3D perspective projection stored as a homogeneous 4x4 matrix. -pub struct Perspective3 { +pub struct Perspective3 { matrix: Matrix4, } @@ -104,6 +103,7 @@ impl Perspective3 { /// Retrieves the inverse of the underlying homogeneous matrix. #[inline] + #[must_use] pub fn inverse(&self) -> Matrix4 { let mut res = self.to_homogeneous(); @@ -123,25 +123,29 @@ impl Perspective3 { /// Computes the corresponding homogeneous matrix. #[inline] - pub fn to_homogeneous(&self) -> Matrix4 { + #[must_use] + pub fn to_homogeneous(self) -> Matrix4 { self.matrix.clone_owned() } /// A reference to the underlying homogeneous transformation matrix. #[inline] + #[must_use] pub fn as_matrix(&self) -> &Matrix4 { &self.matrix } /// A reference to this transformation seen as a `Projective3`. #[inline] + #[must_use] pub fn as_projective(&self) -> &Projective3 { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Perspective3 as *const Projective3) } } /// This transformation seen as a `Projective3`. #[inline] - pub fn to_projective(&self) -> Projective3 { + #[must_use] + pub fn to_projective(self) -> Projective3 { Projective3::from_matrix_unchecked(self.matrix) } @@ -161,18 +165,21 @@ impl Perspective3 { /// Gets the `width / height` aspect ratio of the view frustum. #[inline] + #[must_use] pub fn aspect(&self) -> T { self.matrix[(1, 1)] / self.matrix[(0, 0)] } /// Gets the y field of view of the view frustum. #[inline] + #[must_use] pub fn fovy(&self) -> T { (T::one() / self.matrix[(1, 1)]).atan() * crate::convert(2.0) } /// Gets the near plane offset of the view frustum. #[inline] + #[must_use] pub fn znear(&self) -> T { let ratio = (-self.matrix[(2, 2)] + T::one()) / (-self.matrix[(2, 2)] - T::one()); @@ -182,6 +189,7 @@ impl Perspective3 { /// Gets the far plane offset of the view frustum. #[inline] + #[must_use] pub fn zfar(&self) -> T { let ratio = (-self.matrix[(2, 2)] + T::one()) / (-self.matrix[(2, 2)] - T::one()); @@ -193,6 +201,7 @@ impl Perspective3 { // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a point. Faster than matrix multiplication. #[inline] + #[must_use] pub fn project_point(&self, p: &Point3) -> Point3 { let inverse_denom = -T::one() / p[2]; Point3::new( @@ -204,6 +213,7 @@ impl Perspective3 { /// Un-projects a point. Faster than multiplication by the matrix inverse. #[inline] + #[must_use] pub fn unproject_point(&self, p: &Point3) -> Point3 { let inverse_denom = self.matrix[(2, 3)] / (p[2] + self.matrix[(2, 2)]); @@ -217,6 +227,7 @@ impl Perspective3 { // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a vector. Faster than matrix multiplication. #[inline] + #[must_use] pub fn project_vector(&self, p: &Vector) -> Vector3 where SB: Storage, @@ -244,8 +255,9 @@ impl Perspective3 { #[inline] pub fn set_fovy(&mut self, fovy: T) { let old_m22 = self.matrix[(1, 1)]; - self.matrix[(1, 1)] = T::one() / (fovy / crate::convert(2.0)).tan(); - self.matrix[(0, 0)] = self.matrix[(0, 0)] * (self.matrix[(1, 1)] / old_m22); + let new_m22 = T::one() / (fovy / crate::convert(2.0)).tan(); + self.matrix[(1, 1)] = new_m22; + self.matrix[(0, 0)] *= new_m22 / old_m22; } /// Updates this perspective matrix with a new near plane offset of the view frustum. @@ -276,7 +288,7 @@ where Standard: Distribution, { /// Generate an arbitrary random variate for testing purposes. - fn sample<'a, R: Rng + ?Sized>(&self, r: &'a mut R) -> Perspective3 { + fn sample(&self, r: &mut R) -> Perspective3 { use crate::base::helper; let znear = r.gen(); let zfar = helper::reject_rand(r, |&x: &T| !(x - znear).is_zero()); diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 60fa9156..d4d9dbfc 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -17,7 +17,7 @@ use simba::simd::SimdPartialOrd; use crate::base::allocator::Allocator; use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use crate::base::iter::{MatrixIter, MatrixIterMut}; -use crate::base::{Const, DefaultAllocator, OVector, SVector, Scalar}; +use crate::base::{Const, DefaultAllocator, OVector, Scalar}; /// A point in an euclidean space. /// @@ -40,35 +40,53 @@ use crate::base::{Const, DefaultAllocator, OVector, SVector, Scalar}; /// of said transformations for details. #[repr(C)] #[derive(Debug, Clone)] -pub struct Point { +pub struct OPoint +where + DefaultAllocator: Allocator, +{ /// The coordinates of this point, i.e., the shift from the origin. - pub coords: SVector, + pub coords: OVector, } -impl hash::Hash for Point { +impl hash::Hash for OPoint +where + DefaultAllocator: Allocator, +{ fn hash(&self, state: &mut H) { self.coords.hash(state) } } -impl Copy for Point {} - -#[cfg(feature = "bytemuck")] -unsafe impl bytemuck::Zeroable for Point where - SVector: bytemuck::Zeroable +impl Copy for OPoint +where + DefaultAllocator: Allocator, + OVector: Copy, { } #[cfg(feature = "bytemuck")] -unsafe impl bytemuck::Pod for Point +unsafe impl bytemuck::Zeroable for OPoint +where + OVector: bytemuck::Zeroable, + DefaultAllocator: Allocator, +{ +} + +#[cfg(feature = "bytemuck")] +unsafe impl bytemuck::Pod for OPoint where T: Copy, - SVector: bytemuck::Pod, + OVector: bytemuck::Pod, + DefaultAllocator: Allocator, { } #[cfg(feature = "serde-serialize-no-std")] -impl Serialize for Point { +impl Serialize for OPoint +where + DefaultAllocator: Allocator, + >::Buffer: Serialize, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -78,22 +96,27 @@ impl Serialize for Point { } #[cfg(feature = "serde-serialize-no-std")] -impl<'a, T: Scalar + Deserialize<'a>, const D: usize> Deserialize<'a> for Point { +impl<'a, T: Scalar + Deserialize<'a>, D: DimName> Deserialize<'a> for OPoint +where + DefaultAllocator: Allocator, + >::Buffer: Deserialize<'a>, +{ fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { - let coords = SVector::::deserialize(deserializer)?; + let coords = OVector::::deserialize(deserializer)?; Ok(Self::from(coords)) } } #[cfg(feature = "abomonation-serialize")] -impl Abomonation for Point +impl Abomonation for OPoint where T: Scalar, - SVector: Abomonation, + OVector: Abomonation, + DefaultAllocator: Allocator, { unsafe fn entomb(&self, writer: &mut W) -> IOResult<()> { self.coords.entomb(writer) @@ -108,7 +131,10 @@ where } } -impl Point { +impl OPoint +where + DefaultAllocator: Allocator, +{ /// Returns a point containing the result of `f` applied to each of its entries. /// /// # Example @@ -122,7 +148,11 @@ impl Point { /// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3)); /// ``` #[inline] - pub fn map T2>(&self, f: F) -> Point { + #[must_use] + pub fn map T2>(&self, f: F) -> OPoint + where + DefaultAllocator: Allocator, + { self.coords.map(f).into() } @@ -161,20 +191,22 @@ impl Point { /// assert_eq!(p.to_homogeneous(), Vector4::new(10.0, 20.0, 30.0, 1.0)); /// ``` #[inline] - pub fn to_homogeneous(&self) -> OVector, U1>> + #[must_use] + pub fn to_homogeneous(&self) -> OVector> where T: One, - Const: DimNameAdd, - DefaultAllocator: Allocator, U1>>, + D: DimNameAdd, + DefaultAllocator: Allocator>, { let mut res = unsafe { crate::unimplemented_or_uninitialized_generic!( - , U1> as DimName>::name(), + as DimName>::name(), Const::<1> ) }; - res.fixed_slice_mut::(0, 0).copy_from(&self.coords); - res[(D, 0)] = T::one(); + res.generic_slice_mut((0, 0), (D::name(), Const::<1>)) + .copy_from(&self.coords); + res[(D::dim(), 0)] = T::one(); res } @@ -182,7 +214,7 @@ impl Point { /// Creates a new point with the given coordinates. #[deprecated(note = "Use Point::from(vector) instead.")] #[inline] - pub fn from_coordinates(coords: SVector) -> Self { + pub fn from_coordinates(coords: OVector) -> Self { Self { coords } } @@ -199,6 +231,7 @@ impl Point { /// assert_eq!(p.len(), 3); /// ``` #[inline] + #[must_use] pub fn len(&self) -> usize { self.coords.len() } @@ -212,6 +245,7 @@ impl Point { /// assert!(!p.is_empty()); /// ``` #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -239,13 +273,13 @@ impl Point { #[inline] pub fn iter( &self, - ) -> MatrixIter, Const<1>, >>::Buffer> - { + ) -> MatrixIter, >::Buffer> { self.coords.iter() } /// Gets a reference to i-th element of this point without bound-checking. #[inline] + #[must_use] pub unsafe fn get_unchecked(&self, i: usize) -> &T { self.coords.vget_unchecked(i) } @@ -265,13 +299,13 @@ impl Point { #[inline] pub fn iter_mut( &mut self, - ) -> MatrixIterMut, Const<1>, >>::Buffer> - { + ) -> MatrixIterMut, >::Buffer> { self.coords.iter_mut() } /// Gets a mutable reference to i-th element of this point without bound-checking. #[inline] + #[must_use] pub unsafe fn get_unchecked_mut(&mut self, i: usize) -> &mut T { self.coords.vget_unchecked_mut(i) } @@ -283,9 +317,10 @@ impl Point { } } -impl AbsDiffEq for Point +impl AbsDiffEq for OPoint where T::Epsilon: Copy, + DefaultAllocator: Allocator, { type Epsilon = T::Epsilon; @@ -300,9 +335,10 @@ where } } -impl RelativeEq for Point +impl RelativeEq for OPoint where T::Epsilon: Copy, + DefaultAllocator: Allocator, { #[inline] fn default_max_relative() -> Self::Epsilon { @@ -321,9 +357,10 @@ where } } -impl UlpsEq for Point +impl UlpsEq for OPoint where T::Epsilon: Copy, + DefaultAllocator: Allocator, { #[inline] fn default_max_ulps() -> u32 { @@ -336,16 +373,22 @@ where } } -impl Eq for Point {} +impl Eq for OPoint where DefaultAllocator: Allocator {} -impl PartialEq for Point { +impl PartialEq for OPoint +where + DefaultAllocator: Allocator, +{ #[inline] fn eq(&self, right: &Self) -> bool { self.coords == right.coords } } -impl PartialOrd for Point { +impl PartialOrd for OPoint +where + DefaultAllocator: Allocator, +{ #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.coords.partial_cmp(&other.coords) @@ -375,22 +418,28 @@ impl PartialOrd for Point { /* * inf/sup */ -impl Point { +impl OPoint +where + DefaultAllocator: Allocator, +{ /// Computes the infimum (aka. componentwise min) of two points. #[inline] - pub fn inf(&self, other: &Self) -> Point { + #[must_use] + pub fn inf(&self, other: &Self) -> OPoint { self.coords.inf(&other.coords).into() } /// Computes the supremum (aka. componentwise max) of two points. #[inline] - pub fn sup(&self, other: &Self) -> Point { + #[must_use] + pub fn sup(&self, other: &Self) -> OPoint { self.coords.sup(&other.coords).into() } /// Computes the (infimum, supremum) of two points. #[inline] - pub fn inf_sup(&self, other: &Self) -> (Point, Point) { + #[must_use] + pub fn inf_sup(&self, other: &Self) -> (OPoint, OPoint) { let (inf, sup) = self.coords.inf_sup(&other.coords); (inf.into(), sup.into()) } @@ -401,7 +450,10 @@ impl Point { * Display * */ -impl fmt::Display for Point { +impl fmt::Display for OPoint +where + DefaultAllocator: Allocator, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{")?; diff --git a/src/geometry/point_alias.rs b/src/geometry/point_alias.rs index 54441b09..f5f855c9 100644 --- a/src/geometry/point_alias.rs +++ b/src/geometry/point_alias.rs @@ -1,4 +1,8 @@ -use crate::geometry::Point; +use crate::geometry::OPoint; +use crate::Const; + +/// A point with `D` elements. +pub type Point = OPoint>; /// A statically sized 1-dimensional column point. /// diff --git a/src/geometry/point_construction.rs b/src/geometry/point_construction.rs index 7e34137c..0ffbf4d8 100644 --- a/src/geometry/point_construction.rs +++ b/src/geometry/point_construction.rs @@ -10,22 +10,26 @@ use rand::{ use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; -use crate::base::{DefaultAllocator, SVector, Scalar}; +use crate::base::{DefaultAllocator, Scalar}; use crate::{ - Const, OVector, Point1, Point2, Point3, Point4, Point5, Point6, Vector1, Vector2, Vector3, - Vector4, Vector5, Vector6, + Const, DimName, OPoint, OVector, Point1, Point2, Point3, Point4, Point5, Point6, Vector1, + Vector2, Vector3, Vector4, Vector5, Vector6, }; use simba::scalar::{ClosedDiv, SupersetOf}; use crate::geometry::Point; /// # Other construction methods -impl Point { +impl OPoint +where + DefaultAllocator: Allocator, +{ /// Creates a new point with uninitialized coordinates. #[inline] pub unsafe fn new_uninitialized() -> Self { Self::from(crate::unimplemented_or_uninitialized_generic!( - Const::, Const::<1> + D::name(), + Const::<1> )) } @@ -49,7 +53,7 @@ impl Point { where T: Zero, { - Self::from(SVector::from_element(T::zero())) + Self::from(OVector::from_element(T::zero())) } /// Creates a new point from a slice. @@ -68,7 +72,7 @@ impl Point { /// ``` #[inline] pub fn from_slice(components: &[T]) -> Self { - Self::from(SVector::from_row_slice(components)) + Self::from(OVector::from_row_slice(components)) } /// Creates a new point from its homogeneous vector representation. @@ -102,14 +106,15 @@ impl Point { /// assert_eq!(pt, Some(Point2::new(1.0, 2.0))); /// ``` #[inline] - pub fn from_homogeneous(v: OVector, U1>>) -> Option + pub fn from_homogeneous(v: OVector>) -> Option where T: Scalar + Zero + One + ClosedDiv, - Const: DimNameAdd, - DefaultAllocator: Allocator, U1>>, + D: DimNameAdd, + DefaultAllocator: Allocator>, { - if !v[D].is_zero() { - let coords = v.fixed_slice::(0, 0) / v[D].inlined_clone(); + if !v[D::dim()].is_zero() { + let coords = + v.generic_slice((0, 0), (D::name(), Const::<1>)) / v[D::dim()].inlined_clone(); Some(Self::from(coords)) } else { None @@ -125,9 +130,10 @@ impl Point { /// let pt2 = pt.cast::(); /// assert_eq!(pt2, Point2::new(1.0f32, 2.0)); /// ``` - pub fn cast(self) -> Point + pub fn cast(self) -> OPoint where - Point: SupersetOf, + OPoint: SupersetOf, + DefaultAllocator: Allocator, { crate::convert(self) } @@ -138,38 +144,43 @@ impl Point { * Traits that build points. * */ -impl Bounded for Point { +impl Bounded for OPoint +where + DefaultAllocator: Allocator, +{ #[inline] fn max_value() -> Self { - Self::from(SVector::max_value()) + Self::from(OVector::max_value()) } #[inline] fn min_value() -> Self { - Self::from(SVector::min_value()) + Self::from(OVector::min_value()) } } #[cfg(feature = "rand-no-std")] -impl Distribution> for Standard +impl Distribution> for Standard where Standard: Distribution, + DefaultAllocator: Allocator, { /// Generate a `Point` where each coordinate is an independent variate from `[0, 1)`. #[inline] - fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> Point { - Point::from(rng.gen::>()) + fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> OPoint { + OPoint::from(rng.gen::>()) } } #[cfg(feature = "arbitrary")] -impl Arbitrary for Point +impl Arbitrary for OPoint where - >>::Buffer: Send, + >::Buffer: Send, + DefaultAllocator: Allocator, { #[inline] fn arbitrary(g: &mut Gen) -> Self { - Self::from(SVector::arbitrary(g)) + Self::from(OVector::arbitrary(g)) } } @@ -181,7 +192,7 @@ where // NOTE: the impl for Point1 is not with the others so that we // can add a section with the impl block comment. /// # Construction from individual components -impl Point1 { +impl Point1 { /// Initializes this point from its components. /// /// # Example @@ -192,7 +203,7 @@ impl Point1 { /// assert_eq!(p.x, 1.0); /// ``` #[inline] - pub const fn new(x: T) -> Self { + pub fn new(x: T) -> Self { Point { coords: Vector1::new(x), } @@ -200,13 +211,13 @@ impl Point1 { } macro_rules! componentwise_constructors_impl( ($($doc: expr; $Point: ident, $Vector: ident, $($args: ident:$irow: expr),*);* $(;)*) => {$( - impl $Point { + impl $Point { #[doc = "Initializes this point from its components."] #[doc = "# Example\n```"] #[doc = $doc] #[doc = "```"] #[inline] - pub const fn new($($args: T),*) -> Self { + pub fn new($($args: T),*) -> Self { Point { coords: $Vector::new($($args),*) } } } diff --git a/src/geometry/point_conversion.rs b/src/geometry/point_conversion.rs index 91cd62ce..f35a9fc6 100644 --- a/src/geometry/point_conversion.rs +++ b/src/geometry/point_conversion.rs @@ -7,6 +7,7 @@ use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, Matrix, OVector, Scalar}; use crate::geometry::Point; +use crate::{DimName, OPoint}; /* * This file provides the following conversions: @@ -16,67 +17,69 @@ use crate::geometry::Point; * Point -> Vector (homogeneous) */ -impl SubsetOf> for Point +impl SubsetOf> for OPoint where T1: Scalar, T2: Scalar + SupersetOf, + DefaultAllocator: Allocator + Allocator, { #[inline] - fn to_superset(&self) -> Point { - Point::from(self.coords.to_superset()) + fn to_superset(&self) -> OPoint { + OPoint::from(self.coords.to_superset()) } #[inline] - fn is_in_subset(m: &Point) -> bool { + fn is_in_subset(m: &OPoint) -> bool { // TODO: is there a way to reuse the `.is_in_subset` from the matrix implementation of // SubsetOf? m.iter().all(|e| e.is_in_subset()) } #[inline] - fn from_superset_unchecked(m: &Point) -> Self { + fn from_superset_unchecked(m: &OPoint) -> Self { Self::from(Matrix::from_superset_unchecked(&m.coords)) } } -impl SubsetOf, U1>>> for Point +impl SubsetOf>> for OPoint where - Const: DimNameAdd, + D: DimNameAdd, T1: Scalar, T2: Scalar + Zero + One + ClosedDiv + SupersetOf, - DefaultAllocator: - Allocator, U1>> + Allocator, U1>>, + DefaultAllocator: Allocator + + Allocator + + Allocator> + + Allocator>, // + Allocator // + Allocator, { #[inline] - fn to_superset(&self) -> OVector, U1>> { - let p: Point = self.to_superset(); + fn to_superset(&self) -> OVector> { + let p: OPoint = self.to_superset(); p.to_homogeneous() } #[inline] - fn is_in_subset(v: &OVector, U1>>) -> bool { - crate::is_convertible::<_, OVector, U1>>>(v) && !v[D].is_zero() + fn is_in_subset(v: &OVector>) -> bool { + crate::is_convertible::<_, OVector>>(v) && !v[D::dim()].is_zero() } #[inline] - fn from_superset_unchecked(v: &OVector, U1>>) -> Self { - let coords = v.fixed_slice::(0, 0) / v[D].inlined_clone(); + fn from_superset_unchecked(v: &OVector>) -> Self { + let coords = v.generic_slice((0, 0), (D::name(), Const::<1>)) / v[D::dim()].inlined_clone(); Self { coords: crate::convert_unchecked(coords), } } } -impl From> - for OVector, U1>> +impl From> for OVector> where - Const: DimNameAdd, - DefaultAllocator: Allocator, U1>>, + D: DimNameAdd, + DefaultAllocator: Allocator> + Allocator, { #[inline] - fn from(t: Point) -> Self { + fn from(t: OPoint) -> Self { t.to_homogeneous() } } @@ -90,17 +93,20 @@ impl From<[T; D]> for Point { } } -impl Into<[T; D]> for Point { +impl From> for [T; D] { #[inline] - fn into(self) -> [T; D] { - self.coords.into() + fn from(p: Point) -> Self { + p.coords.into() } } -impl From>> for Point { +impl From> for OPoint +where + DefaultAllocator: Allocator, +{ #[inline] - fn from(coords: OVector>) -> Self { - Point { coords } + fn from(coords: OVector) -> Self { + OPoint { coords } } } diff --git a/src/geometry/point_coordinates.rs b/src/geometry/point_coordinates.rs index 8d9e9ccc..984a2fae 100644 --- a/src/geometry/point_coordinates.rs +++ b/src/geometry/point_coordinates.rs @@ -1,9 +1,9 @@ use std::ops::{Deref, DerefMut}; use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; -use crate::base::Scalar; +use crate::base::{Scalar, U1, U2, U3, U4, U5, U6}; -use crate::geometry::Point; +use crate::geometry::OPoint; /* * @@ -12,8 +12,8 @@ use crate::geometry::Point; */ macro_rules! deref_impl( - ($D: expr, $Target: ident $(, $comps: ident)*) => { - impl Deref for Point + ($D: ty, $Target: ident $(, $comps: ident)*) => { + impl Deref for OPoint { type Target = $Target; @@ -23,7 +23,7 @@ macro_rules! deref_impl( } } - impl DerefMut for Point + impl DerefMut for OPoint { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { @@ -33,9 +33,9 @@ macro_rules! deref_impl( } ); -deref_impl!(1, X, x); -deref_impl!(2, XY, x, y); -deref_impl!(3, XYZ, x, y, z); -deref_impl!(4, XYZW, x, y, z, w); -deref_impl!(5, XYZWA, x, y, z, w, a); -deref_impl!(6, XYZWAB, x, y, z, w, a, b); +deref_impl!(U1, X, x); +deref_impl!(U2, XY, x, y); +deref_impl!(U3, XYZ, x, y, z); +deref_impl!(U4, XYZW, x, y, z, w); +deref_impl!(U5, XYZWA, x, y, z, w, a); +deref_impl!(U6, XYZWAB, x, y, z, w, a, b); diff --git a/src/geometry/point_ops.rs b/src/geometry/point_ops.rs index 576028cc..5b019a9d 100644 --- a/src/geometry/point_ops.rs +++ b/src/geometry/point_ops.rs @@ -8,18 +8,23 @@ use simba::scalar::{ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg, ClosedSub}; use crate::base::constraint::{ AreMultipliable, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; -use crate::base::dimension::{Dim, U1}; +use crate::base::dimension::{Dim, DimName, U1}; use crate::base::storage::Storage; -use crate::base::{Const, Matrix, SVector, Scalar, Vector}; +use crate::base::{Const, Matrix, OVector, Scalar, Vector}; -use crate::geometry::Point; +use crate::allocator::Allocator; +use crate::geometry::{OPoint, Point}; +use crate::DefaultAllocator; /* * * Indexing. * */ -impl Index for Point { +impl Index for OPoint +where + DefaultAllocator: Allocator, +{ type Output = T; #[inline] @@ -28,7 +33,10 @@ impl Index for Point { } } -impl IndexMut for Point { +impl IndexMut for OPoint +where + DefaultAllocator: Allocator, +{ #[inline] fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.coords[i] @@ -40,7 +48,10 @@ impl IndexMut for Point { * Neg. * */ -impl Neg for Point { +impl Neg for OPoint +where + DefaultAllocator: Allocator, +{ type Output = Self; #[inline] @@ -49,8 +60,11 @@ impl Neg for Point { } } -impl<'a, T: Scalar + ClosedNeg, const D: usize> Neg for &'a Point { - type Output = Point; +impl<'a, T: Scalar + ClosedNeg, D: DimName> Neg for &'a OPoint +where + DefaultAllocator: Allocator, +{ + type Output = OPoint; #[inline] fn neg(self) -> Self::Output { @@ -66,102 +80,103 @@ impl<'a, T: Scalar + ClosedNeg, const D: usize> Neg for &'a Point { // Point - Point add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (Const, U1) -> (Const, U1) - const D; for; where; - self: &'a Point, right: &'b Point, Output = SVector; + (D, U1), (D, U1) -> (D, U1) + const; for D; where D: DimName, DefaultAllocator: Allocator; + self: &'a OPoint, right: &'b OPoint, Output = OVector; &self.coords - &right.coords; 'a, 'b); add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (Const, U1) -> (Const, U1) - const D; for; where; - self: &'a Point, right: Point, Output = SVector; + (D, U1), (D, U1) -> (D, U1) + const; for D; where D: DimName, DefaultAllocator: Allocator; + self: &'a OPoint, right: OPoint, Output = OVector; &self.coords - right.coords; 'a); add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (Const, U1) -> (Const, U1) - const D; for; where; - self: Point, right: &'b Point, Output = SVector; + (D, U1), (D, U1) -> (D, U1) + const; for D; where D: DimName, DefaultAllocator: Allocator; + self: OPoint, right: &'b OPoint, Output = OVector; self.coords - &right.coords; 'b); add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (Const, U1) -> (Const, U1) - const D; for; where; - self: Point, right: Point, Output = SVector; + (D, U1), (D, U1) -> (D, U1) + const; for D; where D: DimName, DefaultAllocator: Allocator; + self: OPoint, right: OPoint, Output = OVector; self.coords - right.coords; ); // Point - Vector add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: &'a Point, right: &'b Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: &'a OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(&self.coords - right); 'a, 'b); add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: &'a Point, right: Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: &'a OPoint, right: Vector, Output = OPoint; Self::Output::from(&self.coords - &right); 'a); // TODO: should not be a ref to `right`. add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: Point, right: &'b Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(self.coords - right); 'b); add_sub_impl!(Sub, sub, ClosedSub; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: Point, right: Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: OPoint, right: Vector, Output = OPoint; Self::Output::from(self.coords - right); ); // Point + Vector add_sub_impl!(Add, add, ClosedAdd; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: &'a Point, right: &'b Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: &'a OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(&self.coords + right); 'a, 'b); add_sub_impl!(Add, add, ClosedAdd; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: &'a Point, right: Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: &'a OPoint, right: Vector, Output = OPoint; Self::Output::from(&self.coords + &right); 'a); // TODO: should not be a ref to `right`. add_sub_impl!(Add, add, ClosedAdd; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: Point, right: &'b Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(self.coords + right); 'b); add_sub_impl!(Add, add, ClosedAdd; - (Const, U1), (D2, U1) -> (Const, U1) - const D1; - for D2, SB; - where D2: Dim, SB: Storage; - self: Point, right: Vector, Output = Point; + (D1, U1), (D2, U1) -> (D1, U1) + const; + for D1, D2, SB; + where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; + self: OPoint, right: Vector, Output = OPoint; Self::Output::from(self.coords + right); ); // TODO: replace by the shared macro: add_sub_assign_impl? macro_rules! op_assign_impl( ($($TraitAssign: ident, $method_assign: ident, $bound: ident);* $(;)*) => {$( - impl<'b, T, D2: Dim, SB, const D1: usize> $TraitAssign<&'b Vector> for Point + impl<'b, T, D1: DimName, D2: Dim, SB> $TraitAssign<&'b Vector> for OPoint where T: Scalar + $bound, SB: Storage, - ShapeConstraint: SameNumberOfRows, D2> { + ShapeConstraint: SameNumberOfRows, + DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: &'b Vector) { @@ -169,10 +184,11 @@ macro_rules! op_assign_impl( } } - impl $TraitAssign> for Point + impl $TraitAssign> for OPoint where T: Scalar + $bound, SB: Storage, - ShapeConstraint: SameNumberOfRows, D2> { + ShapeConstraint: SameNumberOfRows, + DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: Vector) { @@ -214,28 +230,30 @@ md_impl_all!( macro_rules! componentwise_scalarop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident) => { - impl $Trait for Point + impl $Trait for OPoint + where DefaultAllocator: Allocator { - type Output = Point; + type Output = OPoint; #[inline] fn $method(self, right: T) -> Self::Output { - Point::from(self.coords.$method(right)) + OPoint::from(self.coords.$method(right)) } } - impl<'a, T: Scalar + $bound, const D: usize> $Trait for &'a Point + impl<'a, T: Scalar + $bound, D: DimName> $Trait for &'a OPoint + where DefaultAllocator: Allocator { - type Output = Point; + type Output = OPoint; #[inline] fn $method(self, right: T) -> Self::Output { - Point::from((&self.coords).$method(right)) + OPoint::from((&self.coords).$method(right)) } } - impl $TraitAssign for Point - /* where DefaultAllocator: Allocator */ + impl $TraitAssign for OPoint + where DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: T) { @@ -250,23 +268,25 @@ componentwise_scalarop_impl!(Div, div, ClosedDiv; DivAssign, div_assign); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( - impl Mul> for $T + impl Mul> for $T + where DefaultAllocator: Allocator<$T, D> { - type Output = Point<$T, D>; + type Output = OPoint<$T, D>; #[inline] - fn mul(self, right: Point<$T, D>) -> Self::Output { - Point::from(self * right.coords) + fn mul(self, right: OPoint<$T, D>) -> Self::Output { + OPoint::from(self * right.coords) } } - impl<'b, const D: usize> Mul<&'b Point<$T, D>> for $T + impl<'b, D: DimName> Mul<&'b OPoint<$T, D>> for $T + where DefaultAllocator: Allocator<$T, D> { - type Output = Point<$T, D>; + type Output = OPoint<$T, D>; #[inline] - fn mul(self, right: &'b Point<$T, D>) -> Self::Output { - Point::from(self * &right.coords) + fn mul(self, right: &'b OPoint<$T, D>) -> Self::Output { + OPoint::from(self * &right.coords) } } )*} diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 6852335b..3449f1ae 100755 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -139,7 +139,7 @@ mod rkyv_impl { impl, S: Fallible + ?Sized> Serialize for Quaternion { fn serialize(&self, serializer: &mut S) -> Result { - Ok(self.coords.serialize(serializer)?) + self.coords.serialize(serializer) } } @@ -191,6 +191,7 @@ where /// The imaginary part of this quaternion. #[inline] + #[must_use] pub fn imag(&self) -> Vector3 { self.coords.xyz() } @@ -223,6 +224,7 @@ where /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6)); /// ``` #[inline] + #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Self { self * (T::one() - t) + other * t } @@ -238,6 +240,7 @@ where /// assert_eq!(q.vector()[2], 4.0); /// ``` #[inline] + #[must_use] pub fn vector(&self) -> MatrixSlice, CStride> { self.coords.fixed_rows::<3>(0) } @@ -251,6 +254,7 @@ where /// assert_eq!(q.scalar(), 1.0); /// ``` #[inline] + #[must_use] pub fn scalar(&self) -> T { self.coords[3] } @@ -266,6 +270,7 @@ where /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); /// ``` #[inline] + #[must_use] pub fn as_vector(&self) -> &Vector4 { &self.coords } @@ -280,6 +285,7 @@ where /// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn norm(&self) -> T { self.coords.norm() } @@ -297,6 +303,7 @@ where /// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn magnitude(&self) -> T { self.norm() } @@ -310,6 +317,7 @@ where /// assert_eq!(q.magnitude_squared(), 30.0); /// ``` #[inline] + #[must_use] pub fn norm_squared(&self) -> T { self.coords.norm_squared() } @@ -326,6 +334,7 @@ where /// assert_eq!(q.magnitude_squared(), 30.0); /// ``` #[inline] + #[must_use] pub fn magnitude_squared(&self) -> T { self.norm_squared() } @@ -340,6 +349,7 @@ where /// assert_eq!(q1.dot(&q2), 70.0); /// ``` #[inline] + #[must_use] pub fn dot(&self, rhs: &Self) -> T { self.coords.dot(&rhs.coords) } @@ -409,6 +419,7 @@ where /// let result = a.inner(&b); /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); #[inline] + #[must_use] pub fn inner(&self, other: &Self) -> Self { (self * other + other * self).half() } @@ -428,6 +439,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn outer(&self, other: &Self) -> Self { #[allow(clippy::eq_op)] (self * other - other * self).half() @@ -448,6 +460,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn project(&self, other: &Self) -> Option where T: RealField, @@ -470,6 +483,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn reject(&self, other: &Self) -> Option where T: RealField, @@ -492,6 +506,7 @@ where /// assert_eq!(half_ang, f32::consts::FRAC_PI_2); /// assert_eq!(axis, Some(Vector3::x_axis())); /// ``` + #[must_use] pub fn polar_decomposition(&self) -> (T, T, Option>>) where T: RealField, @@ -519,6 +534,7 @@ where /// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6) /// ``` #[inline] + #[must_use] pub fn ln(&self) -> Self { let n = self.norm(); let v = self.vector(); @@ -537,6 +553,7 @@ where /// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5) /// ``` #[inline] + #[must_use] pub fn exp(&self) -> Self { self.exp_eps(T::simd_default_epsilon()) } @@ -556,6 +573,7 @@ where /// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity()); /// ``` #[inline] + #[must_use] pub fn exp_eps(&self, eps: T) -> Self { let v = self.vector(); let nn = v.norm_squared(); @@ -579,6 +597,7 @@ where /// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn powf(&self, n: T) -> Self { (self.ln() * n).exp() } @@ -674,18 +693,21 @@ where /// Calculates square of a quaternion. #[inline] + #[must_use] pub fn squared(&self) -> Self { self * self } /// Divides quaternion into two. #[inline] + #[must_use] pub fn half(&self) -> Self { self / crate::convert(2.0f64) } /// Calculates square root. #[inline] + #[must_use] pub fn sqrt(&self) -> Self { self.powf(crate::convert(0.5)) } @@ -694,12 +716,14 @@ where /// /// A quaternion is pure if it has no real part (`self.w == 0.0`). #[inline] + #[must_use] pub fn is_pure(&self) -> bool { self.w.is_zero() } /// Convert quaternion to pure quaternion. #[inline] + #[must_use] pub fn pure(&self) -> Self { Self::from_imag(self.imag()) } @@ -708,6 +732,7 @@ where /// /// Calculates B-1 * A where A = self, B = other. #[inline] + #[must_use] pub fn left_div(&self, other: &Self) -> Option where T: RealField, @@ -730,6 +755,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn right_div(&self, other: &Self) -> Option where T: RealField, @@ -749,6 +775,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn cos(&self) -> Self { let z = self.imag().magnitude(); let w = -self.w.simd_sin() * z.simd_sinhc(); @@ -766,6 +793,7 @@ where /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn acos(&self) -> Self { let u = Self::from_imag(self.imag().normalize()); let identity = Self::identity(); @@ -787,6 +815,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn sin(&self) -> Self { let z = self.imag().magnitude(); let w = self.w.simd_cos() * z.simd_sinhc(); @@ -804,6 +833,7 @@ where /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn asin(&self) -> Self { let u = Self::from_imag(self.imag().normalize()); let identity = Self::identity(); @@ -825,6 +855,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn tan(&self) -> Self where T: RealField, @@ -843,6 +874,7 @@ where /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn atan(&self) -> Self where T: RealField, @@ -867,6 +899,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn sinh(&self) -> Self { (self.exp() - (-self).exp()).half() } @@ -883,6 +916,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn asinh(&self) -> Self { let identity = Self::identity(); (self + (identity + self.squared()).sqrt()).ln() @@ -900,6 +934,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn cosh(&self) -> Self { (self.exp() + (-self).exp()).half() } @@ -916,6 +951,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn acosh(&self) -> Self { let identity = Self::identity(); (self + (self + identity).sqrt() * (self - identity).sqrt()).ln() @@ -933,6 +969,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn tanh(&self) -> Self where T: RealField, @@ -952,6 +989,7 @@ where /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] + #[must_use] pub fn atanh(&self) -> Self { let identity = Self::identity(); ((identity + self).ln() - (identity - self).ln()).half() @@ -1069,6 +1107,7 @@ where /// assert_eq!(rot.angle(), 1.78); /// ``` #[inline] + #[must_use] pub fn angle(&self) -> T { let w = self.quaternion().scalar().simd_abs(); self.quaternion().imag().norm().simd_atan2(w) * crate::convert(2.0f64) @@ -1085,6 +1124,7 @@ where /// assert_eq!(*axis.quaternion(), Quaternion::new(1.0, 0.0, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn quaternion(&self) -> &Quaternion { self.as_ref() } @@ -1133,6 +1173,7 @@ where /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn angle_to(&self, other: &Self) -> T { let delta = self.rotation_to(other); delta.angle() @@ -1152,6 +1193,7 @@ where /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other / self } @@ -1168,6 +1210,7 @@ where /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Quaternion { self.as_ref().lerp(other.as_ref(), t) } @@ -1184,6 +1227,7 @@ where /// assert_eq!(q1.nlerp(&q2, 0.1), UnitQuaternion::new_normalize(Quaternion::new(0.9, 0.1, 0.0, 0.0))); /// ``` #[inline] + #[must_use] pub fn nlerp(&self, other: &Self, t: T) -> Self { let mut res = self.lerp(other, t); let _ = res.normalize_mut(); @@ -1209,6 +1253,7 @@ where /// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -1228,6 +1273,7 @@ where /// * `epsilon`: the value below which the sinus of the angle separating both quaternion /// must be to return `None`. #[inline] + #[must_use] pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, @@ -1287,6 +1333,7 @@ where /// assert!(rot.axis().is_none()); /// ``` #[inline] + #[must_use] pub fn axis(&self) -> Option>> where T: RealField, @@ -1311,6 +1358,7 @@ where /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn scaled_axis(&self) -> Vector3 where T: RealField, @@ -1339,6 +1387,7 @@ where /// assert!(rot.axis_angle().is_none()); /// ``` #[inline] + #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, @@ -1350,6 +1399,7 @@ where /// /// Note that this function yields a `Quaternion` because it loses the unit property. #[inline] + #[must_use] pub fn exp(&self) -> Quaternion { self.as_ref().exp() } @@ -1369,6 +1419,7 @@ where /// assert_relative_eq!(q.ln().vector().into_owned(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn ln(&self) -> Quaternion where T: RealField, @@ -1397,6 +1448,7 @@ where /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] + #[must_use] pub fn powf(&self, n: T) -> Self where T: RealField, @@ -1425,7 +1477,8 @@ where /// assert_relative_eq!(*rot.matrix(), expected, epsilon = 1.0e-6); /// ``` #[inline] - pub fn to_rotation_matrix(&self) -> Rotation { + #[must_use] + pub fn to_rotation_matrix(self) -> Rotation { let i = self.as_ref()[0]; let j = self.as_ref()[1]; let k = self.as_ref()[2]; @@ -1460,7 +1513,7 @@ where /// The angles are produced in the form (roll, pitch, yaw). #[inline] #[deprecated(note = "This is renamed to use `.euler_angles()`.")] - pub fn to_euler_angles(&self) -> (T, T, T) + pub fn to_euler_angles(self) -> (T, T, T) where T: RealField, { @@ -1482,6 +1535,7 @@ where /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn euler_angles(&self) -> (T, T, T) where T: RealField, @@ -1506,7 +1560,8 @@ where /// assert_relative_eq!(rot.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] - pub fn to_homogeneous(&self) -> Matrix4 { + #[must_use] + pub fn to_homogeneous(self) -> Matrix4 { self.to_rotation_matrix().to_homogeneous() } @@ -1526,6 +1581,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point3) -> Point3 { self * pt } @@ -1546,6 +1602,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &Vector3) -> Vector3 { self * v } @@ -1566,6 +1623,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point3) -> Point3 { // TODO: would it be useful performancewise not to call inverse explicitly (i-e. implement // the inverse transformation explicitly here) ? @@ -1588,6 +1646,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse() * v } @@ -1608,6 +1667,7 @@ where /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } @@ -1616,6 +1676,7 @@ where /// /// This is faster, but approximate, way to compute `UnitQuaternion::new(axisangle) * self`. #[inline] + #[must_use] pub fn append_axisangle_linearized(&self, axisangle: &Vector3) -> Self { let half: T = crate::convert(0.5); let q1 = self.into_inner(); diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs index f110e3dd..7a681bb2 100644 --- a/src/geometry/quaternion_construction.rs +++ b/src/geometry/quaternion_construction.rs @@ -171,7 +171,7 @@ where Standard: Distribution, { #[inline] - fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> Quaternion { + fn sample(&self, rng: &mut R) -> Quaternion { Quaternion::new(rng.gen(), rng.gen(), rng.gen(), rng.gen()) } } @@ -535,10 +535,10 @@ where SC: Storage, { // TODO: code duplication with Rotation. - let c = na.cross(&nb); + let c = na.cross(nb); if let Some(axis) = Unit::try_new(c, T::default_epsilon()) { - let cos = na.dot(&nb); + let cos = na.dot(nb); // The cosinus may be out of [-1, 1] because of inaccuracies. if cos <= -T::one() { @@ -548,7 +548,7 @@ where } else { Some(Self::from_axis_angle(&axis, cos.acos() * s)) } - } else if na.dot(&nb) < T::zero() { + } else if na.dot(nb) < T::zero() { // PI // // The rotation axis is undefined but the angle not zero. This is not a @@ -860,7 +860,7 @@ where { /// Generate a uniformly distributed random rotation quaternion. #[inline] - fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> UnitQuaternion { + fn sample(&self, rng: &mut R) -> UnitQuaternion { // Ken Shoemake's Subgroup Algorithm // Uniform random rotations. // In D. Kirk, editor, Graphics Gems III, pages 124-132. Academic, New York, 1992. diff --git a/src/geometry/quaternion_coordinates.rs b/src/geometry/quaternion_coordinates.rs index d9901888..cb16e59e 100644 --- a/src/geometry/quaternion_coordinates.rs +++ b/src/geometry/quaternion_coordinates.rs @@ -1,4 +1,3 @@ -use std::mem; use std::ops::{Deref, DerefMut}; use simba::simd::SimdValue; @@ -13,13 +12,13 @@ impl Deref for Quaternion { #[inline] fn deref(&self) -> &Self::Target { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Self as *const Self::Target) } } } impl DerefMut for Quaternion { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { mem::transmute(self) } + unsafe { &mut *(self as *mut Self as *mut Self::Target) } } } diff --git a/src/geometry/quaternion_ops.rs b/src/geometry/quaternion_ops.rs index 6a2c87a0..eb7a15cd 100644 --- a/src/geometry/quaternion_ops.rs +++ b/src/geometry/quaternion_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + /* * This file provides: * =================== diff --git a/src/geometry/reflection.rs b/src/geometry/reflection.rs index e48c700a..87166b81 100644 --- a/src/geometry/reflection.rs +++ b/src/geometry/reflection.rs @@ -1,5 +1,5 @@ use crate::base::constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint}; -use crate::base::{Const, Matrix, Scalar, Unit, Vector}; +use crate::base::{Const, Matrix, Unit, Vector}; use crate::dimension::{Dim, U1}; use crate::storage::{Storage, StorageMut}; use simba::scalar::ComplexField; @@ -7,7 +7,7 @@ use simba::scalar::ComplexField; use crate::geometry::Point; /// A reflection wrt. a plane. -pub struct Reflection> { +pub struct Reflection { axis: Vector, bias: T, } @@ -34,6 +34,7 @@ impl> Reflection { } /// The reflexion axis. + #[must_use] pub fn axis(&self) -> &Vector { &self.axis } @@ -89,7 +90,7 @@ impl> Reflection { } let m_two: T = crate::convert(-2.0f64); - lhs.gerc(m_two, &work, &self.axis, T::one()); + lhs.gerc(m_two, work, &self.axis, T::one()); } /// Applies the reflection to the rows of `lhs`. @@ -110,6 +111,6 @@ impl> Reflection { } let m_two = sign.scale(crate::convert(-2.0f64)); - lhs.gerc(m_two, &work, &self.axis, sign); + lhs.gerc(m_two, work, &self.axis, sign); } } diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index 7b4d609a..98e8fcbc 100755 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -55,7 +55,7 @@ use crate::geometry::Point; /// #[repr(C)] #[derive(Debug)] -pub struct Rotation { +pub struct Rotation { matrix: SMatrix, } @@ -185,6 +185,7 @@ impl Rotation { /// assert_eq!(*rot.matrix(), expected); /// ``` #[inline] + #[must_use] pub fn matrix(&self) -> &SMatrix { &self.matrix } @@ -198,9 +199,9 @@ impl Rotation { /// A mutable reference to the underlying matrix representation of this rotation. /// - /// This is suffixed by "_unchecked" because this allows the user to replace the matrix by another one that is - /// non-square, non-inversible, or non-orthonormal. If one of those properties is broken, - /// subsequent method calls may be UB. + /// This is suffixed by "_unchecked" because this allows the user to replace the + /// matrix by another one that is non-inversible or non-orthonormal. If one of + /// those properties is broken, subsequent method calls may return bogus results. #[inline] pub fn matrix_mut_unchecked(&mut self) -> &mut SMatrix { &mut self.matrix @@ -262,6 +263,7 @@ impl Rotation { /// assert_eq!(rot.to_homogeneous(), expected); /// ``` #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where T: Zero + One, @@ -403,6 +405,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } @@ -422,6 +425,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } @@ -441,6 +445,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { Point::from(self.inverse_transform_vector(&pt.coords)) } @@ -460,6 +465,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.matrix().tr_mul(v) } @@ -479,6 +485,7 @@ where /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { Unit::new_unchecked(self.inverse_transform_vector(&**v)) } diff --git a/src/geometry/rotation_conversion.rs b/src/geometry/rotation_conversion.rs index 0e83d60f..517010a0 100644 --- a/src/geometry/rotation_conversion.rs +++ b/src/geometry/rotation_conversion.rs @@ -267,10 +267,7 @@ where { #[inline] fn from(arr: [Rotation; 2]) -> Self { - Self::from_matrix_unchecked(OMatrix::from([ - arr[0].clone().into_inner(), - arr[1].clone().into_inner(), - ])) + Self::from_matrix_unchecked(OMatrix::from([arr[0].into_inner(), arr[1].into_inner()])) } } @@ -283,10 +280,10 @@ where #[inline] fn from(arr: [Rotation; 4]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ - arr[0].clone().into_inner(), - arr[1].clone().into_inner(), - arr[2].clone().into_inner(), - arr[3].clone().into_inner(), + arr[0].into_inner(), + arr[1].into_inner(), + arr[2].into_inner(), + arr[3].into_inner(), ])) } } @@ -300,14 +297,14 @@ where #[inline] fn from(arr: [Rotation; 8]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ - arr[0].clone().into_inner(), - arr[1].clone().into_inner(), - arr[2].clone().into_inner(), - arr[3].clone().into_inner(), - arr[4].clone().into_inner(), - arr[5].clone().into_inner(), - arr[6].clone().into_inner(), - arr[7].clone().into_inner(), + arr[0].into_inner(), + arr[1].into_inner(), + arr[2].into_inner(), + arr[3].into_inner(), + arr[4].into_inner(), + arr[5].into_inner(), + arr[6].into_inner(), + arr[7].into_inner(), ])) } } @@ -321,22 +318,22 @@ where #[inline] fn from(arr: [Rotation; 16]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ - arr[0].clone().into_inner(), - arr[1].clone().into_inner(), - arr[2].clone().into_inner(), - arr[3].clone().into_inner(), - arr[4].clone().into_inner(), - arr[5].clone().into_inner(), - arr[6].clone().into_inner(), - arr[7].clone().into_inner(), - arr[8].clone().into_inner(), - arr[9].clone().into_inner(), - arr[10].clone().into_inner(), - arr[11].clone().into_inner(), - arr[12].clone().into_inner(), - arr[13].clone().into_inner(), - arr[14].clone().into_inner(), - arr[15].clone().into_inner(), + arr[0].into_inner(), + arr[1].into_inner(), + arr[2].into_inner(), + arr[3].into_inner(), + arr[4].into_inner(), + arr[5].into_inner(), + arr[6].into_inner(), + arr[7].into_inner(), + arr[8].into_inner(), + arr[9].into_inner(), + arr[10].into_inner(), + arr[11].into_inner(), + arr[12].into_inner(), + arr[13].into_inner(), + arr[14].into_inner(), + arr[15].into_inner(), ])) } } diff --git a/src/geometry/rotation_interpolation.rs b/src/geometry/rotation_interpolation.rs index bf1c9094..dc029d20 100644 --- a/src/geometry/rotation_interpolation.rs +++ b/src/geometry/rotation_interpolation.rs @@ -18,6 +18,7 @@ impl Rotation2 { /// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] + #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T::Element: SimdRealField, @@ -47,6 +48,7 @@ impl Rotation3 { /// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] + #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T: RealField, @@ -67,12 +69,13 @@ impl Rotation3 { /// * `epsilon`: the value below which the sinus of the angle separating both rotations /// must be to return `None`. #[inline] + #[must_use] pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { - let q1 = Rotation3::from(*self); - let q2 = Rotation3::from(*other); + let q1 = UnitQuaternion::from(*self); + let q2 = UnitQuaternion::from(*other); q1.try_slerp(&q2, t, epsilon).map(|q| q.into()) } } diff --git a/src/geometry/rotation_specialization.rs b/src/geometry/rotation_specialization.rs index bb71f4f8..2ad73c69 100644 --- a/src/geometry/rotation_specialization.rs +++ b/src/geometry/rotation_specialization.rs @@ -186,6 +186,7 @@ impl Rotation2 { /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); /// ``` #[inline] + #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } @@ -215,6 +216,7 @@ impl Rotation2 { /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] + #[must_use] pub fn powf(&self, n: T) -> Self { Self::new(self.angle() * n) } @@ -232,6 +234,7 @@ impl Rotation2 { /// assert_relative_eq!(rot.angle(), 1.78); /// ``` #[inline] + #[must_use] pub fn angle(&self) -> T { self.matrix()[(1, 0)].simd_atan2(self.matrix()[(0, 0)]) } @@ -247,6 +250,7 @@ impl Rotation2 { /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); /// ``` #[inline] + #[must_use] pub fn angle_to(&self, other: &Self) -> T { self.rotation_to(other).angle() } @@ -256,6 +260,7 @@ impl Rotation2 { /// This is generally used in the context of generic programming. Using /// the `.angle()` method instead is more common. #[inline] + #[must_use] pub fn scaled_axis(&self) -> SVector { Vector1::new(self.angle()) } @@ -269,7 +274,7 @@ where { /// Generate a uniformly distributed random rotation. #[inline] - fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> Rotation2 { + fn sample(&self, rng: &mut R) -> Rotation2 { let twopi = Uniform::new(T::zero(), T::simd_two_pi()); Rotation2::new(rng.sample(twopi)) } @@ -640,6 +645,7 @@ where /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } @@ -659,6 +665,7 @@ where /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] + #[must_use] pub fn powf(&self, n: T) -> Self where T: RealField, @@ -765,6 +772,7 @@ impl Rotation3 { /// assert_relative_eq!(rot.angle(), 1.78); /// ``` #[inline] + #[must_use] pub fn angle(&self) -> T { ((self.matrix()[(0, 0)] + self.matrix()[(1, 1)] + self.matrix()[(2, 2)] - T::one()) / crate::convert(2.0)) @@ -787,6 +795,7 @@ impl Rotation3 { /// assert!(rot.axis().is_none()); /// ``` #[inline] + #[must_use] pub fn axis(&self) -> Option>> where T: RealField, @@ -811,6 +820,7 @@ impl Rotation3 { /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn scaled_axis(&self) -> Vector3 where T: RealField, @@ -842,15 +852,12 @@ impl Rotation3 { /// assert!(rot.axis_angle().is_none()); /// ``` #[inline] + #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, { - if let Some(axis) = self.axis() { - Some((axis, self.angle())) - } else { - None - } + self.axis().map(|axis| (axis, self.angle())) } /// The rotation angle needed to make `self` and `other` coincide. @@ -864,6 +871,7 @@ impl Rotation3 { /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn angle_to(&self, other: &Self) -> T where T::Element: SimdRealField, @@ -875,7 +883,7 @@ impl Rotation3 { /// /// The angles are produced in the form (roll, pitch, yaw). #[deprecated(note = "This is renamed to use `.euler_angles()`.")] - pub fn to_euler_angles(&self) -> (T, T, T) + pub fn to_euler_angles(self) -> (T, T, T) where T: RealField, { @@ -896,6 +904,7 @@ impl Rotation3 { /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` + #[must_use] pub fn euler_angles(&self) -> (T, T, T) where T: RealField, diff --git a/src/geometry/similarity.rs b/src/geometry/similarity.rs index b6e2c379..19164439 100755 --- a/src/geometry/similarity.rs +++ b/src/geometry/similarity.rs @@ -27,19 +27,19 @@ use crate::geometry::{AbstractRotation, Isometry, Point, Translation}; #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", - serde(bound(serialize = "T: Serialize, + serde(bound(serialize = "T: Scalar + Serialize, R: Serialize, DefaultAllocator: Allocator>, Owned>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", - serde(bound(deserialize = "T: Deserialize<'de>, + serde(bound(deserialize = "T: Scalar + Deserialize<'de>, R: Deserialize<'de>, DefaultAllocator: Allocator>, Owned>: Deserialize<'de>")) )] -pub struct Similarity { +pub struct Similarity { /// The part of this similarity that does not include the scaling factor. pub isometry: Isometry, scaling: T, @@ -122,6 +122,7 @@ where impl Similarity { /// The scaling factor of this similarity transformation. #[inline] + #[must_use] pub fn scaling(&self) -> T { self.scaling.inlined_clone() } @@ -177,7 +178,7 @@ where ); Self::from_parts( - Translation::from(&self.isometry.translation.vector * scaling), + Translation::from(self.isometry.translation.vector * scaling), self.isometry.rotation.clone(), self.scaling * scaling, ) @@ -248,6 +249,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } @@ -269,6 +271,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } @@ -289,6 +292,7 @@ where /// assert_relative_eq!(transformed_point, Point3::new(-1.5, 1.5, 1.5), epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.isometry.inverse_transform_point(pt) / self.scaling() } @@ -309,6 +313,7 @@ where /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.5, 2.0), epsilon = 1.0e-5); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.isometry.inverse_transform_vector(v) / self.scaling() } @@ -321,6 +326,7 @@ where impl Similarity { /// Converts this similarity into its equivalent homogeneous transformation matrix. #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, diff --git a/src/geometry/similarity_conversion.rs b/src/geometry/similarity_conversion.rs index 2a2338b0..6bc12814 100644 --- a/src/geometry/similarity_conversion.rs +++ b/src/geometry/similarity_conversion.rs @@ -197,7 +197,7 @@ where { #[inline] fn from(arr: [Similarity; 2]) -> Self { - let iso = Isometry::from([arr[0].isometry.clone(), arr[1].isometry.clone()]); + let iso = Isometry::from([arr[0].isometry, arr[1].isometry]); let scale = T::from([arr[0].scaling(), arr[1].scaling()]); Self::from_isometry(iso, scale) @@ -216,10 +216,10 @@ where #[inline] fn from(arr: [Similarity; 4]) -> Self { let iso = Isometry::from([ - arr[0].isometry.clone(), - arr[1].isometry.clone(), - arr[2].isometry.clone(), - arr[3].isometry.clone(), + arr[0].isometry, + arr[1].isometry, + arr[2].isometry, + arr[3].isometry, ]); let scale = T::from([ arr[0].scaling(), @@ -244,14 +244,14 @@ where #[inline] fn from(arr: [Similarity; 8]) -> Self { let iso = Isometry::from([ - arr[0].isometry.clone(), - arr[1].isometry.clone(), - arr[2].isometry.clone(), - arr[3].isometry.clone(), - arr[4].isometry.clone(), - arr[5].isometry.clone(), - arr[6].isometry.clone(), - arr[7].isometry.clone(), + arr[0].isometry, + arr[1].isometry, + arr[2].isometry, + arr[3].isometry, + arr[4].isometry, + arr[5].isometry, + arr[6].isometry, + arr[7].isometry, ]); let scale = T::from([ arr[0].scaling(), @@ -280,22 +280,22 @@ where #[inline] fn from(arr: [Similarity; 16]) -> Self { let iso = Isometry::from([ - arr[0].isometry.clone(), - arr[1].isometry.clone(), - arr[2].isometry.clone(), - arr[3].isometry.clone(), - arr[4].isometry.clone(), - arr[5].isometry.clone(), - arr[6].isometry.clone(), - arr[7].isometry.clone(), - arr[8].isometry.clone(), - arr[9].isometry.clone(), - arr[10].isometry.clone(), - arr[11].isometry.clone(), - arr[12].isometry.clone(), - arr[13].isometry.clone(), - arr[14].isometry.clone(), - arr[15].isometry.clone(), + arr[0].isometry, + arr[1].isometry, + arr[2].isometry, + arr[3].isometry, + arr[4].isometry, + arr[5].isometry, + arr[6].isometry, + arr[7].isometry, + arr[8].isometry, + arr[9].isometry, + arr[10].isometry, + arr[11].isometry, + arr[12].isometry, + arr[13].isometry, + arr[14].isometry, + arr[15].isometry, ]); let scale = T::from([ arr[0].scaling(), diff --git a/src/geometry/similarity_ops.rs b/src/geometry/similarity_ops.rs index c164acaa..b88f9442 100644 --- a/src/geometry/similarity_ops.rs +++ b/src/geometry/similarity_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; @@ -219,7 +222,7 @@ md_assign_impl_all!( const D; for; where; self: Similarity, D>, rhs: Rotation; [val] => self.isometry.rotation *= rhs; - [ref] => self.isometry.rotation *= rhs.clone(); + [ref] => self.isometry.rotation *= *rhs; ); md_assign_impl_all!( diff --git a/src/geometry/swizzle.rs b/src/geometry/swizzle.rs index 6e4c6df8..0ad51f00 100644 --- a/src/geometry/swizzle.rs +++ b/src/geometry/swizzle.rs @@ -8,6 +8,7 @@ macro_rules! impl_swizzle { $( /// Builds a new point from components of `self`. #[inline] + #[must_use] pub fn $name(&self) -> $Result where as ToTypenum>::Typenum: Cmp { $Result::new($(self[$i].inlined_clone()),*) diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs index c7fce902..7ea91cd4 100755 --- a/src/geometry/transform.rs +++ b/src/geometry/transform.rs @@ -1,6 +1,7 @@ use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::Any; use std::fmt::Debug; +use std::hash; use std::marker::PhantomData; #[cfg(feature = "serde-serialize-no-std")] @@ -166,14 +167,16 @@ where _phantom: PhantomData, } -// TODO -// impl + hash::Hash, C: TCategory> hash::Hash for Transform -// where DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, -// Owned, U1>, DimNameSum, U1>>: hash::Hash { -// fn hash(&self, state: &mut H) { -// self.matrix.hash(state); -// } -// } +impl hash::Hash for Transform +where + Const: DimNameAdd, + DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, + Owned, U1>, DimNameSum, U1>>: hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.matrix.hash(state); + } +} impl Copy for Transform where @@ -301,6 +304,7 @@ where /// assert_eq!(*t.matrix(), m); /// ``` #[inline] + #[must_use] pub fn matrix(&self) -> &OMatrix, U1>, DimNameSum, U1>> { &self.matrix } @@ -367,6 +371,7 @@ where /// assert_eq!(t.into_inner(), m); /// ``` #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.matrix().clone_owned() } @@ -397,11 +402,9 @@ where #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(self) -> Option> { - if let Some(m) = self.matrix.try_inverse() { - Some(Transform::from_matrix_unchecked(m)) - } else { - None - } + self.matrix + .try_inverse() + .map(Transform::from_matrix_unchecked) } /// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral` @@ -498,6 +501,7 @@ where /// /// This is the same as the multiplication `self * pt`. #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } @@ -507,6 +511,7 @@ where /// /// This is the same as the multiplication `self * v`. #[inline] + #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } @@ -524,6 +529,7 @@ where /// This may be cheaper than inverting the transformation and transforming /// the point. #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.clone().inverse() * pt } @@ -532,6 +538,7 @@ where /// This may be cheaper than inverting the transformation and transforming /// the vector. #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.clone().inverse() * v } diff --git a/src/geometry/transform_ops.rs b/src/geometry/transform_ops.rs index 29781c3c..c4ec5cfc 100644 --- a/src/geometry/transform_ops.rs +++ b/src/geometry/transform_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + use num::{One, Zero}; use std::ops::{Div, DivAssign, Index, IndexMut, Mul, MulAssign}; @@ -121,7 +124,7 @@ md_impl_all!( if C::has_normalizer() { let normalizer = self.matrix().fixed_slice::<1, D>(D, 0); - let n = normalizer.tr_dot(&rhs); + let n = normalizer.tr_dot(rhs); if !n.is_zero() { return transform * (rhs / n); diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index dcb7b603..c667a512 100755 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -38,7 +38,7 @@ where } } -impl Copy for Translation where Owned>: Copy {} +impl Copy for Translation {} impl Clone for Translation where @@ -123,7 +123,7 @@ mod rkyv_impl { impl, S: Fallible + ?Sized, const D: usize> Serialize for Translation { fn serialize(&self, serializer: &mut S) -> Result { - Ok(self.vector.serialize(serializer)?) + self.vector.serialize(serializer) } } @@ -190,6 +190,7 @@ impl Translation { /// assert_eq!(t.to_homogeneous(), expected); /// ``` #[inline] + #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where T: Zero + One, @@ -241,6 +242,7 @@ impl Translation { /// let transformed_point = t.transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_eq!(transformed_point, Point3::new(5.0, 7.0, 9.0)); #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { pt + &self.vector } @@ -256,6 +258,7 @@ impl Translation { /// let transformed_point = t.inverse_transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_eq!(transformed_point, Point3::new(3.0, 3.0, 3.0)); #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { pt - &self.vector } diff --git a/src/geometry/translation_construction.rs b/src/geometry/translation_construction.rs index c6e9d7d3..5371b648 100644 --- a/src/geometry/translation_construction.rs +++ b/src/geometry/translation_construction.rs @@ -69,7 +69,7 @@ where { /// Generate an arbitrary random variate for testing purposes. #[inline] - fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Translation { + fn sample(&self, rng: &mut G) -> Translation { Translation::from(rng.gen::>()) } } diff --git a/src/geometry/translation_conversion.rs b/src/geometry/translation_conversion.rs index ff8e797f..d443a2f4 100644 --- a/src/geometry/translation_conversion.rs +++ b/src/geometry/translation_conversion.rs @@ -77,7 +77,7 @@ where { #[inline] fn to_superset(&self) -> UnitDualQuaternion { - let dq = UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()); + let dq = UnitDualQuaternion::::from_parts(*self, UnitQuaternion::identity()); dq.to_superset() } @@ -212,16 +212,14 @@ impl From<[T; D]> for Translation { impl From> for Translation { #[inline] fn from(pt: Point) -> Self { - Translation { - vector: pt.coords.into(), - } + Translation { vector: pt.coords } } } -impl Into<[T; D]> for Translation { +impl From> for [T; D] { #[inline] - fn into(self) -> [T; D] { - self.vector.into() + fn from(t: Translation) -> Self { + t.vector.into() } } diff --git a/src/geometry/translation_coordinates.rs b/src/geometry/translation_coordinates.rs index 9a9bf44a..80267e06 100644 --- a/src/geometry/translation_coordinates.rs +++ b/src/geometry/translation_coordinates.rs @@ -1,4 +1,3 @@ -use std::mem; use std::ops::{Deref, DerefMut}; use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; @@ -19,15 +18,14 @@ macro_rules! deref_impl( #[inline] fn deref(&self) -> &Self::Target { - unsafe { mem::transmute(self) } + unsafe { &*(self as *const Translation as *const Self::Target) } } } - impl DerefMut for Translation - { + impl DerefMut for Translation { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { mem::transmute(self) } + unsafe { &mut *(self as *mut Translation as *mut Self::Target) } } } } diff --git a/src/geometry/unit_complex.rs b/src/geometry/unit_complex.rs index fe2e87ba..d6a7316c 100755 --- a/src/geometry/unit_complex.rs +++ b/src/geometry/unit_complex.rs @@ -84,6 +84,7 @@ where /// assert_eq!(rot.angle(), 1.78); /// ``` #[inline] + #[must_use] pub fn angle(&self) -> T { self.im.simd_atan2(self.re) } @@ -98,6 +99,7 @@ where /// assert_eq!(rot.sin_angle(), angle.sin()); /// ``` #[inline] + #[must_use] pub fn sin_angle(&self) -> T { self.im } @@ -112,6 +114,7 @@ where /// assert_eq!(rot.cos_angle(),angle.cos()); /// ``` #[inline] + #[must_use] pub fn cos_angle(&self) -> T { self.re } @@ -121,6 +124,7 @@ where /// This is generally used in the context of generic programming. Using /// the `.angle()` method instead is more common. #[inline] + #[must_use] pub fn scaled_axis(&self) -> Vector1 { Vector1::new(self.angle()) } @@ -131,6 +135,7 @@ where /// the `.angle()` method instead is more common. /// Returns `None` if the angle is zero. #[inline] + #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, @@ -157,6 +162,7 @@ where /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); /// ``` #[inline] + #[must_use] pub fn angle_to(&self, other: &Self) -> T { let delta = self.rotation_to(other); delta.angle() @@ -254,7 +260,8 @@ where /// assert_eq!(rot.to_rotation_matrix(), expected); /// ``` #[inline] - pub fn to_rotation_matrix(&self) -> Rotation2 { + #[must_use] + pub fn to_rotation_matrix(self) -> Rotation2 { let r = self.re; let i = self.im; @@ -274,7 +281,8 @@ where /// assert_eq!(rot.to_homogeneous(), expected); /// ``` #[inline] - pub fn to_homogeneous(&self) -> Matrix3 { + #[must_use] + pub fn to_homogeneous(self) -> Matrix3 { self.to_rotation_matrix().to_homogeneous() } } @@ -298,6 +306,7 @@ where /// assert_relative_eq!(transformed_point, Point2::new(-2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_point(&self, pt: &Point2) -> Point2 { self * pt } @@ -316,6 +325,7 @@ where /// assert_relative_eq!(transformed_vector, Vector2::new(-2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn transform_vector(&self, v: &Vector2) -> Vector2 { self * v } @@ -332,6 +342,7 @@ where /// assert_relative_eq!(transformed_point, Point2::new(2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_point(&self, pt: &Point2) -> Point2 { // TODO: would it be useful performancewise not to call inverse explicitly (i-e. implement // the inverse transformation explicitly here) ? @@ -350,6 +361,7 @@ where /// assert_relative_eq!(transformed_vector, Vector2::new(2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_vector(&self, v: &Vector2) -> Vector2 { self.inverse() * v } @@ -366,6 +378,7 @@ where /// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] + #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } @@ -392,6 +405,7 @@ where /// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] + #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self { Self::new(self.angle() * (T::one() - t) + other.angle() * t) } diff --git a/src/geometry/unit_complex_construction.rs b/src/geometry/unit_complex_construction.rs index 62a9b416..a86b2277 100644 --- a/src/geometry/unit_complex_construction.rs +++ b/src/geometry/unit_complex_construction.rs @@ -148,6 +148,7 @@ where /// assert_eq!(*rot.complex(), Complex::new(angle.cos(), angle.sin())); /// ``` #[inline] + #[must_use] pub fn complex(&self) -> &Complex { self.as_ref() } @@ -244,6 +245,7 @@ where /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); /// ``` #[inline] + #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other / self } @@ -262,6 +264,7 @@ where /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] + #[must_use] pub fn powf(&self, n: T) -> Self { Self::from_angle(self.angle() * n) } @@ -380,8 +383,8 @@ where SB: Storage, SC: Storage, { - let sang = na.perp(&nb); - let cang = na.dot(&nb); + let sang = na.perp(nb); + let cang = na.dot(nb); Self::from_angle(sang.simd_atan2(cang) * s) } diff --git a/src/geometry/unit_complex_ops.rs b/src/geometry/unit_complex_ops.rs index abaa9d4f..efa91a95 100644 --- a/src/geometry/unit_complex_ops.rs +++ b/src/geometry/unit_complex_ops.rs @@ -1,3 +1,6 @@ +// The macros break if the references are taken out, for some reason. +#![allow(clippy::op_ref)] + use std::ops::{Div, DivAssign, Mul, MulAssign}; use crate::base::storage::Storage; @@ -314,10 +317,10 @@ complex_op_impl_all!( ; self: Translation, right: UnitComplex, Output = Isometry, 2>; - [val val] => Isometry::from_parts(self, right); - [ref val] => Isometry::from_parts(self.clone(), right); - [val ref] => Isometry::from_parts(self, *right); - [ref ref] => Isometry::from_parts(self.clone(), *right); + [val val] => Isometry::from_parts(self, right); + [ref val] => Isometry::from_parts(*self, right); + [val ref] => Isometry::from_parts(self, *right); + [ref ref] => Isometry::from_parts(*self, *right); ); // UnitComplex ×= UnitComplex @@ -327,7 +330,7 @@ where { #[inline] fn mul_assign(&mut self, rhs: UnitComplex) { - *self = &*self * rhs + *self = *self * rhs } } @@ -337,7 +340,7 @@ where { #[inline] fn mul_assign(&mut self, rhs: &'b UnitComplex) { - *self = &*self * rhs + *self = *self * rhs } } @@ -348,7 +351,7 @@ where { #[inline] fn div_assign(&mut self, rhs: UnitComplex) { - *self = &*self / rhs + *self = *self / rhs } } @@ -358,7 +361,7 @@ where { #[inline] fn div_assign(&mut self, rhs: &'b UnitComplex) { - *self = &*self / rhs + *self = *self / rhs } } @@ -369,7 +372,7 @@ where { #[inline] fn mul_assign(&mut self, rhs: Rotation) { - *self = &*self * rhs + *self = *self * rhs } } @@ -379,7 +382,7 @@ where { #[inline] fn mul_assign(&mut self, rhs: &'b Rotation) { - *self = &*self * rhs + *self = *self * rhs } } @@ -390,7 +393,7 @@ where { #[inline] fn div_assign(&mut self, rhs: Rotation) { - *self = &*self / rhs + *self = *self / rhs } } @@ -400,7 +403,7 @@ where { #[inline] fn div_assign(&mut self, rhs: &'b Rotation) { - *self = &*self / rhs + *self = *self / rhs } } diff --git a/src/lib.rs b/src/lib.rs index 8378b272..c5c4dcd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::type_complexity)] /*! # nalgebra @@ -13,7 +14,7 @@ and the official package manager: [cargo](https://github.com/rust-lang/cargo). Simply add the following to your `Cargo.toml` file: -```.ignore +```ignore [dependencies] // TODO: replace the * by the latest version. nalgebra = "*" @@ -25,7 +26,7 @@ Most useful functionalities of **nalgebra** are grouped in the root module `nalg However, the recommended way to use **nalgebra** is to import types and traits explicitly, and call free-functions using the `na::` prefix: -```.rust +``` #[macro_use] extern crate approx; // For the macro relative_eq! extern crate nalgebra as na; @@ -86,7 +87,6 @@ an optimized set of tools for computer graphics and physics. Those features incl html_root_url = "https://docs.rs/nalgebra/0.25.0" )] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "no_unsound_assume_init", allow(unreachable_code))] #[cfg(feature = "rand-no-std")] @@ -101,6 +101,7 @@ extern crate approx; extern crate num_traits as num; #[cfg(all(feature = "alloc", not(feature = "std")))] +#[cfg_attr(test, macro_use)] extern crate alloc; #[cfg(not(feature = "std"))] @@ -184,6 +185,7 @@ pub fn zero() -> T { /// Wraps `val` into the range `[min, max]` using modular arithmetics. /// /// The range must not be empty. +#[must_use] #[inline] pub fn wrap(mut val: T, min: T, max: T) -> T where @@ -198,19 +200,15 @@ where while val < min { val += width } - - val } else if val > max { val -= width; while val > max { val -= width } - - val - } else { - val } + + val } /// Returns a reference to the input value clamped to the interval `[min, max]`. @@ -219,6 +217,7 @@ where /// * If `min < val < max`, this returns `val`. /// * If `val <= min`, this returns `min`. /// * If `val >= max`, this returns `max`. +#[must_use] #[inline] pub fn clamp(val: T, min: T, max: T) -> T { if val > min { @@ -391,7 +390,7 @@ pub fn center( p1: &Point, p2: &Point, ) -> Point { - ((&p1.coords + &p2.coords) * convert::<_, T>(0.5)).into() + ((p1.coords + p2.coords) * convert::<_, T>(0.5)).into() } /// The distance between two points. @@ -405,7 +404,7 @@ pub fn distance( p1: &Point, p2: &Point, ) -> T::SimdRealField { - (&p2.coords - &p1.coords).norm() + (p2.coords - p1.coords).norm() } /// The squared distance between two points. @@ -419,7 +418,7 @@ pub fn distance_squared( p1: &Point, p2: &Point, ) -> T::SimdRealField { - (&p2.coords - &p1.coords).norm_squared() + (p2.coords - p1.coords).norm_squared() } /* diff --git a/src/linalg/balancing.rs b/src/linalg/balancing.rs index 3b834563..3965caf1 100644 --- a/src/linalg/balancing.rs +++ b/src/linalg/balancing.rs @@ -8,17 +8,17 @@ use crate::base::dimension::Dim; use crate::base::storage::Storage; use crate::base::{Const, DefaultAllocator, OMatrix, OVector}; -/// Applies in-place a modified Parlett and Reinsch matrix balancing with 2-norm to the matrix `m` and returns +/// Applies in-place a modified Parlett and Reinsch matrix balancing with 2-norm to the matrix and returns /// the corresponding diagonal transformation. /// /// See https://arxiv.org/pdf/1401.5766.pdf -pub fn balance_parlett_reinsch(m: &mut OMatrix) -> OVector +pub fn balance_parlett_reinsch(matrix: &mut OMatrix) -> OVector where DefaultAllocator: Allocator + Allocator, { - assert!(m.is_square(), "Unable to balance a non-square matrix."); + assert!(matrix.is_square(), "Unable to balance a non-square matrix."); - let dim = m.data.shape().0; + let dim = matrix.data.shape().0; let radix: T = crate::convert(2.0f64); let mut d = OVector::from_element_generic(dim, Const::<1>, T::one()); @@ -28,36 +28,37 @@ where converged = true; for i in 0..dim.value() { - let mut c = m.column(i).norm_squared(); - let mut r = m.row(i).norm_squared(); + let mut n_col = matrix.column(i).norm_squared(); + let mut n_row = matrix.row(i).norm_squared(); let mut f = T::one(); - let s = c + r; - c = c.sqrt(); - r = r.sqrt(); + let s = n_col + n_row; + n_col = n_col.sqrt(); + n_row = n_row.sqrt(); - if c.is_zero() || r.is_zero() { + if n_col.is_zero() || n_row.is_zero() { continue; } - while c < r / radix { - c *= radix; - r /= radix; + while n_col < n_row / radix { + n_col *= radix; + n_row /= radix; f *= radix; } - while c >= r * radix { - c /= radix; - r *= radix; + while n_col >= n_row * radix { + n_col /= radix; + n_row *= radix; f /= radix; } let eps: T = crate::convert(0.95); - if c * c + r * r < eps * s { + #[allow(clippy::suspicious_operation_groupings)] + if n_col * n_col + n_row * n_row < eps * s { converged = false; d[i] *= f; - m.column_mut(i).mul_assign(f); - m.row_mut(i).div_assign(f); + matrix.column_mut(i).mul_assign(f); + matrix.row_mut(i).div_assign(f); } } } diff --git a/src/linalg/bidiagonal.rs b/src/linalg/bidiagonal.rs index a125ce18..6a462988 100644 --- a/src/linalg/bidiagonal.rs +++ b/src/linalg/bidiagonal.rs @@ -153,6 +153,7 @@ where /// Indicates whether this decomposition contains an upper-diagonal matrix. #[inline] + #[must_use] pub fn is_upper_diagonal(&self) -> bool { self.upper_diagonal } @@ -188,6 +189,7 @@ where /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] + #[must_use] pub fn d(&self) -> OMatrix, DimMinimum> where DefaultAllocator: Allocator, DimMinimum>, @@ -207,6 +209,7 @@ where /// Computes the orthogonal matrix `U` of this `U * D * V` decomposition. // TODO: code duplication with householder::assemble_q. // Except that we are returning a rectangular matrix here. + #[must_use] pub fn u(&self) -> OMatrix> where DefaultAllocator: Allocator>, @@ -237,6 +240,7 @@ where } /// Computes the orthogonal matrix `V_t` of this `U * D * V_t` decomposition. + #[must_use] pub fn v_t(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, @@ -274,6 +278,7 @@ where } /// The diagonal part of this decomposed matrix. + #[must_use] pub fn diagonal(&self) -> OVector> where DefaultAllocator: Allocator>, @@ -282,6 +287,7 @@ where } /// The off-diagonal part of this decomposed matrix. + #[must_use] pub fn off_diagonal(&self) -> OVector, U1>> where DefaultAllocator: Allocator, U1>>, diff --git a/src/linalg/cholesky.rs b/src/linalg/cholesky.rs index 24a6bcf5..f66fb42f 100644 --- a/src/linalg/cholesky.rs +++ b/src/linalg/cholesky.rs @@ -92,6 +92,7 @@ where /// Retrieves the lower-triangular factor of the Cholesky decomposition with its strictly /// uppen-triangular part filled with zeros. + #[must_use] pub fn l(&self) -> OMatrix { self.chol.lower_triangle() } @@ -101,6 +102,7 @@ where /// /// This is an allocation-less version of `self.l()`. The values of the strict upper-triangular /// part are garbage and should be ignored by further computations. + #[must_use] pub fn l_dirty(&self) -> &OMatrix { &self.chol } @@ -119,6 +121,7 @@ where /// Returns the solution of the system `self * x = b` where `self` is the decomposed matrix and /// `x` the unknown. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve(&self, b: &Matrix) -> OMatrix where S2: Storage, @@ -131,6 +134,7 @@ where } /// Computes the inverse of the decomposed matrix. + #[must_use] pub fn inverse(&self) -> OMatrix { let shape = self.chol.data.shape(); let mut res = OMatrix::identity_generic(shape.0, shape.1); @@ -140,6 +144,7 @@ where } /// Computes the determinant of the decomposed matrix. + #[must_use] pub fn determinant(&self) -> T::SimdRealField { let dim = self.chol.nrows(); let mut prod_diag = T::one(); @@ -287,6 +292,7 @@ where /// Updates the decomposition such that we get the decomposition of the factored matrix with its `j`th column removed. /// Since the matrix is square, the `j`th row will also be removed. + #[must_use] pub fn remove_column(&self, j: usize) -> Cholesky> where D: DimSub, diff --git a/src/linalg/col_piv_qr.rs b/src/linalg/col_piv_qr.rs index 10997b43..1a56d2cb 100644 --- a/src/linalg/col_piv_qr.rs +++ b/src/linalg/col_piv_qr.rs @@ -95,6 +95,7 @@ where /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] + #[must_use] pub fn r(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, @@ -126,6 +127,7 @@ where } /// Computes the orthogonal matrix `Q` of this decomposition. + #[must_use] pub fn q(&self) -> OMatrix> where DefaultAllocator: Allocator>, @@ -150,6 +152,7 @@ where } /// Retrieves the column permutation of this decomposition. #[inline] + #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } @@ -201,6 +204,7 @@ where /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, @@ -283,6 +287,7 @@ where /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. + #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.col_piv_qr.is_square(), @@ -301,6 +306,7 @@ where } /// Indicates if the decomposed matrix is invertible. + #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.col_piv_qr.is_square(), @@ -317,6 +323,7 @@ where } /// Computes the determinant of the decomposed matrix. + #[must_use] pub fn determinant(&self) -> T { let dim = self.col_piv_qr.nrows(); assert!( diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index 2729b66b..36cea3a0 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -112,6 +112,7 @@ impl> Vector { /// /// # Errors /// Inputs must satisfy `self.len() >= kernel.len() > 0`. + #[must_use] pub fn convolve_same(&self, kernel: Vector) -> OVector where D2: Dim, diff --git a/src/linalg/determinant.rs b/src/linalg/determinant.rs index aa04ff3f..22b681f5 100644 --- a/src/linalg/determinant.rs +++ b/src/linalg/determinant.rs @@ -12,6 +12,7 @@ impl, S: Storage> SquareMatri /// /// If the matrix has a dimension larger than 3, an LU decomposition is used. #[inline] + #[must_use] pub fn determinant(&self) -> T where DefaultAllocator: Allocator + Allocator<(usize, usize), D>, diff --git a/src/linalg/exp.rs b/src/linalg/exp.rs index 598c6350..c2816ff0 100644 --- a/src/linalg/exp.rs +++ b/src/linalg/exp.rs @@ -435,37 +435,38 @@ where + Allocator, { /// Computes exponential of this matrix + #[must_use] pub fn exp(&self) -> Self { // Simple case if self.nrows() == 1 { return self.map(|v| v.exp()); } - let mut h = ExpmPadeHelper::new(self.clone(), true); + let mut helper = ExpmPadeHelper::new(self.clone(), true); - let eta_1 = T::RealField::max(h.d4_loose(), h.d6_loose()); - if eta_1 < convert(1.495_585_217_958_292e-2) && ell(&h.a, 3) == 0 { - let (u, v) = h.pade3(); + let eta_1 = T::RealField::max(helper.d4_loose(), helper.d6_loose()); + if eta_1 < convert(1.495_585_217_958_292e-2) && ell(&helper.a, 3) == 0 { + let (u, v) = helper.pade3(); return solve_p_q(u, v); } - let eta_2 = T::RealField::max(h.d4_tight(), h.d6_loose()); - if eta_2 < convert(2.539_398_330_063_230e-1) && ell(&h.a, 5) == 0 { - let (u, v) = h.pade5(); + let eta_2 = T::RealField::max(helper.d4_tight(), helper.d6_loose()); + if eta_2 < convert(2.539_398_330_063_23e-1) && ell(&helper.a, 5) == 0 { + let (u, v) = helper.pade5(); return solve_p_q(u, v); } - let eta_3 = T::RealField::max(h.d6_tight(), h.d8_loose()); - if eta_3 < convert(9.504_178_996_162_932e-1) && ell(&h.a, 7) == 0 { - let (u, v) = h.pade7(); + let eta_3 = T::RealField::max(helper.d6_tight(), helper.d8_loose()); + if eta_3 < convert(9.504_178_996_162_932e-1) && ell(&helper.a, 7) == 0 { + let (u, v) = helper.pade7(); return solve_p_q(u, v); } - if eta_3 < convert(2.097_847_961_257_068e+0) && ell(&h.a, 9) == 0 { - let (u, v) = h.pade9(); + if eta_3 < convert(2.097_847_961_257_068e0) && ell(&helper.a, 9) == 0 { + let (u, v) = helper.pade9(); return solve_p_q(u, v); } - let eta_4 = T::RealField::max(h.d8_loose(), h.d10_loose()); + let eta_4 = T::RealField::max(helper.d8_loose(), helper.d10_loose()); let eta_5 = T::RealField::min(eta_3, eta_4); let theta_13 = convert(4.25); @@ -481,9 +482,12 @@ where } }; - s += ell(&(&h.a * convert::(2.0_f64.powf(-(s as f64)))), 13); + s += ell( + &(&helper.a * convert::(2.0_f64.powf(-(s as f64)))), + 13, + ); - let (u, v) = h.pade13_scaled(s); + let (u, v) = helper.pade13_scaled(s); let mut x = solve_p_q(u, v); for _ in 0..s { diff --git a/src/linalg/full_piv_lu.rs b/src/linalg/full_piv_lu.rs index 1555913b..f08af55c 100644 --- a/src/linalg/full_piv_lu.rs +++ b/src/linalg/full_piv_lu.rs @@ -96,6 +96,7 @@ where /// The lower triangular matrix of this decomposition. #[inline] + #[must_use] pub fn l(&self) -> OMatrix> where DefaultAllocator: Allocator>, @@ -109,6 +110,7 @@ where /// The upper triangular matrix of this decomposition. #[inline] + #[must_use] pub fn u(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, @@ -119,12 +121,14 @@ where /// The row permutations of this decomposition. #[inline] + #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } /// The column permutations of this decomposition. #[inline] + #[must_use] pub fn q(&self) -> &PermutationSequence> { &self.q } @@ -159,6 +163,7 @@ where /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if the decomposed matrix is not invertible. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, @@ -210,6 +215,7 @@ where /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. + #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.lu.is_square(), @@ -227,6 +233,7 @@ where } /// Indicates if the decomposed matrix is invertible. + #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.lu.is_square(), @@ -238,6 +245,7 @@ where } /// Computes the determinant of the decomposed matrix. + #[must_use] pub fn determinant(&self) -> T { assert!( self.lu.is_square(), diff --git a/src/linalg/givens.rs b/src/linalg/givens.rs index 6073634e..8be91fe1 100644 --- a/src/linalg/givens.rs +++ b/src/linalg/givens.rs @@ -89,16 +89,19 @@ impl GivensRotation { } /// The cos part of this roration. + #[must_use] pub fn c(&self) -> T::RealField { self.c } /// The sin part of this roration. + #[must_use] pub fn s(&self) -> T { self.s } /// The inverse of this givens rotation. + #[must_use = "This function does not mutate self."] pub fn inverse(&self) -> Self { Self { c: self.c, diff --git a/src/linalg/hessenberg.rs b/src/linalg/hessenberg.rs index be6f667c..6b8ecfee 100644 --- a/src/linalg/hessenberg.rs +++ b/src/linalg/hessenberg.rs @@ -116,6 +116,7 @@ where /// /// This is less efficient than `.unpack_h()` as it allocates a new matrix. #[inline] + #[must_use] pub fn h(&self) -> OMatrix { let dim = self.hess.nrows(); let mut res = self.hess.clone(); @@ -126,6 +127,7 @@ where } /// Computes the orthogonal matrix `Q` of this decomposition. + #[must_use] pub fn q(&self) -> OMatrix { householder::assemble_q(&self.hess, self.subdiag.as_slice()) } diff --git a/src/linalg/householder.rs b/src/linalg/householder.rs index fbb24e77..9314ee45 100644 --- a/src/linalg/householder.rs +++ b/src/linalg/householder.rs @@ -98,7 +98,7 @@ pub fn clear_row_unchecked( reflection_norm.signum().conjugate(), ); top.columns_range_mut(irow + shift..) - .tr_copy_from(&refl.axis()); + .tr_copy_from(refl.axis()); } else { top.columns_range_mut(irow + shift..).tr_copy_from(&axis); } diff --git a/src/linalg/inverse.rs b/src/linalg/inverse.rs index f56a95ec..28b148a1 100644 --- a/src/linalg/inverse.rs +++ b/src/linalg/inverse.rs @@ -127,7 +127,7 @@ fn do_inverse4>( where DefaultAllocator: Allocator, { - let m = m.data.as_slice(); + let m = m.as_slice(); out[(0, 0)] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] diff --git a/src/linalg/lu.rs b/src/linalg/lu.rs index fce3f3cb..36a00807 100644 --- a/src/linalg/lu.rs +++ b/src/linalg/lu.rs @@ -127,6 +127,7 @@ where /// The lower triangular matrix of this decomposition. #[inline] + #[must_use] pub fn l(&self) -> OMatrix> where DefaultAllocator: Allocator>, @@ -170,6 +171,7 @@ where /// The upper triangular matrix of this decomposition. #[inline] + #[must_use] pub fn u(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, @@ -180,6 +182,7 @@ where /// The row permutations of this decomposition. #[inline] + #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } @@ -213,6 +216,7 @@ where /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, @@ -257,6 +261,7 @@ where /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the matrix is not invertible. + #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.lu.is_square(), @@ -291,6 +296,7 @@ where } /// Computes the determinant of the decomposed matrix. + #[must_use] pub fn determinant(&self) -> T { let dim = self.lu.nrows(); assert!( @@ -307,6 +313,7 @@ where } /// Indicates if the decomposed matrix is invertible. + #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.lu.is_square(), diff --git a/src/linalg/permutation_sequence.rs b/src/linalg/permutation_sequence.rs index 4cc8cb25..ea868b5a 100644 --- a/src/linalg/permutation_sequence.rs +++ b/src/linalg/permutation_sequence.rs @@ -140,17 +140,20 @@ where } /// The number of non-identity permutations applied by this sequence. + #[must_use] pub fn len(&self) -> usize { self.len } /// Returns true if the permutation sequence contains no elements. + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// The determinant of the matrix corresponding to this permutation. #[inline] + #[must_use] pub fn determinant(&self) -> T { if self.len % 2 == 0 { T::one() diff --git a/src/linalg/pow.rs b/src/linalg/pow.rs index 33759a90..df513643 100644 --- a/src/linalg/pow.rs +++ b/src/linalg/pow.rs @@ -18,24 +18,21 @@ where { /// Attempts to raise this matrix to an integral power `e` in-place. If this /// matrix is non-invertible and `e` is negative, it leaves this matrix - /// untouched and returns `Err(())`. Otherwise, it returns `Ok(())` and + /// untouched and returns `false`. Otherwise, it returns `true` and /// overwrites this matrix with the result. - #[must_use] - pub fn pow_mut(&mut self, mut e: I) -> Result<(), ()> { + pub fn pow_mut(&mut self, mut e: I) -> bool { let zero = I::zero(); // A matrix raised to the zeroth power is just the identity. if e == zero { self.fill_with_identity(); - return Ok(()); + return true; } // If e is negative, we compute the inverse matrix, then raise it to the // power of -e. - if e < zero { - if !self.try_inverse_mut() { - return Err(()); - } + if e < zero && !self.try_inverse_mut() { + return false; } let one = I::one(); @@ -58,7 +55,7 @@ where multiplier.copy_from(&buf); if e == zero { - return Ok(()); + return true; } } } @@ -77,9 +74,10 @@ where pub fn pow(&self, e: I) -> Option> { let mut clone = self.clone_owned(); - match clone.pow_mut(e) { - Ok(()) => Some(clone), - Err(()) => None, + if clone.pow_mut(e) { + Some(clone) + } else { + None } } } diff --git a/src/linalg/qr.rs b/src/linalg/qr.rs index 6d5274e4..4bdbb364 100644 --- a/src/linalg/qr.rs +++ b/src/linalg/qr.rs @@ -70,6 +70,7 @@ where /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] + #[must_use] pub fn r(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, @@ -96,6 +97,7 @@ where } /// Computes the orthogonal matrix `Q` of this decomposition. + #[must_use] pub fn q(&self) -> OMatrix> where DefaultAllocator: Allocator>, @@ -164,6 +166,7 @@ where /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. + #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, @@ -243,6 +246,7 @@ where /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. + #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.qr.is_square(), @@ -261,6 +265,7 @@ where } /// Indicates if the decomposed matrix is invertible. + #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.qr.is_square(), diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs index 1903b717..c03f6f08 100644 --- a/src/linalg/schur.rs +++ b/src/linalg/schur.rs @@ -1,3 +1,4 @@ +#![allow(clippy::suspicious_operation_groupings)] #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; @@ -385,6 +386,7 @@ where /// Computes the real eigenvalues of the decomposed matrix. /// /// Return `None` if some eigenvalues are complex. + #[must_use] pub fn eigenvalues(&self) -> Option> { let mut out = unsafe { crate::unimplemented_or_uninitialized_generic!(self.t.data.shape().0, Const::<1>) @@ -397,6 +399,7 @@ where } /// Computes the complex eigenvalues of the decomposed matrix. + #[must_use] pub fn complex_eigenvalues(&self) -> OVector, D> where T: RealField, @@ -509,6 +512,7 @@ where + Allocator, { /// Computes the eigenvalues of this matrix. + #[must_use] pub fn eigenvalues(&self) -> Option> { assert!( self.is_square(), @@ -551,6 +555,7 @@ where } /// Computes the eigenvalues of this matrix. + #[must_use] pub fn complex_eigenvalues(&self) -> OVector, D> // TODO: add balancing? where diff --git a/src/linalg/solve.rs b/src/linalg/solve.rs index 8fdab47e..7f9b7dae 100644 --- a/src/linalg/solve.rs +++ b/src/linalg/solve.rs @@ -10,6 +10,7 @@ use crate::base::{DVectorSlice, DefaultAllocator, Matrix, OMatrix, SquareMatrix, impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use solve_lower_triangular_mut()?"] #[inline] pub fn solve_lower_triangular( &self, @@ -30,6 +31,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use solve_upper_triangular_mut()?"] #[inline] pub fn solve_upper_triangular( &self, @@ -186,6 +188,7 @@ impl> SquareMatrix { */ /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use tr_solve_lower_triangular_mut()?"] #[inline] pub fn tr_solve_lower_triangular( &self, @@ -206,6 +209,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use tr_solve_upper_triangular_mut()?"] #[inline] pub fn tr_solve_upper_triangular( &self, @@ -276,6 +280,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use ad_solve_lower_triangular_mut()?"] #[inline] pub fn ad_solve_lower_triangular( &self, @@ -296,6 +301,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use ad_solve_upper_triangular_mut()?"] #[inline] pub fn ad_solve_upper_triangular( &self, @@ -443,6 +449,7 @@ impl> SquareMatrix { impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn solve_lower_triangular_unchecked( &self, @@ -460,6 +467,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn solve_upper_triangular_unchecked( &self, @@ -578,6 +586,7 @@ impl> SquareMatrix { */ /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use tr_solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn tr_solve_lower_triangular_unchecked( &self, @@ -595,6 +604,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use tr_solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn tr_solve_upper_triangular_unchecked( &self, @@ -648,6 +658,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use ad_solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn ad_solve_lower_triangular_unchecked( &self, @@ -665,6 +676,7 @@ impl> SquareMatrix { /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. + #[must_use = "Did you mean to use ad_solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn ad_solve_upper_triangular_unchecked( &self, diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs index 2f5e6a63..241f00ce 100644 --- a/src/linalg/svd.rs +++ b/src/linalg/svd.rs @@ -121,11 +121,15 @@ where matrix.unscale_mut(m_amax); } - let b = Bidiagonal::new(matrix); - let mut u = if compute_u { Some(b.u()) } else { None }; - let mut v_t = if compute_v { Some(b.v_t()) } else { None }; - let mut diagonal = b.diagonal(); - let mut off_diagonal = b.off_diagonal(); + let bi_matrix = Bidiagonal::new(matrix); + let mut u = if compute_u { Some(bi_matrix.u()) } else { None }; + let mut v_t = if compute_v { + Some(bi_matrix.v_t()) + } else { + None + }; + let mut diagonal = bi_matrix.diagonal(); + let mut off_diagonal = bi_matrix.off_diagonal(); let mut niter = 0; let (mut start, mut end) = Self::delimit_subproblem( @@ -133,7 +137,7 @@ where &mut off_diagonal, &mut u, &mut v_t, - b.is_upper_diagonal(), + bi_matrix.is_upper_diagonal(), dim - 1, eps, ); @@ -142,6 +146,7 @@ where let subdim = end - start + 1; // Solve the subproblem. + #[allow(clippy::comparison_chain)] if subdim > 2 { let m = end - 1; let n = end; @@ -201,7 +206,7 @@ where subm[(0, 0)] = norm2; if let Some(ref mut v_t) = v_t { - if b.is_upper_diagonal() { + if bi_matrix.is_upper_diagonal() { rot1.rotate(&mut v_t.fixed_rows_mut::<2>(k)); } else { rot2.rotate(&mut v_t.fixed_rows_mut::<2>(k)); @@ -209,7 +214,7 @@ where } if let Some(ref mut u) = u { - if b.is_upper_diagonal() { + if bi_matrix.is_upper_diagonal() { rot2.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k)); } else { rot1.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k)); @@ -236,8 +241,10 @@ where diagonal[start], off_diagonal[start], diagonal[start + 1], - compute_u && b.is_upper_diagonal() || compute_v && !b.is_upper_diagonal(), - compute_v && b.is_upper_diagonal() || compute_u && !b.is_upper_diagonal(), + compute_u && bi_matrix.is_upper_diagonal() + || compute_v && !bi_matrix.is_upper_diagonal(), + compute_v && bi_matrix.is_upper_diagonal() + || compute_u && !bi_matrix.is_upper_diagonal(), ); let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), T::from_real(u2.s()))); let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), T::from_real(v2.s()))); @@ -247,7 +254,7 @@ where off_diagonal[start] = T::RealField::zero(); if let Some(ref mut u) = u { - let rot = if b.is_upper_diagonal() { + let rot = if bi_matrix.is_upper_diagonal() { u2.unwrap() } else { v2.unwrap() @@ -256,7 +263,7 @@ where } if let Some(ref mut v_t) = v_t { - let rot = if b.is_upper_diagonal() { + let rot = if bi_matrix.is_upper_diagonal() { v2.unwrap() } else { u2.unwrap() @@ -273,7 +280,7 @@ where &mut off_diagonal, &mut u, &mut v_t, - b.is_upper_diagonal(), + bi_matrix.is_upper_diagonal(), end, eps, ); @@ -502,6 +509,7 @@ where /// Computes the rank of the decomposed matrix, i.e., the number of singular values greater /// than `eps`. + #[must_use] pub fn rank(&self, eps: T::RealField) -> usize { assert!( eps >= T::RealField::zero(), @@ -615,6 +623,7 @@ where + Allocator, U1>>, { /// Computes the singular values of this matrix. + #[must_use] pub fn singular_values(&self) -> OVector> { SVD::new(self.clone_owned(), false, false).singular_values } @@ -622,6 +631,7 @@ where /// Computes the rank of this matrix. /// /// All singular values below `eps` are considered equal to 0. + #[must_use] pub fn rank(&self, eps: T::RealField) -> usize { let svd = SVD::new(self.clone_owned(), false, false); svd.rank(eps) diff --git a/src/linalg/symmetric_eigen.rs b/src/linalg/symmetric_eigen.rs index 6e3b67fe..5ac6d5da 100644 --- a/src/linalg/symmetric_eigen.rs +++ b/src/linalg/symmetric_eigen.rs @@ -87,7 +87,7 @@ where } fn do_decompose( - mut m: OMatrix, + mut matrix: OMatrix, eigenvectors: bool, eps: T::RealField, max_niter: usize, @@ -97,33 +97,33 @@ where DefaultAllocator: Allocator> + Allocator>, { assert!( - m.is_square(), + matrix.is_square(), "Unable to compute the eigendecomposition of a non-square matrix." ); - let dim = m.nrows(); - let m_amax = m.camax(); + let dim = matrix.nrows(); + let m_amax = matrix.camax(); if !m_amax.is_zero() { - m.unscale_mut(m_amax); + matrix.unscale_mut(m_amax); } - let (mut q, mut diag, mut off_diag); + let (mut q_mat, mut diag, mut off_diag); if eigenvectors { - let res = SymmetricTridiagonal::new(m).unpack(); - q = Some(res.0); + let res = SymmetricTridiagonal::new(matrix).unpack(); + q_mat = Some(res.0); diag = res.1; off_diag = res.2; } else { - let res = SymmetricTridiagonal::new(m).unpack_tridiagonal(); - q = None; + let res = SymmetricTridiagonal::new(matrix).unpack_tridiagonal(); + q_mat = None; diag = res.0; off_diag = res.1; } if dim == 1 { diag.scale_mut(m_amax); - return Some((diag, q)); + return Some((diag, q_mat)); } let mut niter = 0; @@ -132,11 +132,12 @@ where while end != start { let subdim = end - start + 1; + #[allow(clippy::comparison_chain)] if subdim > 2 { let m = end - 1; let n = end; - let mut v = Vector2::new( + let mut vec = Vector2::new( diag[start] - wilkinson_shift(diag[m], diag[n], off_diag[m]), off_diag[start], ); @@ -144,7 +145,7 @@ where for i in start..n { let j = i + 1; - if let Some((rot, norm)) = GivensRotation::cancel_y(&v) { + if let Some((rot, norm)) = GivensRotation::cancel_y(&vec) { if i > start { // Not the first iteration. off_diag[i - 1] = norm; @@ -165,12 +166,12 @@ where off_diag[i] = cs * (mii - mjj) + mij * (cc - ss); if i != n - 1 { - v.x = off_diag[i]; - v.y = -rot.s() * off_diag[i + 1]; + vec.x = off_diag[i]; + vec.y = -rot.s() * off_diag[i + 1]; off_diag[i + 1] *= rot.c(); } - if let Some(ref mut q) = q { + if let Some(ref mut q) = q_mat { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); rot.inverse().rotate_rows(&mut q.fixed_columns_mut::<2>(i)); } @@ -195,7 +196,7 @@ where diag[start] = eigvals[0]; diag[start + 1] = eigvals[1]; - if let Some(ref mut q) = q { + if let Some(ref mut q) = q_mat { if let Some((rot, _)) = GivensRotation::try_new(basis.x, basis.y, eps) { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); rot.rotate_rows(&mut q.fixed_columns_mut::<2>(start)); @@ -219,7 +220,7 @@ where diag.scale_mut(m_amax); - Some((diag, q)) + Some((diag, q_mat)) } fn delimit_subproblem( @@ -268,6 +269,7 @@ where /// Rebuild the original matrix. /// /// This is useful if some of the eigenvalues have been manually modified. + #[must_use] pub fn recompose(&self) -> OMatrix { let mut u_t = self.eigenvectors.clone(); for i in 0..self.eigenvalues.len() { @@ -311,6 +313,7 @@ where /// Computes the eigenvalues of this symmetric matrix. /// /// Only the lower-triangular part of the matrix is read. + #[must_use] pub fn symmetric_eigenvalues(&self) -> OVector { SymmetricEigen::do_decompose( self.clone_owned(), diff --git a/src/linalg/symmetric_tridiagonal.rs b/src/linalg/symmetric_tridiagonal.rs index 9937f76b..c7e87ba8 100644 --- a/src/linalg/symmetric_tridiagonal.rs +++ b/src/linalg/symmetric_tridiagonal.rs @@ -131,6 +131,7 @@ where } /// The diagonal components of this decomposition. + #[must_use] pub fn diagonal(&self) -> OVector where DefaultAllocator: Allocator, @@ -139,6 +140,7 @@ where } /// The off-diagonal components of this decomposition. + #[must_use] pub fn off_diagonal(&self) -> OVector> where DefaultAllocator: Allocator>, @@ -147,6 +149,7 @@ where } /// Computes the orthogonal matrix `Q` of this decomposition. + #[must_use] pub fn q(&self) -> OMatrix { householder::assemble_q(&self.tri, self.off_diagonal.as_slice()) } diff --git a/src/linalg/udu.rs b/src/linalg/udu.rs index 4d3aca6c..7b4a9cc9 100644 --- a/src/linalg/udu.rs +++ b/src/linalg/udu.rs @@ -92,6 +92,7 @@ where } /// Returns the diagonal elements as a matrix + #[must_use] pub fn d_matrix(&self) -> OMatrix { OMatrix::from_diagonal(&self.d) } diff --git a/src/proptest/mod.rs b/src/proptest/mod.rs index d85cd6a2..794080fe 100644 --- a/src/proptest/mod.rs +++ b/src/proptest/mod.rs @@ -27,7 +27,7 @@ //! In `proptest`, it is usually preferable to have free functions that generate *strategies*. //! Currently, the [matrix](fn.matrix.html) function fills this role. The analogous function for //! column vectors is [vector](fn.vector.html). Let's take a quick look at how it may be used: -//! ```rust +//! ``` //! use nalgebra::proptest::matrix; //! use proptest::prelude::*; //! @@ -52,7 +52,7 @@ //! number of columns to vary. One way to do this is to use `proptest` combinators in combination //! with [matrix](fn.matrix.html) as follows: //! -//! ```rust +//! ``` //! use nalgebra::{Dynamic, OMatrix, Const}; //! use nalgebra::proptest::matrix; //! use proptest::prelude::*; @@ -92,7 +92,7 @@ //! //! If you don't care about the dimensions of matrices, you can write tests like these: //! -//! ```rust +//! ``` //! use nalgebra::{DMatrix, DVector, Dynamic, Matrix3, OMatrix, Vector3, U3}; //! use proptest::prelude::*; //! diff --git a/src/sparse/cs_matrix.rs b/src/sparse/cs_matrix.rs index c4a0c901..cdacd044 100644 --- a/src/sparse/cs_matrix.rs +++ b/src/sparse/cs_matrix.rs @@ -31,11 +31,9 @@ impl<'a, T: Clone> Iterator for ColumnEntries<'a, T> { if self.curr >= self.i.len() { None } else { - let res = Some( - (unsafe { self.i.get_unchecked(self.curr).clone() }, unsafe { - self.v.get_unchecked(self.curr).clone() - }), - ); + let res = Some((unsafe { *self.i.get_unchecked(self.curr) }, unsafe { + self.v.get_unchecked(self.curr).clone() + })); self.curr += 1; res } @@ -80,10 +78,12 @@ pub trait CsStorage: for<'a> CsStorageIter<'a, T, R, C> { fn shape(&self) -> (R, C); /// Retrieve the i-th row index of the underlying row index buffer. /// + /// # Safety /// No bound-checking is performed. unsafe fn row_index_unchecked(&self, i: usize) -> usize; /// The i-th value on the contiguous value buffer of this storage. /// + /// # Safety /// No bound-checking is performed. unsafe fn get_value_unchecked(&self, i: usize) -> &T; /// The i-th value on the contiguous value buffer of this storage. @@ -119,16 +119,19 @@ where DefaultAllocator: Allocator, { /// The value buffer of this storage. + #[must_use] pub fn values(&self) -> &[T] { &self.vals } /// The column shifts buffer. + #[must_use] pub fn p(&self) -> &[usize] { self.p.as_slice() } /// The row index buffers. + #[must_use] pub fn i(&self) -> &[usize] { &self.i } @@ -152,7 +155,7 @@ where #[inline] fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices { let rng = self.column_range(j); - self.i[rng.clone()].iter().cloned() + self.i[rng].iter().cloned() } } @@ -356,27 +359,32 @@ impl> CsMatrix { } /// The size of the data buffer. + #[must_use] pub fn len(&self) -> usize { self.data.len() } /// The number of rows of this matrix. + #[must_use] pub fn nrows(&self) -> usize { self.data.shape().0.value() } /// The number of rows of this matrix. + #[must_use] pub fn ncols(&self) -> usize { self.data.shape().1.value() } /// The shape of this matrix. + #[must_use] pub fn shape(&self) -> (usize, usize) { let (nrows, ncols) = self.data.shape(); (nrows.value(), ncols.value()) } /// Whether this matrix is square or not. + #[must_use] pub fn is_square(&self) -> bool { let (nrows, ncols) = self.data.shape(); nrows.value() == ncols.value() @@ -391,6 +399,7 @@ impl> CsMatrix { /// If at any time this `is_sorted` method returns `false`, then, something went wrong /// and an issue should be open on the nalgebra repository with details on how to reproduce /// this. + #[must_use] pub fn is_sorted(&self) -> bool { for j in 0..self.ncols() { let mut curr = None; @@ -409,6 +418,7 @@ impl> CsMatrix { } /// Computes the transpose of this sparse matrix. + #[must_use = "This function does not mutate the matrix. Consider using the return value or removing the function call. There's also transpose_mut() for square matrices."] pub fn transpose(&self) -> CsMatrix where DefaultAllocator: Allocator, @@ -479,7 +489,7 @@ where // Sort the index vector. let range = self.data.column_range(j); - self.data.i[range.clone()].sort(); + self.data.i[range.clone()].sort_unstable(); // Permute the values too. for (i, irow) in range.clone().zip(self.data.i[range].iter().cloned()) { diff --git a/src/sparse/cs_matrix_cholesky.rs b/src/sparse/cs_matrix_cholesky.rs index 4cd61d2a..6d52d0a6 100644 --- a/src/sparse/cs_matrix_cholesky.rs +++ b/src/sparse/cs_matrix_cholesky.rs @@ -67,6 +67,7 @@ where } /// The lower-triangular matrix of the cholesky decomposition. + #[must_use] pub fn l(&self) -> Option<&CsMatrix> { if self.ok { Some(&self.l) diff --git a/src/sparse/cs_matrix_ops.rs b/src/sparse/cs_matrix_ops.rs index e9daa4ae..e03b12a5 100644 --- a/src/sparse/cs_matrix_ops.rs +++ b/src/sparse/cs_matrix_ops.rs @@ -271,7 +271,7 @@ where // Keep the output sorted. let range = res.data.p[j]..nz; - res.data.i[range.clone()].sort(); + res.data.i[range.clone()].sort_unstable(); for p in range { res.data.vals[p] = workspace[res.data.i[p]].inlined_clone() diff --git a/src/sparse/cs_matrix_solve.rs b/src/sparse/cs_matrix_solve.rs index 52065747..235fcef3 100644 --- a/src/sparse/cs_matrix_solve.rs +++ b/src/sparse/cs_matrix_solve.rs @@ -6,6 +6,7 @@ use crate::{Const, DefaultAllocator, Dim, Matrix, OMatrix, OVector, RealField}; impl> CsMatrix { /// Solve a lower-triangular system with a dense right-hand-side. + #[must_use = "Did you mean to use solve_lower_triangular_mut()?"] pub fn solve_lower_triangular( &self, b: &Matrix, @@ -24,6 +25,7 @@ impl> CsMatrix { } /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. + #[must_use = "Did you mean to use tr_solve_lower_triangular_mut()?"] pub fn tr_solve_lower_triangular( &self, b: &Matrix, @@ -61,7 +63,7 @@ impl> CsMatrix { let mut column = self.data.column_entries(j); let mut diag_found = false; - while let Some((i, val)) = column.next() { + for (i, val) in &mut column { if i == j { if val.is_zero() { return false; @@ -107,7 +109,7 @@ impl> CsMatrix { let mut column = self.data.column_entries(j); let mut diag = None; - while let Some((i, val)) = column.next() { + for (i, val) in &mut column { if i == j { if val.is_zero() { return false; @@ -135,6 +137,7 @@ impl> CsMatrix { } /// Solve a lower-triangular system with a sparse right-hand-side. + #[must_use] pub fn solve_lower_triangular_cs( &self, b: &CsVector, @@ -148,7 +151,7 @@ impl> CsMatrix { // We don't compute a postordered reach here because it will be sorted after anyway. self.lower_triangular_reach(b, &mut reach); // We sort the reach so the result matrix has sorted indices. - reach.sort(); + reach.sort_unstable(); let mut workspace = unsafe { crate::unimplemented_or_uninitialized_generic!(b.data.shape().0, Const::<1>) }; @@ -164,7 +167,7 @@ impl> CsMatrix { let mut column = self.data.column_entries(j); let mut diag_found = false; - while let Some((i, val)) = column.next() { + for (i, val) in &mut column { if i == j { if val.is_zero() { break; diff --git a/src/third_party/alga/alga_dual_quaternion.rs b/src/third_party/alga/alga_dual_quaternion.rs index d6a5861e..bc07cfa4 100644 --- a/src/third_party/alga/alga_dual_quaternion.rs +++ b/src/third_party/alga/alga_dual_quaternion.rs @@ -267,12 +267,12 @@ impl AffineTransformation> #[inline] fn append_translation(&self, translation: &Self::Translation) -> Self { - self * Self::from_parts(translation.clone(), UnitQuaternion::identity()) + self * Self::from_parts(*translation, UnitQuaternion::identity()) } #[inline] fn prepend_translation(&self, translation: &Self::Translation) -> Self { - Self::from_parts(translation.clone(), UnitQuaternion::identity()) * self + Self::from_parts(*translation, UnitQuaternion::identity()) * self } #[inline] @@ -287,12 +287,12 @@ impl AffineTransformation> #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } } diff --git a/src/third_party/alga/alga_matrix.rs b/src/third_party/alga/alga_matrix.rs index 0ddd4fdf..e55ba49e 100644 --- a/src/third_party/alga/alga_matrix.rs +++ b/src/third_party/alga/alga_matrix.rs @@ -272,12 +272,12 @@ where match Self::dimension() { 1 => { - if vs.len() == 0 { + if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)); } } 2 => { - if vs.len() == 0 { + if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)); } else if vs.len() == 1 { @@ -290,7 +290,7 @@ where // Otherwise, nothing. } 3 => { - if vs.len() == 0 { + if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)) && f(&Self::canonical_basis_element(2)); diff --git a/src/third_party/alga/alga_point.rs b/src/third_party/alga/alga_point.rs index f1365af4..b1919bb6 100644 --- a/src/third_party/alga/alga_point.rs +++ b/src/third_party/alga/alga_point.rs @@ -23,7 +23,7 @@ impl EuclideanSpace for #[inline] fn coordinates(&self) -> Self::Coordinates { - self.coords.clone() + self.coords } #[inline] diff --git a/src/third_party/alga/alga_quaternion.rs b/src/third_party/alga/alga_quaternion.rs index 0885f44f..7282e0f1 100755 --- a/src/third_party/alga/alga_quaternion.rs +++ b/src/third_party/alga/alga_quaternion.rs @@ -144,11 +144,7 @@ impl NormedSpace for Quaternion { #[inline] fn try_normalize(&self, min_norm: T) -> Option { - if let Some(v) = self.coords.try_normalize(min_norm) { - Some(Self::from(v)) - } else { - None - } + self.coords.try_normalize(min_norm).map(Self::from) } #[inline] @@ -234,17 +230,17 @@ impl AffineTransformation> #[inline] fn decompose(&self) -> (Id, Self, Id, Self) { - (Id::new(), self.clone(), Id::new(), Self::identity()) + (Id::new(), *self, Id::new(), Self::identity()) } #[inline] fn append_translation(&self, _: &Self::Translation) -> Self { - self.clone() + *self } #[inline] fn prepend_translation(&self, _: &Self::Translation) -> Self { - self.clone() + *self } #[inline] @@ -259,12 +255,12 @@ impl AffineTransformation> #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } } @@ -278,7 +274,7 @@ impl Similarity> for UnitQuat #[inline] fn rotation(&self) -> Self { - self.clone() + *self } #[inline] diff --git a/src/third_party/alga/alga_translation.rs b/src/third_party/alga/alga_translation.rs index bca6d13f..76a68355 100755 --- a/src/third_party/alga/alga_translation.rs +++ b/src/third_party/alga/alga_translation.rs @@ -79,7 +79,7 @@ impl Transformation) -> SVector { - v.clone() + *v } } @@ -93,7 +93,7 @@ impl ProjectiveTransfor #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { - v.clone() + *v } } @@ -176,7 +176,7 @@ impl AlgaTranslation SVector { - self.vector.clone() + self.vector } #[inline] @@ -186,7 +186,7 @@ impl AlgaTranslation Option { - Some(Self::from(&self.vector * n)) + Some(Self::from(self.vector * n)) } #[inline] diff --git a/src/third_party/alga/alga_unit_complex.rs b/src/third_party/alga/alga_unit_complex.rs index 44dadb42..ca55691b 100755 --- a/src/third_party/alga/alga_unit_complex.rs +++ b/src/third_party/alga/alga_unit_complex.rs @@ -90,17 +90,17 @@ impl AffineTransformation> fo #[inline] fn decompose(&self) -> (Id, Self, Id, Self) { - (Id::new(), self.clone(), Id::new(), Self::identity()) + (Id::new(), *self, Id::new(), Self::identity()) } #[inline] fn append_translation(&self, _: &Self::Translation) -> Self { - self.clone() + *self } #[inline] fn prepend_translation(&self, _: &Self::Translation) -> Self { - self.clone() + *self } #[inline] @@ -115,12 +115,12 @@ impl AffineTransformation> fo #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { - self.clone() + *self } } @@ -134,7 +134,7 @@ impl Similarity> for UnitComp #[inline] fn rotation(&self) -> Self { - self.clone() + *self } #[inline] diff --git a/src/third_party/glam/common/glam_isometry.rs b/src/third_party/glam/common/glam_isometry.rs index 6bd970d2..3a8d4961 100644 --- a/src/third_party/glam/common/glam_isometry.rs +++ b/src/third_party/glam/common/glam_isometry.rs @@ -1,5 +1,5 @@ use super::glam::{DMat3, DMat4, DQuat, DVec2, DVec3, Mat3, Mat4, Quat, Vec2, Vec3}; -use crate::{Isometry2, Isometry3, Matrix3, Matrix4}; +use crate::{Isometry2, Isometry3, Matrix3, Matrix4, Translation3, UnitQuaternion, Vector2}; use std::convert::TryFrom; impl From> for Mat3 { @@ -102,61 +102,61 @@ impl From<(DVec2, f64)> for Isometry2 { impl From for Isometry3 { fn from(rot: Quat) -> Self { - Isometry3::from_parts(crate::one(), rot.into()) + Isometry3::from_parts(Translation3::identity(), rot.into()) } } impl From for Isometry3 { fn from(rot: DQuat) -> Self { - Isometry3::from_parts(crate::one(), rot.into()) + Isometry3::from_parts(Translation3::identity(), rot.into()) } } impl From for Isometry2 { fn from(rot: Quat) -> Self { - Isometry2::new(crate::zero(), rot.to_axis_angle().1) + Isometry2::new(Vector2::zeros(), rot.to_axis_angle().1) } } impl From for Isometry2 { fn from(rot: DQuat) -> Self { - Isometry2::new(crate::zero(), rot.to_axis_angle().1) + Isometry2::new(Vector2::zeros(), rot.to_axis_angle().1) } } impl From for Isometry3 { fn from(tra: Vec3) -> Self { - Isometry3::from_parts(tra.into(), crate::one()) + Isometry3::from_parts(tra.into(), UnitQuaternion::identity()) } } impl From for Isometry3 { fn from(tra: DVec3) -> Self { - Isometry3::from_parts(tra.into(), crate::one()) + Isometry3::from_parts(tra.into(), UnitQuaternion::identity()) } } impl From for Isometry2 { fn from(tra: Vec2) -> Self { - Isometry2::new(tra.into(), crate::one()) + Isometry2::new(tra.into(), 0.0) } } impl From for Isometry2 { fn from(tra: DVec2) -> Self { - Isometry2::new(tra.into(), crate::one()) + Isometry2::new(tra.into(), 0.0) } } impl From for Isometry2 { fn from(tra: Vec3) -> Self { - Isometry2::new([tra.x, tra.y].into(), crate::one()) + Isometry2::new([tra.x, tra.y].into(), 0.0) } } impl From for Isometry2 { fn from(tra: DVec3) -> Self { - Isometry2::new([tra.x, tra.y].into(), crate::one()) + Isometry2::new([tra.x, tra.y].into(), 0.0) } } diff --git a/tests/core/matrix.rs b/tests/core/matrix.rs index 7befd351..eaa252db 100644 --- a/tests/core/matrix.rs +++ b/tests/core/matrix.rs @@ -1108,3 +1108,31 @@ fn partial_eq_different_types() { // assert_ne!(static_mat, typenum_static_mat); //assert_ne!(typenum_static_mat, static_mat); } + +fn generic_omatrix_to_string( + vector: &nalgebra::OVector, + matrix: &nalgebra::OMatrix, +) -> (String, String) +where + D: nalgebra::Dim, + nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator, + nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator, +{ + (vector.to_string(), matrix.to_string()) +} + +#[test] +fn omatrix_to_string() { + let dvec: nalgebra::DVector = nalgebra::dvector![1.0, 2.0]; + let dmatr: nalgebra::DMatrix = nalgebra::dmatrix![1.0, 2.0; 3.0, 4.0]; + let svec: nalgebra::SVector = nalgebra::vector![1.0, 2.0]; + let smatr: nalgebra::SMatrix = nalgebra::matrix![1.0, 2.0; 3.0, 4.0]; + assert_eq!( + generic_omatrix_to_string(&dvec, &dmatr), + (dvec.to_string(), dmatr.to_string()) + ); + assert_eq!( + generic_omatrix_to_string(&svec, &smatr), + (svec.to_string(), smatr.to_string()) + ); +} diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs index 84f42a6e..b2e151d3 100644 --- a/tests/linalg/convolution.rs +++ b/tests/linalg/convolution.rs @@ -26,18 +26,20 @@ fn convolve_same_check() { // Panic Tests // These really only apply to dynamic sized vectors assert!(panic::catch_unwind(|| { - DVector::from_vec(vec![1.0, 2.0]) + let _ = DVector::from_vec(vec![1.0, 2.0]) .convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { - DVector::::from_vec(vec![]).convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); + let _ = DVector::::from_vec(vec![]) + .convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { - DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]).convolve_same(DVector::::from_vec(vec![])); + let _ = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) + .convolve_same(DVector::::from_vec(vec![])); }) .is_err()); }