diff --git a/CHANGELOG.md b/CHANGELOG.md index 735e1898..d76638f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [0.17.0] - WIP ### Added - * Add swizzling up to dimension 3. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.w)`. + * Add swizzling up to dimension 3 for vectors. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.y)`. + * Add swizzling up to dimension 3 for points. For example, you can do `p.zxy()` as an equivalent to `Point3::new(p.z, p.x, p.y)`. * Add `.copy_from_slice` to copy matrix components from a slice in column-major order. * Add `.dot` to quaternions. * Add `.zip_zip_map` for iterating on three matrices simultaneously, and applying a closure to them. @@ -25,13 +26,34 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Add impl `From` for `Translation`. * Add the `::from_vec` constructor to construct a matrix from a `Vec` (a `DMatrix` will reuse the original `Vec` as-is for its storage). - + * Add `.to_homogeneous` to square matrices (and with dimensions higher than 1x1). This will increase their number of row + and columns by 1. The new column and row are filled with 0, except for the diagonal element which is set to 1. + * Implement `Extend` for matrices with a dynamic storage. The provided `Vec` is assumed to represent a column-major + matrix with the same number of rows as the one being extended. This will effectively append new columns on the right of + the matrix being extended. + * Implement `Extend` for vectors with a dynamic storage. This will concatenate the vector with the given `Vec`. + * Implement `Extend>` for matrices with dynamic storage. This will concatenate the columns of both matrices. + * Implement `Into` for the `MatrixVec` storage. + * Implement `Hash` for all matrices. + * Add a `.len()` method to retrieve the size of a `MatrixVec`. + ### Modified * The orthographic projection no longer require that `bottom < top`, that `left < right`, and that `znear < zfar`. The only restriction now ith that they must not be equal (in which case the projection would be singular). * The `Point::from_coordinates` methods is deprecated. Use `Point::from` instead. * The `.transform_point` and `.transform_vector` methods are now inherent methods for matrices so that the user does not have to explicitly import the `Transform` trait from the alga crate. + * Renamed the matrix storage types: `MatrixArray` -> `ArrayStorage` and `MatrixVec` -> `VecStorage`. + * Renamed `.unwrap()` to `.into_inner()` for geometric types that wrap another type. + This is for the case of `Unit`, `Transform`, `Orthographic3`, `Perspective3`, `Rotation`. + * Deprecate several functions at the root of the crate (replaced by methods). + +### Removed + * Remove the `Deref` impl for `MatrixVec` as it could cause hard-to-understand compilation errors. + +### nalgebra-glm + * Add several alternative projection computations, e.g., `ortho_lh`, `ortho_lh_no`, `perspective_lh`, etc. + * Add features matching those of nalgebra, in particular: `serde-serialize`, `abmonation-serialize`, std` (enabled by default). ## [0.16.0] All dependencies have been updated to their latest versions. diff --git a/Cargo.toml b/Cargo.toml index 94240d6f..4672dae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.16.10" +version = "0.16.13" authors = [ "Sébastien Crozet " ] description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices." @@ -23,26 +23,31 @@ stdweb = [ "rand/stdweb" ] arbitrary = [ "quickcheck" ] serde-serialize = [ "serde", "serde_derive", "num-complex/serde" ] abomonation-serialize = [ "abomonation" ] +sparse = [ ] debug = [ ] alloc = [ ] +io = [ "pest", "pest_derive" ] [dependencies] typenum = "1.10" -generic-array = "0.11" -rand = { version = "0.5", default-features = false } +generic-array = "0.12" +rand = { version = "0.6", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } -alga = { version = "0.7", default-features = false } -matrixmultiply = { version = "0.1", optional = true } +alga = { version = "0.8", default-features = false } +matrixmultiply = { version = "0.2", optional = true } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } -abomonation = { version = "0.5", optional = true } +abomonation = { version = "0.7", optional = true } mint = { version = "0.5", optional = true } -quickcheck = { version = "0.6", optional = true } +quickcheck = { version = "0.8", optional = true } +pest = { version = "2.0", optional = true } +pest_derive = { version = "2.0", optional = true } [dev-dependencies] serde_json = "1.0" +rand_xorshift = "0.1" [workspace] -members = [ "nalgebra-lapack", "nalgebra-glm" ] +members = [ "nalgebra-lapack", "nalgebra-glm" ] \ No newline at end of file diff --git a/benches/common/macros.rs b/benches/common/macros.rs index 55d6ba3c..758336a8 100644 --- a/benches/common/macros.rs +++ b/benches/common/macros.rs @@ -4,7 +4,8 @@ macro_rules! bench_binop( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -19,7 +20,8 @@ macro_rules! bench_binop_ref( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -34,7 +36,8 @@ macro_rules! bench_binop_fn( ($name: ident, $t1: ty, $t2: ty, $binop: path) => { #[bench] fn $name(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); @@ -51,7 +54,8 @@ macro_rules! bench_unop_na( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; @@ -73,7 +77,8 @@ macro_rules! bench_unop( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; @@ -95,7 +100,8 @@ macro_rules! bench_construction( fn $name(bh: &mut Bencher) { const LEN: usize = 1 << 13; - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); $(let $args: Vec<$types> = (0usize .. LEN).map(|_| rng.gen::<$types>()).collect();)* let mut i = 0; diff --git a/benches/core/vector.rs b/benches/core/vector.rs index 35e25e2d..837eb7ca 100644 --- a/benches/core/vector.rs +++ b/benches/core/vector.rs @@ -50,7 +50,8 @@ bench_binop_ref!(vec10000_dot_f32, VectorN, VectorN, d #[bench] fn vec10000_axpy_f64(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -60,7 +61,8 @@ fn vec10000_axpy_f64(bh: &mut Bencher) { #[bench] fn vec10000_axpy_beta_f64(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -71,7 +73,8 @@ fn vec10000_axpy_beta_f64(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f64_slice(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -86,7 +89,8 @@ fn vec10000_axpy_f64_slice(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f64_static(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = VectorN::::new_random(); let b = VectorN::::new_random(); let n = rng.gen::(); @@ -97,7 +101,8 @@ fn vec10000_axpy_f64_static(bh: &mut Bencher) { #[bench] fn vec10000_axpy_f32(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); @@ -107,7 +112,8 @@ fn vec10000_axpy_f32(bh: &mut Bencher) { #[bench] fn vec10000_axpy_beta_f32(bh: &mut Bencher) { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); diff --git a/benches/lib.rs b/benches/lib.rs index 1ad3a2be..f788c998 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -14,6 +14,7 @@ mod geometry; mod linalg; fn reproductible_dmatrix(nrows: usize, ncols: usize) -> DMatrix { - let mut rng = IsaacRng::new_unseeded(); + use rand::SeedableRng; + let mut rng = IsaacRng::seed_from_u64(0); DMatrix::::from_fn(nrows, ncols, |_, _| rng.gen()) } diff --git a/ci/build.sh b/ci/build.sh index 4c5c4d1b..550c9a69 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -11,7 +11,7 @@ if [ -z "$NO_STD" ]; then cargo build --verbose -p nalgebra --features "serde-serialize"; cargo build --verbose -p nalgebra --features "abomonation-serialize"; cargo build --verbose -p nalgebra --features "debug"; - cargo build --verbose -p nalgebra --features "debug arbitrary mint serde-serialize abomonation-serialize"; + cargo build --verbose -p nalgebra --all-features else cargo build -p nalgebra-lapack; fi diff --git a/examples/matrix_construction.rs b/examples/matrix_construction.rs index ddcb25c3..bb78458f 100644 --- a/examples/matrix_construction.rs +++ b/examples/matrix_construction.rs @@ -51,8 +51,8 @@ fn main() { // Components listed column-by-column. 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ] - .iter() - .cloned(), + .iter() + .cloned(), ); assert_eq!(dm, dm1); diff --git a/examples/transform_matrix4.rs b/examples/transform_matrix4.rs index c877206e..a9e6cd6e 100644 --- a/examples/transform_matrix4.rs +++ b/examples/transform_matrix4.rs @@ -3,7 +3,6 @@ extern crate alga; extern crate approx; extern crate nalgebra as na; -use alga::linear::Transformation; use na::{Matrix4, Point3, Vector3}; fn main() { diff --git a/examples/transform_vector_point3.rs b/examples/transform_vector_point3.rs index cd36cad1..57a189cc 100644 --- a/examples/transform_vector_point3.rs +++ b/examples/transform_vector_point3.rs @@ -1,7 +1,6 @@ extern crate alga; extern crate nalgebra as na; -use alga::linear::Transformation; use na::{Matrix4, Point3, Vector3, Vector4}; fn main() { diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml index 02708ef1..508c1c7d 100644 --- a/nalgebra-glm/Cargo.toml +++ b/nalgebra-glm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-glm" -version = "0.2.0" +version = "0.2.1" authors = ["sebcrozet "] description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library." @@ -12,8 +12,16 @@ categories = [ "science" ] keywords = [ "linear", "algebra", "matrix", "vector", "math" ] license = "BSD-3-Clause" +[features] +default = [ "std" ] +std = [ "nalgebra/std", "alga/std" ] +stdweb = [ "nalgebra/stdweb" ] +arbitrary = [ "nalgebra/arbitrary" ] +serde-serialize = [ "nalgebra/serde-serialize" ] +abomonation-serialize = [ "nalgebra/abomonation-serialize" ] + [dependencies] num-traits = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } -alga = "0.7" -nalgebra = { path = "..", version = "^0.16.4" } +alga = { version = "0.8", default-features = false } +nalgebra = { path = "..", version = "^0.16.13", default-features = false } diff --git a/nalgebra-glm/src/ext/matrix_clip_space.rs b/nalgebra-glm/src/ext/matrix_clip_space.rs index 89c137ea..02179759 100644 --- a/nalgebra-glm/src/ext/matrix_clip_space.rs +++ b/nalgebra-glm/src/ext/matrix_clip_space.rs @@ -1,5 +1,5 @@ use aliases::TMat4; -use na::{Orthographic3, Perspective3, Real}; +use na::{Real}; //pub fn frustum(left: N, right: N, bottom: N, top: N, near: N, far: N) -> TMat4 { // unimplemented!() @@ -53,119 +53,644 @@ use na::{Orthographic3, Perspective3, Real}; // unimplemented!() //} -/// Creates a matrix for an orthographic parallel viewing volume, using the right handedness and OpenGL near and far clip planes definition. +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// pub fn ortho(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { - Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap() + ortho_rh_no(left, right, bottom, top, znear, zfar) } -//pub fn ortho_lh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_lh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_lh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_rh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn ortho_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { -// unimplemented!() -//} +/// Creates a left hand matrix for a orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_lh_no(left, right, bottom, top, znear, zfar) +} -/// Creates a matrix for a perspective-view frustum based on the right handedness and OpenGL near and far clip planes definition. +/// Creates a left hand matrix for a orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = -(right + left) / (right - left); + mat[(1, 1)] = two / (top-bottom); + mat[(1, 3)] = -(top + bottom) / (top - bottom); + mat[(2, 2)] = two / (zfar - znear); + mat[(2, 3)] = -(zfar + znear) / (zfar - znear); + + mat +} + +/// Creates a matrix for a left hand orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_lh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let one : N = N::one(); + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two / (top - bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = one / (zfar - znear); + mat[(2, 3)] = - znear / (zfar - znear); + + mat +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_no(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_no(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand orthographic-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh_no(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two/(top-bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = - two / (zfar - znear); + mat[(2, 3)] = - (zfar + znear) / (zfar - znear); + + mat +} + +/// Creates a right hand matrix for a orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_rh_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + let one : N = N::one(); + let two : N = ::convert(2.0); + let mut mat : TMat4 = TMat4::::identity(); + + mat[(0, 0)] = two / (right - left); + mat[(0, 3)] = - (right + left) / (right - left); + mat[(1, 1)] = two/(top-bottom); + mat[(1, 3)] = - (top + bottom) / (top - bottom); + mat[(2, 2)] = - one / (zfar - znear); + mat[(2, 3)] = - znear / (zfar - znear); + + mat +} + +/// Creates a right hand matrix for a orthographic-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `left` - Coordinate for left bound of matrix +/// * `right` - Coordinate for right bound of matrix +/// * `bottom` - Coordinate for bottom bound of matrix +/// * `top` - Coordinate for top bound of matrix +/// * `znear` - Distance from the viewer to the near clipping plane +/// * `zfar` - Distance from the viewer to the far clipping plane +/// +pub fn ortho_zo(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> TMat4 { + ortho_rh_zo(left, right, bottom, top, znear, zfar) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_lh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = (far + near) / (far - near); + mat[(2, 3)] = - (far * near * ::convert(2.0)) / (far - near); + mat[(3, 2)] = N::one(); + + mat +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_lh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = far / (far - near); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_no(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = - (far + near) / (far - near); + mat[(2, 3)] = - (far * near * ::convert(2.0)) / (far - near); + mat[(3, 2)] = -N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_rh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + assert!( + width > N::zero(), + "The width must be greater than zero" + ); + assert!( + height > N::zero(), + "The height must be greater than zero." + ); + assert!( + fov > N::zero(), + "The fov must be greater than zero" + ); + + let mut mat = TMat4::zeros(); + + let rad = fov; + let h = (rad * ::convert(0.5)).cos() / (rad * ::convert(0.5)).sin(); + let w = h * height / width; + + mat[(0, 0)] = w; + mat[(1, 1)] = h; + mat[(2, 2)] = far / (near - far); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = -N::one(); + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fov` - Field of view, in radians +/// * `width` - Width of the viewport +/// * `height` - Height of the viewport +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +pub fn perspective_fov_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { + perspective_fov_rh_zo(fov, width, height, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane /// /// # Important note /// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. pub fn perspective(aspect: N, fovy: N, near: N, far: N) -> TMat4 { - Perspective3::new(aspect, fovy, near, far).unwrap() + // TODO: Breaking change - revert back to proper glm conventions? + // + // Prior to changes to support configuring the behaviour of this function it was simply + // a wrapper around Perspective3::new(). The argument order for that function is different + // than the glm convention, but reordering the arguments would've caused pointlessly + // un-optimal code to be generated so they were rearranged so the function would just call + // straight through. + // + // Now this call to Perspective3::new() is no longer made so the functions can have their + // arguments reordered to the glm convention. Unfortunately this is a breaking change so + // can't be cleanly integrated into the existing library version without breaking other + // people's code. Reordering to glm isn't a huge deal but if it is done it will have to be + // in a major API breaking update. + // + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_lh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat : TMat4 = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = (far + near) / (far - near); + mat[(2, 3)] = -(two * far * near) / (far - near); + mat[(3, 2)] = one; + + mat +} + +/// Creates a matrix for a left hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_lh_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat: TMat4 = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = far / (far - near); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = one; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_no(aspect, fovy, near, far) +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of -1 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh_no(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let negone = -N::one(); + let one = N::one(); + let two: N = ::convert( 2.0); + let mut mat = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = - (far + near) / (far - near); + mat[(2, 3)] = -(two * far * near) / (far - near); + mat[(3, 2)] = negone; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_rh_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + assert!( + !relative_eq!(far - near, N::zero()), + "The near-plane and far-plane must not be superimposed." + ); + assert!( + !relative_eq!(aspect, N::zero()), + "The apsect ratio must not be zero." + ); + + let negone = -N::one(); + let one = N::one(); + let two = ::convert( 2.0); + let mut mat = TMat4::zeros(); + + let tan_half_fovy = (fovy / two).tan(); + + mat[(0, 0)] = one / (aspect * tan_half_fovy); + mat[(1, 1)] = one / tan_half_fovy; + mat[(2, 2)] = far / (near - far); + mat[(2, 3)] = -(far * near) / (far - near); + mat[(3, 2)] = negone; + + mat +} + +/// Creates a matrix for a right hand perspective-view frustum with a depth range of 0 to 1 +/// +/// # Parameters +/// +/// * `fovy` - Field of view, in radians +/// * `aspect` - Ratio of viewport width to height (width/height) +/// * `near` - Distance from the viewer to the near clipping plane +/// * `far` - Distance from the viewer to the far clipping plane +/// +/// # Important note +/// The `aspect` and `fovy` argument are interchanged compared to the original GLM API. +pub fn perspective_zo(aspect: N, fovy: N, near: N, far: N) -> TMat4 { + perspective_rh_zo(aspect, fovy, near, far) } -//pub fn perspective_fov(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_lh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh_no(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_rh_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_fov_zo(fov: N, width: N, height: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_lh_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh_no(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_rh_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// -//pub fn perspective_zo(fovy: N, aspect: N, near: N, far: N) -> TMat4 { -// unimplemented!() -//} -// //pub fn tweaked_infinite_perspective(fovy: N, aspect: N, near: N) -> TMat4 { // unimplemented!() //} diff --git a/nalgebra-glm/src/ext/matrix_projection.rs b/nalgebra-glm/src/ext/matrix_projection.rs index d56103a6..4ed23fb3 100644 --- a/nalgebra-glm/src/ext/matrix_projection.rs +++ b/nalgebra-glm/src/ext/matrix_projection.rs @@ -24,7 +24,8 @@ pub fn pick_matrix(center: &TVec2, delta: &TVec2, viewport: &TVec )) } -/// Map the specified object coordinates `(obj.x, obj.y, obj.z)` into window coordinates using OpenGL near and far clip planes definition. +/// Map the specified object coordinates `(obj.x, obj.y, obj.z)` into window coordinates with a +/// depth range of -1 to 1 /// /// # Parameters: /// @@ -114,7 +115,8 @@ pub fn project_zo( ) } -/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using OpenGL near and far clip planes definition. +/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using a +/// depth range of -1 to 1 /// /// # Parameters: /// diff --git a/nalgebra-glm/src/ext/mod.rs b/nalgebra-glm/src/ext/mod.rs index 6ad74ff6..0f9879ef 100644 --- a/nalgebra-glm/src/ext/mod.rs +++ b/nalgebra-glm/src/ext/mod.rs @@ -1,6 +1,16 @@ //! (Reexported) Additional features not specified by GLSL specification -pub use self::matrix_clip_space::{ortho, perspective}; +pub use self::matrix_clip_space::{ + ortho, ortho_lh, ortho_lh_no, ortho_lh_zo, ortho_no, ortho_rh, ortho_rh_no, ortho_rh_zo, + ortho_zo, + + perspective, perspective_lh, perspective_lh_no, perspective_lh_zo, perspective_no, + perspective_rh, perspective_rh_no, perspective_rh_zo, perspective_zo, + + perspective_fov, perspective_fov_lh,perspective_fov_lh_no, perspective_fov_lh_zo, + perspective_fov_no, perspective_fov_rh, perspective_fov_rh_no, perspective_fov_rh_zo, + perspective_fov_zo, +}; pub use self::matrix_projection::{ pick_matrix, project, project_no, project_zo, unproject, unproject_no, unproject_zo, }; diff --git a/nalgebra-glm/src/ext/quaternion_common.rs b/nalgebra-glm/src/ext/quaternion_common.rs index 208601ed..3a91d0c7 100644 --- a/nalgebra-glm/src/ext/quaternion_common.rs +++ b/nalgebra-glm/src/ext/quaternion_common.rs @@ -33,5 +33,5 @@ pub fn quat_lerp(x: &Qua, y: &Qua, a: N) -> Qua { pub fn quat_slerp(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_normalize(*x) .slerp(&Unit::new_normalize(*y), a) - .unwrap() + .into_inner() } diff --git a/nalgebra-glm/src/ext/quaternion_transform.rs b/nalgebra-glm/src/ext/quaternion_transform.rs index a1459269..a4a60210 100644 --- a/nalgebra-glm/src/ext/quaternion_transform.rs +++ b/nalgebra-glm/src/ext/quaternion_transform.rs @@ -19,7 +19,7 @@ pub fn quat_pow(q: &Qua, y: N) -> Qua { /// Builds a quaternion from an axis and an angle, and right-multiply it to the quaternion `q`. pub fn quat_rotate(q: &Qua, angle: N, axis: &TVec3) -> Qua { - q * UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).unwrap() + q * UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).into_inner() } //pub fn quat_sqrt(q: &Qua) -> Qua { diff --git a/nalgebra-glm/src/ext/quaternion_trigonometric.rs b/nalgebra-glm/src/ext/quaternion_trigonometric.rs index 6e9be030..762bd9e9 100644 --- a/nalgebra-glm/src/ext/quaternion_trigonometric.rs +++ b/nalgebra-glm/src/ext/quaternion_trigonometric.rs @@ -9,13 +9,13 @@ pub fn quat_angle(x: &Qua) -> N { /// Creates a quaternion from an axis and an angle. pub fn quat_angle_axis(angle: N, axis: &TVec3) -> Qua { - UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).unwrap() + UnitQuaternion::from_axis_angle(&Unit::new_normalize(*axis), angle).into_inner() } /// The rotation axis of a quaternion assumed to be normalized. pub fn quat_axis(x: &Qua) -> TVec3 { if let Some(a) = UnitQuaternion::from_quaternion(*x).axis() { - a.unwrap() + a.into_inner() } else { TVec3::zeros() } diff --git a/nalgebra-glm/src/gtc/integer.rs b/nalgebra-glm/src/gtc/integer.rs index a5f1cc8f..df65843f 100644 --- a/nalgebra-glm/src/gtc/integer.rs +++ b/nalgebra-glm/src/gtc/integer.rs @@ -15,4 +15,4 @@ //pub fn uround(x: &TVec) -> TVec // where DefaultAllocator: Alloc { // unimplemented!() -//} \ No newline at end of file +//} diff --git a/nalgebra-glm/src/gtc/packing.rs b/nalgebra-glm/src/gtc/packing.rs index 414afe72..b0261239 100644 --- a/nalgebra-glm/src/gtc/packing.rs +++ b/nalgebra-glm/src/gtc/packing.rs @@ -288,4 +288,4 @@ pub fn unpackUnorm4x16(p: u64) -> Vec4 { pub fn unpackUnorm4x4(p: u16) -> Vec4 { unimplemented!() -} \ No newline at end of file +} diff --git a/nalgebra-glm/src/gtc/quaternion.rs b/nalgebra-glm/src/gtc/quaternion.rs index fd3e22bf..d31a0bcb 100644 --- a/nalgebra-glm/src/gtc/quaternion.rs +++ b/nalgebra-glm/src/gtc/quaternion.rs @@ -5,7 +5,7 @@ use aliases::{Qua, TMat4, TVec, TVec3}; /// Euler angles of the quaternion `q` as (pitch, yaw, roll). pub fn quat_euler_angles(x: &Qua) -> TVec3 { let q = UnitQuaternion::new_unchecked(*x); - let a = q.to_euler_angles(); + let a = q.euler_angles(); TVec3::new(a.2, a.1, a.0) } @@ -34,19 +34,25 @@ pub fn quat_cast(x: &Qua) -> TMat4 { ::quat_to_mat4(x) } -/// Computes a right-handed look-at quaternion (equivalent to a right-handed look-at matrix). +/// Computes a right hand look-at quaternion +/// +/// # Parameters +/// +/// * `direction` - Direction vector point at where to look +/// * `up` - Object up vector +/// pub fn quat_look_at(direction: &TVec3, up: &TVec3) -> Qua { quat_look_at_rh(direction, up) } /// Computes a left-handed look-at quaternion (equivalent to a left-handed look-at matrix). pub fn quat_look_at_lh(direction: &TVec3, up: &TVec3) -> Qua { - UnitQuaternion::look_at_lh(direction, up).unwrap() + UnitQuaternion::look_at_lh(direction, up).into_inner() } /// Computes a right-handed look-at quaternion (equivalent to a right-handed look-at matrix). pub fn quat_look_at_rh(direction: &TVec3, up: &TVec3) -> Qua { - UnitQuaternion::look_at_rh(direction, up).unwrap() + UnitQuaternion::look_at_rh(direction, up).into_inner() } /// The "roll" Euler angle of the quaternion `x` assumed to be normalized. diff --git a/nalgebra-glm/src/gtx/quaternion.rs b/nalgebra-glm/src/gtx/quaternion.rs index 5762b58d..70005ca8 100644 --- a/nalgebra-glm/src/gtx/quaternion.rs +++ b/nalgebra-glm/src/gtx/quaternion.rs @@ -21,7 +21,7 @@ pub fn quat_extract_real_component(q: &Qua) -> N { pub fn quat_fast_mix(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_unchecked(*x) .nlerp(&Unit::new_unchecked(*y), a) - .unwrap() + .into_inner() } //pub fn quat_intermediate(prev: &Qua, curr: &Qua, next: &Qua) -> Qua { @@ -40,7 +40,7 @@ pub fn quat_magnitude2(q: &Qua) -> N { /// The quaternion representing the identity rotation. pub fn quat_identity() -> Qua { - UnitQuaternion::identity().unwrap() + UnitQuaternion::identity().into_inner() } /// Rotates a vector by a quaternion assumed to be normalized. @@ -58,14 +58,14 @@ pub fn quat_rotate_vec(q: &Qua, v: &TVec4) -> TVec4 { pub fn quat_rotation(orig: &TVec3, dest: &TVec3) -> Qua { UnitQuaternion::rotation_between(orig, dest) .unwrap_or_else(UnitQuaternion::identity) - .unwrap() + .into_inner() } /// The spherical linear interpolation between two quaternions. pub fn quat_short_mix(x: &Qua, y: &Qua, a: N) -> Qua { Unit::new_normalize(*x) .slerp(&Unit::new_normalize(*y), a) - .unwrap() + .into_inner() } //pub fn quat_squad(q1: &Qua, q2: &Qua, s1: &Qua, s2: &Qua, h: N) -> Qua { @@ -76,7 +76,7 @@ pub fn quat_short_mix(x: &Qua, y: &Qua, a: N) -> Qua { pub fn quat_to_mat3(x: &Qua) -> TMat3 { UnitQuaternion::new_unchecked(*x) .to_rotation_matrix() - .unwrap() + .into_inner() } /// Converts a quaternion to a rotation matrix in homogenous coordinates. @@ -87,7 +87,7 @@ pub fn quat_to_mat4(x: &Qua) -> TMat4 { /// Converts a rotation matrix to a quaternion. pub fn mat3_to_quat(x: &TMat3) -> Qua { let r = Rotation3::from_matrix_unchecked(*x); - UnitQuaternion::from_rotation_matrix(&r).unwrap() + UnitQuaternion::from_rotation_matrix(&r).into_inner() } /// Converts a rotation matrix in homogeneous coordinates to a quaternion. diff --git a/nalgebra-glm/src/gtx/rotate_normalized_axis.rs b/nalgebra-glm/src/gtx/rotate_normalized_axis.rs index 1ac9a7dc..224d7bfe 100644 --- a/nalgebra-glm/src/gtx/rotate_normalized_axis.rs +++ b/nalgebra-glm/src/gtx/rotate_normalized_axis.rs @@ -21,5 +21,5 @@ pub fn rotate_normalized_axis(m: &TMat4, angle: N, axis: &TVec3) /// * `angle` - Angle expressed in radians. /// * `axis` - Normalized axis of the rotation, must be normalized. pub fn quat_rotate_normalized_axis(q: &Qua, angle: N, axis: &TVec3) -> Qua { - q * UnitQuaternion::from_axis_angle(&Unit::new_unchecked(*axis), angle).unwrap() + q * UnitQuaternion::from_axis_angle(&Unit::new_unchecked(*axis), angle).into_inner() } diff --git a/nalgebra-glm/src/gtx/rotate_vector.rs b/nalgebra-glm/src/gtx/rotate_vector.rs index 0855244d..64ce7244 100644 --- a/nalgebra-glm/src/gtx/rotate_vector.rs +++ b/nalgebra-glm/src/gtx/rotate_vector.rs @@ -60,5 +60,5 @@ pub fn rotate_z_vec4(v: &TVec4, angle: N) -> TVec4 { pub fn slerp(x: &TVec3, y: &TVec3, a: N) -> TVec3 { Unit::new_unchecked(*x) .slerp(&Unit::new_unchecked(*y), a) - .unwrap() + .into_inner() } diff --git a/nalgebra-glm/src/lib.rs b/nalgebra-glm/src/lib.rs index 85783014..bd74598c 100644 --- a/nalgebra-glm/src/lib.rs +++ b/nalgebra-glm/src/lib.rs @@ -110,6 +110,9 @@ and keep in mind it is possible to convert, e.g., an `Isometry3` to a `Mat4` and vice-versa (see the [conversions section](#conversions)). */ +#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico")] +#![cfg_attr(not(feature = "std"), no_std)] + extern crate num_traits as num; #[macro_use] extern crate approx; @@ -142,12 +145,17 @@ pub use ext::{ epsilon, equal_columns, equal_columns_eps, equal_columns_eps_vec, equal_eps, equal_eps_vec, identity, look_at, look_at_lh, look_at_rh, max, max2, max3, max3_scalar, max4, max4_scalar, min, min2, min3, min3_scalar, min4, min4_scalar, not_equal_columns, not_equal_columns_eps, - not_equal_columns_eps_vec, not_equal_eps, not_equal_eps_vec, ortho, perspective, pi, - pick_matrix, project, project_no, project_zo, quat_angle, quat_angle_axis, quat_axis, - quat_conjugate, quat_cross, quat_dot, quat_equal, quat_equal_eps, quat_exp, quat_inverse, - quat_length, quat_lerp, quat_log, quat_magnitude, quat_normalize, quat_not_equal, - quat_not_equal_eps, quat_pow, quat_rotate, quat_slerp, rotate, rotate_x, rotate_y, rotate_z, - scale, translate, unproject, unproject_no, unproject_zo, + not_equal_columns_eps_vec, not_equal_eps, not_equal_eps_vec, ortho, perspective, perspective_fov, + perspective_fov_lh,perspective_fov_lh_no, perspective_fov_lh_zo, perspective_fov_no, + perspective_fov_rh, perspective_fov_rh_no, perspective_fov_rh_zo, perspective_fov_zo, + perspective_lh, perspective_lh_no, perspective_lh_zo, perspective_no, perspective_rh, + perspective_rh_no, perspective_rh_zo, perspective_zo, ortho_lh, ortho_lh_no, ortho_lh_zo, + ortho_no, ortho_rh, ortho_rh_no, ortho_rh_zo, ortho_zo, pi, pick_matrix, project, project_no, + project_zo, quat_angle, quat_angle_axis, quat_axis, quat_conjugate, quat_cross, quat_dot, + quat_equal, quat_equal_eps, quat_exp, quat_inverse, quat_length, quat_lerp, quat_log, + quat_magnitude, quat_normalize, quat_not_equal, quat_not_equal_eps, quat_pow, quat_rotate, + quat_slerp, rotate, rotate_x, rotate_y, rotate_z, scale, translate, unproject, unproject_no, + unproject_zo, }; pub use gtc::{ affine_inverse, column, e, euler, four_over_pi, golden_ratio, half_pi, inverse_transpose, diff --git a/nalgebra-glm/tests/lib.rs b/nalgebra-glm/tests/lib.rs new file mode 100644 index 00000000..ce95c64f --- /dev/null +++ b/nalgebra-glm/tests/lib.rs @@ -0,0 +1,55 @@ +extern crate nalgebra as na; +extern crate nalgebra_glm as glm; + +use na::Perspective3; +use na::Orthographic3; +use glm::Mat4; +use glm::Vec4; + +#[test] +pub fn orthographic_glm_nalgebra_same() +{ + let na_mat : Mat4 = Orthographic3::new(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::ortho(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32); + + assert_eq!(na_mat, gl_mat); +} + +#[test] +pub fn perspective_glm_nalgebra_same() +{ + let na_mat : Mat4 = Perspective3::new(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::perspective(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32); + + assert_eq!(na_mat, gl_mat); +} + +#[test] +pub fn orthographic_glm_nalgebra_project_same() +{ + let point = Vec4::new(1.0,0.0,-20.0,1.0); + + let na_mat : Mat4 = Orthographic3::new(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::ortho(-100.0f32,100.0f32, -50.0f32, 50.0f32, 0.1f32, 100.0f32); + + let na_pt = na_mat * point; + let gl_pt = gl_mat * point; + + assert_eq!(na_mat, gl_mat); + assert_eq!(na_pt, gl_pt); +} + +#[test] +pub fn perspective_glm_nalgebra_project_same() +{ + let point = Vec4::new(1.0,0.0,-20.0,1.0); + + let na_mat : Mat4 = Perspective3::new(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32).into_inner(); + let gl_mat : Mat4 = glm::perspective(16.0f32/9.0f32, 3.14f32/2.0f32, 0.1f32, 100.0f32); + + let na_pt = na_mat * point; + let gl_pt = gl_mat * point; + + assert_eq!(na_mat, gl_mat); + assert_eq!(na_pt, gl_pt); +} diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml index e21891c1..939217ff 100644 --- a/nalgebra-lapack/Cargo.toml +++ b/nalgebra-lapack/Cargo.toml @@ -25,7 +25,7 @@ intel-mkl = ["lapack-src/intel-mkl"] nalgebra = { version = "0.16", path = ".." } num-traits = "0.2" num-complex = { version = "0.2", default-features = false } -alga = { version = "0.7", default-features = false } +alga = { version = "0.8", default-features = false } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } lapack = { version = "0.16", default-features = false } @@ -34,6 +34,6 @@ lapack-src = { version = "0.2", default-features = false } [dev-dependencies] nalgebra = { version = "0.16", path = "..", features = [ "arbitrary" ] } -quickcheck = "0.6" +quickcheck = "0.8" approx = "0.3" -rand = "0.5" +rand = "0.6" diff --git a/nalgebra-lapack/src/cholesky.rs b/nalgebra-lapack/src/cholesky.rs index 7615a899..3976373d 100644 --- a/nalgebra-lapack/src/cholesky.rs +++ b/nalgebra-lapack/src/cholesky.rs @@ -160,7 +160,7 @@ where DefaultAllocator: Allocator // Copy lower triangle to upper triangle. for i in 0..dim { for j in i + 1..dim { - unsafe { *self.l.get_unchecked_mut(i, j) = *self.l.get_unchecked(j, i) }; + unsafe { *self.l.get_unchecked_mut((i, j)) = *self.l.get_unchecked((j, i)) }; } } diff --git a/nalgebra-lapack/src/lib.rs b/nalgebra-lapack/src/lib.rs index a001dcc3..c343ba83 100644 --- a/nalgebra-lapack/src/lib.rs +++ b/nalgebra-lapack/src/lib.rs @@ -68,8 +68,10 @@ #![deny(unused_qualifications)] #![deny(unused_results)] #![deny(missing_docs)] -#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico", - html_root_url = "http://nalgebra.org/rustdoc")] +#![doc( + html_favicon_url = "http://nalgebra.org/img/favicon.ico", + html_root_url = "http://nalgebra.org/rustdoc" +)] extern crate alga; extern crate lapack; diff --git a/rustfmt.toml b/rustfmt.toml index dff7375b..8dc8e61a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,3 @@ unstable_features = true indent_style = "Block" -where_single_line = true +where_single_line = true \ No newline at end of file diff --git a/src/base/alias.rs b/src/base/alias.rs index 3942b94e..f7ca2b34 100644 --- a/src/base/alias.rs +++ b/src/base/alias.rs @@ -2,7 +2,7 @@ use base::dimension::Dynamic; use base::dimension::{U1, U2, U3, U4, U5, U6}; #[cfg(any(feature = "std", feature = "alloc"))] -use base::matrix_vec::MatrixVec; +use base::vec_storage::VecStorage; use base::storage::Owned; use base::Matrix; @@ -119,7 +119,7 @@ pub type Matrix6x5 = MatrixMN; */ /// A dynamically sized column vector. #[cfg(any(feature = "std", feature = "alloc"))] -pub type DVector = Matrix>; +pub type DVector = Matrix>; /// A statically sized D-dimensional column vector. pub type VectorN = MatrixMN; @@ -146,7 +146,7 @@ pub type Vector6 = VectorN; */ /// A dynamically sized row vector. #[cfg(any(feature = "std", feature = "alloc"))] -pub type RowDVector = Matrix>; +pub type RowDVector = Matrix>; /// A statically sized D-dimensional row vector. pub type RowVectorN = MatrixMN; diff --git a/src/base/alias_slice.rs b/src/base/alias_slice.rs index 1b368c8e..790e4e59 100644 --- a/src/base/alias_slice.rs +++ b/src/base/alias_slice.rs @@ -175,24 +175,24 @@ pub type MatrixSliceXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMN<'a, N, Dynamic, U6, RStride, CStride>; /// A column vector slice with `D` rows. -pub type VectorSliceN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A column vector slice dynamic numbers of rows and columns. -pub type DVectorSlice<'a, N, Stride = U1> = VectorSliceN<'a, N, Dynamic, Stride>; +pub type DVectorSlice<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceN<'a, N, Dynamic, RStride, CStride>; /// A 1D column vector slice. -pub type VectorSlice1<'a, N, Stride = U1> = VectorSliceN<'a, N, U1, Stride>; +pub type VectorSlice1<'a, N, RStride = U1, CStride = U1> = VectorSliceN<'a, N, U1, RStride, CStride>; /// A 2D column vector slice. -pub type VectorSlice2<'a, N, Stride = U1> = VectorSliceN<'a, N, U2, Stride>; +pub type VectorSlice2<'a, N, RStride = U1, CStride = U2> = VectorSliceN<'a, N, U2, RStride, CStride>; /// A 3D column vector slice. -pub type VectorSlice3<'a, N, Stride = U1> = VectorSliceN<'a, N, U3, Stride>; +pub type VectorSlice3<'a, N, RStride = U1, CStride = U3> = VectorSliceN<'a, N, U3, RStride, CStride>; /// A 4D column vector slice. -pub type VectorSlice4<'a, N, Stride = U1> = VectorSliceN<'a, N, U4, Stride>; +pub type VectorSlice4<'a, N, RStride = U1, CStride = U4> = VectorSliceN<'a, N, U4, RStride, CStride>; /// A 5D column vector slice. -pub type VectorSlice5<'a, N, Stride = U1> = VectorSliceN<'a, N, U5, Stride>; +pub type VectorSlice5<'a, N, RStride = U1, CStride = U5> = VectorSliceN<'a, N, U5, RStride, CStride>; /// A 6D column vector slice. -pub type VectorSlice6<'a, N, Stride = U1> = VectorSliceN<'a, N, U6, Stride>; +pub type VectorSlice6<'a, N, RStride = U1, CStride = U6> = VectorSliceN<'a, N, U6, RStride, CStride>; /* * @@ -367,21 +367,21 @@ pub type MatrixSliceMutXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMutMN<'a, N, Dynamic, U6, RStride, CStride>; /// A mutable column vector slice with `D` rows. -pub type VectorSliceMutN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceMutN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A mutable column vector slice dynamic numbers of rows and columns. -pub type DVectorSliceMut<'a, N, Stride = U1> = VectorSliceMutN<'a, N, Dynamic, Stride>; +pub type DVectorSliceMut<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceMutN<'a, N, Dynamic, RStride, CStride>; /// A 1D mutable column vector slice. -pub type VectorSliceMut1<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U1, Stride>; +pub type VectorSliceMut1<'a, N, RStride = U1, CStride = U1> = VectorSliceMutN<'a, N, U1, RStride, CStride>; /// A 2D mutable column vector slice. -pub type VectorSliceMut2<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U2, Stride>; +pub type VectorSliceMut2<'a, N, RStride = U1, CStride = U2> = VectorSliceMutN<'a, N, U2, RStride, CStride>; /// A 3D mutable column vector slice. -pub type VectorSliceMut3<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U3, Stride>; +pub type VectorSliceMut3<'a, N, RStride = U1, CStride = U3> = VectorSliceMutN<'a, N, U3, RStride, CStride>; /// A 4D mutable column vector slice. -pub type VectorSliceMut4<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U4, Stride>; +pub type VectorSliceMut4<'a, N, RStride = U1, CStride = U4> = VectorSliceMutN<'a, N, U4, RStride, CStride>; /// A 5D mutable column vector slice. -pub type VectorSliceMut5<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U5, Stride>; +pub type VectorSliceMut5<'a, N, RStride = U1, CStride = U5> = VectorSliceMutN<'a, N, U5, RStride, CStride>; /// A 6D mutable column vector slice. -pub type VectorSliceMut6<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U6, Stride>; +pub type VectorSliceMut6<'a, N, RStride = U1, CStride = U6> = VectorSliceMutN<'a, N, U6, RStride, CStride>; diff --git a/src/base/matrix_array.rs b/src/base/array_storage.rs similarity index 83% rename from src/base/matrix_array.rs rename to src/base/array_storage.rs index 384b3ae5..3beab9e1 100644 --- a/src/base/matrix_array.rs +++ b/src/base/array_storage.rs @@ -34,7 +34,7 @@ use base::Scalar; */ /// A array-based statically sized matrix data storage. #[repr(C)] -pub struct MatrixArray +pub struct ArrayStorage where R: DimName, C: DimName, @@ -44,7 +44,11 @@ where data: GenericArray>, } -impl Hash for MatrixArray +#[deprecated(note="renamed to `ArrayStorage`")] +/// Renamed to [ArrayStorage]. +pub type MatrixArray = ArrayStorage; + +impl Hash for ArrayStorage where N: Hash, R: DimName, @@ -57,7 +61,7 @@ where } } -impl Deref for MatrixArray +impl Deref for ArrayStorage where R: DimName, C: DimName, @@ -72,7 +76,7 @@ where } } -impl DerefMut for MatrixArray +impl DerefMut for ArrayStorage where R: DimName, C: DimName, @@ -85,7 +89,7 @@ where } } -impl Debug for MatrixArray +impl Debug for ArrayStorage where N: Debug, R: DimName, @@ -99,7 +103,7 @@ where } } -impl Copy for MatrixArray +impl Copy for ArrayStorage where N: Copy, R: DimName, @@ -109,7 +113,7 @@ where GenericArray>: Copy, {} -impl Clone for MatrixArray +impl Clone for ArrayStorage where N: Clone, R: DimName, @@ -119,13 +123,13 @@ where { #[inline] fn clone(&self) -> Self { - MatrixArray { + ArrayStorage { data: self.data.clone(), } } } -impl Eq for MatrixArray +impl Eq for ArrayStorage where N: Eq, R: DimName, @@ -134,7 +138,7 @@ where Prod: ArrayLength, {} -impl PartialEq for MatrixArray +impl PartialEq for ArrayStorage where N: PartialEq, R: DimName, @@ -148,7 +152,7 @@ where } } -unsafe impl Storage for MatrixArray +unsafe impl Storage for ArrayStorage where N: Scalar, R: DimName, @@ -200,7 +204,7 @@ where } } -unsafe impl StorageMut for MatrixArray +unsafe impl StorageMut for ArrayStorage where N: Scalar, R: DimName, @@ -220,7 +224,7 @@ where } } -unsafe impl ContiguousStorage for MatrixArray +unsafe impl ContiguousStorage for ArrayStorage where N: Scalar, R: DimName, @@ -230,7 +234,7 @@ where DefaultAllocator: Allocator, {} -unsafe impl ContiguousStorageMut for MatrixArray +unsafe impl ContiguousStorageMut for ArrayStorage where N: Scalar, R: DimName, @@ -247,7 +251,7 @@ where */ // XXX: open an issue for GenericArray so that it implements serde traits? #[cfg(feature = "serde-serialize")] -impl Serialize for MatrixArray +impl Serialize for ArrayStorage where N: Scalar + Serialize, R: DimName, @@ -268,7 +272,7 @@ where } #[cfg(feature = "serde-serialize")] -impl<'a, N, R, C> Deserialize<'a> for MatrixArray +impl<'a, N, R, C> Deserialize<'a> for ArrayStorage where N: Scalar + Deserialize<'a>, R: DimName, @@ -278,18 +282,18 @@ where { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - deserializer.deserialize_seq(MatrixArrayVisitor::new()) + deserializer.deserialize_seq(ArrayStorageVisitor::new()) } } #[cfg(feature = "serde-serialize")] /// A visitor that produces a matrix array. -struct MatrixArrayVisitor { +struct ArrayStorageVisitor { marker: PhantomData<(N, R, C)>, } #[cfg(feature = "serde-serialize")] -impl MatrixArrayVisitor +impl ArrayStorageVisitor where N: Scalar, R: DimName, @@ -299,14 +303,14 @@ where { /// Construct a new sequence visitor. pub fn new() -> Self { - MatrixArrayVisitor { + ArrayStorageVisitor { marker: PhantomData, } } } #[cfg(feature = "serde-serialize")] -impl<'a, N, R, C> Visitor<'a> for MatrixArrayVisitor +impl<'a, N, R, C> Visitor<'a> for ArrayStorageVisitor where N: Scalar + Deserialize<'a>, R: DimName, @@ -314,20 +318,20 @@ where R::Value: Mul, Prod: ArrayLength, { - type Value = MatrixArray; + type Value = ArrayStorage; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str("a matrix array") } #[inline] - fn visit_seq(self, mut visitor: V) -> Result, V::Error> + fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: SeqAccess<'a> { let mut out: Self::Value = unsafe { mem::uninitialized() }; let mut curr = 0; while let Some(value) = try!(visitor.next_element()) { - out[curr] = value; + *out.get_mut(curr).ok_or_else(|| V::Error::invalid_length(curr, &self))? = value; curr += 1; } @@ -340,7 +344,7 @@ where } #[cfg(feature = "abomonation-serialize")] -impl Abomonation for MatrixArray +impl Abomonation for ArrayStorage where R: DimName, C: DimName, diff --git a/src/base/blas.rs b/src/base/blas.rs index 073f519b..c16496d4 100644 --- a/src/base/blas.rs +++ b/src/base/blas.rs @@ -13,18 +13,18 @@ use base::dimension::{Dim, Dynamic, U1, U2, U3, U4}; use base::storage::{Storage, StorageMut}; use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector}; -impl> Vector { - /// Computes the index of the vector component with the largest value. +impl> Vector { + /// Computes the index and value of the vector component with the largest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); - /// assert_eq!(vec.imax(), 2); + /// assert_eq!(vec.argmax(), (2, 13)); /// ``` #[inline] - pub fn imax(&self) -> usize { + pub fn argmax(&self) -> (usize, N) { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0) }; @@ -39,7 +39,21 @@ impl> Vector } } - the_i + (the_i, *the_max) + } + + /// Computes the index of the vector component with the largest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.imax(), 2); + /// ``` + #[inline] + pub fn imax(&self) -> usize { + self.argmax().0 } /// Computes the index of the vector component with the largest absolute value. @@ -52,7 +66,8 @@ impl> Vector /// assert_eq!(vec.iamax(), 1); /// ``` #[inline] - pub fn iamax(&self) -> usize { + pub fn iamax(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0).abs() }; @@ -70,6 +85,34 @@ impl> Vector the_i } + /// Computes the index and value of the vector component with the smallest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.argmin(), (1, -15)); + /// ``` + #[inline] + pub fn argmin(&self) -> (usize, N) { + assert!(!self.is_empty(), "The input vector must not be empty."); + + let mut the_min = unsafe { self.vget_unchecked(0) }; + let mut the_i = 0; + + for i in 1..self.nrows() { + let val = unsafe { self.vget_unchecked(i) }; + + if val < the_min { + the_min = val; + the_i = i; + } + } + + (the_i, *the_min) + } + /// Computes the index of the vector component with the smallest value. /// /// # Examples: @@ -81,21 +124,7 @@ impl> Vector /// ``` #[inline] pub fn imin(&self) -> usize { - assert!(!self.is_empty(), "The input vector must not be empty."); - - let mut the_max = unsafe { self.vget_unchecked(0) }; - let mut the_i = 0; - - for i in 1..self.nrows() { - let val = unsafe { self.vget_unchecked(i) }; - - if val < the_max { - the_max = val; - the_i = i; - } - } - - the_i + self.argmin().0 } /// Computes the index of the vector component with the smallest absolute value. @@ -108,17 +137,18 @@ impl> Vector /// assert_eq!(vec.iamin(), 0); /// ``` #[inline] - pub fn iamin(&self) -> usize { + pub fn iamin(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); - let mut the_max = unsafe { self.vget_unchecked(0).abs() }; + let mut the_min = unsafe { self.vget_unchecked(0).abs() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).abs() }; - if val < the_max { - the_max = val; + if val < the_min { + the_min = val; the_i = i; } } @@ -142,12 +172,12 @@ impl> Matri pub fn iamax_full(&self) -> (usize, usize) { assert!(!self.is_empty(), "The input matrix must not be empty."); - let mut the_max = unsafe { self.get_unchecked(0, 0).abs() }; + let mut the_max = unsafe { self.get_unchecked((0, 0)).abs() }; let mut the_ij = (0, 0); for j in 0..self.ncols() { for i in 0..self.nrows() { - let val = unsafe { self.get_unchecked(i, j).abs() }; + let val = unsafe { self.get_unchecked((i, j)).abs() }; if val > the_max { the_max = val; @@ -197,27 +227,27 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul // because the `for` loop below won't be very efficient on those. if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); + let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); return a + b; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); - let c = *self.get_unchecked(2, 0) * *rhs.get_unchecked(2, 0); + let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); + let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0)); return a + b + c; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { - let mut a = *self.get_unchecked(0, 0) * *rhs.get_unchecked(0, 0); - let mut b = *self.get_unchecked(1, 0) * *rhs.get_unchecked(1, 0); - let c = *self.get_unchecked(2, 0) * *rhs.get_unchecked(2, 0); - let d = *self.get_unchecked(3, 0) * *rhs.get_unchecked(3, 0); + let mut a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0)); + let mut b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0)); + let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0)); + let d = *self.get_unchecked((3, 0)) * *rhs.get_unchecked((3, 0)); a += c; b += d; @@ -257,14 +287,14 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul acc7 = N::zero(); while self.nrows() - i >= 8 { - acc0 += unsafe { *self.get_unchecked(i + 0, j) * *rhs.get_unchecked(i + 0, j) }; - acc1 += unsafe { *self.get_unchecked(i + 1, j) * *rhs.get_unchecked(i + 1, j) }; - acc2 += unsafe { *self.get_unchecked(i + 2, j) * *rhs.get_unchecked(i + 2, j) }; - acc3 += unsafe { *self.get_unchecked(i + 3, j) * *rhs.get_unchecked(i + 3, j) }; - acc4 += unsafe { *self.get_unchecked(i + 4, j) * *rhs.get_unchecked(i + 4, j) }; - acc5 += unsafe { *self.get_unchecked(i + 5, j) * *rhs.get_unchecked(i + 5, j) }; - acc6 += unsafe { *self.get_unchecked(i + 6, j) * *rhs.get_unchecked(i + 6, j) }; - acc7 += unsafe { *self.get_unchecked(i + 7, j) * *rhs.get_unchecked(i + 7, j) }; + acc0 += unsafe { *self.get_unchecked((i + 0, j)) * *rhs.get_unchecked((i + 0, j)) }; + acc1 += unsafe { *self.get_unchecked((i + 1, j)) * *rhs.get_unchecked((i + 1, j)) }; + acc2 += unsafe { *self.get_unchecked((i + 2, j)) * *rhs.get_unchecked((i + 2, j)) }; + acc3 += unsafe { *self.get_unchecked((i + 3, j)) * *rhs.get_unchecked((i + 3, j)) }; + acc4 += unsafe { *self.get_unchecked((i + 4, j)) * *rhs.get_unchecked((i + 4, j)) }; + acc5 += unsafe { *self.get_unchecked((i + 5, j)) * *rhs.get_unchecked((i + 5, j)) }; + acc6 += unsafe { *self.get_unchecked((i + 6, j)) * *rhs.get_unchecked((i + 6, j)) }; + acc7 += unsafe { *self.get_unchecked((i + 7, j)) * *rhs.get_unchecked((i + 7, j)) }; i += 8; } @@ -274,7 +304,7 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul res += acc3 + acc7; for k in i..self.nrows() { - res += unsafe { *self.get_unchecked(k, j) * *rhs.get_unchecked(k, j) } + res += unsafe { *self.get_unchecked((k, j)) * *rhs.get_unchecked((k, j)) } } } @@ -314,7 +344,7 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul for j in 0..self.nrows() { for i in 0..self.ncols() { - res += unsafe { *self.get_unchecked(j, i) * *rhs.get_unchecked(i, j) } + res += unsafe { *self.get_unchecked((j, i)) * *rhs.get_unchecked((i, j)) } } } @@ -627,7 +657,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2x3, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix2x3::new(1.0, 2.0, 3.0, @@ -760,7 +789,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3x2, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix3x2::new(1.0, 4.0, @@ -879,7 +907,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -934,7 +961,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3, Matrix2x3, Vector2}; /// let mut mat = Matrix2::identity(); /// let lhs = Matrix2x3::new(1.0, 2.0, 3.0, @@ -971,7 +997,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -1026,7 +1051,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3x2, Matrix3}; /// let mut mat = Matrix2::identity(); /// let rhs = Matrix3x2::new(1.0, 2.0, diff --git a/src/base/cg.rs b/src/base/cg.rs index 63586dc5..5883e710 100644 --- a/src/base/cg.rs +++ b/src/base/cg.rs @@ -115,13 +115,13 @@ impl Matrix4 { /// Creates a new homogeneous matrix for an orthographic projection. #[inline] pub fn new_orthographic(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self { - Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap() + Orthographic3::new(left, right, bottom, top, znear, zfar).into_inner() } /// Creates a new homogeneous matrix for a perspective projection. #[inline] pub fn new_perspective(aspect: N, fovy: N, znear: N, zfar: N) -> Self { - Perspective3::new(aspect, fovy, znear, zfar).unwrap() + Perspective3::new(aspect, fovy, znear, zfar).into_inner() } /// Creates an isometry that corresponds to the local frame of an observer standing at the @@ -130,8 +130,14 @@ impl Matrix4 { /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. #[inline] + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { + IsometryMatrix3::face_towards(eye, target, up).to_homogeneous() + } + + /// Deprecated: Use [Matrix4::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { - IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous() + Matrix4::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. @@ -348,7 +354,7 @@ where DefaultAllocator: Allocator let translation = self.fixed_slice::, U1>(0, D::dim() - 1); let normalizer = self.fixed_slice::>(D::dim() - 1, 0); let n = normalizer.tr_dot(&pt.coords) - + unsafe { *self.get_unchecked(D::dim() - 1, D::dim() - 1) }; + + unsafe { *self.get_unchecked((D::dim() - 1, D::dim() - 1)) }; if !n.is_zero() { return transform * (pt / n) + translation; diff --git a/src/base/componentwise.rs b/src/base/componentwise.rs index 958feb0a..9081cf36 100644 --- a/src/base/componentwise.rs +++ b/src/base/componentwise.rs @@ -1,4 +1,4 @@ -// Non-conventional componentwise operators. +// Non-conventional component-wise operators. use num::{Signed, Zero}; use std::ops::{Add, Mul}; @@ -61,7 +61,7 @@ macro_rules! component_binop_impl( for j in 0 .. res.ncols() { for i in 0 .. res.nrows() { unsafe { - res.get_unchecked_mut(i, j).$op_assign(*rhs.get_unchecked(i, j)); + res.get_unchecked_mut((i, j)).$op_assign(*rhs.get_unchecked((i, j))); } } } @@ -89,8 +89,8 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let res = alpha * a.get_unchecked(i, j).$op(*b.get_unchecked(i, j)); - *self.get_unchecked_mut(i, j) = res; + let res = alpha * a.get_unchecked((i, j)).$op(*b.get_unchecked((i, j))); + *self.get_unchecked_mut((i, j)) = res; } } } @@ -99,8 +99,8 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let res = alpha * a.get_unchecked(i, j).$op(*b.get_unchecked(i, j)); - *self.get_unchecked_mut(i, j) = beta * *self.get_unchecked(i, j) + res; + let res = alpha * a.get_unchecked((i, j)).$op(*b.get_unchecked((i, j))); + *self.get_unchecked_mut((i, j)) = beta * *self.get_unchecked((i, j)) + res; } } } @@ -121,7 +121,7 @@ macro_rules! component_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - self.get_unchecked_mut(i, j).$op_assign(*rhs.get_unchecked(i, j)); + self.get_unchecked_mut((i, j)).$op_assign(*rhs.get_unchecked((i, j))); } } } diff --git a/src/base/construction.rs b/src/base/construction.rs index 7a12dc0f..aeee6121 100644 --- a/src/base/construction.rs +++ b/src/base/construction.rs @@ -82,7 +82,7 @@ where DefaultAllocator: Allocator for i in 0..nrows.value() { for j in 0..ncols.value() { - unsafe { *res.get_unchecked_mut(i, j) = *iter.next().unwrap() } + unsafe { *res.get_unchecked_mut((i, j)) = *iter.next().unwrap() } } } @@ -105,7 +105,7 @@ where DefaultAllocator: Allocator for j in 0..ncols.value() { for i in 0..nrows.value() { - unsafe { *res.get_unchecked_mut(i, j) = f(i, j) } + unsafe { *res.get_unchecked_mut((i, j)) = f(i, j) } } } @@ -132,7 +132,7 @@ where DefaultAllocator: Allocator let mut res = Self::zeros_generic(nrows, ncols); for i in 0..::min(nrows.value(), ncols.value()) { - unsafe { *res.get_unchecked_mut(i, i) = elt } + unsafe { *res.get_unchecked_mut((i, i)) = elt } } res @@ -152,7 +152,7 @@ where DefaultAllocator: Allocator ); for (i, elt) in elts.iter().enumerate() { - unsafe { *res.get_unchecked_mut(i, i) = *elt } + unsafe { *res.get_unchecked_mut((i, i)) = *elt } } res @@ -270,7 +270,7 @@ where DefaultAllocator: Allocator /// let vec_ptr = vec.as_ptr(); /// /// let matrix = Matrix::from_vec_generic(Dynamic::new(vec.len()), U1, vec); - /// let matrix_storage_ptr = matrix.data.as_ptr(); + /// let matrix_storage_ptr = matrix.data.as_vec().as_ptr(); /// /// // `matrix` is backed by exactly the same `Vec` as it was constructed from. /// assert_eq!(matrix_storage_ptr, vec_ptr); @@ -296,7 +296,7 @@ where /// /// let m = Matrix3::from_diagonal(&Vector3::new(1.0, 2.0, 3.0)); /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(3, &[1.0, 2.0, 3.0])); + /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(&[1.0, 2.0, 3.0])); /// /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && @@ -313,7 +313,7 @@ where for i in 0..diag.len() { unsafe { - *res.get_unchecked_mut(i, i) = *diag.vget_unchecked(i); + *res.get_unchecked_mut((i, i)) = *diag.vget_unchecked(i); } } @@ -444,63 +444,6 @@ macro_rules! impl_constructors( Self::from_iterator_generic($($gargs, )* iter) } - /// Creates a matrix with its elements filled with the components provided by a slice - /// in row-major order. - /// - /// The order of elements in the slice must follow the usual mathematic writing, i.e., - /// row-by-row. - /// - /// # Example - /// ``` - /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; - /// # use std::iter; - /// - /// let v = Vector3::from_row_slice(&[0, 1, 2]); - /// // The additional argument represents the vector dimension. - /// let dv = DVector::from_row_slice(3, &[0, 1, 2]); - /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); - /// - /// assert!(v.x == 0 && v.y == 1 && v.z == 2); - /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); - /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && - /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && - /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); - /// ``` - #[inline] - pub fn from_row_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_row_slice_generic($($gargs, )* slice) - } - - /// Creates a matrix with its elements filled with the components provided by a slice - /// in column-major order. - /// - /// # Example - /// ``` - /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; - /// # use std::iter; - /// - /// let v = Vector3::from_column_slice(&[0, 1, 2]); - /// // The additional argument represents the vector dimension. - /// let dv = DVector::from_column_slice(3, &[0, 1, 2]); - /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); - /// - /// assert!(v.x == 0 && v.y == 1 && v.z == 2); - /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); - /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && - /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && - /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); - /// ``` - #[inline] - pub fn from_column_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_column_slice_generic($($gargs, )* slice) - } - /// Creates a matrix or vector filled with the results of a function applied to each of its /// component coordinates. /// @@ -612,32 +555,6 @@ macro_rules! impl_constructors( ) -> Self { Self::from_distribution_generic($($gargs, )* distribution, rng) } - - /// Creates a matrix backed by a given `Vec`. - /// - /// The output matrix is filled column-by-column. - /// - /// # Example - /// ``` - /// # use nalgebra::{DMatrix, Matrix2x3}; - /// - /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); - /// - /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && - /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); - /// - /// - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); - /// - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && - /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); - /// ``` - #[inline] - #[cfg(feature = "std")] - pub fn from_vec($($args: usize,)* data: Vec) -> Self { - Self::from_vec_generic($($gargs, )* data) - } } impl MatrixMN @@ -676,6 +593,125 @@ impl_constructors!(Dynamic, Dynamic; Dynamic::new(nrows), Dynamic::new(ncols); nrows, ncols); +/* + * + * Constructors that don't necessarily require all dimensions + * to be specified whon one dimension is already known. + * + */ +macro_rules! impl_constructors_from_data( + ($data: ident; $($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { + impl MatrixMN + where DefaultAllocator: Allocator { + /// Creates a matrix with its elements filled with the components provided by a slice + /// in row-major order. + /// + /// The order of elements in the slice must follow the usual mathematic writing, i.e., + /// row-by-row. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_row_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_row_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && + /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && + /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_row_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_row_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix with its elements filled with the components provided by a slice + /// in column-major order. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_column_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_column_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_column_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_column_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix backed by a given `Vec`. + /// + /// The output matrix is filled column-by-column. + /// + /// # Example + /// ``` + /// # use nalgebra::{DMatrix, Matrix2x3}; + /// + /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// + /// + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + #[cfg(feature = "std")] + pub fn from_vec($($args: usize,)* $data: Vec) -> Self { + Self::from_vec_generic($($gargs, )* $data) + } + } + } +); + +// FIXME: this is not very pretty. We could find a better call syntax. +impl_constructors_from_data!(data; R, C; // Arguments for Matrix + => R: DimName, => C: DimName; // Type parameters for impl + R::name(), C::name(); // Arguments for `_generic` constructors. + ); // Arguments for non-generic constructors. + +impl_constructors_from_data!(data; R, Dynamic; + => R: DimName; + R::name(), Dynamic::new(data.len() / R::dim()); + ); + +impl_constructors_from_data!(data; Dynamic, C; + => C: DimName; + Dynamic::new(data.len() / C::dim()), C::name(); + ); + +impl_constructors_from_data!(data; Dynamic, Dynamic; + ; + Dynamic::new(nrows), Dynamic::new(ncols); + nrows, ncols); + + + /* * * Zero, One, Rand traits. @@ -791,7 +827,7 @@ macro_rules! componentwise_constructors_impl( pub fn new($($args: N),*) -> Self { unsafe { let mut res = Self::new_uninitialized(); - $( *res.get_unchecked_mut($irow, $icol) = $args; )* + $( *res.get_unchecked_mut(($irow, $icol)) = $args; )* res } diff --git a/src/base/conversion.rs b/src/base/conversion.rs index 930fc24d..fcb6907d 100644 --- a/src/base/conversion.rs +++ b/src/base/conversion.rs @@ -12,13 +12,15 @@ use typenum::Prod; use base::allocator::{Allocator, SameShapeAllocator}; use base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use base::dimension::{ - Dim, DimName, Dynamic, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9, + Dim, DimName, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9, }; +#[cfg(any(feature = "std", feature = "alloc"))] +use base::dimension::Dynamic; use base::iter::{MatrixIter, MatrixIterMut}; use base::storage::{ContiguousStorage, ContiguousStorageMut, Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] -use base::MatrixVec; -use base::{DefaultAllocator, Matrix, MatrixArray, MatrixMN, MatrixSlice, MatrixSliceMut, Scalar}; +use base::VecStorage; +use base::{DefaultAllocator, Matrix, ArrayStorage, MatrixMN, MatrixSlice, MatrixSliceMut, Scalar}; // FIXME: too bad this won't work allo slice conversions. impl SubsetOf> for MatrixMN @@ -42,7 +44,7 @@ where let mut res = unsafe { MatrixMN::::new_uninitialized_generic(nrows2, ncols2) }; for i in 0..nrows { for j in 0..ncols { - unsafe { *res.get_unchecked_mut(i, j) = N2::from_subset(self.get_unchecked(i, j)) } + unsafe { *res.get_unchecked_mut((i, j)) = N2::from_subset(self.get_unchecked((i, j))) } } } @@ -63,7 +65,7 @@ where let mut res = Self::new_uninitialized_generic(nrows, ncols); for i in 0..nrows2 { for j in 0..ncols2 { - *res.get_unchecked_mut(i, j) = m.get_unchecked(i, j).to_subset_unchecked() + *res.get_unchecked_mut((i, j)) = m.get_unchecked((i, j)).to_subset_unchecked() } } @@ -336,7 +338,7 @@ impl_from_into_mint_2D!( ); impl<'a, N, R, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -353,7 +355,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, C: Dim, @@ -367,7 +369,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, R, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -380,7 +382,7 @@ where } impl<'a, N, R, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, @@ -397,7 +399,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, C, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, C: Dim, @@ -411,7 +413,7 @@ where #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, N, R, RStride, CStride> From> - for Matrix> + for Matrix> where N: Scalar, R: DimName, diff --git a/src/base/default_allocator.rs b/src/base/default_allocator.rs index 6bdbd30d..5926f39d 100644 --- a/src/base/default_allocator.rs +++ b/src/base/default_allocator.rs @@ -18,9 +18,9 @@ use base::allocator::{Allocator, Reallocator}; #[cfg(any(feature = "alloc", feature = "std"))] use base::dimension::Dynamic; use base::dimension::{Dim, DimName}; -use base::matrix_array::MatrixArray; +use base::array_storage::ArrayStorage; #[cfg(any(feature = "std", feature = "alloc"))] -use base::matrix_vec::MatrixVec; +use base::vec_storage::VecStorage; use base::storage::{Storage, StorageMut}; use base::Scalar; @@ -29,7 +29,7 @@ use base::Scalar; * Allocator. * */ -/// An allocator based on `GenericArray` and `MatrixVec` for statically-sized and dynamically-sized +/// An allocator based on `GenericArray` and `VecStorage` for statically-sized and dynamically-sized /// matrices respectively. pub struct DefaultAllocator; @@ -42,7 +42,7 @@ where R::Value: Mul, Prod: ArrayLength, { - type Buffer = MatrixArray; + type Buffer = ArrayStorage; #[inline] unsafe fn allocate_uninitialized(_: R, _: C) -> Self::Buffer { @@ -77,7 +77,7 @@ where // Dynamic - Dynamic #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { - type Buffer = MatrixVec; + type Buffer = VecStorage; #[inline] unsafe fn allocate_uninitialized(nrows: Dynamic, ncols: C) -> Self::Buffer { @@ -86,7 +86,7 @@ impl Allocator for DefaultAllocator { res.reserve_exact(length); res.set_len(length); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } #[inline] @@ -101,14 +101,14 @@ impl Allocator for DefaultAllocator { assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } } // Static - Dynamic #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { - type Buffer = MatrixVec; + type Buffer = VecStorage; #[inline] unsafe fn allocate_uninitialized(nrows: R, ncols: Dynamic) -> Self::Buffer { @@ -117,7 +117,7 @@ impl Allocator for DefaultAllocator { res.reserve_exact(length); res.set_len(length); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } #[inline] @@ -132,7 +132,7 @@ impl Allocator for DefaultAllocator { assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); - MatrixVec::new(nrows, ncols, res) + VecStorage::new(nrows, ncols, res) } } @@ -157,7 +157,7 @@ where rto: RTo, cto: CTo, buf: >::Buffer, - ) -> MatrixArray + ) -> ArrayStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -185,8 +185,8 @@ where unsafe fn reallocate_copy( rto: Dynamic, cto: CTo, - buf: MatrixArray, - ) -> MatrixVec + buf: ArrayStorage, + ) -> VecStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -214,8 +214,8 @@ where unsafe fn reallocate_copy( rto: RTo, cto: Dynamic, - buf: MatrixArray, - ) -> MatrixVec + buf: ArrayStorage, + ) -> VecStorage { let mut res = >::allocate_uninitialized(rto, cto); @@ -238,11 +238,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -254,11 +254,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -270,11 +270,11 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } @@ -286,10 +286,10 @@ impl Reallocator, - ) -> MatrixVec + buf: VecStorage, + ) -> VecStorage { let new_buf = buf.resize(rto.value() * cto.value()); - MatrixVec::new(rto, cto, new_buf) + VecStorage::new(rto, cto, new_buf) } } diff --git a/src/base/dimension.rs b/src/base/dimension.rs index 50d1e5d5..9ba509b9 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -181,7 +181,7 @@ dim_ops!( DimMul, DimNameMul, Mul, mul, Mul::mul, DimProd, DimNameProd, Prod; DimSub, DimNameSub, Sub, sub, Sub::sub, DimDiff, DimNameDiff, Diff; DimDiv, DimNameDiv, Div, div, Div::div, DimQuot, DimNameQuot, Quot; - DimMin, DimNameMin, Min, min, cmp::min, DimMinimum, DimNameNimimum, Minimum; + DimMin, DimNameMin, Min, min, cmp::min, DimMinimum, DimNameMinimum, Minimum; DimMax, DimNameMax, Max, max, cmp::max, DimMaximum, DimNameMaximum, Maximum; ); @@ -364,7 +364,8 @@ impl< G: Bit + Any + Debug + Copy + PartialEq + Send + Sync, > IsNotStaticOne for UInt, A>, B>, C>, D>, E>, F>, G> -{} +{ +} impl NamedDim for UInt @@ -405,4 +406,5 @@ impl IsNotStaticOne for UInt -{} +{ +} diff --git a/src/base/edition.rs b/src/base/edition.rs index 445a4ffd..32e2c1aa 100644 --- a/src/base/edition.rs +++ b/src/base/edition.rs @@ -1,12 +1,18 @@ use num::{One, Zero}; use std::cmp; use std::ptr; +#[cfg(any(feature = "std", feature = "alloc"))] +use std::iter::ExactSizeIterator; +#[cfg(any(feature = "std", feature = "alloc"))] +use std::mem; use base::allocator::{Allocator, Reallocator}; use base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use base::dimension::{ - Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, Dynamic, U1, + Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, U1, }; +#[cfg(any(feature = "std", feature = "alloc"))] +use base::dimension::Dynamic; use base::storage::{Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] use base::DMatrix; @@ -23,7 +29,7 @@ impl> Matrix { res } - /// Extracts the upper triangular part of this matrix (including the diagonal). + /// Extracts the lower triangular part of this matrix (including the diagonal). #[inline] pub fn lower_triangle(&self) -> MatrixMN where DefaultAllocator: Allocator { @@ -32,6 +38,54 @@ impl> Matrix { res } + + /// Creates a new matrix by extracting the given set of rows from `self`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn select_rows<'a, I>(&self, irows: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator + Clone, + DefaultAllocator: Allocator { + let irows = irows.into_iter(); + let ncols = self.data.shape().1; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(Dynamic::new(irows.len()), ncols) }; + + // First, check that all the indices from irows are valid. + // This will allow us to use unchecked access in the inner loop. + for i in irows.clone() { + assert!(*i < self.nrows(), "Row index out of bounds.") + } + + for j in 0..ncols.value() { + // FIXME: use unchecked column indexing + let mut res = res.column_mut(j); + let mut src = self.column(j); + + for (destination, source) in irows.clone().enumerate() { + unsafe { + *res.vget_unchecked_mut(destination) = *src.vget_unchecked(*source) + } + } + } + + res + } + + /// Creates a new matrix by extracting the given set of columns from `self`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn select_columns<'a, I>(&self, icols: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator, + DefaultAllocator: Allocator { + let icols = icols.into_iter(); + let nrows = self.data.shape().0; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, Dynamic::new(icols.len())) }; + + for (destination, source) in icols.enumerate() { + res.column_mut(destination).copy_from(&self.column(*source)) + } + + res + } } impl> Matrix { @@ -58,7 +112,7 @@ impl> Matrix { let n = cmp::min(nrows, ncols); for i in 0..n { - unsafe { *self.get_unchecked_mut(i, i) = val } + unsafe { *self.get_unchecked_mut((i, i)) = val } } } @@ -67,7 +121,7 @@ impl> Matrix { pub fn fill_row(&mut self, i: usize, val: N) { assert!(i < self.nrows(), "Row index out of bounds."); for j in 0..self.ncols() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } @@ -76,7 +130,7 @@ impl> Matrix { pub fn fill_column(&mut self, j: usize, val: N) { assert!(j < self.ncols(), "Row index out of bounds."); for i in 0..self.nrows() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } @@ -93,7 +147,7 @@ impl> Matrix { assert_eq!(diag.len(), min_nrows_ncols, "Mismatched dimensions."); for i in 0..min_nrows_ncols { - unsafe { *self.get_unchecked_mut(i, i) = *diag.vget_unchecked(i) } + unsafe { *self.get_unchecked_mut((i, i)) = *diag.vget_unchecked(i) } } } @@ -128,7 +182,7 @@ impl> Matrix { pub fn fill_lower_triangle(&mut self, val: N, shift: usize) { for j in 0..self.ncols() { for i in (j + shift)..self.nrows() { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } } @@ -146,7 +200,7 @@ impl> Matrix { // FIXME: is there a more efficient way to avoid the min ? // (necessary for rectangular matrices) for i in 0..cmp::min(j + 1 - shift, self.nrows()) { - unsafe { *self.get_unchecked_mut(i, j) = val } + unsafe { *self.get_unchecked_mut((i, j)) = val } } } } @@ -191,7 +245,7 @@ impl> Matrix { for j in 0..dim { for i in j + 1..dim { unsafe { - *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *self.get_unchecked((j, i)); } } } @@ -206,7 +260,7 @@ impl> Matrix { for j in 1..self.ncols() { for i in 0..j { unsafe { - *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *self.get_unchecked((j, i)); } } } @@ -248,6 +302,7 @@ impl> Matrix { /// Removes `n` consecutive columns from this matrix, starting with the `i`-th (included). #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_columns(self, i: usize, n: usize) -> MatrixMN where C: DimSub, @@ -330,6 +385,7 @@ impl> Matrix { /// Removes `n` consecutive rows from this matrix, starting with the `i`-th (included). #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_rows(self, i: usize, n: usize) -> MatrixMN where R: DimSub, @@ -407,6 +463,7 @@ impl> Matrix { /// Inserts `n` columns filled with `val` starting at the `i-th` position. #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_columns(self, i: usize, n: usize, val: N) -> MatrixMN where C: DimAdd, @@ -484,6 +541,7 @@ impl> Matrix { /// Inserts `n` rows filled with `val` starting at the `i-th` position. #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_rows(self, i: usize, n: usize, val: N) -> MatrixMN where R: DimAdd, @@ -549,6 +607,29 @@ impl> Matrix { self.resize_generic(Dynamic::new(new_nrows), Dynamic::new(new_ncols), val) } + /// Resizes this matrix vertically, i.e., so that it contains `new_nrows` rows while keeping the same number of columns. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows than `self`, then the extra rows are filled with `val`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_vertically(self, new_nrows: usize, val: N) -> MatrixMN + where DefaultAllocator: Reallocator { + let ncols = self.data.shape().1; + self.resize_generic(Dynamic::new(new_nrows), ncols, val) + } + + /// Resizes this matrix horizontally, i.e., so that it contains `new_ncolumns` columns while keeping the same number of columns. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// columns than `self`, then the extra columns are filled with `val`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_horizontally(self, new_ncols: usize, val: N) -> MatrixMN + where DefaultAllocator: Reallocator { + let nrows = self.data.shape().0; + self.resize_generic(nrows, Dynamic::new(new_ncols), val) + } + + /// Resizes this matrix so that it contains `R2::value()` rows and `C2::value()` columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more @@ -626,6 +707,61 @@ impl> Matrix { } } +#[cfg(any(feature = "std", feature = "alloc"))] +impl DMatrix { + /// Resizes this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. + /// + /// Defined only for owned fully-dynamic matrices, i.e., `DMatrix`. + pub fn resize_mut(&mut self, new_nrows: usize, new_ncols: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized(0, 0) }; + let old = mem::replace(self, placeholder); + let new = old.resize(new_nrows, new_ncols, val); + let _ = mem::replace(self, new); + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl MatrixMN + where DefaultAllocator: Allocator { + /// Changes the number of rows of this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows than `self`, then the extra rows are filled with `val`. + /// + /// Defined only for owned matrices with a dynamic number of rows (for example, `DVector`). + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_vertically_mut(&mut self, new_nrows: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized_generic(Dynamic::new(0), self.data.shape().1) }; + let old = mem::replace(self, placeholder); + let new = old.resize_vertically(new_nrows, val); + let _ = mem::replace(self, new); + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl MatrixMN + where DefaultAllocator: Allocator { + /// Changes the number of column of this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// columns than `self`, then the extra columns are filled with `val`. + /// + /// Defined only for owned matrices with a dynamic number of columns (for example, `DVector`). + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_horizontally_mut(&mut self, new_ncols: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized_generic(self.data.shape().0, Dynamic::new(0)) }; + let old = mem::replace(self, placeholder); + let new = old.resize_horizontally(new_ncols, val); + let _ = mem::replace(self, new); + } +} + unsafe fn compress_rows( data: &mut [N], nrows: usize, @@ -706,6 +842,7 @@ unsafe fn extend_rows( /// Extend the number of columns of the `Matrix` with elements from /// a given iterator. +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where N: Scalar, @@ -753,6 +890,7 @@ where /// Extend the number of rows of the `Vector` with elements from /// a given iterator. +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where N: Scalar, @@ -764,15 +902,16 @@ where /// # Example /// ``` /// # use nalgebra::DVector; - /// let mut vector = DVector::from_vec(3, vec![0, 1, 2]); + /// let mut vector = DVector::from_vec(vec![0, 1, 2]); /// vector.extend(vec![3, 4, 5]); - /// assert!(vector.eq(&DVector::from_vec(6, vec![0, 1, 2, 3, 4, 5]))); + /// assert!(vector.eq(&DVector::from_vec(vec![0, 1, 2, 3, 4, 5]))); /// ``` fn extend>(&mut self, iter: I) { self.data.extend(iter); } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend> for Matrix where N: Scalar, diff --git a/src/base/indexing.rs b/src/base/indexing.rs new file mode 100644 index 00000000..976eef87 --- /dev/null +++ b/src/base/indexing.rs @@ -0,0 +1,714 @@ +//! Indexing + +use base::{Dim, DimName, DimDiff, DimSub, Dynamic, Matrix, MatrixSlice, MatrixSliceMut, Scalar, U1}; +use base::storage::{Storage, StorageMut}; + +use std::ops; + +// N.B.: Not a public trait! +trait DimRange +{ + /// The number of elements indexed by this range. + type Length: Dim; + + /// The lower bound of the range, inclusive. + fn lower(&self, dimension: D) -> usize; + + /// The number of elements included in the range. + fn length(&self, dimension: D) -> Self::Length; + + /// Produces true if `Self` is contained within `dimension`. + fn contained_by(&self, dimension: D) -> bool; +} + +impl DimRange for usize { + type Length = U1; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + *self + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + U1 + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + *self < dimension.value() + } +} + +#[test] +fn dimrange_usize() { + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&0, U0), false); + assert_eq!(DimRange::contained_by(&0, U1), true); +} + +impl DimRange for ops::Range { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end.saturating_sub(self.start)) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + (self.start < dimension.value()) && (self.end <= dimension.value()) + } +} + +#[test] +fn dimrange_range_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..0), U0), false); + assert_eq!(DimRange::contained_by(&(0..1), U0), false); + assert_eq!(DimRange::contained_by(&(0..1), U1), true); + assert_eq!(DimRange::contained_by(&((MAX - 1)..MAX), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&((MAX - 1)..MAX), Dynamic::new(MAX)), Dynamic::new(1)); + assert_eq!(DimRange::length(&(MAX..(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(0)); + assert_eq!(DimRange::length(&(MAX..MAX), Dynamic::new(MAX)), Dynamic::new(0)); +} + +impl DimRange for ops::RangeFrom { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + (self.start..dimension.value()).length(dimension) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.start < dimension.value() + } +} + +#[test] +fn dimrange_rangefrom_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..), U0), false); + assert_eq!(DimRange::contained_by(&(0..), U0), false); + assert_eq!(DimRange::contained_by(&(0..), U1), true); + assert_eq!(DimRange::contained_by(&((MAX - 1)..), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&((MAX - 1)..), Dynamic::new(MAX)), Dynamic::new(1)); + assert_eq!(DimRange::length(&(MAX..), Dynamic::new(MAX)), Dynamic::new(0)); +} + +impl DimRange for ops::RangeFrom +where D: DimSub +{ + type Length = DimDiff; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + self.start.value() + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + dimension.sub(self.start) + } + + #[inline(always)] + fn contained_by(&self, _: D) -> bool { + true + } +} + +#[test] +fn dimrange_rangefrom_dimname() { + use base::dimension::{U5, U4}; + assert_eq!(DimRange::length(&(U1..), U5), U4); +} + +impl DimRange for ops::RangeFull { + type Length = D; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, dimension: D) -> Self::Length { + dimension + } + + #[inline(always)] + fn contained_by(&self, _: D) -> bool { + true + } +} + +#[test] +fn dimrange_rangefull() { + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..), U0), true); + assert_eq!(DimRange::length(&(..), U1), U1); +} + +impl DimRange for ops::RangeInclusive { + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + *self.start() + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new( + if self.end() < self.start() { + 0 + } else { + self.end().wrapping_sub(self.start().wrapping_sub(1)) + }) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + (*self.start() < dimension.value()) && (*self.end() < dimension.value()) + } +} + +#[test] +fn dimrange_rangeinclusive_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(0..=0), U0), false); + assert_eq!(DimRange::contained_by(&(0..=0), U1), true); + assert_eq!(DimRange::contained_by(&(MAX..=MAX), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&((MAX-1)..=MAX), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&((MAX-1)..=(MAX-1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(0..=0), U1), Dynamic::new(1)); + assert_eq!(DimRange::length(&((MAX - 1)..=MAX), Dynamic::new(MAX)), Dynamic::new(2)); + assert_eq!(DimRange::length(&(MAX..=(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(0)); + assert_eq!(DimRange::length(&(MAX..=MAX), Dynamic::new(MAX)), Dynamic::new(1)); +} + +impl DimRange for ops::RangeTo +{ + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.end <= dimension.value() + } +} + +#[test] +fn dimrange_rangeto_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..0), U0), true); + assert_eq!(DimRange::contained_by(&(..1), U0), false); + assert_eq!(DimRange::contained_by(&(..0), U1), true); + assert_eq!(DimRange::contained_by(&(..(MAX - 1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(..(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(MAX - 1)); + assert_eq!(DimRange::length(&(..MAX), Dynamic::new(MAX)), Dynamic::new(MAX)); +} + +impl DimRange for ops::RangeToInclusive +{ + type Length = Dynamic; + + #[inline(always)] + fn lower(&self, _: D) -> usize { + 0 + } + + #[inline(always)] + fn length(&self, _: D) -> Self::Length { + Dynamic::new(self.end + 1) + } + + #[inline(always)] + fn contained_by(&self, dimension: D) -> bool { + self.end < dimension.value() + } +} + +#[test] +fn dimrange_rangetoinclusive_usize() { + use std::usize::MAX; + use base::dimension::U0; + assert_eq!(DimRange::contained_by(&(..=0), U0), false); + assert_eq!(DimRange::contained_by(&(..=1), U0), false); + assert_eq!(DimRange::contained_by(&(..=0), U1), true); + assert_eq!(DimRange::contained_by(&(..=(MAX)), Dynamic::new(MAX)), false); + assert_eq!(DimRange::contained_by(&(..=(MAX - 1)), Dynamic::new(MAX)), true); + assert_eq!(DimRange::length(&(..=(MAX - 1)), Dynamic::new(MAX)), Dynamic::new(MAX)); +} + +/// A helper trait used for indexing operations. +pub trait MatrixIndex<'a, N: Scalar, R: Dim, C: Dim, S: Storage>: Sized { + + /// The output type returned by methods. + type Output : 'a; + + /// Produces true if the given matrix is contained by this index. + #[doc(hidden)] + fn contained_by(&self, matrix: &Matrix) -> bool; + + /// Produces a shared view of the data at this location if in bounds, + /// or `None`, otherwise. + #[doc(hidden)] + #[inline(always)] + fn get(self, matrix: &'a Matrix) -> Option { + if self.contained_by(matrix) { + Some(unsafe{self.get_unchecked(matrix)}) + } else { + None + } + } + + /// Produces a shared view of the data at this location if in bounds + /// without any bounds checking. + #[doc(hidden)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output; + + /// Produces a shared view to the data at this location, or panics + /// if out of bounds. + #[doc(hidden)] + #[inline(always)] + fn index(self, matrix: &'a Matrix) -> Self::Output { + self.get(matrix).expect("Index out of bounds.") + } +} + +/// A helper trait used for indexing operations. +pub trait MatrixIndexMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut>: MatrixIndex<'a, N, R, C, S> { + /// The output type returned by methods. + type OutputMut : 'a; + + /// Produces a mutable view of the data at this location, without + /// performing any bounds checking. + #[doc(hidden)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut; + + /// Produces a mutable view of the data at this location, if in + /// bounds. + #[doc(hidden)] + #[inline(always)] + fn get_mut(self, matrix: &'a mut Matrix) -> Option { + if self.contained_by(matrix) { + Some(unsafe{self.get_unchecked_mut(matrix)}) + } else { + None + } + } + + /// Produces a mutable view of the data at this location, or panics + /// if out of bounds. + #[doc(hidden)] + #[inline(always)] + fn index_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { + self.get_mut(matrix).expect("Index out of bounds.") + } +} + +/// # Indexing Operations +/// ## Indices to Individual Elements +/// ### Two-Dimensional Indices +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert_eq!(matrix.index((0, 0)), &0); +/// assert_eq!(matrix.index((1, 0)), &1); +/// assert_eq!(matrix.index((0, 1)), &2); +/// assert_eq!(matrix.index((1, 1)), &3); +/// ``` +/// +/// ### Linear Address Indexing +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert_eq!(matrix.get(0), Some(&0)); +/// assert_eq!(matrix.get(1), Some(&1)); +/// assert_eq!(matrix.get(2), Some(&2)); +/// assert_eq!(matrix.get(3), Some(&3)); +/// ``` +/// +/// ## Indices to Individual Rows and Columns +/// ### Index to a Row +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert!(matrix.index((0, ..)) +/// .eq(&Matrix1x2::new(0, 2))); +/// ``` +/// +/// ### Index to a Column +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix2::new(0, 2, +/// 1, 3); +/// +/// assert!(matrix.index((.., 0)) +/// .eq(&Matrix2x1::new(0, +/// 1))); +/// ``` +/// +/// ## Indices to Parts of Individual Rows and Columns +/// ### Index to a Partial Row +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((0, ..2)) +/// .eq(&Matrix1x2::new(0, 3))); +/// ``` +/// +/// ### Index to a Partial Column +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((..2, 0)) +/// .eq(&Matrix2x1::new(0, +/// 1))); +/// +/// assert!(matrix.index((U1.., 0)) +/// .eq(&Matrix2x1::new(1, +/// 2))); +/// ``` +/// ## Indices to Ranges of Rows and Columns +/// ### Index to a Range of Rows +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((1..3, ..)) +/// .eq(&Matrix2x3::new(1, 4, 7, +/// 2, 5, 8))); +/// ``` +/// ### Index to a Range of Columns +/// ``` +/// # use nalgebra::*; +/// let matrix = Matrix3::new(0, 3, 6, +/// 1, 4, 7, +/// 2, 5, 8); +/// +/// assert!(matrix.index((.., 1..3)) +/// .eq(&Matrix3x2::new(3, 6, +/// 4, 7, +/// 5, 8))); +/// ``` +impl> Matrix +{ + /// Produces a view of the data at the given index, or + /// `None` if the index is out of bounds. + #[inline] + pub fn get<'a, I>(&'a self, index: I) -> Option + where + I: MatrixIndex<'a, N, R, C, S> + { + index.get(self) + } + + /// Produces a mutable view of the data at the given index, or + /// `None` if the index is out of bounds. + #[inline] + pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.get_mut(self) + } + + /// Produces a view of the data at the given index, or + /// panics if the index is out of bounds. + #[inline] + pub fn index<'a, I>(&'a self, index: I) -> I::Output + where + I: MatrixIndex<'a, N, R, C, S> + { + index.index(self) + } + + /// Produces a mutable view of the data at the given index, or + /// panics if the index is out of bounds. + #[inline] + pub fn index_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.index_mut(self) + } + + /// Produces a view of the data at the given index, without doing + /// any bounds checking. + #[inline] + pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Output + where + I: MatrixIndex<'a, N, R, C, S> + { + index.get_unchecked(self) + } + + /// Returns a mutable view of the data at the given index, without doing + /// any bounds checking. + #[inline] + pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut + where + S: StorageMut, + I: MatrixIndexMut<'a, N, R, C, S> + { + index.get_unchecked_mut(self) + } +} + +// EXTRACT A SINGLE ELEMENT BY 1D LINEAR ADDRESS + +impl<'a, N, R, C, S> MatrixIndex<'a, N, R, C, S> for usize +where + N: Scalar, + R: Dim, + C: Dim, + S: Storage +{ + type Output = &'a N; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + *self < matrix.len() + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + matrix.data.get_unchecked_linear(self) + } +} + +impl<'a, N, R, C, S> MatrixIndexMut<'a, N, R, C, S> for usize +where + N: Scalar, + R: Dim, + C: Dim, + S: StorageMut +{ + type OutputMut = &'a mut N; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut + where S: StorageMut, + { + matrix.data.get_unchecked_linear_mut(self) + } +} + +// EXTRACT A SINGLE ELEMENT BY 2D COORDINATES + +impl<'a, N, R, C, S> MatrixIndex<'a, N, R, C, S> for (usize, usize) +where + N: Scalar, + R: Dim, + C: Dim, + S: Storage +{ + type Output = &'a N; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + let (row, col) = self; + matrix.data.get_unchecked(row, col) + } +} + +impl<'a, N, R, C, S> MatrixIndexMut<'a, N, R, C, S> for (usize, usize) +where + N: Scalar, + R: Dim, + C: Dim, + S: StorageMut +{ + type OutputMut = &'a mut N; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut + where S: StorageMut, + { + let (row, col) = self; + matrix.data.get_unchecked_mut(row, col) + } +} + +macro_rules! impl_index_pair { + ( + $R: ident, + $C: ident, + [<$($RTyP: ident : $RTyPB: ty,)*> usize => $ROut: ty + $(where $RConstraintType: ty: $RConstraintBound: ident<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)*], + [<$($CTyP: ident : $CTyPB: ty,)*> usize => $COut: ty + $(where $CConstraintType: ty: $CConstraintBound: ident<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)*] + ) => {}; + + ( + $R: ident, + $C: ident, + [<$($RTyP: ident: $RTyPB: tt),*> $RIdx: ty => $ROut: ty + $(where $RConstraintType: ty: $RConstraintBound: ident $(<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)* )*], + [<$($CTyP: ident: $CTyPB: tt),*> $CIdx: ty => $COut: ty + $(where $CConstraintType: ty: $CConstraintBound: ident $(<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)* )*] + ) => + { + impl<'a, N, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndex<'a, N, $R, $C, S> for ($RIdx, $CIdx) + where + N: Scalar, + $R: Dim, + $C: Dim, + S: Storage, + $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* + $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* + { + type Output = MatrixSlice<'a, N, $ROut, $COut, S::RStride, S::CStride>; + + #[doc(hidden)] + #[inline(always)] + fn contained_by(&self, matrix: &Matrix) -> bool { + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) + } + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { + use base::SliceStorage; + + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + + let data = + SliceStorage::new_unchecked(&matrix.data, + (rows.lower(nrows), cols.lower(ncols)), + (rows.length(nrows), cols.length(ncols))); + + Matrix::from_data_statically_unchecked(data) + } + } + + impl<'a, N, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndexMut<'a, N, $R, $C, S> for ($RIdx, $CIdx) + where + N: Scalar, + $R: Dim, + $C: Dim, + S: StorageMut, + $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* + $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* + { + type OutputMut = MatrixSliceMut<'a, N, $ROut, $COut, S::RStride, S::CStride>; + + #[doc(hidden)] + #[inline(always)] + unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { + use base::SliceStorageMut; + + let (rows, cols) = self; + let (nrows, ncols) = matrix.data.shape(); + + let data = + SliceStorageMut::new_unchecked(&mut matrix.data, + (rows.lower(nrows), cols.lower(ncols)), + (rows.length(nrows), cols.length(ncols))); + + Matrix::from_data_statically_unchecked(data) + } + } + } +} + +macro_rules! impl_index_pairs { + (index $R: ident with {} index $C: ident with {$($r: tt,)* }) => {}; + + (index $R: ident with {$lh : tt, $($lt : tt,)*} + index $C: ident with { $($r: tt,)* }) => + { + $( + impl_index_pair!{$R, $C, $lh, $r} + )* + impl_index_pairs!{index $R with {$($lt,)*} index $C with {$($r,)*}} + } +} + +impl_index_pairs!{ + index R with { + [<> usize => U1], + [<> ops::Range => Dynamic], + [<> ops::RangeFrom => Dynamic], + [<> ops::RangeFull => R], + [<> ops::RangeInclusive => Dynamic], + [<> ops::RangeTo => Dynamic], + [<> ops::RangeToInclusive => Dynamic], + + [ ops::RangeFrom + => DimDiff + where R: DimSub], + } + index C with { + [<> usize => U1], + [<> ops::Range => Dynamic], + [<> ops::RangeFrom => Dynamic], + [<> ops::RangeFull => C], + [<> ops::RangeInclusive => Dynamic], + [<> ops::RangeTo => Dynamic], + [<> ops::RangeToInclusive => Dynamic], + + [ ops::RangeFrom + => DimDiff + where C: DimSub], + } +} diff --git a/src/base/iter.rs b/src/base/iter.rs index fe8fd4f6..8a5f10ab 100644 --- a/src/base/iter.rs +++ b/src/base/iter.rs @@ -3,9 +3,9 @@ use std::marker::PhantomData; use std::mem; -use base::dimension::Dim; +use base::dimension::{Dim, U1}; use base::storage::{Storage, StorageMut}; -use base::Scalar; +use base::{Scalar, Matrix, MatrixSlice, MatrixSliceMut}; macro_rules! iterator { (struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty) => { @@ -96,3 +96,226 @@ macro_rules! iterator { iterator!(struct MatrixIter for Storage.ptr -> *const N, &'a N, &'a S); iterator!(struct MatrixIterMut for StorageMut.ptr_mut -> *mut N, &'a mut N, &'a mut S); + + +/* + * + * Row iterators. + * + */ +#[derive(Clone)] +/// An iterator through the rows of a matrix. +pub struct RowIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> RowIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + RowIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for RowIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.nrows() { + let res = self.mat.row(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.nrows() - self.curr, Some(self.mat.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for RowIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.nrows() - self.curr + } +} + + +/// An iterator through the mutable rows of a matrix. +pub struct RowIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> RowIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + RowIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn nrows(&self) -> usize { + unsafe { + (*self.mat).nrows() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for RowIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.nrows() { + let res = unsafe { (*self.mat).row_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.nrows() - self.curr, Some(self.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for RowIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.nrows() - self.curr + } +} + + +/* + * + * Column iterators. + * + */ +#[derive(Clone)] +/// An iterator through the columns of a matrix. +pub struct ColumnIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ColumnIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + ColumnIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for ColumnIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.ncols() { + let res = self.mat.column(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.ncols() - self.curr, Some(self.mat.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for ColumnIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.ncols() - self.curr + } +} + + +/// An iterator through the mutable columns of a matrix. +pub struct ColumnIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ColumnIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + ColumnIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn ncols(&self) -> usize { + unsafe { + (*self.mat).ncols() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for ColumnIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.ncols() { + let res = unsafe { (*self.mat).column_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.ncols() - self.curr, Some(self.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for ColumnIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.ncols() - self.curr + } +} + diff --git a/src/base/matrix.rs b/src/base/matrix.rs index 95893e22..cb687983 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -7,6 +7,7 @@ use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::TypeId; use std::cmp::Ordering; use std::fmt; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem; @@ -20,8 +21,8 @@ use alga::general::{ClosedAdd, ClosedMul, ClosedSub, Real, Ring}; use base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; -use base::dimension::{Dim, DimAdd, DimSum, U1, U2, U3}; -use base::iter::{MatrixIter, MatrixIterMut}; +use base::dimension::{Dim, DimAdd, DimSum, IsNotStaticOne, U1, U2, U3}; +use base::iter::{MatrixIter, MatrixIterMut, RowIter, RowIterMut, ColumnIter, ColumnIterMut}; use base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; @@ -72,7 +73,7 @@ pub type MatrixCross = /// dynamically-sized column vector should be represented as a `Matrix` (given /// some concrete types for `N` and a compatible data storage type `S`). #[repr(C)] -#[derive(Hash, Clone, Copy)] +#[derive(Clone, Copy)] pub struct Matrix { /// The data storage that contains all the matrix components and informations about its number /// of rows and column (if needed). @@ -246,6 +247,37 @@ impl> Matrix { MatrixIter::new(&self.data) } + /// Iterate through the rows of this matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, row) in a.row_iter().enumerate() { + /// assert_eq!(row, a.row(i)) + /// } + /// ``` + #[inline] + pub fn row_iter(&self) -> RowIter { + RowIter::new(self) + } + + /// Iterate through the columns of this matrix. + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, column) in a.column_iter().enumerate() { + /// assert_eq!(column, a.column(i)) + /// } + /// ``` + #[inline] + pub fn column_iter(&self) -> ColumnIter { + ColumnIter::new(self) + } + /// Computes the row and column coordinates of the i-th element of this matrix seen as a /// vector. #[inline] @@ -263,17 +295,6 @@ impl> Matrix { } } - /// Gets a reference to the element of this matrix at row `irow` and column `icol` without - /// bound-checking. - #[inline] - pub unsafe fn get_unchecked(&self, irow: usize, icol: usize) -> &N { - debug_assert!( - irow < self.nrows() && icol < self.ncols(), - "Matrix index out of bounds." - ); - self.data.get_unchecked(irow, icol) - } - /// Returns a pointer to the start of the matrix. /// /// If the matrix is not empty, this pointer is guaranteed to be aligned @@ -383,7 +404,7 @@ impl> Matrix { for j in 0..res.ncols() { for i in 0..res.nrows() { unsafe { - *res.get_unchecked_mut(i, j) = *self.get_unchecked(i, j); + *res.get_unchecked_mut((i, j)) = *self.get_unchecked((i, j)); } } } @@ -412,7 +433,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(value, row, col)`. + /// `f` also gets passed the row and column index, i.e. `f(row, col, value)`. #[inline] pub fn map_with_location N2>( &self, @@ -512,6 +533,57 @@ impl> Matrix { res } + /// Folds a function `f` on each entry of `self`. + #[inline] + pub fn fold(&self, init: Acc, mut f: impl FnMut(Acc, N) -> Acc) -> Acc { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + res = f(res, a) + } + } + } + + res + } + + /// Folds a function `f` on each pairs of entries from `self` and `rhs`. + #[inline] + pub fn zip_fold(&self, rhs: &Matrix, init: Acc, mut f: impl FnMut(Acc, N, N2) -> Acc) -> Acc + where + N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + assert!( + (nrows.value(), ncols.value()) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + let b = *rhs.data.get_unchecked(i, j); + res = f(res, a, b) + } + } + } + + res + } + /// Transposes `self` and store the result into `out`. #[inline] pub fn transpose_to(&self, out: &mut Matrix) @@ -531,7 +603,7 @@ impl> Matrix { for i in 0..nrows { for j in 0..ncols { unsafe { - *out.get_unchecked_mut(j, i) = *self.get_unchecked(i, j); + *out.get_unchecked_mut((j, i)) = *self.get_unchecked((i, j)); } } } @@ -568,14 +640,44 @@ impl> Matrix { self.data.ptr_mut() } - /// Gets a mutable reference to the i-th element of this matrix. + /// Mutably iterates through this matrix rows. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut row) in a.row_iter_mut().enumerate() { + /// row *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 20, 30, + /// 80, 100, 120); + /// assert_eq!(a, expected); + /// ``` #[inline] - pub unsafe fn get_unchecked_mut(&mut self, irow: usize, icol: usize) -> &mut N { - debug_assert!( - irow < self.nrows() && icol < self.ncols(), - "Matrix index out of bounds." - ); - self.data.get_unchecked_mut(irow, icol) + pub fn row_iter_mut(&mut self) -> RowIterMut { + RowIterMut::new(self) + } + + /// Mutably iterates through this matrix columns. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut col) in a.column_iter_mut().enumerate() { + /// col *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 40, 90, + /// 40, 100, 180); + /// assert_eq!(a, expected); + /// ``` + #[inline] + pub fn column_iter_mut(&mut self) -> ColumnIterMut { + ColumnIterMut::new(self) } /// Swaps two entries without bound-checking. @@ -616,7 +718,7 @@ impl> Matrix { for j in 0..ncols { for i in 0..nrows { unsafe { - *self.get_unchecked_mut(i, j) = *slice.get_unchecked(i + j * nrows); + *self.get_unchecked_mut((i, j)) = *slice.get_unchecked(i + j * nrows); } } } @@ -639,7 +741,7 @@ impl> Matrix { for j in 0..self.ncols() { for i in 0..self.nrows() { unsafe { - *self.get_unchecked_mut(i, j) = *other.get_unchecked(i, j); + *self.get_unchecked_mut((i, j)) = *other.get_unchecked((i, j)); } } } @@ -663,7 +765,7 @@ impl> Matrix { for j in 0..ncols { for i in 0..nrows { unsafe { - *self.get_unchecked_mut(i, j) = *other.get_unchecked(j, i); + *self.get_unchecked_mut((i, j)) = *other.get_unchecked((j, i)); } } } @@ -671,8 +773,7 @@ impl> Matrix { /// Replaces each component of `self` by the result of a closure `f` applied on it. #[inline] - pub fn apply N>(&mut self, mut f: F) - where DefaultAllocator: Allocator { + pub fn apply N>(&mut self, mut f: F) { let (nrows, ncols) = self.shape(); for j in 0..ncols { @@ -684,6 +785,71 @@ impl> Matrix { } } } + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `rhs`. + #[inline] + pub fn zip_apply(&mut self, rhs: &Matrix, mut f: impl FnMut(N, N2) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let rhs = rhs.get_unchecked((i, j)); + *e = f(*e, *rhs) + } + } + } + } + + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `b` and `c`. + #[inline] + pub fn zip_zip_apply(&mut self, b: &Matrix, c: &Matrix, mut f: impl FnMut(N, N2, N3) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + N3: Scalar, + R3: Dim, + C3: Dim, + S3: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == b.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + assert!( + (nrows, ncols) == c.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let b = b.get_unchecked((i, j)); + let c = c.get_unchecked((i, j)); + *e = f(*e, *b, *c) + } + } + } + } } impl> Vector { @@ -760,7 +926,7 @@ impl, R, C>> Matrix, R for i in 0..nrows { for j in 0..ncols { unsafe { - *out.get_unchecked_mut(j, i) = self.get_unchecked(i, j).conj(); + *out.get_unchecked_mut((j, i)) = self.get_unchecked((i, j)).conj(); } } } @@ -794,8 +960,8 @@ impl, D, D>> Matrix, D, D, for i in 1..dim { for j in 0..i { unsafe { - let ref_ij = self.get_unchecked_mut(i, j) as *mut Complex; - let ref_ji = self.get_unchecked_mut(j, i) as *mut Complex; + let ref_ij = self.get_unchecked_mut((i, j)) as *mut Complex; + let ref_ji = self.get_unchecked_mut((j, i)) as *mut Complex; let conj_ij = (*ref_ij).conj(); let conj_ji = (*ref_ji).conj(); *ref_ij = conj_ji; @@ -821,7 +987,7 @@ impl> SquareMatrix { for i in 0..dim.value() { unsafe { - *res.vget_unchecked_mut(i) = *self.get_unchecked(i, i); + *res.vget_unchecked_mut(i) = *self.get_unchecked((i, i)); } } @@ -841,27 +1007,36 @@ impl> SquareMatrix { let mut res = N::zero(); for i in 0..dim.value() { - res += unsafe { *self.get_unchecked(i, i) }; + res += unsafe { *self.get_unchecked((i, i)) }; } res } } +impl + IsNotStaticOne, S: Storage> Matrix { + + /// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and + /// and setting the diagonal element to `1`. + #[inline] + pub fn to_homogeneous(&self) -> MatrixN> + where DefaultAllocator: Allocator, DimSum> { + assert!(self.is_square(), "Only square matrices can currently be transformed to homogeneous coordinates."); + let dim = DimSum::::from_usize(self.nrows() + 1); + let mut res = MatrixN::identity_generic(dim, dim); + res.generic_slice_mut::((0, 0), self.data.shape()).copy_from(&self); + res + } + +} + impl, S: Storage> Vector { /// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its /// coordinates. #[inline] pub fn to_homogeneous(&self) -> VectorN> where DefaultAllocator: Allocator> { - let len = self.len(); - let hnrows = DimSum::::from_usize(len + 1); - let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; - res.generic_slice_mut((0, 0), self.data.shape()) - .copy_from(self); - res[(len, 0)] = N::zero(); - - res + self.push(N::zero()) } /// Constructs a vector from coordinates in projective space, i.e., removes a `0` at the end of @@ -881,6 +1056,22 @@ impl, S: Storage> Vector { } } +impl, S: Storage> Vector { + /// Constructs a new vector of higher dimension by appending `element` to the end of `self`. + #[inline] + pub fn push(&self, element: N) -> VectorN> + where DefaultAllocator: Allocator> { + let len = self.len(); + let hnrows = DimSum::::from_usize(len + 1); + let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; + res.generic_slice_mut((0, 0), self.data.shape()) + .copy_from(self); + res[(len, 0)] = element; + + res + } +} + impl AbsDiffEq for Matrix where N: Scalar + AbsDiffEq, @@ -1141,8 +1332,8 @@ impl> Matrix { assert!(self.shape() == (2, 1), "2D perpendicular product "); unsafe { - *self.get_unchecked(0, 0) * *b.get_unchecked(1, 0) - - *self.get_unchecked(1, 0) * *b.get_unchecked(0, 0) + *self.get_unchecked((0, 0)) * *b.get_unchecked((1, 0)) + - *self.get_unchecked((1, 0)) * *b.get_unchecked((0, 0)) } } @@ -1177,17 +1368,17 @@ impl> Matrix { let ncols = SameShapeC::::from_usize(1); let mut res = Matrix::new_uninitialized_generic(nrows, ncols); - let ax = *self.get_unchecked(0, 0); - let ay = *self.get_unchecked(1, 0); - let az = *self.get_unchecked(2, 0); + let ax = *self.get_unchecked((0, 0)); + let ay = *self.get_unchecked((1, 0)); + let az = *self.get_unchecked((2, 0)); - let bx = *b.get_unchecked(0, 0); - let by = *b.get_unchecked(1, 0); - let bz = *b.get_unchecked(2, 0); + let bx = *b.get_unchecked((0, 0)); + let by = *b.get_unchecked((1, 0)); + let bz = *b.get_unchecked((2, 0)); - *res.get_unchecked_mut(0, 0) = ay * bz - az * by; - *res.get_unchecked_mut(1, 0) = az * bx - ax * bz; - *res.get_unchecked_mut(2, 0) = ax * by - ay * bx; + *res.get_unchecked_mut((0, 0)) = ay * bz - az * by; + *res.get_unchecked_mut((1, 0)) = az * bx - ax * bz; + *res.get_unchecked_mut((2, 0)) = ax * by - ay * bx; res } @@ -1198,17 +1389,17 @@ impl> Matrix { let ncols = SameShapeC::::from_usize(3); let mut res = Matrix::new_uninitialized_generic(nrows, ncols); - let ax = *self.get_unchecked(0, 0); - let ay = *self.get_unchecked(0, 1); - let az = *self.get_unchecked(0, 2); + let ax = *self.get_unchecked((0, 0)); + let ay = *self.get_unchecked((0, 1)); + let az = *self.get_unchecked((0, 2)); - let bx = *b.get_unchecked(0, 0); - let by = *b.get_unchecked(0, 1); - let bz = *b.get_unchecked(0, 2); + let bx = *b.get_unchecked((0, 0)); + let by = *b.get_unchecked((0, 1)); + let bz = *b.get_unchecked((0, 2)); - *res.get_unchecked_mut(0, 0) = ay * bz - az * by; - *res.get_unchecked_mut(0, 1) = az * bx - ax * bz; - *res.get_unchecked_mut(0, 2) = ax * by - ay * bx; + *res.get_unchecked_mut((0, 0)) = ay * bz - az * by; + *res.get_unchecked_mut((0, 1)) = az * bx - ax * bz; + *res.get_unchecked_mut((0, 2)) = ax * by - ay * bx; res } @@ -1264,67 +1455,6 @@ impl> Matrix { } } -impl> Matrix { - /// The squared L2 norm of this vector. - #[inline] - pub fn norm_squared(&self) -> N { - let mut res = N::zero(); - - for i in 0..self.ncols() { - let col = self.column(i); - res += col.dot(&col) - } - - res - } - - /// The L2 norm of this matrix. - #[inline] - pub fn norm(&self) -> N { - self.norm_squared().sqrt() - } - - /// A synonym for the norm of this matrix. - /// - /// Aka the length. - /// - /// This function is simply implemented as a call to `norm()` - #[inline] - pub fn magnitude(&self) -> N { - self.norm() - } - - /// A synonym for the squared norm of this matrix. - /// - /// Aka the squared length. - /// - /// This function is simply implemented as a call to `norm_squared()` - #[inline] - pub fn magnitude_squared(&self) -> N { - self.norm_squared() - } - - /// Returns a normalized version of this matrix. - #[inline] - pub fn normalize(&self) -> MatrixMN - where DefaultAllocator: Allocator { - self / self.norm() - } - - /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. - #[inline] - pub fn try_normalize(&self, min_norm: N) -> Option> - where DefaultAllocator: Allocator { - let n = self.norm(); - - if n <= min_norm { - None - } else { - Some(self / n) - } - } -} - impl> Vector { @@ -1399,32 +1529,6 @@ impl> Unit> { } } -impl> Matrix { - /// Normalizes this matrix in-place and returns its norm. - #[inline] - pub fn normalize_mut(&mut self) -> N { - let n = self.norm(); - *self /= n; - - n - } - - /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. - /// - /// If the normalization succeeded, returns the old normal of this matrix. - #[inline] - pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { - let n = self.norm(); - - if n <= min_norm { - None - } else { - *self /= n; - Some(n) - } - } -} - impl AbsDiffEq for Unit> where N: Scalar + AbsDiffEq, @@ -1484,3 +1588,24 @@ where self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } + +impl Hash for Matrix +where + N: Scalar + Hash, + R: Dim, + C: Dim, + S: Storage, +{ + fn hash(&self, state: &mut H) { + let (nrows, ncols) = self.shape(); + (nrows, ncols).hash(state); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + self.get_unchecked((i, j)).hash(state); + } + } + } + } +} diff --git a/src/base/matrix_alga.rs b/src/base/matrix_alga.rs index 7c454986..427cdc84 100644 --- a/src/base/matrix_alga.rs +++ b/src/base/matrix_alga.rs @@ -6,7 +6,7 @@ use num::{One, Zero}; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedAdd, ClosedMul, - ClosedNeg, Field, Identity, Inverse, JoinSemilattice, Lattice, MeetSemilattice, Module, + ClosedNeg, Field, Identity, TwoSidedInverse, JoinSemilattice, Lattice, MeetSemilattice, Module, Multiplicative, Real, RingCommutative, }; use alga::linear::{ @@ -45,18 +45,18 @@ where } } -impl Inverse for MatrixMN +impl TwoSidedInverse for MatrixMN where N: Scalar + ClosedNeg, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> MatrixMN { + fn two_sided_inverse(&self) -> MatrixMN { -self } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { *self = -self.clone() } } diff --git a/src/base/matrix_slice.rs b/src/base/matrix_slice.rs index a217332e..d26fffb6 100644 --- a/src/base/matrix_slice.rs +++ b/src/base/matrix_slice.rs @@ -4,9 +4,9 @@ use std::slice; use base::allocator::Allocator; use base::default_allocator::DefaultAllocator; -use base::dimension::{Dim, DimName, Dynamic, U1}; +use base::dimension::{Dim, DimName, Dynamic, U1, IsNotStaticOne}; use base::iter::MatrixIter; -use base::storage::{Owned, Storage, StorageMut}; +use base::storage::{Owned, Storage, StorageMut, ContiguousStorage, ContiguousStorageMut}; use base::{Matrix, Scalar}; macro_rules! slice_storage_impl( @@ -91,7 +91,8 @@ slice_storage_impl!("A mutable matrix data storage for mutable matrix slice. Onl impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Copy for SliceStorage<'a, N, R, C, RStride, CStride> -{} +{ +} impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone for SliceStorage<'a, N, R, C, RStride, CStride> @@ -146,8 +147,6 @@ macro_rules! storage_impl( } } - - #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator { @@ -199,6 +198,14 @@ unsafe impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu } } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorage for SliceStorage<'a, N, R, U1, U1, CStride> { } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorage for SliceStorageMut<'a, N, R, U1, U1, CStride> { } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorageMut for SliceStorageMut<'a, N, R, U1, U1, CStride> { } + +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage for SliceStorage<'a, N, R, C, U1, R> { } +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage for SliceStorageMut<'a, N, R, C, U1, R> { } +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorageMut for SliceStorageMut<'a, N, R, C, U1, R> { } + impl> Matrix { #[inline] fn assert_slice_index( @@ -859,3 +866,25 @@ impl> Matrix { self.slice_range_mut(.., cols) } } + + +impl<'a, N, R, C, RStride, CStride> From> +for MatrixSlice<'a, N, R, C, RStride, CStride> + where + N: Scalar, + R: Dim, + C: Dim, + RStride: Dim, + CStride: Dim, +{ + fn from(slice_mut: MatrixSliceMut<'a, N, R, C, RStride, CStride>) -> Self { + let data = SliceStorage { + ptr: slice_mut.data.ptr, + shape: slice_mut.data.shape, + strides: slice_mut.data.strides, + _phantoms: PhantomData, + }; + + unsafe { Matrix::from_data_statically_unchecked(data) } + } +} \ No newline at end of file diff --git a/src/base/mod.rs b/src/base/mod.rs index 2f573025..0ec6311c 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -18,16 +18,19 @@ mod construction; mod construction_slice; mod conversion; mod edition; +pub mod indexing; mod matrix; mod matrix_alga; -mod matrix_array; +mod array_storage; mod matrix_slice; #[cfg(any(feature = "std", feature = "alloc"))] -mod matrix_vec; +mod vec_storage; mod properties; mod scalar; mod swizzle; mod unit; +mod statistics; +mod norm; #[doc(hidden)] pub mod helper; @@ -35,13 +38,14 @@ pub mod helper; pub use self::matrix::*; pub use self::scalar::*; pub use self::unit::*; +pub use self::norm::*; pub use self::default_allocator::*; pub use self::dimension::*; pub use self::alias::*; pub use self::alias_slice::*; -pub use self::matrix_array::*; +pub use self::array_storage::*; pub use self::matrix_slice::*; #[cfg(any(feature = "std", feature = "alloc"))] -pub use self::matrix_vec::*; +pub use self::vec_storage::*; diff --git a/src/base/norm.rs b/src/base/norm.rs new file mode 100644 index 00000000..68fb88a1 --- /dev/null +++ b/src/base/norm.rs @@ -0,0 +1,238 @@ +use num::Signed; +use std::cmp::PartialOrd; + +use allocator::Allocator; +use ::{Real, Scalar}; +use storage::{Storage, StorageMut}; +use base::{DefaultAllocator, Matrix, Dim, MatrixMN}; +use constraint::{SameNumberOfRows, SameNumberOfColumns, ShapeConstraint}; + + +// FIXME: this should be be a trait on alga? +/// A trait for abstract matrix norms. +/// +/// This may be moved to the alga crate in the future. +pub trait Norm { + /// Apply this norm to the given matrix. + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage; + /// Use the metric induced by this norm to compute the metric distance between the two given matrices. + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns; +} + +/// Euclidean norm. +pub struct EuclideanNorm; +/// Lp norm. +pub struct LpNorm(pub i32); +/// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm. +pub struct UniformNorm; + +impl Norm for EuclideanNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.norm_squared().sqrt() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff * diff + }).sqrt() + } +} + +impl Norm for LpNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.fold(N::zero(), |a, b| { + a + b.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } +} + +impl Norm for UniformNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.amax() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let val = (a - b).abs(); + if val > acc { + val + } else { + acc + } + }) + } +} + + +impl> Matrix { + /// The squared L2 norm of this vector. + #[inline] + pub fn norm_squared(&self) -> N { + let mut res = N::zero(); + + for i in 0..self.ncols() { + let col = self.column(i); + res += col.dot(&col) + } + + res + } + + /// The L2 norm of this matrix. + /// + /// Use `.apply_norm` to apply a custom norm. + #[inline] + pub fn norm(&self) -> N { + self.norm_squared().sqrt() + } + + /// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm. + /// + /// Use `.apply_metric_distance` to apply a custom norm. + #[inline] + pub fn metric_distance(&self, rhs: &Matrix) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + self.apply_metric_distance(rhs, &EuclideanNorm) + } + + /// Uses the given `norm` to compute the norm of `self`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v = Vector3::new(1.0, 2.0, 3.0); + /// assert_eq!(v.apply_norm(&UniformNorm), 3.0); + /// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0); + /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm()); + /// ``` + #[inline] + pub fn apply_norm(&self, norm: &impl Norm) -> N { + norm.norm(self) + } + + /// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v1 = Vector3::new(1.0, 2.0, 3.0); + /// let v2 = Vector3::new(10.0, 20.0, 30.0); + /// + /// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm()); + /// ``` + #[inline] + pub fn apply_metric_distance(&self, rhs: &Matrix, norm: &impl Norm) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + norm.metric_distance(self,rhs) + } + + /// The Lp norm of this matrix. + #[inline] + pub fn lp_norm(&self, p: i32) -> N { + self.apply_norm(&LpNorm(p)) + } + + /// A synonym for the norm of this matrix. + /// + /// Aka the length. + /// + /// This function is simply implemented as a call to `norm()` + #[inline] + pub fn magnitude(&self) -> N { + self.norm() + } + + /// A synonym for the squared norm of this matrix. + /// + /// Aka the squared length. + /// + /// This function is simply implemented as a call to `norm_squared()` + #[inline] + pub fn magnitude_squared(&self) -> N { + self.norm_squared() + } + + /// Returns a normalized version of this matrix. + #[inline] + pub fn normalize(&self) -> MatrixMN + where DefaultAllocator: Allocator { + self / self.norm() + } + + /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. + #[inline] + pub fn try_normalize(&self, min_norm: N) -> Option> + where DefaultAllocator: Allocator { + let n = self.norm(); + + if n <= min_norm { + None + } else { + Some(self / n) + } + } +} + +impl> Matrix { + /// Normalizes this matrix in-place and returns its norm. + #[inline] + pub fn normalize_mut(&mut self) -> N { + let n = self.norm(); + *self /= n; + + n + } + + /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. + /// + /// If the normalization succeeded, returns the old normal of this matrix. + #[inline] + pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { + let n = self.norm(); + + if n <= min_norm { + None + } else { + *self /= n; + Some(n) + } + } +} diff --git a/src/base/ops.rs b/src/base/ops.rs index 14ec98f0..8167dd86 100644 --- a/src/base/ops.rs +++ b/src/base/ops.rs @@ -45,7 +45,7 @@ where "Matrix index out of bounds." ); - unsafe { self.get_unchecked(ij.0, ij.1) } + unsafe { self.get_unchecked((ij.0, ij.1)) } } } @@ -71,7 +71,7 @@ where "Matrix index out of bounds." ); - unsafe { self.get_unchecked_mut(ij.0, ij.1) } + unsafe { self.get_unchecked_mut((ij.0, ij.1)) } } } @@ -172,8 +172,8 @@ macro_rules! componentwise_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let val = self.get_unchecked(i, j).$method(*rhs.get_unchecked(i, j)); - *out.get_unchecked_mut(i, j) = val; + let val = self.get_unchecked((i, j)).$method(*rhs.get_unchecked((i, j))); + *out.get_unchecked_mut((i, j)) = val; } } } @@ -204,7 +204,7 @@ macro_rules! componentwise_binop_impl( for j in 0 .. rhs.ncols() { for i in 0 .. rhs.nrows() { unsafe { - self.get_unchecked_mut(i, j).$method_assign(*rhs.get_unchecked(i, j)) + self.get_unchecked_mut((i, j)).$method_assign(*rhs.get_unchecked((i, j))) } } } @@ -235,8 +235,8 @@ macro_rules! componentwise_binop_impl( for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { - let r = rhs.get_unchecked_mut(i, j); - *r = self.get_unchecked(i, j).$method(*r) + let r = rhs.get_unchecked_mut((i, j)); + *r = self.get_unchecked((i, j)).$method(*r) } } } @@ -448,7 +448,7 @@ macro_rules! componentwise_scalarop_impl( fn $method_assign(&mut self, rhs: N) { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { - unsafe { self.get_unchecked_mut(i, j).$method_assign(rhs) }; + unsafe { self.get_unchecked_mut((i, j)).$method_assign(rhs) }; } } } @@ -657,7 +657,7 @@ where for i in 0..ncols1 { for j in 0..ncols2 { let dot = self.column(i).dot(&rhs.column(j)); - unsafe { *out.get_unchecked_mut(i, j) = dot }; + unsafe { *out.get_unchecked_mut((i, j)) = dot }; } } } @@ -704,10 +704,10 @@ where for j2 in 0..ncols2.value() { for i1 in 0..nrows1.value() { unsafe { - let coeff = *self.get_unchecked(i1, j1); + let coeff = *self.get_unchecked((i1, j1)); for i2 in 0..nrows2.value() { - *data_res = coeff * *rhs.get_unchecked(i2, j2); + *data_res = coeff * *rhs.get_unchecked((i2, j2)); data_res = data_res.offset(1); } } @@ -761,7 +761,7 @@ where } impl> Matrix { - /// Returns the absolute value of the coefficient with the largest absolute value. + /// Returns the absolute value of the component with the largest absolute value. #[inline] pub fn amax(&self) -> N { let mut max = N::zero(); @@ -777,7 +777,7 @@ impl> Matri max } - /// Returns the absolute value of the coefficient with the smallest absolute value. + /// Returns the absolute value of the component with the smallest absolute value. #[inline] pub fn amin(&self) -> N { let mut it = self.iter(); @@ -796,4 +796,42 @@ impl> Matri min } + + /// Returns the component with the largest value. + #[inline] + pub fn max(&self) -> N { + let mut it = self.iter(); + let mut max = it + .next() + .expect("max: empty matrices not supported."); + + for e in it { + let ae = e; + + if ae > max { + max = ae; + } + } + + *max + } + + /// Returns the component with the smallest value. + #[inline] + pub fn min(&self) -> N { + let mut it = self.iter(); + let mut min = it + .next() + .expect("min: empty matrices not supported."); + + for e in it { + let ae = e; + + if ae < min { + min = ae; + } + } + + *min + } } diff --git a/src/base/statistics.rs b/src/base/statistics.rs new file mode 100644 index 00000000..11cc9e1c --- /dev/null +++ b/src/base/statistics.rs @@ -0,0 +1,309 @@ +use ::{Real, Dim, Matrix, VectorN, RowVectorN, DefaultAllocator, U1, VectorSliceN}; +use storage::Storage; +use allocator::Allocator; + +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] + pub fn compress_rows(&self, f: impl Fn(VectorSliceN) -> N) -> RowVectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { RowVectorN::new_uninitialized_generic(U1, ncols) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.get_unchecked_mut((0, i)) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector where each element is the result of the application of `f` on the + /// corresponding column of the original matrix. + /// + /// This is the same as `self.compress_rows(f).transpose()`. + #[inline] + pub fn compress_rows_tr(&self, f: impl Fn(VectorSliceN) -> N) -> VectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { VectorN::new_uninitialized_generic(ncols, U1) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.vget_unchecked_mut(i) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector resulting from the folding of `f` on each column of this matrix. + #[inline] + pub fn compress_columns(&self, init: VectorN, f: impl Fn(&mut VectorN, VectorSliceN)) -> VectorN + where DefaultAllocator: Allocator { + let mut res = init; + + for i in 0..self.ncols() { + f(&mut res, self.column(i)) + } + + res + } +} + +impl> Matrix { + /* + * + * Sum computation. + * + */ + /// The sum of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.sum(), 21.0); + /// ``` + #[inline] + pub fn sum(&self) -> N { + self.iter().cloned().fold(N::zero(), |a, b| a + b) + } + + /// The sum of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum(), RowVector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.sum()) + } + + /// The sum of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum_tr(), Vector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.sum()) + } + + /// The sum of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_sum(), Vector2::new(6.0, 15.0)); + /// ``` + #[inline] + pub fn column_sum(&self) -> VectorN + where DefaultAllocator: Allocator { + let nrows = self.data.shape().0; + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(N::one(), &col, N::one()) + }) + } + + /* + * + * Variance computation. + * + */ + /// The variance of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn variance(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + let val = self.iter().cloned().fold((N::zero(), N::zero()), |a, b| (a.0 + b * b, a.1 + b)); + let denom = N::one() / ::convert::<_, N>(self.len() as f64); + val.0 * denom - (val.1 * denom) * (val.1 * denom) + } + } + + /// The variance of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.variance()) + } + + /// The variance of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.variance()) + } + + /// The variance of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn column_variance(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + + let mut mean = self.column_mean(); + mean.apply(|e| -(e * e)); + + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(mean, |out, col| { + for i in 0..nrows.value() { + unsafe { + let val = col.vget_unchecked(i); + *out.vget_unchecked_mut(i) += denom * *val * *val + } + } + }) + } + + /* + * + * Mean computation. + * + */ + /// The mean of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.mean(), 3.5); + /// ``` + #[inline] + pub fn mean(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + self.sum() / ::convert(self.len() as f64) + } + } + + /// The mean of all the rows of this matrix. + /// + /// Use `.row_mean_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.mean()) + } + + /// The mean of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.mean()) + } + + /// The mean of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0)); + /// ``` + #[inline] + pub fn column_mean(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(denom, &col, N::one()) + }) + } +} diff --git a/src/base/storage.rs b/src/base/storage.rs index b96f69d0..0a07713b 100644 --- a/src/base/storage.rs +++ b/src/base/storage.rs @@ -34,7 +34,7 @@ pub type CStride = /// Note that `Self` must always have a number of elements compatible with the matrix length (given /// by `R` and `C` if they are known at compile-time). For example, implementors of this trait /// should **not** allow the user to modify the size of the underlying buffer with safe methods -/// (for example the `MatrixVec::data_mut` method is unsafe because the user could change the +/// (for example the `VecStorage::data_mut` method is unsafe because the user could change the /// vector's size so that it no longer contains enough elements: this will lead to UB. pub unsafe trait Storage: Debug + Sized { /// The static stride of this storage's rows. diff --git a/src/base/swizzle.rs b/src/base/swizzle.rs index e1908e35..b09316d4 100644 --- a/src/base/swizzle.rs +++ b/src/base/swizzle.rs @@ -3,64 +3,60 @@ use storage::Storage; use typenum::{self, Cmp, Greater}; macro_rules! impl_swizzle { - ($(where $BaseDim: ident: $name: ident() -> $Result: ident[$($i: expr),*]);*) => { + ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { $( - impl> Vector { - /// Builds a new vector from components of `self`. - #[inline] - pub fn $name(&self) -> $Result - where D::Value: Cmp { - $Result::new($(self[$i]),*) - } + impl> Vector + where D::Value: Cmp + { + $( + /// Builds a new vector from components of `self`. + #[inline] + pub fn $name(&self) -> $Result { + $Result::new($(self[$i]),*) + } + )* } )* } } impl_swizzle!( - where U0: xx() -> Vector2[0, 0]; - where U1: xy() -> Vector2[0, 1]; - where U2: xz() -> Vector2[0, 2]; - where U1: yx() -> Vector2[1, 0]; - where U1: yy() -> Vector2[1, 1]; - where U1: yz() -> Vector2[1, 2]; - where U2: zx() -> Vector2[2, 0]; - where U2: zy() -> Vector2[2, 1]; - where U2: zz() -> Vector2[2, 2]; + where U0: xx() -> Vector2[0, 0], + xxx() -> Vector3[0, 0, 0]; - where U0: xxx() -> Vector3[0, 0, 0]; - where U1: xxy() -> Vector3[0, 0, 1]; - where U2: xxz() -> Vector3[0, 0, 2]; + where U1: xy() -> Vector2[0, 1], + yx() -> Vector2[1, 0], + yy() -> Vector2[1, 1], + xxy() -> Vector3[0, 0, 1], + xyx() -> Vector3[0, 1, 0], + xyy() -> Vector3[0, 1, 1], + yxx() -> Vector3[1, 0, 0], + yxy() -> Vector3[1, 0, 1], + yyx() -> Vector3[1, 1, 0], + yyy() -> Vector3[1, 1, 1]; - where U1: xyx() -> Vector3[0, 1, 0]; - where U1: xyy() -> Vector3[0, 1, 1]; - where U2: xyz() -> Vector3[0, 1, 2]; - - where U2: xzx() -> Vector3[0, 2, 0]; - where U2: xzy() -> Vector3[0, 2, 1]; - where U2: xzz() -> Vector3[0, 2, 2]; - - where U1: yxx() -> Vector3[1, 0, 0]; - where U1: yxy() -> Vector3[1, 0, 1]; - where U2: yxz() -> Vector3[1, 0, 2]; - - where U1: yyx() -> Vector3[1, 1, 0]; - where U1: yyy() -> Vector3[1, 1, 1]; - where U2: yyz() -> Vector3[1, 1, 2]; - - where U2: yzx() -> Vector3[1, 2, 0]; - where U2: yzy() -> Vector3[1, 2, 1]; - where U2: yzz() -> Vector3[1, 2, 2]; - - where U2: zxx() -> Vector3[2, 0, 0]; - where U2: zxy() -> Vector3[2, 0, 1]; - where U2: zxz() -> Vector3[2, 0, 2]; - - where U2: zyx() -> Vector3[2, 1, 0]; - where U2: zyy() -> Vector3[2, 1, 1]; - where U2: zyz() -> Vector3[2, 1, 2]; - - where U2: zzx() -> Vector3[2, 2, 0]; - where U2: zzy() -> Vector3[2, 2, 1]; - where U2: zzz() -> Vector3[2, 2, 2] + where U2: xz() -> Vector2[0, 2], + yz() -> Vector2[1, 2], + zx() -> Vector2[2, 0], + zy() -> Vector2[2, 1], + zz() -> Vector2[2, 2], + xxz() -> Vector3[0, 0, 2], + xyz() -> Vector3[0, 1, 2], + xzx() -> Vector3[0, 2, 0], + xzy() -> Vector3[0, 2, 1], + xzz() -> Vector3[0, 2, 2], + yxz() -> Vector3[1, 0, 2], + yyz() -> Vector3[1, 1, 2], + yzx() -> Vector3[1, 2, 0], + yzy() -> Vector3[1, 2, 1], + yzz() -> Vector3[1, 2, 2], + zxx() -> Vector3[2, 0, 0], + zxy() -> Vector3[2, 0, 1], + zxz() -> Vector3[2, 0, 2], + zyx() -> Vector3[2, 1, 0], + zyy() -> Vector3[2, 1, 1], + zyz() -> Vector3[2, 1, 2], + zzx() -> Vector3[2, 2, 0], + zzy() -> Vector3[2, 2, 1], + zzz() -> Vector3[2, 2, 2]; ); diff --git a/src/base/unit.rs b/src/base/unit.rs index 1c2fa1e2..af400389 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -15,7 +15,7 @@ use alga::linear::NormedSpace; /// A wrapper that ensures the underlying algebraic entity has a unit norm. /// -/// Use `.as_ref()` or `.unwrap()` to obtain the underlying value by-reference or by-move. +/// Use `.as_ref()` or `.into_inner()` to obtain the underlying value by-reference or by-move. #[repr(transparent)] #[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)] pub struct Unit { @@ -113,6 +113,14 @@ impl Unit { /// Retrieves the underlying value. #[inline] + pub fn into_inner(self) -> T { + self.value + } + + /// Retrieves the underlying value. + /// Deprecated: use [Unit::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> T { self.value } @@ -143,7 +151,7 @@ where T::Field: RelativeEq { #[inline] fn to_superset(&self) -> T { - self.clone().unwrap() + self.clone().into_inner() } #[inline] diff --git a/src/base/matrix_vec.rs b/src/base/vec_storage.rs similarity index 76% rename from src/base/matrix_vec.rs rename to src/base/vec_storage.rs index 761e7fa2..4a852f25 100644 --- a/src/base/matrix_vec.rs +++ b/src/base/vec_storage.rs @@ -1,6 +1,5 @@ #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; -use std::ops::Deref; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; @@ -24,21 +23,25 @@ use abomonation::Abomonation; #[repr(C)] #[derive(Eq, Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -pub struct MatrixVec { +pub struct VecStorage { data: Vec, nrows: R, ncols: C, } -impl MatrixVec { +#[deprecated(note="renamed to `VecStorage`")] +/// Renamed to [VecStorage]. +pub type MatrixVec = VecStorage; + +impl VecStorage { /// Creates a new dynamic matrix data storage from the given vector and shape. #[inline] - pub fn new(nrows: R, ncols: C, data: Vec) -> MatrixVec { + pub fn new(nrows: R, ncols: C, data: Vec) -> VecStorage { assert!( nrows.value() * ncols.value() == data.len(), "Data storage buffer dimension mismatch." ); - MatrixVec { + VecStorage { data: data, nrows: nrows, ncols: ncols, @@ -47,15 +50,16 @@ impl MatrixVec { /// The underlying data storage. #[inline] - pub fn data(&self) -> &Vec { + pub fn as_vec(&self) -> &Vec { &self.data } /// The underlying mutable data storage. /// - /// This is unsafe because this may cause UB if the vector is modified by the user. + /// This is unsafe because this may cause UB if the size of the vector is changed + /// by the user. #[inline] - pub unsafe fn data_mut(&mut self) -> &mut Vec { + pub unsafe fn as_vec_mut(&mut self) -> &mut Vec { &mut self.data } @@ -77,14 +81,18 @@ impl MatrixVec { self.data } + + /// The number of elements on the underlying vector. + #[inline] + pub fn len(&self) -> usize { + self.data.len() + } } -impl Deref for MatrixVec { - type Target = Vec; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.data +impl Into> for VecStorage +{ + fn into(self) -> Vec { + self.data } } @@ -94,7 +102,7 @@ impl Deref for MatrixVec { * Dynamic − Dynamic * */ -unsafe impl Storage for MatrixVec +unsafe impl Storage for VecStorage where DefaultAllocator: Allocator { type RStride = U1; @@ -134,11 +142,11 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } -unsafe impl Storage for MatrixVec +unsafe impl Storage for VecStorage where DefaultAllocator: Allocator { type RStride = U1; @@ -178,7 +186,7 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } @@ -187,7 +195,7 @@ where DefaultAllocator: Allocator * StorageMut, ContiguousStorage. * */ -unsafe impl StorageMut for MatrixVec +unsafe impl StorageMut for VecStorage where DefaultAllocator: Allocator { #[inline] @@ -201,13 +209,13 @@ where DefaultAllocator: Allocator } } -unsafe impl ContiguousStorage for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorage for VecStorage where DefaultAllocator: Allocator {} -unsafe impl ContiguousStorageMut for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorageMut for VecStorage where DefaultAllocator: Allocator {} -unsafe impl StorageMut for MatrixVec +unsafe impl StorageMut for VecStorage where DefaultAllocator: Allocator { #[inline] @@ -222,7 +230,7 @@ where DefaultAllocator: Allocator } #[cfg(feature = "abomonation-serialize")] -impl Abomonation for MatrixVec { +impl Abomonation for VecStorage { unsafe fn entomb(&self, writer: &mut W) -> IOResult<()> { self.data.entomb(writer) } @@ -236,21 +244,21 @@ impl Abomonation for MatrixVec { } } -unsafe impl ContiguousStorage for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorage for VecStorage where DefaultAllocator: Allocator {} -unsafe impl ContiguousStorageMut for MatrixVec where DefaultAllocator: Allocator +unsafe impl ContiguousStorageMut for VecStorage where DefaultAllocator: Allocator {} -impl Extend for MatrixVec +impl Extend for VecStorage { - /// Extends the number of columns of the `MatrixVec` with elements + /// Extends the number of columns of the `VecStorage` with elements /// from the given iterator. /// /// # Panics /// This function panics if the number of elements yielded by the /// given iterator is not a multiple of the number of rows of the - /// `MatrixVec`. + /// `VecStorage`. fn extend>(&mut self, iter: I) { self.data.extend(iter); @@ -260,7 +268,7 @@ impl Extend for MatrixVec } } -impl Extend> for MatrixVec +impl Extend> for VecStorage where N: Scalar, R: Dim, @@ -268,13 +276,13 @@ where SV: Storage, ShapeConstraint: SameNumberOfRows, { - /// Extends the number of columns of the `MatrixVec` with vectors + /// Extends the number of columns of the `VecStorage` with vectors /// from the given iterator. /// /// # Panics /// This function panics if the number of rows of each `Vector` /// yielded by the iterator is not equal to the number of rows - /// of this `MatrixVec`. + /// of this `VecStorage`. fn extend>>(&mut self, iter: I) { let nrows = self.nrows.value(); @@ -289,9 +297,9 @@ where } } -impl Extend for MatrixVec +impl Extend for VecStorage { - /// Extends the number of rows of the `MatrixVec` with elements + /// Extends the number of rows of the `VecStorage` with elements /// from the given iterator. fn extend>(&mut self, iter: I) { diff --git a/src/debug/random_orthogonal.rs b/src/debug/random_orthogonal.rs index da06805b..6d393a24 100644 --- a/src/debug/random_orthogonal.rs +++ b/src/debug/random_orthogonal.rs @@ -48,6 +48,7 @@ where Owned: Clone + Send, { fn arbitrary(g: &mut G) -> Self { + use rand::Rng; let dim = D::try_to_usize().unwrap_or(g.gen_range(1, 50)); Self::new(D::from_usize(dim), || N::arbitrary(g)) } diff --git a/src/debug/random_sdp.rs b/src/debug/random_sdp.rs index c78d1fd1..7835c775 100644 --- a/src/debug/random_sdp.rs +++ b/src/debug/random_sdp.rs @@ -49,6 +49,7 @@ where Owned: Clone + Send, { fn arbitrary(g: &mut G) -> Self { + use rand::Rng; let dim = D::try_to_usize().unwrap_or(g.gen_range(1, 50)); Self::new(D::from_usize(dim), || N::arbitrary(g)) } diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index 7ad3d4d9..1097be47 100644 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -113,7 +113,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); @@ -167,7 +166,7 @@ where DefaultAllocator: Allocator /// ``` #[inline] pub fn inverse_mut(&mut self) { - self.rotation.inverse_mut(); + self.rotation.two_sided_inverse_mut(); self.translation.inverse_mut(); self.translation.vector = self.rotation.transform_vector(&self.translation.vector); } @@ -197,7 +196,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::PI / 6.0); @@ -220,7 +218,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); @@ -272,7 +269,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Vector2, Matrix3}; /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6); diff --git a/src/geometry/isometry_alga.rs b/src/geometry/isometry_alga.rs index 7decea8e..fee8b63d 100644 --- a/src/geometry/isometry_alga.rs +++ b/src/geometry/isometry_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Isometry as AlgaIsometry; use alga::linear::{ @@ -30,18 +30,18 @@ where } } -impl Inverse for Isometry +impl TwoSidedInverse for Isometry where R: Rotation>, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/isometry_construction.rs b/src/geometry/isometry_construction.rs index 4d0702f0..5a402c0a 100644 --- a/src/geometry/isometry_construction.rs +++ b/src/geometry/isometry_construction.rs @@ -16,7 +16,7 @@ use base::{DefaultAllocator, Vector2, Vector3}; use geometry::{ Isometry, Point, Point3, Rotation, Rotation2, Rotation3, Translation, UnitComplex, - UnitQuaternion, + UnitQuaternion, Translation2, Translation3 }; impl>> Isometry @@ -49,7 +49,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::PI); @@ -130,6 +129,18 @@ impl Isometry> { Rotation::::new(angle), ) } + + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N) -> Self { + Self::new(Vector2::new(x, y), N::zero()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(angle: N) -> Self { + Self::new(Vector2::zeros(), angle) + } } impl Isometry> { @@ -153,6 +164,18 @@ impl Isometry> { UnitComplex::from_angle(angle), ) } + + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N) -> Self { + Self::from_parts(Translation2::new(x, y), UnitComplex::identity()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(angle: N) -> Self { + Self::new(Vector2::zeros(), angle) + } } // 3D rotation. @@ -165,7 +188,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -191,6 +213,18 @@ macro_rules! isometry_construction_impl( $RotId::<$($RotParams),*>::from_scaled_axis(axisangle)) } + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N, z: N) -> Self { + Self::from_parts(Translation3::new(x, y, z), $RotId::identity()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(axisangle: Vector3) -> Self { + Self::new(Vector3::zeros(), axisangle) + } + /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// @@ -206,7 +240,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -214,23 +247,32 @@ macro_rules! isometry_construction_impl( /// let up = Vector3::y(); /// /// // Isometry with its rotation part represented as a UnitQuaternion - /// let iso = Isometry3::new_observer_frame(&eye, &target, &up); + /// let iso = Isometry3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). - /// let iso = IsometryMatrix3::new_observer_frame(&eye, &target, &up); + /// let iso = IsometryMatrix3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Self::from_parts( Translation::from(eye.coords.clone()), - $RotId::new_observer_frame(&(target - eye), up)) + $RotId::face_towards(&(target - eye), up)) + } + + /// Deprecated: Use [Isometry::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frame(eye: &Point3, + target: &Point3, + up: &Vector3) + -> Self { + Self::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. @@ -249,7 +291,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -293,7 +334,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); diff --git a/src/geometry/isometry_ops.rs b/src/geometry/isometry_ops.rs index a3608a63..6a4b921e 100644 --- a/src/geometry/isometry_ops.rs +++ b/src/geometry/isometry_ops.rs @@ -200,8 +200,8 @@ isometry_binop_assign_impl_all!( DivAssign, div_assign; self: Isometry, rhs: R; // FIXME: don't invert explicitly? - [val] => *self *= rhs.inverse(); - [ref] => *self *= rhs.inverse(); + [val] => *self *= rhs.two_sided_inverse(); + [ref] => *self *= rhs.two_sided_inverse(); ); // Isometry × R diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index af712461..9e25dc29 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -37,6 +37,7 @@ mod translation_alga; mod translation_alias; mod translation_construction; mod translation_conversion; +mod translation_coordinates; mod translation_ops; mod isometry; @@ -53,6 +54,8 @@ mod similarity_construction; mod similarity_conversion; mod similarity_ops; +mod swizzle; + mod transform; mod transform_alga; mod transform_alias; diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index eb3ce0fa..c6c5628b 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -69,7 +69,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// // Check this projection actually transforms the view cuboid into the double-unit cube. @@ -170,7 +169,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let inv = proj.inverse(); @@ -271,7 +269,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let expected = Matrix4::new( @@ -280,9 +277,17 @@ impl Orthographic3 { /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, /// 0.0, 0.0, 0.0, 1.0 /// ); - /// assert_eq!(proj.unwrap(), expected); + /// assert_eq!(proj.into_inner(), expected); /// ``` #[inline] + pub fn into_inner(self) -> Matrix4 { + self.matrix + } + + /// Retrieves the underlying homogeneous matrix. + /// Deprecated: Use [Orthographic3::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } @@ -291,7 +296,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.left(), 1.0, epsilon = 1.0e-6); @@ -308,7 +312,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.right(), 10.0, epsilon = 1.0e-6); @@ -325,7 +328,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.bottom(), 2.0, epsilon = 1.0e-6); @@ -342,7 +344,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.top(), 20.0, epsilon = 1.0e-6); @@ -359,7 +360,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.znear(), 0.1, epsilon = 1.0e-6); @@ -376,7 +376,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.zfar(), 1000.0, epsilon = 1.0e-6); @@ -395,7 +394,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -431,7 +429,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -470,7 +467,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Vector3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -496,7 +492,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left(2.0); @@ -516,7 +511,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_right(15.0); @@ -536,7 +530,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom(8.0); @@ -556,7 +549,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_top(15.0); @@ -576,7 +568,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear(8.0); @@ -596,7 +587,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_zfar(15.0); @@ -616,7 +606,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left_and_right(7.0, 70.0); @@ -642,7 +631,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom_and_top(7.0, 70.0); @@ -668,7 +656,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear_and_zfar(50.0, 5000.0); @@ -725,6 +712,6 @@ where Matrix4: Send impl From> for Matrix4 { #[inline] fn from(orth: Orthographic3) -> Self { - orth.unwrap() + orth.into_inner() } } diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index 77642554..b7e0d8ea 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -141,6 +141,14 @@ impl Perspective3 { /// Retrieves the underlying homogeneous matrix. #[inline] + pub fn into_inner(self) -> Matrix4 { + self.matrix + } + + /// Retrieves the underlying homogeneous matrix. + /// Deprecated: Use [Perspective3::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } @@ -279,6 +287,6 @@ impl Arbitrary for Perspective3 { impl From> for Matrix4 { #[inline] fn from(orth: Perspective3) -> Self { - orth.unwrap() + orth.into_inner() } } diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index fc82e061..96e4f19a 100644 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -114,7 +114,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let q_normalized = q.normalize(); @@ -150,7 +149,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let inv_q = q.try_inverse(); @@ -240,7 +238,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6); @@ -258,7 +255,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6); @@ -345,7 +341,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(2.0, 5.0, 0.0, 0.0); /// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6) @@ -364,7 +359,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5) @@ -380,7 +374,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp_eps(1.0e-6), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5); @@ -410,7 +403,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6); @@ -476,7 +468,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// @@ -506,7 +497,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// q.normalize_mut(); @@ -672,7 +662,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -691,7 +680,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -747,7 +735,7 @@ impl UnitQuaternion { Unit::new_unchecked(Quaternion::from( Unit::new_unchecked(self.coords) .slerp(&Unit::new_unchecked(other.coords), t) - .unwrap(), + .into_inner(), )) } @@ -771,7 +759,7 @@ impl UnitQuaternion { { Unit::new_unchecked(self.coords) .try_slerp(&Unit::new_unchecked(other.coords), t, epsilon) - .map(|q| Unit::new_unchecked(Quaternion::from(q.unwrap()))) + .map(|q| Unit::new_unchecked(Quaternion::from(q.into_inner()))) } /// Compute the conjugate of this unit quaternion in-place. @@ -785,7 +773,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let mut rot = UnitQuaternion::new(axisangle); @@ -828,7 +815,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = UnitQuaternion::new(axisangle); @@ -837,7 +823,7 @@ impl UnitQuaternion { #[inline] pub fn scaled_axis(&self) -> Vector3 { if let Some(axis) = self.axis() { - axis.unwrap() * self.angle() + axis.into_inner() * self.angle() } else { Vector3::zero() } @@ -885,7 +871,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let q = UnitQuaternion::new(axisangle); @@ -894,7 +879,7 @@ impl UnitQuaternion { #[inline] pub fn ln(&self) -> Quaternion { if let Some(v) = self.axis() { - Quaternion::from_parts(N::zero(), v.unwrap() * self.angle()) + Quaternion::from_parts(N::zero(), v.into_inner() * self.angle()) } else { Quaternion::zero() } @@ -908,7 +893,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -932,7 +916,6 @@ impl UnitQuaternion { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix3}; /// let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); @@ -990,7 +973,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -1009,7 +991,6 @@ impl UnitQuaternion { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix4}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); @@ -1029,7 +1010,7 @@ impl UnitQuaternion { impl fmt::Display for UnitQuaternion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(axis) = self.axis() { - let axis = axis.unwrap(); + let axis = axis.into_inner(); write!( f, "UnitQuaternion angle: {} − axis: ({}, {}, {})", diff --git a/src/geometry/quaternion_alga.rs b/src/geometry/quaternion_alga.rs index fe1a33b8..529d27f4 100644 --- a/src/geometry/quaternion_alga.rs +++ b/src/geometry/quaternion_alga.rs @@ -2,7 +2,7 @@ use num::Zero; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, - AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, Inverse, Module, + AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, TwoSidedInverse, Module, Multiplicative, Real, }; use alga::linear::{ @@ -42,9 +42,9 @@ impl AbstractMagma for Quaternion { } } -impl Inverse for Quaternion { +impl TwoSidedInverse for Quaternion { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { -self } } @@ -173,14 +173,14 @@ impl AbstractMagma for UnitQuaternion { } } -impl Inverse for UnitQuaternion { +impl TwoSidedInverse for UnitQuaternion { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs index 65f206ef..257cb719 100644 --- a/src/geometry/quaternion_construction.rs +++ b/src/geometry/quaternion_construction.rs @@ -76,7 +76,7 @@ impl Quaternion { where SB: Storage { let rot = UnitQuaternion::::from_axis_angle(&axis, theta * ::convert(2.0f64)); - rot.unwrap() * scale + rot.into_inner() * scale } /// The quaternion multiplicative identity. @@ -166,7 +166,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axis = Vector3::y_axis(); @@ -208,7 +207,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -237,7 +235,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, UnitQuaternion, Vector3}; /// let axis = Vector3::y_axis(); /// let angle = 0.1; @@ -302,7 +299,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -325,7 +321,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -361,7 +356,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); @@ -387,7 +381,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); @@ -446,22 +439,31 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// - /// let q = UnitQuaternion::new_observer_frame(&dir, &up); + /// let q = UnitQuaternion::face_towards(&dir, &up); /// assert_relative_eq!(q * Vector3::z(), dir.normalize()); /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::from_rotation_matrix(&Rotation::::new_observer_frame(dir, up)) + Self::from_rotation_matrix(&Rotation::::face_towards(dir, up)) + } + + /// Deprecated: Use [UnitQuaternion::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) } /// Builds a right-handed look-at view matrix without translation. @@ -478,7 +480,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -493,7 +494,7 @@ impl UnitQuaternion { SB: Storage, SC: Storage, { - Self::new_observer_frame(&-dir, up).inverse() + Self::face_towards(&-dir, up).inverse() } /// Builds a left-handed look-at view matrix without translation. @@ -510,7 +511,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -525,7 +525,7 @@ impl UnitQuaternion { SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. @@ -535,7 +535,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -565,7 +564,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -596,7 +594,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -625,7 +622,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -693,15 +689,16 @@ where #[cfg(test)] mod tests { + extern crate rand_xorshift; use super::*; - use rand::{self, SeedableRng}; + use rand::SeedableRng; #[test] fn random_unit_quats_are_unit() { - let mut rng = rand::prng::XorShiftRng::from_seed([0xAB; 16]); + let mut rng = rand_xorshift::XorShiftRng::from_seed([0xAB; 16]); for _ in 0..1000 { let x = rng.gen::>(); - assert!(relative_eq!(x.unwrap().norm(), 1.0)) + assert!(relative_eq!(x.into_inner().norm(), 1.0)) } } } diff --git a/src/geometry/quaternion_conversion.rs b/src/geometry/quaternion_conversion.rs index 0f8a0d68..c2d70726 100644 --- a/src/geometry/quaternion_conversion.rs +++ b/src/geometry/quaternion_conversion.rs @@ -228,7 +228,7 @@ impl From> for Matrix4 { impl From> for Matrix3 { #[inline] fn from(q: UnitQuaternion) -> Matrix3 { - q.to_rotation_matrix().unwrap() + q.to_rotation_matrix().into_inner() } } diff --git a/src/geometry/quaternion_ops.rs b/src/geometry/quaternion_ops.rs index c4ccdf13..842b4edd 100644 --- a/src/geometry/quaternion_ops.rs +++ b/src/geometry/quaternion_ops.rs @@ -469,7 +469,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitQuaternion, rhs: Unit>, Output = Unit> => U3, U4; - Unit::new_unchecked(self * rhs.unwrap()); + Unit::new_unchecked(self * rhs.into_inner()); 'a); quaternion_op_impl!( @@ -485,7 +485,7 @@ quaternion_op_impl!( (U4, U1), (U3, U1) for SB: Storage ; self: UnitQuaternion, rhs: Unit>, Output = Unit> => U3, U4; - Unit::new_unchecked(self * rhs.unwrap()); + Unit::new_unchecked(self * rhs.into_inner()); ); macro_rules! scalar_op_impl( diff --git a/src/geometry/reflection.rs b/src/geometry/reflection.rs index bd4da0cd..fa08dcdd 100644 --- a/src/geometry/reflection.rs +++ b/src/geometry/reflection.rs @@ -20,7 +20,7 @@ impl> Reflection { /// represents a plane that passes through the origin. pub fn new(axis: Unit>, bias: N) -> Reflection { Reflection { - axis: axis.unwrap(), + axis: axis.into_inner(), bias: bias, } } diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index f53668c7..6c50230e 100644 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -154,7 +154,7 @@ where DefaultAllocator: Allocator /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix2, Matrix3}; /// # use std::f32; /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); - /// let mat = rot.unwrap(); + /// let mat = rot.into_inner(); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); @@ -162,12 +162,20 @@ where DefaultAllocator: Allocator /// /// /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); - /// let mat = rot.unwrap(); + /// let mat = rot.into_inner(); /// let expected = Matrix2::new(0.8660254, -0.5, /// 0.5, 0.8660254); /// assert_eq!(mat, expected); /// ``` #[inline] + pub fn into_inner(self) -> MatrixN { + self.matrix + } + + /// Unwraps the underlying matrix. + /// Deprecated: Use [Rotation::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> MatrixN { self.matrix } @@ -201,6 +209,9 @@ where DefaultAllocator: Allocator D: DimNameAdd, DefaultAllocator: Allocator, DimNameSum>, { + // We could use `MatrixN::to_homogeneous()` here, but that would imply + // adding the additional traits `DimAdd` and `IsNotStaticOne`. Maybe + // these things will get nicer once specialization lands in Rust. let mut res = MatrixN::>::identity(); res.fixed_slice_mut::(0, 0).copy_from(&self.matrix); @@ -246,7 +257,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let tr_rot = rot.transpose(); @@ -270,7 +280,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let inv = rot.inverse(); @@ -294,7 +303,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut tr_rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); @@ -322,7 +330,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut inv = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); diff --git a/src/geometry/rotation_alga.rs b/src/geometry/rotation_alga.rs index b3bf7477..18c47b41 100644 --- a/src/geometry/rotation_alga.rs +++ b/src/geometry/rotation_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ self, AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, @@ -27,16 +27,16 @@ where DefaultAllocator: Allocator } } -impl Inverse for Rotation +impl TwoSidedInverse for Rotation where DefaultAllocator: Allocator { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.transpose() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.transpose_mut() } } diff --git a/src/geometry/rotation_conversion.rs b/src/geometry/rotation_conversion.rs index ecabb71f..3b574041 100644 --- a/src/geometry/rotation_conversion.rs +++ b/src/geometry/rotation_conversion.rs @@ -227,7 +227,7 @@ impl From> for Matrix3 { impl From> for Matrix2 { #[inline] fn from(q: Rotation2) -> Matrix2 { - q.unwrap() + q.into_inner() } } @@ -241,6 +241,6 @@ impl From> for Matrix4 { impl From> for Matrix3 { #[inline] fn from(q: Rotation3) -> Matrix3 { - q.unwrap() + q.into_inner() } } diff --git a/src/geometry/rotation_ops.rs b/src/geometry/rotation_ops.rs index 94bf6da2..fae70921 100644 --- a/src/geometry/rotation_ops.rs +++ b/src/geometry/rotation_ops.rs @@ -46,9 +46,9 @@ md_impl_all!( Mul, mul; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation, Output = Rotation; - [val val] => Rotation::from_matrix_unchecked(self.unwrap() * right.unwrap()); - [ref val] => Rotation::from_matrix_unchecked(self.matrix() * right.unwrap()); - [val ref] => Rotation::from_matrix_unchecked(self.unwrap() * right.matrix()); + [val val] => Rotation::from_matrix_unchecked(self.into_inner() * right.into_inner()); + [ref val] => Rotation::from_matrix_unchecked(self.matrix() * right.into_inner()); + [val ref] => Rotation::from_matrix_unchecked(self.into_inner() * right.matrix()); [ref ref] => Rotation::from_matrix_unchecked(self.matrix() * right.matrix()); ); @@ -71,9 +71,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Matrix, Output = MatrixMN; - [val val] => self.unwrap() * right; + [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; - [val ref] => self.unwrap() * right; + [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); @@ -84,8 +84,8 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Matrix, right: Rotation, Output = MatrixMN; - [val val] => self * right.unwrap(); - [ref val] => self * right.unwrap(); + [val val] => self * right.into_inner(); + [ref val] => self * right.into_inner(); [val ref] => self * right.matrix(); [ref ref] => self * right.matrix(); ); @@ -112,9 +112,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Point, Output = Point; - [val val] => self.unwrap() * right; + [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; - [val ref] => self.unwrap() * right; + [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); @@ -125,9 +125,9 @@ md_impl_all!( where DefaultAllocator: Allocator where ShapeConstraint: AreMultipliable; self: Rotation, right: Unit>, Output = Unit>; - [val val] => Unit::new_unchecked(self.unwrap() * right.unwrap()); - [ref val] => Unit::new_unchecked(self.matrix() * right.unwrap()); - [val ref] => Unit::new_unchecked(self.unwrap() * right.as_ref()); + [val val] => Unit::new_unchecked(self.into_inner() * right.into_inner()); + [ref val] => Unit::new_unchecked(self.matrix() * right.into_inner()); + [val ref] => Unit::new_unchecked(self.into_inner() * right.as_ref()); [ref ref] => Unit::new_unchecked(self.matrix() * right.as_ref()); ); @@ -138,7 +138,7 @@ md_assign_impl_all!( MulAssign, mul_assign; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation; - [val] => self.matrix_mut_unchecked().mul_assign(right.unwrap()); + [val] => self.matrix_mut_unchecked().mul_assign(right.into_inner()); [ref] => self.matrix_mut_unchecked().mul_assign(right.matrix()); ); @@ -146,7 +146,7 @@ md_assign_impl_all!( DivAssign, div_assign; (D, D), (D, D) for D: DimName; self: Rotation, right: Rotation; - [val] => self.matrix_mut_unchecked().mul_assign(right.inverse().unwrap()); + [val] => self.matrix_mut_unchecked().mul_assign(right.inverse().into_inner()); [ref] => self.matrix_mut_unchecked().mul_assign(right.inverse().matrix()); ); @@ -160,7 +160,7 @@ md_assign_impl_all!( MulAssign, mul_assign; (R1, C1), (C1, C1) for R1: DimName, C1: DimName; self: MatrixMN, right: Rotation; - [val] => self.mul_assign(right.unwrap()); + [val] => self.mul_assign(right.into_inner()); [ref] => self.mul_assign(right.matrix()); ); @@ -168,6 +168,6 @@ md_assign_impl_all!( DivAssign, div_assign; (R1, C1), (C1, C1) for R1: DimName, C1: DimName; self: MatrixMN, right: Rotation; - [val] => self.mul_assign(right.inverse().unwrap()); + [val] => self.mul_assign(right.inverse().into_inner()); [ref] => self.mul_assign(right.inverse().matrix()); ); diff --git a/src/geometry/rotation_specialization.rs b/src/geometry/rotation_specialization.rs index cdb7aed7..35ae1ea2 100644 --- a/src/geometry/rotation_specialization.rs +++ b/src/geometry/rotation_specialization.rs @@ -27,7 +27,6 @@ impl Rotation2 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation2, Point2}; /// let rot = Rotation2::new(f32::consts::FRAC_PI_2); @@ -56,7 +55,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -79,7 +77,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -107,9 +104,10 @@ impl Rotation2 { /// /// # Example /// ``` + /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(1.78); - /// assert_eq!(rot.angle(), 1.78); + /// assert_relative_eq!(rot.angle(), 1.78); /// ``` #[inline] pub fn angle(&self) -> N { @@ -121,7 +119,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); @@ -139,7 +136,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); @@ -158,10 +154,11 @@ impl Rotation2 { /// /// # Example /// ``` + /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(0.78); /// let pow = rot.powf(2.0); - /// assert_eq!(pow.angle(), 2.0 * 0.78); + /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] pub fn powf(&self, n: N) -> Rotation2 { @@ -213,7 +210,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -241,7 +237,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -265,7 +260,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axis = Vector3::y_axis(); @@ -318,7 +312,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -359,7 +352,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -370,12 +362,12 @@ impl Rotation3 { pub fn euler_angles(&self) -> (N, N, N) { // Implementation informed by "Computing Euler angles from a rotation matrix", by Gregory G. Slabaugh // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.371.6578 - if self[(2, 0)].abs() != N::one() { + if self[(2, 0)].abs() < N::one() { let yaw = -self[(2, 0)].asin(); let roll = (self[(2, 1)] / yaw.cos()).atan2(self[(2, 2)] / yaw.cos()); let pitch = (self[(1, 0)] / yaw.cos()).atan2(self[(0, 0)] / yaw.cos()); (roll, yaw, pitch) - } else if self[(2, 0)] == -N::one() { + } else if self[(2, 0)] <= -N::one() { (self[(0, 1)].atan2(self[(0, 2)]), N::frac_pi_2(), N::zero()) } else { ( @@ -399,17 +391,16 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// - /// let rot = Rotation3::new_observer_frame(&dir, &up); + /// let rot = Rotation3::face_towards(&dir, &up); /// assert_relative_eq!(rot * Vector3::z(), dir.normalize()); /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, @@ -423,6 +414,16 @@ impl Rotation3 { )) } + /// Deprecated: Use [Rotation3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) + } + /// Builds a right-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **negative** `z` axis. @@ -437,7 +438,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -452,7 +452,7 @@ impl Rotation3 { SB: Storage, SC: Storage, { - Self::new_observer_frame(&dir.neg(), up).inverse() + Self::face_towards(&dir.neg(), up).inverse() } /// Builds a left-handed look-at view matrix without translation. @@ -469,7 +469,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -484,7 +483,7 @@ impl Rotation3 { SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// The rotation matrix required to align `a` and `b` but with its angle. @@ -494,7 +493,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -517,7 +515,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -562,7 +559,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Rotation3, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = Rotation3::from_axis_angle(&axis, 1.78); @@ -580,7 +576,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -607,7 +602,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = Rotation3::new(axisangle); @@ -616,7 +610,7 @@ impl Rotation3 { #[inline] pub fn scaled_axis(&self) -> Vector3 { if let Some(axis) = self.axis() { - axis.unwrap() * self.angle() + axis.into_inner() * self.angle() } else { Vector::zero() } @@ -629,7 +623,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -656,7 +649,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -674,7 +666,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -692,7 +683,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; diff --git a/src/geometry/similarity_alga.rs b/src/geometry/similarity_alga.rs index c416cad8..e8a6b154 100644 --- a/src/geometry/similarity_alga.rs +++ b/src/geometry/similarity_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Similarity as AlgaSimilarity; use alga::linear::{AffineTransformation, ProjectiveTransformation, Rotation, Transformation}; @@ -27,18 +27,18 @@ where } } -impl Inverse for Similarity +impl TwoSidedInverse for Similarity where R: Rotation>, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/similarity_construction.rs b/src/geometry/similarity_construction.rs index ccb39866..47bb46c7 100644 --- a/src/geometry/similarity_construction.rs +++ b/src/geometry/similarity_construction.rs @@ -86,7 +86,6 @@ where /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); @@ -135,7 +134,6 @@ impl Similarity> { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{SimilarityMatrix2, Vector2, Point2}; /// let sim = SimilarityMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); @@ -159,7 +157,6 @@ impl Similarity> { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity2, Vector2, Point2}; /// let sim = Similarity2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); @@ -187,7 +184,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -227,7 +223,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -235,22 +230,32 @@ macro_rules! similarity_construction_impl( /// let up = Vector3::y(); /// /// // Similarity with its rotation part represented as a UnitQuaternion - /// let sim = Similarity3::new_observer_frame(&eye, &target, &up, 3.0); + /// let sim = Similarity3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). - /// let sim = SimilarityMatrix3::new_observer_frame(&eye, &target, &up, 3.0); + /// let sim = SimilarityMatrix3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3, scaling: N) -> Self { - Self::from_isometry(Isometry::<_, U3, $Rot>::new_observer_frame(eye, target, up), scaling) + Self::from_isometry(Isometry::<_, U3, $Rot>::face_towards(eye, target, up), scaling) + } + + /// Deprecated: Use [SimilarityMatrix3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(eye: &Point3, + target: &Point3, + up: &Vector3, + scaling: N) + -> Self { + Self::face_towards(eye, target, up, scaling) } /// Builds a right-handed look-at view matrix including scaling factor. @@ -268,7 +273,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -307,7 +311,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); diff --git a/src/geometry/similarity_ops.rs b/src/geometry/similarity_ops.rs index 57fdc05f..081e5133 100644 --- a/src/geometry/similarity_ops.rs +++ b/src/geometry/similarity_ops.rs @@ -222,8 +222,8 @@ similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: R; // FIXME: don't invert explicitly? - [val] => *self *= rhs.inverse(); - [ref] => *self *= rhs.inverse(); + [val] => *self *= rhs.two_sided_inverse(); + [ref] => *self *= rhs.two_sided_inverse(); ); // Similarity × R diff --git a/src/geometry/swizzle.rs b/src/geometry/swizzle.rs new file mode 100644 index 00000000..20c8dffd --- /dev/null +++ b/src/geometry/swizzle.rs @@ -0,0 +1,65 @@ +use base::allocator::Allocator; +use base::{DefaultAllocator, DimName, Scalar}; +use geometry::{Point, Point2, Point3}; +use typenum::{self, Cmp, Greater}; + +macro_rules! impl_swizzle { + ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { + $( + impl Point + where + DefaultAllocator: Allocator, + D::Value: Cmp + { + $( + /// Builds a new point from components of `self`. + #[inline] + pub fn $name(&self) -> $Result { + $Result::new($(self[$i]),*) + } + )* + } + )* + } +} + +impl_swizzle!( + where U0: xx() -> Point2[0, 0], + xxx() -> Point3[0, 0, 0]; + + where U1: xy() -> Point2[0, 1], + yx() -> Point2[1, 0], + yy() -> Point2[1, 1], + xxy() -> Point3[0, 0, 1], + xyx() -> Point3[0, 1, 0], + xyy() -> Point3[0, 1, 1], + yxx() -> Point3[1, 0, 0], + yxy() -> Point3[1, 0, 1], + yyx() -> Point3[1, 1, 0], + yyy() -> Point3[1, 1, 1]; + + where U2: xz() -> Point2[0, 2], + yz() -> Point2[1, 2], + zx() -> Point2[2, 0], + zy() -> Point2[2, 1], + zz() -> Point2[2, 2], + xxz() -> Point3[0, 0, 2], + xyz() -> Point3[0, 1, 2], + xzx() -> Point3[0, 2, 0], + xzy() -> Point3[0, 2, 1], + xzz() -> Point3[0, 2, 2], + yxz() -> Point3[1, 0, 2], + yyz() -> Point3[1, 1, 2], + yzx() -> Point3[1, 2, 0], + yzy() -> Point3[1, 2, 1], + yzz() -> Point3[1, 2, 2], + zxx() -> Point3[2, 0, 0], + zxy() -> Point3[2, 0, 1], + zxz() -> Point3[2, 0, 2], + zyx() -> Point3[2, 1, 0], + zyy() -> Point3[2, 1, 1], + zyz() -> Point3[2, 1, 2], + zzx() -> Point3[2, 2, 0], + zzy() -> Point3[2, 2, 1], + zzz() -> Point3[2, 2, 2]; +); diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs index 11060977..0d5d9c4a 100644 --- a/src/geometry/transform.rs +++ b/src/geometry/transform.rs @@ -248,9 +248,17 @@ where DefaultAllocator: Allocator, DimNameSum> /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); - /// assert_eq!(t.unwrap(), m); + /// assert_eq!(t.into_inner(), m); /// ``` #[inline] + pub fn into_inner(self) -> MatrixN> { + self.matrix + } + + /// Retrieves the underlying matrix. + /// Deprecated: Use [Transform::into_inner] instead. + #[deprecated(note="use `.into_inner()` instead")] + #[inline] pub fn unwrap(self) -> MatrixN> { self.matrix } @@ -329,7 +337,7 @@ where DefaultAllocator: Allocator, DimNameSum> /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); - /// assert_eq!(t.unwrap(), m); + /// assert_eq!(t.into_inner(), m); /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> { @@ -342,7 +350,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -375,7 +382,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -399,7 +405,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -429,7 +434,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, diff --git a/src/geometry/transform_alga.rs b/src/geometry/transform_alga.rs index 652da373..c5ba675b 100644 --- a/src/geometry/transform_alga.rs +++ b/src/geometry/transform_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ProjectiveTransformation, Transformation}; @@ -26,18 +26,18 @@ where } } -impl, C> Inverse for Transform +impl, C> TwoSidedInverse for Transform where C: SubTCategoryOf, DefaultAllocator: Allocator, DimNameSum>, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.clone().inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } @@ -116,12 +116,12 @@ where { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { - self.inverse() * pt + self.two_sided_inverse() * pt } #[inline] fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { - self.inverse() * v + self.two_sided_inverse() * v } } diff --git a/src/geometry/transform_ops.rs b/src/geometry/transform_ops.rs index 6fc4ec32..2ede5c60 100644 --- a/src/geometry/transform_ops.rs +++ b/src/geometry/transform_ops.rs @@ -143,7 +143,7 @@ md_impl_all!( if C::has_normalizer() { let normalizer = self.matrix().fixed_slice::(D::dim(), 0); - let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked(D::dim(), D::dim()) }; + let n = normalizer.tr_dot(&rhs.coords) + unsafe { *self.matrix().get_unchecked((D::dim(), D::dim())) }; if !n.is_zero() { return (transform * rhs + translation) / n; @@ -159,9 +159,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (DimNameSum, DimNameSum) for D: DimNameAdd, CA: TCategoryMul, CB: TCategory; self: Transform, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.unwrap()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.matrix()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.into_inner()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.matrix()); ); @@ -170,9 +170,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (D, D) for D: DimNameAdd, C: TCategoryMul; self: Transform, rhs: Rotation, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -181,8 +181,8 @@ md_impl_all!( Mul, mul where N: Real; (D, D), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul; self: Rotation, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -192,9 +192,9 @@ md_impl_all!( Mul, mul where N: Real; (U4, U4), (U4, U1) for C: TCategoryMul; self: Transform, rhs: UnitQuaternion, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -203,8 +203,8 @@ md_impl_all!( Mul, mul where N: Real; (U4, U1), (U4, U4) for C: TCategoryMul; self: UnitQuaternion, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -215,9 +215,9 @@ md_impl_all!( (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Transform, rhs: Isometry, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -227,8 +227,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Isometry, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -239,9 +239,9 @@ md_impl_all!( (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Transform, rhs: Similarity, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -251,8 +251,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> >; self: Similarity, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -270,9 +270,9 @@ md_impl_all!( Mul, mul where N: Real; (DimNameSum, DimNameSum), (D, U1) for D: DimNameAdd, C: TCategoryMul; self: Transform, rhs: Translation, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); - [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); + [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); @@ -282,8 +282,8 @@ md_impl_all!( (D, U1), (DimNameSum, DimNameSum) for D: DimNameAdd, C: TCategoryMul; self: Translation, rhs: Transform, Output = Transform; - [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); - [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); + [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); + [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); @@ -350,9 +350,9 @@ md_impl_all!( // for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> > // where SB::Alloc: Allocator, DimNameSum >; // self: Transform, rhs: Isometry, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.inverse().to_homogeneous()); +// [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); -// [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.inverse().to_homogeneous()); +// [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); // ); @@ -363,8 +363,8 @@ md_impl_all!( // for D: DimNameAdd, C: TCategoryMul, R: SubsetOf> > // where SA::Alloc: Allocator, DimNameSum >; // self: Isometry, rhs: Transform, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); -// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); +// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); +// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); @@ -377,9 +377,9 @@ md_impl_all!( // where SB::Alloc: Allocator // where SB::Alloc: Allocator, DimNameSum >; // self: Transform, rhs: Similarity, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); +// [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); -// [val ref] => Self::Output::from_matrix_unchecked(self.unwrap() * rhs.to_homogeneous()); +// [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); // ); @@ -391,8 +391,8 @@ md_impl_all!( // where SA::Alloc: Allocator // where SA::Alloc: Allocator, DimNameSum >; // self: Similarity, rhs: Transform, Output = Transform; -// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); -// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.unwrap()); +// [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); +// [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); @@ -425,7 +425,7 @@ md_assign_impl_all!( MulAssign, mul_assign where N: Real; (DimNameSum, DimNameSum), (DimNameSum, DimNameSum) for D: DimNameAdd, CA: TCategory, CB: SubTCategoryOf; self: Transform, rhs: Transform; - [val] => *self.matrix_mut_unchecked() *= rhs.unwrap(); + [val] => *self.matrix_mut_unchecked() *= rhs.into_inner(); [ref] => *self.matrix_mut_unchecked() *= rhs.matrix(); ); diff --git a/src/geometry/translation_alga.rs b/src/geometry/translation_alga.rs index 24aa28d2..fdd24014 100644 --- a/src/geometry/translation_alga.rs +++ b/src/geometry/translation_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Translation as AlgaTranslation; use alga::linear::{ @@ -28,16 +28,16 @@ where DefaultAllocator: Allocator } } -impl Inverse for Translation +impl TwoSidedInverse for Translation where DefaultAllocator: Allocator { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/translation_coordinates.rs b/src/geometry/translation_coordinates.rs new file mode 100644 index 00000000..01207a32 --- /dev/null +++ b/src/geometry/translation_coordinates.rs @@ -0,0 +1,44 @@ +use std::mem; +use std::ops::{Deref, DerefMut}; + +use base::allocator::Allocator; +use base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; +use base::dimension::{U1, U2, U3, U4, U5, U6}; +use base::{DefaultAllocator, Scalar}; + +use geometry::Translation; + +/* + * + * Give coordinates to Translation{1 .. 6} + * + */ + +macro_rules! deref_impl( + ($D: ty, $Target: ident $(, $comps: ident)*) => { + impl Deref for Translation + where DefaultAllocator: Allocator { + type Target = $Target; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { mem::transmute(self) } + } + } + + impl DerefMut for Translation + where DefaultAllocator: Allocator { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { mem::transmute(self) } + } + } + } +); + +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/unit_complex.rs b/src/geometry/unit_complex.rs index 32bb0238..f8d94dc8 100644 --- a/src/geometry/unit_complex.rs +++ b/src/geometry/unit_complex.rs @@ -85,7 +85,6 @@ impl UnitComplex { /// # Example /// ``` /// # extern crate num_complex; - /// # extern crate nalgebra; /// # use num_complex::Complex; /// # use nalgebra::UnitComplex; /// let angle = 1.78f32; @@ -117,7 +116,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(1.2); /// let inv = rot.inverse(); @@ -134,7 +132,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); @@ -153,7 +150,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); @@ -172,7 +168,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let rot = UnitComplex::new(angle); @@ -192,7 +187,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let mut rot = UnitComplex::new(angle); @@ -212,10 +206,11 @@ impl UnitComplex { /// /// # Example /// ``` + /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(0.78); /// let pow = rot.powf(2.0); - /// assert_eq!(pow.angle(), 2.0 * 0.78); + /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] pub fn powf(&self, n: N) -> Self { @@ -320,6 +315,6 @@ impl From> for Matrix3 { impl From> for Matrix2 { #[inline] fn from(q: UnitComplex) -> Matrix2 { - q.to_rotation_matrix().unwrap() + q.to_rotation_matrix().into_inner() } } diff --git a/src/geometry/unit_complex_alga.rs b/src/geometry/unit_complex_alga.rs index 59b11903..21b956d9 100644 --- a/src/geometry/unit_complex_alga.rs +++ b/src/geometry/unit_complex_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, @@ -31,14 +31,14 @@ impl AbstractMagma for UnitComplex { } } -impl Inverse for UnitComplex { +impl TwoSidedInverse for UnitComplex { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/unit_complex_construction.rs b/src/geometry/unit_complex_construction.rs index d8917e41..c4c9cc0d 100644 --- a/src/geometry/unit_complex_construction.rs +++ b/src/geometry/unit_complex_construction.rs @@ -7,10 +7,9 @@ use rand::distributions::{Distribution, OpenClosed01, Standard}; use rand::Rng; use alga::general::Real; -use base::allocator::Allocator; use base::dimension::{U1, U2}; use base::storage::Storage; -use base::{DefaultAllocator, Unit, Vector}; +use base::{Unit, Vector}; use geometry::{Rotation2, UnitComplex}; impl UnitComplex { @@ -36,7 +35,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); @@ -57,7 +55,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::from_angle(f32::consts::FRAC_PI_2); @@ -79,7 +76,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Vector2, Point2}; /// let angle = f32::consts::FRAC_PI_2; @@ -139,7 +135,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -162,7 +157,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -198,7 +192,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); @@ -224,7 +217,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); diff --git a/src/geometry/unit_complex_ops.rs b/src/geometry/unit_complex_ops.rs index 8f154423..c9d31ad4 100644 --- a/src/geometry/unit_complex_ops.rs +++ b/src/geometry/unit_complex_ops.rs @@ -50,7 +50,7 @@ impl Mul> for UnitComplex { #[inline] fn mul(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.into_inner()) } } @@ -59,7 +59,7 @@ impl<'a, N: Real> Mul> for &'a UnitComplex { #[inline] fn mul(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.unwrap()) + Unit::new_unchecked(self.complex() * rhs.into_inner()) } } @@ -68,7 +68,7 @@ impl<'b, N: Real> Mul<&'b UnitComplex> for UnitComplex { #[inline] fn mul(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.complex()) + Unit::new_unchecked(self.into_inner() * rhs.complex()) } } @@ -87,7 +87,7 @@ impl Div> for UnitComplex { #[inline] fn div(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -96,7 +96,7 @@ impl<'a, N: Real> Div> for &'a UnitComplex { #[inline] fn div(self, rhs: UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } @@ -105,7 +105,7 @@ impl<'b, N: Real> Div<&'b UnitComplex> for UnitComplex { #[inline] fn div(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.unwrap() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -114,7 +114,7 @@ impl<'a, 'b, N: Real> Div<&'b UnitComplex> for &'a UnitComplex { #[inline] fn div(self, rhs: &'b UnitComplex) -> UnitComplex { - Unit::new_unchecked(self.complex() * rhs.conjugate().unwrap()) + Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } @@ -425,11 +425,11 @@ impl UnitComplex { for j in 0..rhs.ncols() { unsafe { - let a = *rhs.get_unchecked(0, j); - let b = *rhs.get_unchecked(1, j); + let a = *rhs.get_unchecked((0, j)); + let b = *rhs.get_unchecked((1, j)); - *rhs.get_unchecked_mut(0, j) = r * a - i * b; - *rhs.get_unchecked_mut(1, j) = i * a + r * b; + *rhs.get_unchecked_mut((0, j)) = r * a - i * b; + *rhs.get_unchecked_mut((1, j)) = i * a + r * b; } } } @@ -452,11 +452,11 @@ impl UnitComplex { // FIXME: can we optimize that to iterate on one column at a time ? for j in 0..lhs.nrows() { unsafe { - let a = *lhs.get_unchecked(j, 0); - let b = *lhs.get_unchecked(j, 1); + let a = *lhs.get_unchecked((j, 0)); + let b = *lhs.get_unchecked((j, 1)); - *lhs.get_unchecked_mut(j, 0) = r * a + i * b; - *lhs.get_unchecked_mut(j, 1) = -i * a + r * b; + *lhs.get_unchecked_mut((j, 0)) = r * a + i * b; + *lhs.get_unchecked_mut((j, 1)) = -i * a + r * b; } } } diff --git a/src/io/matrix_market.pest b/src/io/matrix_market.pest new file mode 100644 index 00000000..e024ec57 --- /dev/null +++ b/src/io/matrix_market.pest @@ -0,0 +1,16 @@ +WHITESPACE = _{ " " } + +Comments = _{ "%" ~ (!NEWLINE ~ ANY)* } +Header = { "%%" ~ (!NEWLINE ~ ANY)* } +Shape = { Dimension ~ Dimension ~ Dimension } +Document = { + SOI ~ + NEWLINE* ~ + Header ~ + (NEWLINE ~ Comments)* ~ + (NEWLINE ~ Shape) ~ + (NEWLINE ~ Entry?)* +} +Dimension = @{ ASCII_DIGIT+ } +Value = @{ ("+" | "-")? ~ NUMBER+ ~ ("." ~ NUMBER+)? ~ ("e" ~ ("+" | "-")? ~ NUMBER+)? } +Entry = { Dimension ~ Dimension ~ Value } \ No newline at end of file diff --git a/src/io/matrix_market.rs b/src/io/matrix_market.rs new file mode 100644 index 00000000..f4919cd4 --- /dev/null +++ b/src/io/matrix_market.rs @@ -0,0 +1,53 @@ +use std::fs; +use std::path::Path; + +use pest::Parser; +use sparse::CsMatrix; +use Real; + +#[derive(Parser)] +#[grammar = "io/matrix_market.pest"] +struct MatrixMarketParser; + +// FIXME: return an Error instead of an Option. +/// Parses a Matrix Market file at the given path, and returns the corresponding sparse matrix. +pub fn cs_matrix_from_matrix_market>(path: P) -> Option> { + let file = fs::read_to_string(path).ok()?; + cs_matrix_from_matrix_market_str(&file) +} + +// FIXME: return an Error instead of an Option. +/// Parses a Matrix Market file described by the given string, and returns the corresponding sparse matrix. +pub fn cs_matrix_from_matrix_market_str(data: &str) -> Option> { + let file = MatrixMarketParser::parse(Rule::Document, data) + .unwrap() + .next()?; + let mut shape = (0, 0, 0); + let mut rows: Vec = Vec::new(); + let mut cols: Vec = Vec::new(); + let mut data: Vec = Vec::new(); + + for line in file.into_inner() { + match line.as_rule() { + Rule::Header => {} + Rule::Shape => { + let mut inner = line.into_inner(); + shape.0 = inner.next()?.as_str().parse::().ok()?; + shape.1 = inner.next()?.as_str().parse::().ok()?; + shape.2 = inner.next()?.as_str().parse::().ok()?; + } + Rule::Entry => { + let mut inner = line.into_inner(); + // NOTE: indices are 1-based. + rows.push(inner.next()?.as_str().parse::().ok()? - 1); + cols.push(inner.next()?.as_str().parse::().ok()? - 1); + data.push(::convert(inner.next()?.as_str().parse::().ok()?)); + } + _ => return None, // FIXME: return an Err instead. + } + } + + Some(CsMatrix::from_triplet( + shape.0, shape.1, &rows, &cols, &data, + )) +} diff --git a/src/io/mod.rs b/src/io/mod.rs new file mode 100644 index 00000000..1b172b20 --- /dev/null +++ b/src/io/mod.rs @@ -0,0 +1,5 @@ +//! Parsers for various matrix formats. + +pub use self::matrix_market::{cs_matrix_from_matrix_market, cs_matrix_from_matrix_market_str}; + +mod matrix_market; diff --git a/src/lib.rs b/src/lib.rs index b8b807a8..f92cd36a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,10 +81,12 @@ an optimized set of tools for computer graphics and physics. Those features incl #![deny(non_upper_case_globals)] #![deny(unused_qualifications)] #![deny(unused_results)] -#![deny(missing_docs)] +#![warn(missing_docs)] // FIXME: deny this #![warn(incoherent_fundamental_impls)] -#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico", - html_root_url = "http://nalgebra.org/rustdoc")] +#![doc( + html_favicon_url = "http://nalgebra.org/img/favicon.ico", + html_root_url = "http://nalgebra.org/rustdoc" +)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] @@ -121,11 +123,21 @@ extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; +#[cfg(feature = "io")] +extern crate pest; +#[macro_use] +#[cfg(feature = "io")] +extern crate pest_derive; + pub mod base; #[cfg(feature = "debug")] pub mod debug; pub mod geometry; +#[cfg(feature = "io")] +pub mod io; pub mod linalg; +#[cfg(feature = "sparse")] +pub mod sparse; #[cfg(feature = "std")] #[deprecated( @@ -135,11 +147,13 @@ pub use base as core; pub use base::*; pub use geometry::*; pub use linalg::*; +#[cfg(feature = "sparse")] +pub use sparse::*; use std::cmp::{self, Ordering, PartialOrd}; use alga::general::{ - Additive, AdditiveGroup, Identity, Inverse, JoinSemilattice, Lattice, MeetSemilattice, + Additive, AdditiveGroup, Identity, TwoSidedInverse, JoinSemilattice, Lattice, MeetSemilattice, Multiplicative, SupersetOf, }; use alga::linear::SquareMatrix as AlgaSquareMatrix; @@ -189,6 +203,12 @@ pub fn zero>() -> T { /// /// * [`one`](fn.one.html) /// * [`zero`](fn.zero.html) +/// +/// # Deprecated +/// Use [Point::origin] instead. +/// +/// Or, use [EuclideanSpace::origin](https://docs.rs/alga/0.7.2/alga/linear/trait.EuclideanSpace.html#tymethod.origin). +#[deprecated(note = "use `Point::origin` instead")] #[inline] pub fn origin() -> P { P::origin() @@ -275,6 +295,9 @@ pub fn min(a: T, b: T) -> T { } /// The absolute value of `a`. +/// +/// Deprecated: Use [Matrix::abs] or [Real::abs] instead. +#[deprecated(note = "use `Matrix::abs` or `Real::abs` instead")] #[inline] pub fn abs(a: &T) -> T { a.abs() @@ -404,8 +427,8 @@ pub fn try_inverse(m: &M) -> Option { /// /// * [`try_inverse`](fn.try_inverse.html) #[inline] -pub fn inverse>(m: &M) -> M { - m.inverse() +pub fn inverse>(m: &M) -> M { + m.two_sided_inverse() } /* @@ -413,12 +436,26 @@ pub fn inverse>(m: &M) -> M { */ /// Computes the dot product of two vectors. +/// +/// ## Deprecated +/// Use these methods instead: +/// - [Matrix::dot] +/// - [Quaternion::dot] +/// +/// Or, use [FiniteDimVectorSpace::dot](https://docs.rs/alga/0.7.2/alga/linear/trait.FiniteDimVectorSpace.html#tymethod.dot). +#[deprecated(note = "use `Matrix::dot` or `Quaternion::dot` instead")] #[inline] pub fn dot(a: &V, b: &V) -> V::Field { a.dot(b) } /// Computes the smallest angle between two vectors. +/// +/// ## Deprecated +/// Use [Matrix::angle] instead. +/// +/// Or, use [InnerSpace::angle](https://docs.rs/alga/0.7.2/alga/linear/trait.InnerSpace.html#method.angle). +#[deprecated(note = "use `Matrix::angle` instead")] #[inline] pub fn angle(a: &V, b: &V) -> V::Real { a.angle(b) @@ -435,6 +472,14 @@ pub fn angle(a: &V, b: &V) -> V::Real { /// * [`magnitude`](fn.magnitude.html) /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::norm] +/// * [Quaternion::norm] +/// +/// Or, use [NormedSpace::norm](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm). +#[deprecated(note = "use `Matrix::norm` or `Quaternion::norm` instead")] #[inline] pub fn norm(v: &V) -> V::Field { v.norm() @@ -447,6 +492,14 @@ pub fn norm(v: &V) -> V::Field { /// * [`magnitude`](fn.magnitude.html) /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm`](fn.norm.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::norm_squared] +/// * [Quaternion::norm_squared] +/// +/// Or, use [NormedSpace::norm_squared](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm_squared). +#[deprecated(note = "use `Matrix::norm_squared` or `Quaternion::norm_squared` instead")] #[inline] pub fn norm_squared(v: &V) -> V::Field { v.norm_squared() @@ -459,6 +512,14 @@ pub fn norm_squared(v: &V) -> V::Field { /// * [`magnitude_squared`](fn.magnitude_squared.html) /// * [`norm`](fn.norm.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::magnitude] +/// * [Quaternion::magnitude] +/// +/// Or, use [NormedSpace::norm](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm). +#[deprecated(note = "use `Matrix::magnitude` or `Quaternion::magnitude` instead")] #[inline] pub fn magnitude(v: &V) -> V::Field { v.norm() @@ -472,18 +533,42 @@ pub fn magnitude(v: &V) -> V::Field { /// * [`magnitude`](fn.magnitude.html) /// * [`norm`](fn.norm.html) /// * [`norm_squared`](fn.norm_squared.html) +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::magnitude_squared] +/// * [Quaternion::magnitude_squared] +/// +/// Or, use [NormedSpace::norm_squared](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.norm_squared). +#[deprecated(note = "use `Matrix::magnitude_squared` or `Quaternion::magnitude_squared` instead")] #[inline] pub fn magnitude_squared(v: &V) -> V::Field { v.norm_squared() } /// Computes the normalized version of the vector `v`. +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::normalize] +/// * [Quaternion::normalize] +/// +/// Or, use [NormedSpace::normalize](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.normalize). +#[deprecated(note = "use `Matrix::normalize` or `Quaternion::normalize` instead")] #[inline] pub fn normalize(v: &V) -> V { v.normalize() } /// Computes the normalized version of the vector `v` or returns `None` if its norm is smaller than `min_norm`. +/// +/// # Deprecated +/// Use these methods instead: +/// * [Matrix::try_normalize] +/// * [Quaternion::try_normalize] +/// +/// Or, use [NormedSpace::try_normalize](https://docs.rs/alga/0.7.2/alga/linear/trait.NormedSpace.html#tymethod.try_normalize). +#[deprecated(note = "use `Matrix::try_normalize` or `Quaternion::try_normalize` instead")] #[inline] pub fn try_normalize(v: &V, min_norm: V::Field) -> Option { v.try_normalize(min_norm) diff --git a/src/linalg/cholesky.rs b/src/linalg/cholesky.rs index c0868c1a..c1fd7b85 100644 --- a/src/linalg/cholesky.rs +++ b/src/linalg/cholesky.rs @@ -52,7 +52,7 @@ where DefaultAllocator: Allocator for j in 0..n { for k in 0..j { - let factor = unsafe { -*matrix.get_unchecked(j, k) }; + let factor = unsafe { -*matrix.get_unchecked((j, k)) }; let (mut col_j, col_k) = matrix.columns_range_pair_mut(j, k); let mut col_j = col_j.rows_range_mut(j..); @@ -61,11 +61,11 @@ where DefaultAllocator: Allocator col_j.axpy(factor, &col_k, N::one()); } - let diag = unsafe { *matrix.get_unchecked(j, j) }; + let diag = unsafe { *matrix.get_unchecked((j, j)) }; if diag > N::zero() { let denom = diag.sqrt(); unsafe { - *matrix.get_unchecked_mut(j, j) = denom; + *matrix.get_unchecked_mut((j, j)) = denom; } let mut col = matrix.slice_range_mut(j + 1.., j); diff --git a/src/linalg/determinant.rs b/src/linalg/determinant.rs index 100a21e7..6229da0e 100644 --- a/src/linalg/determinant.rs +++ b/src/linalg/determinant.rs @@ -23,27 +23,27 @@ impl, S: Storage> SquareMatrix N::one(), - 1 => *self.get_unchecked(0, 0), + 1 => *self.get_unchecked((0, 0)), 2 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); m11 * m22 - m21 * m12 } 3 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m13 = *self.get_unchecked(0, 2); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m13 = *self.get_unchecked((0, 2)); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); - let m23 = *self.get_unchecked(1, 2); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); + let m23 = *self.get_unchecked((1, 2)); - let m31 = *self.get_unchecked(2, 0); - let m32 = *self.get_unchecked(2, 1); - let m33 = *self.get_unchecked(2, 2); + let m31 = *self.get_unchecked((2, 0)); + let m32 = *self.get_unchecked((2, 1)); + let m33 = *self.get_unchecked((2, 2)); let minor_m12_m23 = m22 * m33 - m32 * m23; let minor_m11_m23 = m21 * m33 - m31 * m23; diff --git a/src/linalg/full_piv_lu.rs b/src/linalg/full_piv_lu.rs index 9bab4ee9..022152f4 100644 --- a/src/linalg/full_piv_lu.rs +++ b/src/linalg/full_piv_lu.rs @@ -251,7 +251,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), D> let mut res = self.lu[(dim - 1, dim - 1)]; if !res.is_zero() { for i in 0..dim - 1 { - res *= unsafe { *self.lu.get_unchecked(i, i) }; + res *= unsafe { *self.lu.get_unchecked((i, i)) }; } res * self.p.determinant() * self.q.determinant() diff --git a/src/linalg/inverse.rs b/src/linalg/inverse.rs index 08cebd2a..5748900f 100644 --- a/src/linalg/inverse.rs +++ b/src/linalg/inverse.rs @@ -35,46 +35,46 @@ impl> SquareMatrix { match dim { 0 => true, 1 => { - let determinant = self.get_unchecked(0, 0).clone(); + let determinant = self.get_unchecked((0, 0)).clone(); if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = N::one() / determinant; + *self.get_unchecked_mut((0, 0)) = N::one() / determinant; true } } 2 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); let determinant = m11 * m22 - m21 * m12; if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = m22 / determinant; - *self.get_unchecked_mut(0, 1) = -m12 / determinant; + *self.get_unchecked_mut((0, 0)) = m22 / determinant; + *self.get_unchecked_mut((0, 1)) = -m12 / determinant; - *self.get_unchecked_mut(1, 0) = -m21 / determinant; - *self.get_unchecked_mut(1, 1) = m11 / determinant; + *self.get_unchecked_mut((1, 0)) = -m21 / determinant; + *self.get_unchecked_mut((1, 1)) = m11 / determinant; true } } 3 => { - let m11 = *self.get_unchecked(0, 0); - let m12 = *self.get_unchecked(0, 1); - let m13 = *self.get_unchecked(0, 2); + let m11 = *self.get_unchecked((0, 0)); + let m12 = *self.get_unchecked((0, 1)); + let m13 = *self.get_unchecked((0, 2)); - let m21 = *self.get_unchecked(1, 0); - let m22 = *self.get_unchecked(1, 1); - let m23 = *self.get_unchecked(1, 2); + let m21 = *self.get_unchecked((1, 0)); + let m22 = *self.get_unchecked((1, 1)); + let m23 = *self.get_unchecked((1, 2)); - let m31 = *self.get_unchecked(2, 0); - let m32 = *self.get_unchecked(2, 1); - let m33 = *self.get_unchecked(2, 2); + let m31 = *self.get_unchecked((2, 0)); + let m32 = *self.get_unchecked((2, 1)); + let m33 = *self.get_unchecked((2, 2)); let minor_m12_m23 = m22 * m33 - m32 * m23; let minor_m11_m23 = m21 * m33 - m31 * m23; @@ -86,17 +86,17 @@ impl> SquareMatrix { if determinant == N::zero() { false } else { - *self.get_unchecked_mut(0, 0) = minor_m12_m23 / determinant; - *self.get_unchecked_mut(0, 1) = (m13 * m32 - m33 * m12) / determinant; - *self.get_unchecked_mut(0, 2) = (m12 * m23 - m22 * m13) / determinant; + *self.get_unchecked_mut((0, 0)) = minor_m12_m23 / determinant; + *self.get_unchecked_mut((0, 1)) = (m13 * m32 - m33 * m12) / determinant; + *self.get_unchecked_mut((0, 2)) = (m12 * m23 - m22 * m13) / determinant; - *self.get_unchecked_mut(1, 0) = -minor_m11_m23 / determinant; - *self.get_unchecked_mut(1, 1) = (m11 * m33 - m31 * m13) / determinant; - *self.get_unchecked_mut(1, 2) = (m13 * m21 - m23 * m11) / determinant; + *self.get_unchecked_mut((1, 0)) = -minor_m11_m23 / determinant; + *self.get_unchecked_mut((1, 1)) = (m11 * m33 - m31 * m13) / determinant; + *self.get_unchecked_mut((1, 2)) = (m13 * m21 - m23 * m11) / determinant; - *self.get_unchecked_mut(2, 0) = minor_m11_m22 / determinant; - *self.get_unchecked_mut(2, 1) = (m12 * m31 - m32 * m11) / determinant; - *self.get_unchecked_mut(2, 2) = (m11 * m22 - m21 * m12) / determinant; + *self.get_unchecked_mut((2, 0)) = minor_m11_m22 / determinant; + *self.get_unchecked_mut((2, 1)) = (m12 * m31 - m32 * m11) / determinant; + *self.get_unchecked_mut((2, 2)) = (m11 * m22 - m21 * m12) / determinant; true } diff --git a/src/linalg/lu.rs b/src/linalg/lu.rs index 66c2743e..67fae23f 100644 --- a/src/linalg/lu.rs +++ b/src/linalg/lu.rs @@ -290,7 +290,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), D> let mut res = N::one(); for i in 0..dim { - res *= unsafe { *self.lu.get_unchecked(i, i) }; + res *= unsafe { *self.lu.get_unchecked((i, i)) }; } res * self.p.determinant() diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs index a3bd5404..66299c07 100644 --- a/src/linalg/schur.rs +++ b/src/linalg/schur.rs @@ -412,7 +412,7 @@ where rot.rotate_rows(&mut m); if compute_q { - let c = rot.unwrap(); + let c = rot.into_inner(); // XXX: we have to build the matrix manually because // rot.to_rotation_matrix().unwrap() causes an ICE. q = Some(MatrixN::from_column_slice_generic( diff --git a/src/linalg/solve.rs b/src/linalg/solve.rs index 904a1b29..4d2d103f 100644 --- a/src/linalg/solve.rs +++ b/src/linalg/solve.rs @@ -79,7 +79,7 @@ impl> SquareMatrix { let coeff; unsafe { - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -161,7 +161,7 @@ impl> SquareMatrix { let coeff; unsafe { - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -258,7 +258,7 @@ impl> SquareMatrix { unsafe { let b_i = b.vget_unchecked_mut(i); - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; @@ -304,7 +304,7 @@ impl> SquareMatrix { unsafe { let b_i = b.vget_unchecked_mut(i); - let diag = *self.get_unchecked(i, i); + let diag = *self.get_unchecked((i, i)); if diag.is_zero() { return false; diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs index 88752891..5c1d9a0f 100644 --- a/src/linalg/svd.rs +++ b/src/linalg/svd.rs @@ -484,90 +484,94 @@ where /// Rebuild the original matrix. /// - /// This is useful if some of the singular values have been manually modified. Panics if the - /// right- and left- singular vectors have not been computed at construction-time. - pub fn recompose(self) -> MatrixMN { - let mut u = self.u.expect("SVD recomposition: U has not been computed."); - let v_t = self - .v_t - .expect("SVD recomposition: V^t has not been computed."); - - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - u.column_mut(i).mul_assign(val); + /// This is useful if some of the singular values have been manually modified. + /// Returns `Err` if the right- and left- singular vectors have not been + /// computed at construction-time. + pub fn recompose(self) -> Result, &'static str> { + match (self.u, self.v_t) { + (Some(mut u), Some(v_t)) => { + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; + u.column_mut(i).mul_assign(val); + } + Ok(u * v_t) + } + (None, None) => Err("SVD recomposition: U and V^t have not been computed."), + (None, _) => Err("SVD recomposition: U has not been computed."), + (_, None) => Err("SVD recomposition: V^t has not been computed.") } - - u * v_t } /// Computes the pseudo-inverse of the decomposed matrix. /// /// Any singular value smaller than `eps` is assumed to be zero. - /// Panics if the right- and left- singular vectors have not been computed at - /// construction-time. - pub fn pseudo_inverse(mut self, eps: N) -> MatrixMN - where DefaultAllocator: Allocator { - assert!( - eps >= N::zero(), - "SVD pseudo inverse: the epsilon must be non-negative." - ); - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - - if val > eps { - self.singular_values[i] = N::one() / val; - } else { - self.singular_values[i] = N::zero(); - } + /// Returns `Err` if the right- and left- singular vectors have not + /// been computed at construction-time. + pub fn pseudo_inverse(mut self, eps: N) -> Result, &'static str> + where + DefaultAllocator: Allocator, + { + if eps < N::zero() { + Err("SVD pseudo inverse: the epsilon must be non-negative.") } + else { + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; - self.recompose().transpose() + if val > eps { + self.singular_values[i] = N::one() / val; + } else { + self.singular_values[i] = N::zero(); + } + } + + self.recompose().map(|m| m.transpose()) + } } /// Solves the system `self * x = b` where `self` is the decomposed matrix and `x` the unknown. /// /// Any singular value smaller than `eps` is assumed to be zero. - /// Returns `None` if the singular vectors `U` and `V` have not been computed. + /// Returns `Err` if the singular vectors `U` and `V` have not been computed. // FIXME: make this more generic wrt the storage types and the dimensions for `b`. pub fn solve( &self, b: &Matrix, eps: N, - ) -> MatrixMN + ) -> Result, &'static str> where S2: Storage, DefaultAllocator: Allocator + Allocator, C2>, ShapeConstraint: SameNumberOfRows, { - assert!( - eps >= N::zero(), - "SVD solve: the epsilon must be non-negative." - ); - let u = self - .u - .as_ref() - .expect("SVD solve: U has not been computed."); - let v_t = self - .v_t - .as_ref() - .expect("SVD solve: V^t has not been computed."); + if eps < N::zero() { + Err("SVD solve: the epsilon must be non-negative.") + } + else { + match (&self.u, &self.v_t) { + (Some(u), Some(v_t)) => { + let mut ut_b = u.tr_mul(b); - let mut ut_b = u.tr_mul(b); + for j in 0..ut_b.ncols() { + let mut col = ut_b.column_mut(j); - for j in 0..ut_b.ncols() { - let mut col = ut_b.column_mut(j); + for i in 0..self.singular_values.len() { + let val = self.singular_values[i]; + if val > eps { + col[i] /= val; + } else { + col[i] = N::zero(); + } + } + } - for i in 0..self.singular_values.len() { - let val = self.singular_values[i]; - if val > eps { - col[i] /= val; - } else { - col[i] = N::zero(); + Ok(v_t.tr_mul(&ut_b)) } + (None, None) => Err("SVD solve: U and V^t have not been computed."), + (None, _) => Err("SVD solve: U has not been computed."), + (_, None) => Err("SVD solve: V^t has not been computed.") } } - - v_t.tr_mul(&ut_b) } } @@ -624,8 +628,10 @@ where /// Computes the pseudo-inverse of this matrix. /// /// All singular values below `eps` are considered equal to 0. - pub fn pseudo_inverse(self, eps: N) -> MatrixMN - where DefaultAllocator: Allocator { + pub fn pseudo_inverse(self, eps: N) -> Result, &'static str> + where + DefaultAllocator: Allocator, + { SVD::new(self.clone_owned(), true, true).pseudo_inverse(eps) } } diff --git a/src/sparse/cs_matrix.rs b/src/sparse/cs_matrix.rs new file mode 100644 index 00000000..7ddc4deb --- /dev/null +++ b/src/sparse/cs_matrix.rs @@ -0,0 +1,517 @@ +use alga::general::ClosedAdd; +use num::Zero; +use std::iter; +use std::marker::PhantomData; +use std::ops::Range; +use std::slice; + +use allocator::Allocator; +use sparse::cs_utils; +use { + DefaultAllocator, Dim, Dynamic, Scalar, Vector, VectorN, U1 +}; + +pub struct ColumnEntries<'a, N> { + curr: usize, + i: &'a [usize], + v: &'a [N], +} + +impl<'a, N> ColumnEntries<'a, N> { + #[inline] + pub fn new(i: &'a [usize], v: &'a [N]) -> Self { + assert_eq!(i.len(), v.len()); + ColumnEntries { curr: 0, i, v } + } +} + +impl<'a, N: Copy> Iterator for ColumnEntries<'a, N> { + type Item = (usize, N); + + #[inline] + fn next(&mut self) -> Option<(usize, N)> { + if self.curr >= self.i.len() { + None + } else { + let res = Some((unsafe { *self.i.get_unchecked(self.curr) }, unsafe { + *self.v.get_unchecked(self.curr) + })); + self.curr += 1; + res + } + } +} + +// FIXME: this structure exists for now only because impl trait +// cannot be used for trait method return types. +/// Trait for iterable compressed-column matrix storage. +pub trait CsStorageIter<'a, N, R, C = U1> { + /// Iterator through all the rows of a specific columns. + /// + /// The elements are given as a tuple (row_index, value). + type ColumnEntries: Iterator; + /// Iterator through the row indices of a specific column. + type ColumnRowIndices: Iterator; + + /// Iterates through all the row indices of the j-th column. + fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices; + #[inline(always)] + /// Iterates through all the entries of the j-th column. + fn column_entries(&'a self, j: usize) -> Self::ColumnEntries; +} + +/// Trait for mutably iterable compressed-column sparse matrix storage. +pub trait CsStorageIterMut<'a, N: 'a, R, C = U1> { + /// Mutable iterator through all the values of the sparse matrix. + type ValuesMut: Iterator; + /// Mutable iterator through all the rows of a specific columns. + /// + /// The elements are given as a tuple (row_index, value). + type ColumnEntriesMut: Iterator; + + /// A mutable iterator through the values buffer of the sparse matrix. + fn values_mut(&'a mut self) -> Self::ValuesMut; + /// Iterates mutably through all the entries of the j-th column. + fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut; +} + +/// Trait for compressed column sparse matrix storage. +pub trait CsStorage: for<'a> CsStorageIter<'a, N, R, C> { + /// The shape of the stored matrix. + fn shape(&self) -> (R, C); + /// Retrieve the i-th row index of the underlying row index buffer. + /// + /// 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. + /// + /// No bound-checking is performed. + unsafe fn get_value_unchecked(&self, i: usize) -> &N; + /// The i-th value on the contiguous value buffer of this storage. + fn get_value(&self, i: usize) -> &N; + /// Retrieve the i-th row index of the underlying row index buffer. + fn row_index(&self, i: usize) -> usize; + /// The value indices for the `i`-th column. + fn column_range(&self, i: usize) -> Range; + /// The size of the value buffer (i.e. the entries known as possibly being non-zero). + fn len(&self) -> usize; +} + +/// Trait for compressed column sparse matrix mutable storage. +pub trait CsStorageMut: + CsStorage + for<'a> CsStorageIterMut<'a, N, R, C> +{ +} + +/// A storage of column-compressed sparse matrix based on a Vec. +#[derive(Clone, Debug, PartialEq)] +pub struct CsVecStorage +where DefaultAllocator: Allocator +{ + pub(crate) shape: (R, C), + pub(crate) p: VectorN, + pub(crate) i: Vec, + pub(crate) vals: Vec, +} + +impl CsVecStorage +where DefaultAllocator: Allocator +{ + /// The value buffer of this storage. + pub fn values(&self) -> &[N] { + &self.vals + } + + /// The column shifts buffer. + pub fn p(&self) -> &[usize] { + self.p.as_slice() + } + + /// The row index buffers. + pub fn i(&self) -> &[usize] { + &self.i + } +} + +impl CsVecStorage where DefaultAllocator: Allocator {} + +impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIter<'a, N, R, C> for CsVecStorage +where DefaultAllocator: Allocator +{ + type ColumnEntries = ColumnEntries<'a, N>; + type ColumnRowIndices = iter::Cloned>; + + #[inline] + fn column_entries(&'a self, j: usize) -> Self::ColumnEntries { + let rng = self.column_range(j); + ColumnEntries::new(&self.i[rng.clone()], &self.vals[rng]) + } + + #[inline] + fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices { + let rng = self.column_range(j); + self.i[rng.clone()].iter().cloned() + } +} + +impl CsStorage for CsVecStorage +where DefaultAllocator: Allocator +{ + #[inline] + fn shape(&self) -> (R, C) { + self.shape + } + + #[inline] + fn len(&self) -> usize { + self.vals.len() + } + + #[inline] + fn row_index(&self, i: usize) -> usize { + self.i[i] + } + + #[inline] + unsafe fn row_index_unchecked(&self, i: usize) -> usize { + *self.i.get_unchecked(i) + } + + #[inline] + unsafe fn get_value_unchecked(&self, i: usize) -> &N { + self.vals.get_unchecked(i) + } + + #[inline] + fn get_value(&self, i: usize) -> &N { + &self.vals[i] + } + + #[inline] + fn column_range(&self, j: usize) -> Range { + let end = if j + 1 == self.p.len() { + self.len() + } else { + self.p[j + 1] + }; + + self.p[j]..end + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIterMut<'a, N, R, C> for CsVecStorage +where DefaultAllocator: Allocator +{ + type ValuesMut = slice::IterMut<'a, N>; + type ColumnEntriesMut = iter::Zip>, slice::IterMut<'a, N>>; + + #[inline] + fn values_mut(&'a mut self) -> Self::ValuesMut { + self.vals.iter_mut() + } + + #[inline] + fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut { + let rng = self.column_range(j); + self.i[rng.clone()] + .iter() + .cloned() + .zip(self.vals[rng].iter_mut()) + } +} + +impl CsStorageMut for CsVecStorage where DefaultAllocator: Allocator +{} + +/* +pub struct CsSliceStorage<'a, N: Scalar, R: Dim, C: DimAdd> { + shape: (R, C), + p: VectorSlice>, + i: VectorSlice, + vals: VectorSlice, +}*/ + +/// A compressed sparse column matrix. +#[derive(Clone, Debug, PartialEq)] +pub struct CsMatrix< + N: Scalar, + R: Dim = Dynamic, + C: Dim = Dynamic, + S: CsStorage = CsVecStorage, +> { + pub(crate) data: S, + _phantoms: PhantomData<(N, R, C)>, +} + +/// A column compressed sparse vector. +pub type CsVector> = CsMatrix; + +impl CsMatrix +where DefaultAllocator: Allocator +{ + /// Creates a new compressed sparse column matrix with the specified dimension and + /// `nvals` possible non-zero values. + pub fn new_uninitialized_generic(nrows: R, ncols: C, nvals: usize) -> Self { + let mut i = Vec::with_capacity(nvals); + unsafe { + i.set_len(nvals); + } + i.shrink_to_fit(); + + let mut vals = Vec::with_capacity(nvals); + unsafe { + vals.set_len(nvals); + } + vals.shrink_to_fit(); + + CsMatrix { + data: CsVecStorage { + shape: (nrows, ncols), + p: VectorN::zeros_generic(ncols, U1), + i, + vals, + }, + _phantoms: PhantomData, + } + } + + /* + pub(crate) fn from_parts_generic( + nrows: R, + ncols: C, + p: VectorN, + i: Vec, + vals: Vec, + ) -> Self + where + N: Zero + ClosedAdd, + DefaultAllocator: Allocator, + { + assert_eq!(ncols.value(), p.len(), "Invalid inptr size."); + assert_eq!(i.len(), vals.len(), "Invalid value size."); + + // Check p. + for ptr in &p { + assert!(*ptr < i.len(), "Invalid inptr value."); + } + + for ptr in p.as_slice().windows(2) { + assert!(ptr[0] <= ptr[1], "Invalid inptr ordering."); + } + + // Check i. + for i in &i { + assert!(*i < nrows.value(), "Invalid row ptr value.") + } + + let mut res = CsMatrix { + data: CsVecStorage { + shape: (nrows, ncols), + p, + i, + vals, + }, + _phantoms: PhantomData, + }; + + // Sort and remove duplicates. + res.sort(); + res.dedup(); + + res + }*/ +} + +/* +impl CsMatrix { + pub(crate) fn from_parts( + nrows: usize, + ncols: usize, + p: Vec, + i: Vec, + vals: Vec, + ) -> Self + { + let nrows = Dynamic::new(nrows); + let ncols = Dynamic::new(ncols); + let p = DVector::from_data(VecStorage::new(ncols, U1, p)); + Self::from_parts_generic(nrows, ncols, p, i, vals) + } +} +*/ + +impl> CsMatrix { + pub(crate) fn from_data(data: S) -> Self { + CsMatrix { + data, + _phantoms: PhantomData, + } + } + + /// The size of the data buffer. + pub fn len(&self) -> usize { + self.data.len() + } + + /// The number of rows of this matrix. + pub fn nrows(&self) -> usize { + self.data.shape().0.value() + } + + /// The number of rows of this matrix. + pub fn ncols(&self) -> usize { + self.data.shape().1.value() + } + + /// The shape of this matrix. + pub fn shape(&self) -> (usize, usize) { + let (nrows, ncols) = self.data.shape(); + (nrows.value(), ncols.value()) + } + + /// Whether this matrix is square or not. + pub fn is_square(&self) -> bool { + let (nrows, ncols) = self.data.shape(); + nrows.value() == ncols.value() + } + + /// Should always return `true`. + /// + /// This method is generally used for debugging and should typically not be called in user code. + /// This checks that the row inner indices of this matrix are sorted. It takes `O(n)` time, + /// where n` is `self.len()`. + /// All operations of CSC matrices on nalgebra assume, and will return, sorted indices. + /// 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. + pub fn is_sorted(&self) -> bool { + for j in 0..self.ncols() { + let mut curr = None; + for idx in self.data.column_row_indices(j) { + if let Some(curr) = curr { + if idx <= curr { + return false; + } + } + + curr = Some(idx); + } + } + + true + } + + /// Computes the transpose of this sparse matrix. + pub fn transpose(&self) -> CsMatrix + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + + let nvals = self.len(); + let mut res = CsMatrix::new_uninitialized_generic(ncols, nrows, nvals); + let mut workspace = Vector::zeros_generic(nrows, U1); + + // Compute p. + for i in 0..nvals { + let row_id = self.data.row_index(i); + workspace[row_id] += 1; + } + + let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); + + // Fill the result. + for j in 0..ncols.value() { + for (row_id, value) in self.data.column_entries(j) { + let shift = workspace[row_id]; + + res.data.vals[shift] = value; + res.data.i[shift] = j; + workspace[row_id] += 1; + } + } + + res + } +} + +impl> CsMatrix { + /// Iterator through all the mutable values of this sparse matrix. + #[inline] + pub fn values_mut(&mut self) -> impl Iterator { + self.data.values_mut() + } +} + +impl CsMatrix +where DefaultAllocator: Allocator +{ + pub(crate) fn sort(&mut self) + where DefaultAllocator: Allocator { + // Size = R + let nrows = self.data.shape().0; + let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows, U1) }; + self.sort_with_workspace(workspace.as_mut_slice()); + } + + pub(crate) fn sort_with_workspace(&mut self, workspace: &mut [N]) { + assert!( + workspace.len() >= self.nrows(), + "Workspace must be able to hold at least self.nrows() elements." + ); + + for j in 0..self.ncols() { + // Scatter the row in the workspace. + for (irow, val) in self.data.column_entries(j) { + workspace[irow] = val; + } + + // Sort the index vector. + let range = self.data.column_range(j); + self.data.i[range.clone()].sort(); + + // Permute the values too. + for (i, irow) in range.clone().zip(self.data.i[range].iter().cloned()) { + self.data.vals[i] = workspace[irow]; + } + } + } + + // Remove dupliate entries on a sorted CsMatrix. + pub(crate) fn dedup(&mut self) + where N: Zero + ClosedAdd { + let mut curr_i = 0; + + for j in 0..self.ncols() { + let range = self.data.column_range(j); + self.data.p[j] = curr_i; + + if range.start != range.end { + let mut value = N::zero(); + let mut irow = self.data.i[range.start]; + + for idx in range { + let curr_irow = self.data.i[idx]; + + if curr_irow == irow { + value += self.data.vals[idx]; + } else { + self.data.i[curr_i] = irow; + self.data.vals[curr_i] = value; + value = self.data.vals[idx]; + irow = curr_irow; + curr_i += 1; + } + } + + // Handle the last entry. + self.data.i[curr_i] = irow; + self.data.vals[curr_i] = value; + curr_i += 1; + } + } + + self.data.i.truncate(curr_i); + self.data.i.shrink_to_fit(); + self.data.vals.truncate(curr_i); + self.data.vals.shrink_to_fit(); + } +} diff --git a/src/sparse/cs_matrix_cholesky.rs b/src/sparse/cs_matrix_cholesky.rs new file mode 100644 index 00000000..5d834ef2 --- /dev/null +++ b/src/sparse/cs_matrix_cholesky.rs @@ -0,0 +1,408 @@ +use std::iter; +use std::mem; + +use allocator::Allocator; +use sparse::{CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsVecStorage}; +use {DefaultAllocator, Dim, Real, VectorN, U1}; + +/// The cholesky decomposition of a column compressed sparse matrix. +pub struct CsCholesky +where DefaultAllocator: Allocator + Allocator +{ + // Non-zero pattern of the original matrix upper-triangular part. + // Unlike the original matrix, the `original_p` array does contain the last sentinel value + // equal to `original_i.len()` at the end. + original_p: Vec, + original_i: Vec, + // Decomposition result. + l: CsMatrix, + // Used only for the pattern. + // FIXME: store only the nonzero pattern instead. + u: CsMatrix, + ok: bool, + // Workspaces. + work_x: VectorN, + work_c: VectorN, +} + +impl CsCholesky +where DefaultAllocator: Allocator + Allocator +{ + /// Computes the cholesky decomposition of the sparse matrix `m`. + pub fn new(m: &CsMatrix) -> Self { + let mut me = Self::new_symbolic(m); + let _ = me.decompose_left_looking(&m.data.vals); + me + } + /// Perform symbolic analysis for the given matrix. + /// + /// This does not access the numerical values of `m`. + pub fn new_symbolic(m: &CsMatrix) -> Self { + assert!( + m.is_square(), + "The matrix `m` must be square to compute its elimination tree." + ); + + let (l, u) = Self::nonzero_pattern(m); + + // Workspaces. + let work_x = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) }; + let work_c = unsafe { VectorN::new_uninitialized_generic(m.data.shape().1, U1) }; + let mut original_p = m.data.p.as_slice().to_vec(); + original_p.push(m.data.i.len()); + + CsCholesky { + original_p, + original_i: m.data.i.clone(), + l, + u, + ok: false, + work_x, + work_c, + } + } + + /// The lower-triangular matrix of the cholesky decomposition. + pub fn l(&self) -> Option<&CsMatrix> { + if self.ok { + Some(&self.l) + } else { + None + } + } + + /// Extracts the lower-triangular matrix of the cholesky decomposition. + pub fn unwrap_l(self) -> Option> { + if self.ok { + Some(self.l) + } else { + None + } + } + + /// Perform a numerical left-looking cholesky decomposition of a matrix with the same structure as the + /// one used to initialize `self`, but with different non-zero values provided by `values`. + pub fn decompose_left_looking(&mut self, values: &[N]) -> bool { + assert!( + values.len() >= self.original_i.len(), + "The set of values is too small." + ); + + let n = self.l.nrows(); + + // Reset `work_c` to the column pointers of `l`. + self.work_c.copy_from(&self.l.data.p); + + unsafe { + for k in 0..n { + // Scatter the k-th column of the original matrix with the values provided. + let range_k = + *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); + + *self.work_x.vget_unchecked_mut(k) = N::zero(); + for p in range_k.clone() { + let irow = *self.original_i.get_unchecked(p); + + if irow >= k { + *self.work_x.vget_unchecked_mut(irow) = *values.get_unchecked(p); + } + } + + for j in self.u.data.column_row_indices(k) { + let factor = -*self + .l + .data + .vals + .get_unchecked(*self.work_c.vget_unchecked(j)); + *self.work_c.vget_unchecked_mut(j) += 1; + + if j < k { + for (z, val) in self.l.data.column_entries(j) { + if z >= k { + *self.work_x.vget_unchecked_mut(z) += val * factor; + } + } + } + } + + let diag = *self.work_x.vget_unchecked(k); + + if diag > N::zero() { + let denom = diag.sqrt(); + *self + .l + .data + .vals + .get_unchecked_mut(*self.l.data.p.vget_unchecked(k)) = denom; + + for (p, val) in self.l.data.column_entries_mut(k) { + *val = *self.work_x.vget_unchecked(p) / denom; + *self.work_x.vget_unchecked_mut(p) = N::zero(); + } + } else { + self.ok = false; + return false; + } + } + } + + self.ok = true; + true + } + + /// Perform a numerical up-looking cholesky decomposition of a matrix with the same structure as the + /// one used to initialize `self`, but with different non-zero values provided by `values`. + pub fn decompose_up_looking(&mut self, values: &[N]) -> bool { + assert!( + values.len() >= self.original_i.len(), + "The set of values is too small." + ); + + // Reset `work_c` to the column pointers of `l`. + self.work_c.copy_from(&self.l.data.p); + + // Perform the decomposition. + for k in 0..self.l.nrows() { + unsafe { + // Scatter the k-th column of the original matrix with the values provided. + let column_range = + *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); + + *self.work_x.vget_unchecked_mut(k) = N::zero(); + for p in column_range.clone() { + let irow = *self.original_i.get_unchecked(p); + + if irow <= k { + *self.work_x.vget_unchecked_mut(irow) = *values.get_unchecked(p); + } + } + + let mut diag = *self.work_x.vget_unchecked(k); + *self.work_x.vget_unchecked_mut(k) = N::zero(); + + // Triangular solve. + for irow in self.u.data.column_row_indices(k) { + if irow >= k { + continue; + } + + let lki = *self.work_x.vget_unchecked(irow) + / *self + .l + .data + .vals + .get_unchecked(*self.l.data.p.vget_unchecked(irow)); + *self.work_x.vget_unchecked_mut(irow) = N::zero(); + + for p in + *self.l.data.p.vget_unchecked(irow) + 1..*self.work_c.vget_unchecked(irow) + { + *self + .work_x + .vget_unchecked_mut(*self.l.data.i.get_unchecked(p)) -= + *self.l.data.vals.get_unchecked(p) * lki; + } + + diag -= lki * lki; + let p = *self.work_c.vget_unchecked(irow); + *self.work_c.vget_unchecked_mut(irow) += 1; + *self.l.data.i.get_unchecked_mut(p) = k; + *self.l.data.vals.get_unchecked_mut(p) = lki; + } + + if diag <= N::zero() { + self.ok = false; + return false; + } + + // Deal with the diagonal element. + let p = *self.work_c.vget_unchecked(k); + *self.work_c.vget_unchecked_mut(k) += 1; + *self.l.data.i.get_unchecked_mut(p) = k; + *self.l.data.vals.get_unchecked_mut(p) = diag.sqrt(); + } + } + + self.ok = true; + true + } + + fn elimination_tree>(m: &CsMatrix) -> Vec { + let nrows = m.nrows(); + let mut forest: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); + let mut ancestor: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); + + for k in 0..nrows { + for irow in m.data.column_row_indices(k) { + let mut i = irow; + + while i < k { + let i_ancestor = ancestor[i]; + ancestor[i] = k; + + if i_ancestor == usize::max_value() { + forest[i] = k; + break; + } + + i = i_ancestor; + } + } + } + + forest + } + + fn reach>( + m: &CsMatrix, + j: usize, + max_j: usize, + tree: &[usize], + marks: &mut Vec, + out: &mut Vec, + ) + { + marks.clear(); + marks.resize(tree.len(), false); + + // FIXME: avoid all those allocations. + let mut tmp = Vec::new(); + let mut res = Vec::new(); + + for irow in m.data.column_row_indices(j) { + let mut curr = irow; + while curr != usize::max_value() && curr <= max_j && !marks[curr] { + marks[curr] = true; + tmp.push(curr); + curr = tree[curr]; + } + + tmp.append(&mut res); + mem::swap(&mut tmp, &mut res); + } + + out.append(&mut res); + } + + fn nonzero_pattern>( + m: &CsMatrix, + ) -> (CsMatrix, CsMatrix) { + let etree = Self::elimination_tree(m); + let (nrows, ncols) = m.data.shape(); + let mut rows = Vec::with_capacity(m.len()); + let mut cols = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) }; + let mut marks = Vec::new(); + + // NOTE: the following will actually compute the non-zero pattern of + // the transpose of l. + for i in 0..nrows.value() { + cols[i] = rows.len(); + Self::reach(m, i, i, &etree, &mut marks, &mut rows); + } + + let mut vals = Vec::with_capacity(rows.len()); + unsafe { + vals.set_len(rows.len()); + } + vals.shrink_to_fit(); + + let data = CsVecStorage { + shape: (nrows, ncols), + p: cols, + i: rows, + vals, + }; + + let u = CsMatrix::from_data(data); + // XXX: avoid this transpose. + let l = u.transpose(); + + (l, u) + } + + /* + * + * NOTE: All the following methods are untested and currently unused. + * + * + fn column_counts>( + m: &CsMatrix, + tree: &[usize], + ) -> Vec { + let len = m.data.shape().0.value(); + let mut counts: Vec<_> = iter::repeat(0).take(len).collect(); + let mut reach = Vec::new(); + let mut marks = Vec::new(); + + for i in 0..len { + Self::reach(m, i, i, tree, &mut marks, &mut reach); + + for j in reach.drain(..) { + counts[j] += 1; + } + } + + counts + } + + fn tree_postorder(tree: &[usize]) -> Vec { + // FIXME: avoid all those allocations? + let mut first_child: Vec<_> = iter::repeat(usize::max_value()).take(tree.len()).collect(); + let mut other_children: Vec<_> = + iter::repeat(usize::max_value()).take(tree.len()).collect(); + + // Build the children list from the parent list. + // The set of children of the node `i` is given by the linked list + // starting at `first_child[i]`. The nodes of this list are then: + // { first_child[i], other_children[first_child[i]], other_children[other_children[first_child[i]], ... } + for (i, parent) in tree.iter().enumerate() { + if *parent != usize::max_value() { + let brother = first_child[*parent]; + first_child[*parent] = i; + other_children[i] = brother; + } + } + + let mut stack = Vec::with_capacity(tree.len()); + let mut postorder = Vec::with_capacity(tree.len()); + + for (i, node) in tree.iter().enumerate() { + if *node == usize::max_value() { + Self::dfs( + i, + &mut first_child, + &other_children, + &mut stack, + &mut postorder, + ) + } + } + + postorder + } + + fn dfs( + i: usize, + first_child: &mut [usize], + other_children: &[usize], + stack: &mut Vec, + result: &mut Vec, + ) { + stack.clear(); + stack.push(i); + + while let Some(n) = stack.pop() { + let child = first_child[n]; + + if child == usize::max_value() { + // No children left. + result.push(n); + } else { + stack.push(n); + stack.push(child); + first_child[n] = other_children[child]; + } + } + } + */ +} diff --git a/src/sparse/cs_matrix_conversion.rs b/src/sparse/cs_matrix_conversion.rs new file mode 100644 index 00000000..0017340f --- /dev/null +++ b/src/sparse/cs_matrix_conversion.rs @@ -0,0 +1,114 @@ +use alga::general::ClosedAdd; +use num::Zero; + +use allocator::Allocator; +use sparse::cs_utils; +use sparse::{CsMatrix, CsStorage}; +use storage::Storage; +use {DefaultAllocator, Dim, Dynamic, Matrix, MatrixMN, Scalar}; + +impl<'a, N: Scalar + Zero + ClosedAdd> CsMatrix { + /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. + pub fn from_triplet( + nrows: usize, + ncols: usize, + irows: &[usize], + icols: &[usize], + vals: &[N], + ) -> Self + { + Self::from_triplet_generic(Dynamic::new(nrows), Dynamic::new(ncols), irows, icols, vals) + } +} + +impl<'a, N: Scalar + Zero + ClosedAdd, R: Dim, C: Dim> CsMatrix +where DefaultAllocator: Allocator + Allocator +{ + /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. + pub fn from_triplet_generic( + nrows: R, + ncols: C, + irows: &[usize], + icols: &[usize], + vals: &[N], + ) -> Self + { + assert!(vals.len() == irows.len()); + assert!(vals.len() == icols.len()); + + let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, vals.len()); + let mut workspace = res.data.p.clone(); + + // Column count. + for j in icols.iter().cloned() { + workspace[j] += 1; + } + + let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); + + // Fill i and vals. + for ((i, j), val) in irows + .iter() + .cloned() + .zip(icols.iter().cloned()) + .zip(vals.iter().cloned()) + { + let offset = workspace[j]; + res.data.i[offset] = i; + res.data.vals[offset] = val; + workspace[j] = offset + 1; + } + + // Sort the result. + res.sort(); + res.dedup(); + res + } +} + +impl<'a, N: Scalar + Zero, R: Dim, C: Dim, S> From> for MatrixMN +where + S: CsStorage, + DefaultAllocator: Allocator, +{ + fn from(m: CsMatrix) -> Self { + let (nrows, ncols) = m.data.shape(); + let mut res = MatrixMN::zeros_generic(nrows, ncols); + + for j in 0..ncols.value() { + for (i, val) in m.data.column_entries(j) { + res[(i, j)] = val; + } + } + + res + } +} + +impl<'a, N: Scalar + Zero, R: Dim, C: Dim, S> From> for CsMatrix +where + S: Storage, + DefaultAllocator: Allocator + Allocator, +{ + fn from(m: Matrix) -> Self { + let (nrows, ncols) = m.data.shape(); + let len = m.iter().filter(|e| !e.is_zero()).count(); + let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, len); + let mut nz = 0; + + for j in 0..ncols.value() { + let column = m.column(j); + res.data.p[j] = nz; + + for i in 0..nrows.value() { + if !column[i].is_zero() { + res.data.i[nz] = i; + res.data.vals[nz] = column[i]; + nz += 1; + } + } + } + + res + } +} diff --git a/src/sparse/cs_matrix_ops.rs b/src/sparse/cs_matrix_ops.rs new file mode 100644 index 00000000..47804847 --- /dev/null +++ b/src/sparse/cs_matrix_ops.rs @@ -0,0 +1,304 @@ +use alga::general::{ClosedAdd, ClosedMul}; +use num::{One, Zero}; +use std::ops::{Add, Mul}; + +use allocator::Allocator; +use constraint::{AreMultipliable, DimEq, ShapeConstraint}; +use sparse::{CsMatrix, CsStorage, CsStorageMut, CsVector}; +use storage::StorageMut; +use {DefaultAllocator, Dim, Scalar, Vector, VectorN, U1}; + +impl> CsMatrix { + fn scatter( + &self, + j: usize, + beta: N, + timestamps: &mut [usize], + timestamp: usize, + workspace: &mut [N], + mut nz: usize, + res: &mut CsMatrix, + ) -> usize + where + N: ClosedAdd + ClosedMul, + DefaultAllocator: Allocator, + { + for (i, val) in self.data.column_entries(j) { + if timestamps[i] < timestamp { + timestamps[i] = timestamp; + res.data.i[nz] = i; + nz += 1; + workspace[i] = val * beta; + } else { + workspace[i] += val * beta; + } + } + + nz + } +} + +/* +impl CsVector { + pub fn axpy(&mut self, alpha: N, x: CsVector, beta: N) { + // First, compute the number of non-zero entries. + let mut nnzero = 0; + + // Allocate a size large enough. + self.data.set_column_len(0, nnzero); + + // Fill with the axpy. + let mut i = self.len(); + let mut j = x.len(); + let mut k = nnzero - 1; + let mut rid1 = self.data.row_index(0, i - 1); + let mut rid2 = x.data.row_index(0, j - 1); + + while k > 0 { + if rid1 == rid2 { + self.data.set_row_index(0, k, rid1); + self[k] = alpha * x[j] + beta * self[k]; + i -= 1; + j -= 1; + } else if rid1 < rid2 { + self.data.set_row_index(0, k, rid1); + self[k] = beta * self[i]; + i -= 1; + } else { + self.data.set_row_index(0, k, rid2); + self[k] = alpha * x[j]; + j -= 1; + } + + k -= 1; + } + } +} +*/ + +impl> Vector { + /// Perform a sparse axpy operation: `self = alpha * x + beta * self` operation. + pub fn axpy_cs(&mut self, alpha: N, x: &CsVector, beta: N) + where + S2: CsStorage, + ShapeConstraint: DimEq, + { + if beta.is_zero() { + for i in 0..x.len() { + unsafe { + let k = x.data.row_index_unchecked(i); + let y = self.vget_unchecked_mut(k); + *y = alpha * *x.data.get_value_unchecked(i); + } + } + } else { + // Needed to be sure even components not present on `x` are multiplied. + *self *= beta; + + for i in 0..x.len() { + unsafe { + let k = x.data.row_index_unchecked(i); + let y = self.vget_unchecked_mut(k); + *y += alpha * *x.data.get_value_unchecked(i); + } + } + } + } + + /* + pub fn gemv_sparse(&mut self, alpha: N, a: &CsMatrix, x: &DVector, beta: N) + where + S2: CsStorage { + let col2 = a.column(0); + let val = unsafe { *x.vget_unchecked(0) }; + self.axpy_sparse(alpha * val, &col2, beta); + + for j in 1..ncols2 { + let col2 = a.column(j); + let val = unsafe { *x.vget_unchecked(j) }; + + self.axpy_sparse(alpha * val, &col2, N::one()); + } + } + */ +} + +impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Mul<&'b CsMatrix> + for &'a CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + Zero, + R1: Dim, + C1: Dim, + R2: Dim, + C2: Dim, + S1: CsStorage, + S2: CsStorage, + ShapeConstraint: AreMultipliable, + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Output = CsMatrix; + + fn mul(self, rhs: &'b CsMatrix) -> CsMatrix { + let (nrows1, ncols1) = self.data.shape(); + let (nrows2, ncols2) = rhs.data.shape(); + assert_eq!( + ncols1.value(), + nrows2.value(), + "Mismatched dimensions for matrix multiplication." + ); + + let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + let mut workspace = VectorN::::zeros_generic(nrows1, U1); + let mut nz = 0; + + for j in 0..ncols2.value() { + res.data.p[j] = nz; + let new_size_bound = nz + nrows1.value(); + res.data.i.resize(new_size_bound, 0); + res.data.vals.resize(new_size_bound, N::zero()); + + for (i, beta) in rhs.data.column_entries(j) { + for (k, val) in self.data.column_entries(i) { + workspace[k] += val * beta; + } + } + + for (i, val) in workspace.as_mut_slice().iter_mut().enumerate() { + if !val.is_zero() { + res.data.i[nz] = i; + res.data.vals[nz] = *val; + *val = N::zero(); + nz += 1; + } + } + } + + // NOTE: the following has a lower complexity, but is slower in many cases, likely because + // of branching inside of the inner loop. + // + // let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + // let mut timestamps = VectorN::zeros_generic(nrows1, U1); + // let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) }; + // let mut nz = 0; + // + // for j in 0..ncols2.value() { + // res.data.p[j] = nz; + // let new_size_bound = nz + nrows1.value(); + // res.data.i.resize(new_size_bound, 0); + // res.data.vals.resize(new_size_bound, N::zero()); + // + // for (i, val) in rhs.data.column_entries(j) { + // nz = self.scatter( + // i, + // val, + // timestamps.as_mut_slice(), + // j + 1, + // workspace.as_mut_slice(), + // nz, + // &mut res, + // ); + // } + // + // // Keep the output sorted. + // let range = res.data.p[j]..nz; + // res.data.i[range.clone()].sort(); + // + // for p in range { + // res.data.vals[p] = workspace[res.data.i[p]] + // } + // } + + res.data.i.truncate(nz); + res.data.i.shrink_to_fit(); + res.data.vals.truncate(nz); + res.data.vals.shrink_to_fit(); + res + } +} + +impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Add<&'b CsMatrix> + for &'a CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + One, + R1: Dim, + C1: Dim, + R2: Dim, + C2: Dim, + S1: CsStorage, + S2: CsStorage, + ShapeConstraint: DimEq + DimEq, + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Output = CsMatrix; + + fn add(self, rhs: &'b CsMatrix) -> CsMatrix { + let (nrows1, ncols1) = self.data.shape(); + let (nrows2, ncols2) = rhs.data.shape(); + assert_eq!( + (nrows1.value(), ncols1.value()), + (nrows2.value(), ncols2.value()), + "Mismatched dimensions for matrix sum." + ); + + let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + let mut timestamps = VectorN::zeros_generic(nrows1, U1); + let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) }; + let mut nz = 0; + + for j in 0..ncols2.value() { + res.data.p[j] = nz; + + nz = self.scatter( + j, + N::one(), + timestamps.as_mut_slice(), + j + 1, + workspace.as_mut_slice(), + nz, + &mut res, + ); + + nz = rhs.scatter( + j, + N::one(), + timestamps.as_mut_slice(), + j + 1, + workspace.as_mut_slice(), + nz, + &mut res, + ); + + // Keep the output sorted. + let range = res.data.p[j]..nz; + res.data.i[range.clone()].sort(); + + for p in range { + res.data.vals[p] = workspace[res.data.i[p]] + } + } + + res.data.i.truncate(nz); + res.data.i.shrink_to_fit(); + res.data.vals.truncate(nz); + res.data.vals.shrink_to_fit(); + res + } +} + +impl<'a, 'b, N, R, C, S> Mul for CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + Zero, + R: Dim, + C: Dim, + S: CsStorageMut, +{ + type Output = Self; + + fn mul(mut self, rhs: N) -> Self { + for e in self.values_mut() { + *e *= rhs + } + + self + } +} diff --git a/src/sparse/cs_matrix_solve.rs b/src/sparse/cs_matrix_solve.rs new file mode 100644 index 00000000..2a13188e --- /dev/null +++ b/src/sparse/cs_matrix_solve.rs @@ -0,0 +1,283 @@ +use allocator::Allocator; +use constraint::{SameNumberOfRows, ShapeConstraint}; +use sparse::{CsMatrix, CsStorage, CsVector}; +use storage::{Storage, StorageMut}; +use {DefaultAllocator, Dim, Matrix, MatrixMN, Real, VectorN, U1}; + +impl> CsMatrix { + /// Solve a lower-triangular system with a dense right-hand-side. + pub fn solve_lower_triangular( + &self, + b: &Matrix, + ) -> Option> + where + S2: Storage, + DefaultAllocator: Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut b = b.clone_owned(); + if self.solve_lower_triangular_mut(&mut b) { + Some(b) + } else { + None + } + } + + /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. + pub fn tr_solve_lower_triangular( + &self, + b: &Matrix, + ) -> Option> + where + S2: Storage, + DefaultAllocator: Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut b = b.clone_owned(); + if self.tr_solve_lower_triangular_mut(&mut b) { + Some(b) + } else { + None + } + } + + /// Solve in-place a lower-triangular system with a dense right-hand-side. + pub fn solve_lower_triangular_mut( + &self, + b: &mut Matrix, + ) -> bool + where + S2: StorageMut, + ShapeConstraint: SameNumberOfRows, + { + let (nrows, ncols) = self.data.shape(); + assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); + assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); + + for j2 in 0..b.ncols() { + let mut b = b.column_mut(j2); + + for j in 0..ncols.value() { + let mut column = self.data.column_entries(j); + let mut diag_found = false; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + return false; + } + + b[j] /= val; + diag_found = true; + break; + } + } + + if !diag_found { + return false; + } + + for (i, val) in column { + b[i] -= b[j] * val; + } + } + } + + true + } + + /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. + pub fn tr_solve_lower_triangular_mut( + &self, + b: &mut Matrix, + ) -> bool + where + S2: StorageMut, + ShapeConstraint: SameNumberOfRows, + { + let (nrows, ncols) = self.data.shape(); + assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); + assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); + + for j2 in 0..b.ncols() { + let mut b = b.column_mut(j2); + + for j in (0..ncols.value()).rev() { + let mut column = self.data.column_entries(j); + let mut diag = None; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + return false; + } + + diag = Some(val); + break; + } + } + + if let Some(diag) = diag { + for (i, val) in column { + b[j] -= val * b[i]; + } + + b[j] /= diag; + } else { + return false; + } + } + } + + true + } + + /// Solve a lower-triangular system with a sparse right-hand-side. + pub fn solve_lower_triangular_cs( + &self, + b: &CsVector, + ) -> Option> + where + S2: CsStorage, + DefaultAllocator: Allocator + Allocator + Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut reach = Vec::new(); + // 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(); + let mut workspace = unsafe { VectorN::new_uninitialized_generic(b.data.shape().0, U1) }; + + for i in reach.iter().cloned() { + workspace[i] = N::zero(); + } + + for (i, val) in b.data.column_entries(0) { + workspace[i] = val; + } + + for j in reach.iter().cloned() { + let mut column = self.data.column_entries(j); + let mut diag_found = false; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + break; + } + + workspace[j] /= val; + diag_found = true; + break; + } + } + + if !diag_found { + return None; + } + + for (i, val) in column { + workspace[i] -= workspace[j] * val; + } + } + + // Copy the result into a sparse vector. + let mut result = CsVector::new_uninitialized_generic(b.data.shape().0, U1, reach.len()); + + for (i, val) in reach.iter().zip(result.data.vals.iter_mut()) { + *val = workspace[*i]; + } + + result.data.i = reach; + Some(result) + } + + /* + // Computes the reachable, post-ordered, nodes from `b`. + fn lower_triangular_reach_postordered( + &self, + b: &CsVector, + xi: &mut Vec, + ) where + S2: CsStorage, + DefaultAllocator: Allocator, + { + let mut visited = VectorN::repeat_generic(self.data.shape().1, U1, false); + let mut stack = Vec::new(); + + for i in b.data.column_range(0) { + let row_index = b.data.row_index(i); + + if !visited[row_index] { + let rng = self.data.column_range(row_index); + stack.push((row_index, rng)); + self.lower_triangular_dfs(visited.as_mut_slice(), &mut stack, xi); + } + } + } + + fn lower_triangular_dfs( + &self, + visited: &mut [bool], + stack: &mut Vec<(usize, Range)>, + xi: &mut Vec, + ) + { + 'recursion: while let Some((j, rng)) = stack.pop() { + visited[j] = true; + + for i in rng.clone() { + let row_id = self.data.row_index(i); + if row_id > j && !visited[row_id] { + stack.push((j, (i + 1)..rng.end)); + stack.push((row_id, self.data.column_range(row_id))); + continue 'recursion; + } + } + + xi.push(j) + } + } + */ + + // Computes the nodes reachable from `b` in an arbitrary order. + fn lower_triangular_reach(&self, b: &CsVector, xi: &mut Vec) + where + S2: CsStorage, + DefaultAllocator: Allocator, + { + let mut visited = VectorN::repeat_generic(self.data.shape().1, U1, false); + let mut stack = Vec::new(); + + for irow in b.data.column_row_indices(0) { + self.lower_triangular_bfs(irow, visited.as_mut_slice(), &mut stack, xi); + } + } + + fn lower_triangular_bfs( + &self, + start: usize, + visited: &mut [bool], + stack: &mut Vec, + xi: &mut Vec, + ) + { + if !visited[start] { + stack.clear(); + stack.push(start); + xi.push(start); + visited[start] = true; + + while let Some(j) = stack.pop() { + for irow in self.data.column_row_indices(j) { + if irow > j && !visited[irow] { + stack.push(irow); + xi.push(irow); + visited[irow] = true; + } + } + } + } + } +} diff --git a/src/sparse/cs_utils.rs b/src/sparse/cs_utils.rs new file mode 100644 index 00000000..3c5db43e --- /dev/null +++ b/src/sparse/cs_utils.rs @@ -0,0 +1,16 @@ +use allocator::Allocator; +use {DefaultAllocator, Dim, VectorN}; + +pub fn cumsum(a: &mut VectorN, b: &mut VectorN) -> usize +where DefaultAllocator: Allocator { + assert!(a.len() == b.len()); + let mut sum = 0; + + for i in 0..a.len() { + b[i] = sum; + sum += a[i]; + a[i] = b[i]; + } + + sum +} diff --git a/src/sparse/mod.rs b/src/sparse/mod.rs new file mode 100644 index 00000000..5df2d75d --- /dev/null +++ b/src/sparse/mod.rs @@ -0,0 +1,13 @@ +//! Sparse matrices. + +pub use self::cs_matrix::{ + CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsStorageMut, CsVecStorage, CsVector, +}; +pub use self::cs_matrix_cholesky::CsCholesky; + +mod cs_matrix; +mod cs_matrix_cholesky; +mod cs_matrix_conversion; +mod cs_matrix_ops; +mod cs_matrix_solve; +pub(crate) mod cs_utils; diff --git a/tests/core/edition.rs b/tests/core/edition.rs index edaf2f08..37452a2d 100644 --- a/tests/core/edition.rs +++ b/tests/core/edition.rs @@ -565,4 +565,4 @@ fn resize_empty_matrix() { assert_eq!(m1, m5.resize(0, 0, 42)); assert_eq!(m1, m6.resize(0, 0, 42)); assert_eq!(m1, m7.resize(0, 0, 42)); -} \ No newline at end of file +} diff --git a/tests/core/matrix.rs b/tests/core/matrix.rs index 9cf2a66b..9c6d468a 100644 --- a/tests/core/matrix.rs +++ b/tests/core/matrix.rs @@ -195,10 +195,10 @@ fn from_columns() { #[test] fn from_columns_dynamic() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32]), - DVector::from_row_slice(3, &[13, 23, 33]), - DVector::from_row_slice(3, &[14, 24, 34]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32]), + DVector::from_row_slice(&[13, 23, 33]), + DVector::from_row_slice(&[14, 24, 34]), ]; let expected = DMatrix::from_row_slice(3, 4, &[11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]); @@ -233,8 +233,8 @@ fn from_not_enough_columns() { #[should_panic] fn from_rows_with_different_dimensions() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32, 33]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32, 33]), ]; let _ = DMatrix::from_columns(columns); @@ -272,11 +272,31 @@ fn to_homogeneous() { let a = Vector3::new(1.0, 2.0, 3.0); let expected_a = Vector4::new(1.0, 2.0, 3.0, 0.0); - let b = DVector::from_row_slice(3, &[1.0, 2.0, 3.0]); - let expected_b = DVector::from_row_slice(4, &[1.0, 2.0, 3.0, 0.0]); + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 0.0]); + + let c = Matrix2::new(1.0, 2.0, 3.0, 4.0); + let expected_c = Matrix3::new(1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0); + + let d = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 3.0, 4.0]); + let expected_d = DMatrix::from_row_slice(3, 3, &[1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0]); assert_eq!(a.to_homogeneous(), expected_a); assert_eq!(b.to_homogeneous(), expected_b); + assert_eq!(c.to_homogeneous(), expected_c); + assert_eq!(d.to_homogeneous(), expected_d); +} + +#[test] +fn push() { + let a = Vector3::new(1.0, 2.0, 3.0); + let expected_a = Vector4::new(1.0, 2.0, 3.0, 4.0); + + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 4.0]); + + assert_eq!(a.push(4.0), expected_a); + assert_eq!(b.push(4.0), expected_b); } #[test] diff --git a/tests/geometry/isometry.rs b/tests/geometry/isometry.rs index c72a2475..a0e00272 100644 --- a/tests/geometry/isometry.rs +++ b/tests/geometry/isometry.rs @@ -25,27 +25,35 @@ quickcheck!( let viewmatrix = Isometry3::look_at_rh(&eye, &target, &up); let origin = Point3::origin(); - relative_eq!(viewmatrix * eye, origin, epsilon = 1.0e-7) && - relative_eq!((viewmatrix * (target - eye)).normalize(), -Vector3::z(), epsilon = 1.0e-7) + relative_eq!(viewmatrix * eye, origin, epsilon = 1.0e-7) + && relative_eq!( + (viewmatrix * (target - eye)).normalize(), + -Vector3::z(), + epsilon = 1.0e-7 + ) } fn observer_frame_3(eye: Point3, target: Point3, up: Vector3) -> bool { - let observer = Isometry3::new_observer_frame(&eye, &target, &up); + let observer = Isometry3::face_towards(&eye, &target, &up); let origin = Point3::origin(); - relative_eq!(observer * origin, eye, epsilon = 1.0e-7) && - relative_eq!(observer * Vector3::z(), (target - eye).normalize(), epsilon = 1.0e-7) + relative_eq!(observer * origin, eye, epsilon = 1.0e-7) + && relative_eq!( + observer * Vector3::z(), + (target - eye).normalize(), + epsilon = 1.0e-7 + ) } fn inverse_is_identity(i: Isometry3, p: Point3, v: Vector3) -> bool { let ii = i.inverse(); - relative_eq!(i * ii, Isometry3::identity(), epsilon = 1.0e-7) && - relative_eq!(ii * i, Isometry3::identity(), epsilon = 1.0e-7) && - relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && - relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && - relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && - relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) + relative_eq!(i * ii, Isometry3::identity(), epsilon = 1.0e-7) + && relative_eq!(ii * i, Isometry3::identity(), epsilon = 1.0e-7) + && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) + && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) + && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) + && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) } fn inverse_is_parts_inversion(t: Translation3, r: UnitQuaternion) -> bool { @@ -54,14 +62,29 @@ quickcheck!( } fn multiply_equals_alga_transform(i: Isometry3, v: Vector3, p: Point3) -> bool { - i * v == i.transform_vector(&v) && - i * p == i.transform_point(&p) && - relative_eq!(i.inverse() * v, i.inverse_transform_vector(&v), epsilon = 1.0e-7) && - relative_eq!(i.inverse() * p, i.inverse_transform_point(&p), epsilon = 1.0e-7) + i * v == i.transform_vector(&v) + && i * p == i.transform_point(&p) + && relative_eq!( + i.inverse() * v, + i.inverse_transform_vector(&v), + epsilon = 1.0e-7 + ) + && relative_eq!( + i.inverse() * p, + i.inverse_transform_point(&p), + epsilon = 1.0e-7 + ) } - fn composition2(i: Isometry2, uc: UnitComplex, r: Rotation2, - t: Translation2, v: Vector2, p: Point2) -> bool { + fn composition2( + i: Isometry2, + uc: UnitComplex, + r: Rotation2, + t: Translation2, + v: Vector2, + p: Point2, + ) -> bool + { // (rotation × translation) * point = rotation × (translation * point) relative_eq!((uc * t) * v, uc * v, epsilon = 1.0e-7) && relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7) && @@ -91,8 +114,15 @@ quickcheck!( relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7) } - fn composition3(i: Isometry3, uq: UnitQuaternion, r: Rotation3, - t: Translation3, v: Vector3, p: Point3) -> bool { + fn composition3( + i: Isometry3, + uq: UnitQuaternion, + r: Rotation3, + t: Translation3, + v: Vector3, + p: Point3, + ) -> bool + { // (rotation × translation) * point = rotation × (translation * point) relative_eq!((uq * t) * v, uq * v, epsilon = 1.0e-7) && relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7) && @@ -122,11 +152,18 @@ quickcheck!( relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7) } - fn all_op_exist(i: Isometry3, uq: UnitQuaternion, t: Translation3, - v: Vector3, p: Point3, r: Rotation3) -> bool { - let iMi = i * i; + fn all_op_exist( + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + r: Rotation3, + ) -> bool + { + let iMi = i * i; let iMuq = i * uq; - let iDi = i / i; + let iDi = i / i; let iDuq = i / uq; let iMp = i * p; @@ -135,13 +172,13 @@ quickcheck!( let iMt = i * t; let tMi = t * i; - let tMr = t * r; + let tMr = t * r; let tMuq = t * uq; let uqMi = uq * i; let uqDi = uq / i; - let rMt = r * t; + let rMt = r * t; let uqMt = uq * t; let mut iMt1 = i; @@ -174,75 +211,57 @@ quickcheck!( iDuq1 /= uq; iDuq2 /= &uq; - iMt == iMt1 && - iMt == iMt2 && - - iMi == iMi1 && - iMi == iMi2 && - - iMuq == iMuq1 && - iMuq == iMuq2 && - - iDi == iDi1 && - iDi == iDi2 && - - iDuq == iDuq1 && - iDuq == iDuq2 && - - iMi == &i * &i && - iMi == i * &i && - iMi == &i * i && - - iMuq == &i * &uq && - iMuq == i * &uq && - iMuq == &i * uq && - - iDi == &i / &i && - iDi == i / &i && - iDi == &i / i && - - iDuq == &i / &uq && - iDuq == i / &uq && - iDuq == &i / uq && - - iMp == &i * &p && - iMp == i * &p && - iMp == &i * p && - - iMv == &i * &v && - iMv == i * &v && - iMv == &i * v && - - iMt == &i * &t && - iMt == i * &t && - iMt == &i * t && - - tMi == &t * &i && - tMi == t * &i && - tMi == &t * i && - - tMr == &t * &r && - tMr == t * &r && - tMr == &t * r && - - tMuq == &t * &uq && - tMuq == t * &uq && - tMuq == &t * uq && - - uqMi == &uq * &i && - uqMi == uq * &i && - uqMi == &uq * i && - - uqDi == &uq / &i && - uqDi == uq / &i && - uqDi == &uq / i && - - rMt == &r * &t && - rMt == r * &t && - rMt == &r * t && - - uqMt == &uq * &t && - uqMt == uq * &t && - uqMt == &uq * t + iMt == iMt1 + && iMt == iMt2 + && iMi == iMi1 + && iMi == iMi2 + && iMuq == iMuq1 + && iMuq == iMuq2 + && iDi == iDi1 + && iDi == iDi2 + && iDuq == iDuq1 + && iDuq == iDuq2 + && iMi == &i * &i + && iMi == i * &i + && iMi == &i * i + && iMuq == &i * &uq + && iMuq == i * &uq + && iMuq == &i * uq + && iDi == &i / &i + && iDi == i / &i + && iDi == &i / i + && iDuq == &i / &uq + && iDuq == i / &uq + && iDuq == &i / uq + && iMp == &i * &p + && iMp == i * &p + && iMp == &i * p + && iMv == &i * &v + && iMv == i * &v + && iMv == &i * v + && iMt == &i * &t + && iMt == i * &t + && iMt == &i * t + && tMi == &t * &i + && tMi == t * &i + && tMi == &t * i + && tMr == &t * &r + && tMr == t * &r + && tMr == &t * r + && tMuq == &t * &uq + && tMuq == t * &uq + && tMuq == &t * uq + && uqMi == &uq * &i + && uqMi == uq * &i + && uqMi == &uq * i + && uqDi == &uq / &i + && uqDi == uq / &i + && uqDi == &uq / i + && rMt == &r * &t + && rMt == r * &t + && rMt == &r * t + && uqMt == &uq * &t + && uqMt == uq * &t + && uqMt == &uq * t } ); diff --git a/tests/geometry/projection.rs b/tests/geometry/projection.rs index 17d04a13..0f38bc37 100644 --- a/tests/geometry/projection.rs +++ b/tests/geometry/projection.rs @@ -5,7 +5,7 @@ fn perspective_inverse() { let proj = Perspective3::new(800.0 / 600.0, 3.14 / 2.0, 1.0, 1000.0); let inv = proj.inverse(); - let id = inv * proj.unwrap(); + let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } @@ -15,7 +15,7 @@ fn orthographic_inverse() { let proj = Orthographic3::new(1.0, 2.0, -3.0, -2.5, 10.0, 900.0); let inv = proj.inverse(); - let id = inv * proj.unwrap(); + let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } diff --git a/tests/geometry/quaternion.rs b/tests/geometry/quaternion.rs index c5b91554..4dd3da62 100644 --- a/tests/geometry/quaternion.rs +++ b/tests/geometry/quaternion.rs @@ -26,9 +26,9 @@ quickcheck!( relative_eq!(yaw * pitch * roll, rpy, epsilon = 1.0e-7) } - fn to_euler_angles(r: f64, p: f64, y: f64) -> bool { + fn euler_angles(r: f64, p: f64, y: f64) -> bool { let rpy = UnitQuaternion::from_euler_angles(r, p, y); - let (roll, pitch, yaw) = rpy.to_euler_angles(); + let (roll, pitch, yaw) = rpy.euler_angles(); relative_eq!(UnitQuaternion::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7) } diff --git a/tests/geometry/rotation.rs b/tests/geometry/rotation.rs index 9bd3e590..e4b1f9d7 100644 --- a/tests/geometry/rotation.rs +++ b/tests/geometry/rotation.rs @@ -1,4 +1,4 @@ -use na::{Vector2, Vector3}; +use na::{Quaternion, Real, UnitQuaternion, Vector2, Vector3}; #[test] fn angle_2() { @@ -16,6 +16,20 @@ fn angle_3() { assert_eq!(a.angle(&b), 0.0); } +#[test] +fn quaternion_euler_angles_issue_494() { + let quat = UnitQuaternion::from_quaternion(Quaternion::new( + -0.10405792, + -0.6993922f32, + -0.10406871, + 0.69942284, + )); + let angs = quat.euler_angles(); + assert_eq!(angs.0, 2.8461843); + assert_eq!(angs.1, f32::frac_pi_2()); + assert_eq!(angs.2, 0.0); +} + #[cfg(feature = "arbitrary")] mod quickcheck_tests { use alga::general::Real; @@ -41,17 +55,17 @@ mod quickcheck_tests { yaw * pitch * roll == rpy } - fn to_euler_angles(r: f64, p: f64, y: f64) -> bool { + fn euler_angles(r: f64, p: f64, y: f64) -> bool { let rpy = Rotation3::from_euler_angles(r, p, y); - let (roll, pitch, yaw) = rpy.to_euler_angles(); + let (roll, pitch, yaw) = rpy.euler_angles(); relative_eq!(Rotation3::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7) } - fn to_euler_angles_gimble_lock(r: f64, y: f64) -> bool { + fn euler_angles_gimble_lock(r: f64, y: f64) -> bool { let pos = Rotation3::from_euler_angles(r, f64::frac_pi_2(), y); let neg = Rotation3::from_euler_angles(r, -f64::frac_pi_2(), y); - let (pos_r, pos_p, pos_y) = pos.to_euler_angles(); - let (neg_r, neg_p, neg_y) = neg.to_euler_angles(); + let (pos_r, pos_p, pos_y) = pos.euler_angles(); + let (neg_r, neg_p, neg_y) = neg.euler_angles(); relative_eq!(Rotation3::from_euler_angles(pos_r, pos_p, pos_y), pos, epsilon = 1.0e-7) && relative_eq!(Rotation3::from_euler_angles(neg_r, neg_p, neg_y), neg, epsilon = 1.0e-7) } diff --git a/tests/geometry/similarity.rs b/tests/geometry/similarity.rs index e9fde466..68b86943 100644 --- a/tests/geometry/similarity.rs +++ b/tests/geometry/similarity.rs @@ -8,33 +8,57 @@ quickcheck!( fn inverse_is_identity(i: Similarity3, p: Point3, v: Vector3) -> bool { let ii = i.inverse(); - relative_eq!(i * ii, Similarity3::identity(), epsilon = 1.0e-7) && - relative_eq!(ii * i, Similarity3::identity(), epsilon = 1.0e-7) && - relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && - relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && - relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && - relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) + relative_eq!(i * ii, Similarity3::identity(), epsilon = 1.0e-7) + && relative_eq!(ii * i, Similarity3::identity(), epsilon = 1.0e-7) + && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) + && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) + && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) + && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) } - fn inverse_is_parts_inversion(t: Translation3, r: UnitQuaternion, scaling: f64) -> bool { + fn inverse_is_parts_inversion( + t: Translation3, + r: UnitQuaternion, + scaling: f64, + ) -> bool + { if relative_eq!(scaling, 0.0) { true - } - else { + } else { let s = Similarity3::from_isometry(t * r, scaling); s.inverse() == Similarity3::from_scaling(1.0 / scaling) * r.inverse() * t.inverse() } } - fn multiply_equals_alga_transform(s: Similarity3, v: Vector3, p: Point3) -> bool { - s * v == s.transform_vector(&v) && - s * p == s.transform_point(&p) && - relative_eq!(s.inverse() * v, s.inverse_transform_vector(&v), epsilon = 1.0e-7) && - relative_eq!(s.inverse() * p, s.inverse_transform_point(&p), epsilon = 1.0e-7) + fn multiply_equals_alga_transform( + s: Similarity3, + v: Vector3, + p: Point3, + ) -> bool + { + s * v == s.transform_vector(&v) + && s * p == s.transform_point(&p) + && relative_eq!( + s.inverse() * v, + s.inverse_transform_vector(&v), + epsilon = 1.0e-7 + ) + && relative_eq!( + s.inverse() * p, + s.inverse_transform_point(&p), + epsilon = 1.0e-7 + ) } - fn composition(i: Isometry3, uq: UnitQuaternion, - t: Translation3, v: Vector3, p: Point3, scaling: f64) -> bool { + fn composition( + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + scaling: f64, + ) -> bool + { if relative_eq!(scaling, 0.0) { return true; } @@ -122,11 +146,18 @@ quickcheck!( relative_eq!((s * i * t) * p, scaling * (i * (t * p)), epsilon = 1.0e-7) } - fn all_op_exist(s: Similarity3, i: Isometry3, uq: UnitQuaternion, - t: Translation3, v: Vector3, p: Point3) -> bool { - let sMs = s * s; + fn all_op_exist( + s: Similarity3, + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + ) -> bool + { + let sMs = s * s; let sMuq = s * uq; - let sDs = s / s; + let sDs = s / s; let sDuq = s / uq; let sMp = s * p; @@ -186,81 +217,61 @@ quickcheck!( sDi1 /= i; sDi2 /= &i; - sMt == sMt1 && - sMt == sMt2 && - - sMs == sMs1 && - sMs == sMs2 && - - sMuq == sMuq1 && - sMuq == sMuq2 && - - sMi == sMi1 && - sMi == sMi2 && - - sDs == sDs1 && - sDs == sDs2 && - - sDuq == sDuq1 && - sDuq == sDuq2 && - - sDi == sDi1 && - sDi == sDi2 && - - sMs == &s * &s && - sMs == s * &s && - sMs == &s * s && - - sMuq == &s * &uq && - sMuq == s * &uq && - sMuq == &s * uq && - - sDs == &s / &s && - sDs == s / &s && - sDs == &s / s && - - sDuq == &s / &uq && - sDuq == s / &uq && - sDuq == &s / uq && - - sMp == &s * &p && - sMp == s * &p && - sMp == &s * p && - - sMv == &s * &v && - sMv == s * &v && - sMv == &s * v && - - sMt == &s * &t && - sMt == s * &t && - sMt == &s * t && - - tMs == &t * &s && - tMs == t * &s && - tMs == &t * s && - - uqMs == &uq * &s && - uqMs == uq * &s && - uqMs == &uq * s && - - uqDs == &uq / &s && - uqDs == uq / &s && - uqDs == &uq / s && - - sMi == &s * &i && - sMi == s * &i && - sMi == &s * i && - - sDi == &s / &i && - sDi == s / &i && - sDi == &s / i && - - iMs == &i * &s && - iMs == i * &s && - iMs == &i * s && - - iDs == &i / &s && - iDs == i / &s && - iDs == &i / s + sMt == sMt1 + && sMt == sMt2 + && sMs == sMs1 + && sMs == sMs2 + && sMuq == sMuq1 + && sMuq == sMuq2 + && sMi == sMi1 + && sMi == sMi2 + && sDs == sDs1 + && sDs == sDs2 + && sDuq == sDuq1 + && sDuq == sDuq2 + && sDi == sDi1 + && sDi == sDi2 + && sMs == &s * &s + && sMs == s * &s + && sMs == &s * s + && sMuq == &s * &uq + && sMuq == s * &uq + && sMuq == &s * uq + && sDs == &s / &s + && sDs == s / &s + && sDs == &s / s + && sDuq == &s / &uq + && sDuq == s / &uq + && sDuq == &s / uq + && sMp == &s * &p + && sMp == s * &p + && sMp == &s * p + && sMv == &s * &v + && sMv == s * &v + && sMv == &s * v + && sMt == &s * &t + && sMt == s * &t + && sMt == &s * t + && tMs == &t * &s + && tMs == t * &s + && tMs == &t * s + && uqMs == &uq * &s + && uqMs == uq * &s + && uqMs == &uq * s + && uqDs == &uq / &s + && uqDs == uq / &s + && uqDs == &uq / s + && sMi == &s * &i + && sMi == s * &i + && sMi == &s * i + && sDi == &s / &i + && sDi == s / &i + && sDi == &s / i + && iMs == &i * &s + && iMs == i * &s + && iMs == &i * s + && iDs == &i / &s + && iDs == i / &s + && iDs == &i / s } ); diff --git a/tests/geometry/unit_complex.rs b/tests/geometry/unit_complex.rs index 7da0d20c..88988aa8 100644 --- a/tests/geometry/unit_complex.rs +++ b/tests/geometry/unit_complex.rs @@ -4,19 +4,17 @@ use na::{Point2, Rotation2, Unit, UnitComplex, Vector2}; quickcheck!( - /* * * From/to rotation matrix. * */ fn unit_complex_rotation_conversion(c: UnitComplex) -> bool { - let r = c.to_rotation_matrix(); + let r = c.to_rotation_matrix(); let cc = UnitComplex::from_rotation_matrix(&r); let rr = cc.to_rotation_matrix(); - relative_eq!(c, cc, epsilon = 1.0e-7) && - relative_eq!(r, rr, epsilon = 1.0e-7) + relative_eq!(c, cc, epsilon = 1.0e-7) && relative_eq!(r, rr, epsilon = 1.0e-7) } /* @@ -25,19 +23,18 @@ quickcheck!( * */ fn unit_complex_transformation(c: UnitComplex, v: Vector2, p: Point2) -> bool { - let r = c.to_rotation_matrix(); + let r = c.to_rotation_matrix(); let rv = r * v; let rp = r * p; - relative_eq!( c * v, rv, epsilon = 1.0e-7) && - relative_eq!( c * &v, rv, epsilon = 1.0e-7) && - relative_eq!(&c * v, rv, epsilon = 1.0e-7) && - relative_eq!(&c * &v, rv, epsilon = 1.0e-7) && - - relative_eq!( c * p, rp, epsilon = 1.0e-7) && - relative_eq!( c * &p, rp, epsilon = 1.0e-7) && - relative_eq!(&c * p, rp, epsilon = 1.0e-7) && - relative_eq!(&c * &p, rp, epsilon = 1.0e-7) + relative_eq!(c * v, rv, epsilon = 1.0e-7) + && relative_eq!(c * &v, rv, epsilon = 1.0e-7) + && relative_eq!(&c * v, rv, epsilon = 1.0e-7) + && relative_eq!(&c * &v, rv, epsilon = 1.0e-7) + && relative_eq!(c * p, rp, epsilon = 1.0e-7) + && relative_eq!(c * &p, rp, epsilon = 1.0e-7) + && relative_eq!(&c * p, rp, epsilon = 1.0e-7) + && relative_eq!(&c * &p, rp, epsilon = 1.0e-7) } /* @@ -47,15 +44,14 @@ quickcheck!( */ fn unit_complex_inv(c: UnitComplex) -> bool { let iq = c.inverse(); - relative_eq!(&iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!(&iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && - - relative_eq!(&c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!(&c * iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( c * iq, UnitComplex::identity(), epsilon = 1.0e-7) + relative_eq!(&iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&iq * c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(iq * c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&c * iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(c * iq, UnitComplex::identity(), epsilon = 1.0e-7) } /* @@ -66,25 +62,30 @@ quickcheck!( fn unit_complex_mul_vector(c: UnitComplex, v: Vector2, p: Point2) -> bool { let r = c.to_rotation_matrix(); - relative_eq!(c * v, r * v, epsilon = 1.0e-7) && - relative_eq!(c * p, r * p, epsilon = 1.0e-7) + relative_eq!(c * v, r * v, epsilon = 1.0e-7) && relative_eq!(c * p, r * p, epsilon = 1.0e-7) } // Test that all operators (incl. all combinations of references) work. // See the top comment on `geometry/quaternion_ops.rs` for details on which operations are // supported. - fn all_op_exist(uc: UnitComplex, v: Vector2, p: Point2, r: Rotation2) -> bool { + fn all_op_exist( + uc: UnitComplex, + v: Vector2, + p: Point2, + r: Rotation2, + ) -> bool + { let uv = Unit::new_normalize(v); let ucMuc = uc * uc; - let ucMr = uc * r; - let rMuc = r * uc; + let ucMr = uc * r; + let rMuc = r * uc; let ucDuc = uc / uc; - let ucDr = uc / r; - let rDuc = r / uc; + let ucDr = uc / r; + let rDuc = r / uc; - let ucMp = uc * p; - let ucMv = uc * v; + let ucMp = uc * p; + let ucMv = uc * v; let ucMuv = uc * uv; let mut ucMuc1 = uc; @@ -111,52 +112,40 @@ quickcheck!( ucDr1 /= r; ucDr2 /= &r; - ucMuc1 == ucMuc && - ucMuc1 == ucMuc2 && - - ucMr1 == ucMr && - ucMr1 == ucMr2 && - - ucDuc1 == ucDuc && - ucDuc1 == ucDuc2 && - - ucDr1 == ucDr && - ucDr1 == ucDr2 && - - ucMuc == &uc * &uc && - ucMuc == uc * &uc && - ucMuc == &uc * uc && - - ucMr == &uc * &r && - ucMr == uc * &r && - ucMr == &uc * r && - - rMuc == &r * &uc && - rMuc == r * &uc && - rMuc == &r * uc && - - ucDuc == &uc / &uc && - ucDuc == uc / &uc && - ucDuc == &uc / uc && - - ucDr == &uc / &r && - ucDr == uc / &r && - ucDr == &uc / r && - - rDuc == &r / &uc && - rDuc == r / &uc && - rDuc == &r / uc && - - ucMp == &uc * &p && - ucMp == uc * &p && - ucMp == &uc * p && - - ucMv == &uc * &v && - ucMv == uc * &v && - ucMv == &uc * v && - - ucMuv == &uc * &uv && - ucMuv == uc * &uv && - ucMuv == &uc * uv + ucMuc1 == ucMuc + && ucMuc1 == ucMuc2 + && ucMr1 == ucMr + && ucMr1 == ucMr2 + && ucDuc1 == ucDuc + && ucDuc1 == ucDuc2 + && ucDr1 == ucDr + && ucDr1 == ucDr2 + && ucMuc == &uc * &uc + && ucMuc == uc * &uc + && ucMuc == &uc * uc + && ucMr == &uc * &r + && ucMr == uc * &r + && ucMr == &uc * r + && rMuc == &r * &uc + && rMuc == r * &uc + && rMuc == &r * uc + && ucDuc == &uc / &uc + && ucDuc == uc / &uc + && ucDuc == &uc / uc + && ucDr == &uc / &r + && ucDr == uc / &r + && ucDr == &uc / r + && rDuc == &r / &uc + && rDuc == r / &uc + && rDuc == &r / uc + && ucMp == &uc * &p + && ucMp == uc * &p + && ucMp == &uc * p + && ucMv == &uc * &v + && ucMv == uc * &v + && ucMv == &uc * v + && ucMuv == &uc * &uv + && ucMuv == uc * &uv + && ucMuv == &uc * uv } ); diff --git a/tests/lib.rs b/tests/lib.rs index bfe95a5e..c32f4066 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -16,3 +16,5 @@ extern crate serde_json; mod core; mod geometry; mod linalg; +#[cfg(feature = "sparse")] +mod sparse; diff --git a/tests/linalg/eigen.rs b/tests/linalg/eigen.rs index 0bcd672e..36855acf 100644 --- a/tests/linalg/eigen.rs +++ b/tests/linalg/eigen.rs @@ -104,3 +104,89 @@ fn symmetric_eigen_singular_24x24() { epsilon = 1.0e-5 )); } + +// #[cfg(feature = "arbitrary")] +// quickcheck! { +// FIXME: full eigendecomposition is not implemented yet because of its complexity when some +// eigenvalues have multiplicity > 1. +// +// /* +// * NOTE: for the following tests, we use only upper-triangular matrices. +// * Thes ensures the schur decomposition will work, and allows use to test the eigenvector +// * computation. +// */ +// fn eigen(n: usize) -> bool { +// let n = cmp::max(1, cmp::min(n, 10)); +// let m = DMatrix::::new_random(n, n).upper_triangle(); +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_with_adjascent_duplicate_diagonals(n: usize) -> bool { +// let n = cmp::max(1, cmp::min(n, 10)); +// let mut m = DMatrix::::new_random(n, n).upper_triangle(); +// +// // Suplicate some adjascent diagonal elements. +// for i in 0 .. n / 2 { +// m[(i * 2 + 1, i * 2 + 1)] = m[(i * 2, i * 2)]; +// } +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_with_nonadjascent_duplicate_diagonals(n: usize) -> bool { +// let n = cmp::max(3, cmp::min(n, 10)); +// let mut m = DMatrix::::new_random(n, n).upper_triangle(); +// +// // Suplicate some diagonal elements. +// for i in n / 2 .. n { +// m[(i, i)] = m[(i - n / 2, i - n / 2)]; +// } +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_4x4(m: Matrix4) -> bool { +// let m = m.upper_triangle(); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_3x3(m: Matrix3) -> bool { +// let m = m.upper_triangle(); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_2x2(m: Matrix2) -> bool { +// let m = m.upper_triangle(); +// println!("{}", m); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// } +// +// fn verify_eigenvectors(m: MatrixN, mut eig: RealEigen) -> bool +// where DefaultAllocator: Allocator + +// Allocator + +// Allocator + +// Allocator, +// MatrixN: Display, +// VectorN: Display { +// let mv = &m * &eig.eigenvectors; +// +// println!("eigenvalues: {}eigenvectors: {}", eig.eigenvalues, eig.eigenvectors); +// +// let dim = m.nrows(); +// for i in 0 .. dim { +// let mut col = eig.eigenvectors.column_mut(i); +// col *= eig.eigenvalues[i]; +// } +// +// println!("{}{:.5}{:.5}", m, mv, eig.eigenvectors); +// +// relative_eq!(eig.eigenvectors, mv, epsilon = 1.0e-5) +// } diff --git a/tests/linalg/full_piv_lu.rs b/tests/linalg/full_piv_lu.rs index 06d87156..5a0ad75b 100644 --- a/tests/linalg/full_piv_lu.rs +++ b/tests/linalg/full_piv_lu.rs @@ -459,4 +459,4 @@ fn resize() { assert_eq!(add_del, m.resize(5, 2, 42)); assert_eq!(del_add, m.resize(1, 8, 42)); } -*/ \ No newline at end of file +*/ diff --git a/tests/linalg/svd.rs b/tests/linalg/svd.rs index fdde3776..e84108ed 100644 --- a/tests/linalg/svd.rs +++ b/tests/linalg/svd.rs @@ -3,14 +3,16 @@ use na::{DMatrix, Matrix6}; #[cfg(feature = "arbitrary")] mod quickcheck_tests { + use na::{ + DMatrix, DVector, Matrix2, Matrix2x5, Matrix3, Matrix3x5, Matrix4, Matrix5x2, Matrix5x3, + }; use std::cmp; - use na::{DMatrix, Matrix2, Matrix3, Matrix4, Matrix5x2, Matrix5x3, Matrix2x5, Matrix3x5, DVector}; quickcheck! { fn svd(m: DMatrix) -> bool { if m.len() > 0 { let svd = m.clone().svd(true, true); - let recomp_m = svd.clone().recompose(); + let recomp_m = svd.clone().recompose().unwrap(); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s); @@ -91,7 +93,7 @@ mod quickcheck_tests { fn svd_pseudo_inverse(m: DMatrix) -> bool { if m.len() > 0 { let svd = m.clone().svd(true, true); - let pinv = svd.pseudo_inverse(1.0e-10); + let pinv = svd.pseudo_inverse(1.0e-10).unwrap(); if m.nrows() > m.ncols() { println!("{}", &pinv * &m); @@ -118,10 +120,10 @@ mod quickcheck_tests { let b1 = DVector::new_random(n); let b2 = DMatrix::new_random(n, nb); - let sol1 = svd.solve(&b1, 1.0e-7); - let sol2 = svd.solve(&b2, 1.0e-7); + let sol1 = svd.solve(&b1, 1.0e-7).unwrap(); + let sol2 = svd.solve(&b2, 1.0e-7).unwrap(); - let recomp = svd.recompose(); + let recomp = svd.recompose().unwrap(); if !relative_eq!(m, recomp, epsilon = 1.0e-6) { println!("{}{}", m, recomp); } @@ -258,27 +260,26 @@ fn svd_singular_horizontal() { assert!(relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5)); } - #[test] fn svd_zeros() { let m = DMatrix::from_element(10, 10, 0.0); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); } #[test] fn svd_identity() { let m = DMatrix::::identity(10, 10); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(10, 15); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(15, 10); let svd = m.clone().svd(true, true); - assert_eq!(m, svd.recompose()); + assert_eq!(Ok(m), svd.recompose()); } #[test] @@ -295,7 +296,7 @@ fn svd_with_delimited_subproblem() { m[(8,8)] = 16.0; m[(3,9)] = 17.0; m[(9,9)] = 18.0; let svd = m.clone().svd(true, true); - assert!(relative_eq!(m, svd.recompose(), epsilon = 1.0e-7)); + assert!(relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7)); // Rectangular versions. let mut m = DMatrix::::from_element(15, 10, 0.0); @@ -310,10 +311,10 @@ fn svd_with_delimited_subproblem() { m[(8,8)] = 16.0; m[(3,9)] = 17.0; m[(9,9)] = 18.0; let svd = m.clone().svd(true, true); - assert!(relative_eq!(m, svd.recompose(), epsilon = 1.0e-7)); + assert!(relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7)); let svd = m.transpose().svd(true, true); - assert!(relative_eq!(m.transpose(), svd.recompose(), epsilon = 1.0e-7)); + assert!(relative_eq!(m.transpose(), svd.recompose().unwrap(), epsilon = 1.0e-7)); } #[test] @@ -329,7 +330,15 @@ fn svd_fail() { println!("Singular values: {}", svd.singular_values); println!("u: {:.5}", svd.u.unwrap()); println!("v: {:.5}", svd.v_t.unwrap()); - let recomp = svd.recompose(); + let recomp = svd.recompose().unwrap(); println!("{:.5}{:.5}", m, recomp); assert!(relative_eq!(m, recomp, epsilon = 1.0e-5)); } + +#[test] +fn svd_err() { + let m = DMatrix::from_element(10, 10, 0.0); + let svd = m.clone().svd(false, false); + assert_eq!(Err("SVD recomposition: U and V^t have not been computed."), svd.clone().recompose()); + assert_eq!(Err("SVD pseudo inverse: the epsilon must be non-negative."), svd.clone().pseudo_inverse(-1.0)); +} diff --git a/tests/sparse/cs_cholesky.rs b/tests/sparse/cs_cholesky.rs new file mode 100644 index 00000000..72a9a08f --- /dev/null +++ b/tests/sparse/cs_cholesky.rs @@ -0,0 +1,75 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{CsMatrix, CsVector, CsCholesky, Cholesky, Matrix5, Vector5}; + +#[test] +fn cs_cholesky() { + let mut a = Matrix5::new( + 40.0, 0.0, 0.0, 0.0, 0.0, + 2.0, 60.0, 0.0, 0.0, 0.0, + 1.0, 0.0, 11.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 50.0, 0.0, + 1.0, 0.0, 0.0, 4.0, 10.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + test_cholesky(a); + + let a = Matrix5::from_diagonal(&Vector5::new(40.0, 60.0, 11.0, 50.0, 10.0)); + test_cholesky(a); + + let mut a = Matrix5::new( + 40.0, 0.0, 0.0, 0.0, 0.0, + 2.0, 60.0, 0.0, 0.0, 0.0, + 1.0, 0.0, 11.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 50.0, 0.0, + 0.0, 0.0, 0.0, 4.0, 10.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + test_cholesky(a); + + let mut a = Matrix5::new( + 2.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 2.0, 0.0, + 1.0, 1.0, 0.0, 0.0, 2.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + // Test ::new, left_looking, and up_looking implementations. + test_cholesky(a); +} + +fn test_cholesky(a: Matrix5) { + // Test ::new + test_cholesky_variant(a, 0); + // Test up-looking + test_cholesky_variant(a, 1); + // Test left-looking + test_cholesky_variant(a, 2); +} + +fn test_cholesky_variant(a: Matrix5, option: usize) { + let cs_a: CsMatrix<_, _, _> = a.into(); + + let chol_a = Cholesky::new(a).unwrap(); + let mut chol_cs_a; + + match option { + 0 => chol_cs_a = CsCholesky::new(&cs_a), + 1 => { + chol_cs_a = CsCholesky::new_symbolic(&cs_a); + chol_cs_a.decompose_up_looking(cs_a.data.values()); + } + _ => { + chol_cs_a = CsCholesky::new_symbolic(&cs_a); + chol_cs_a.decompose_left_looking(cs_a.data.values()); + } + }; + + let l = chol_a.l(); + let cs_l = chol_cs_a.unwrap_l().unwrap(); + assert!(cs_l.is_sorted()); + + let cs_l_mat: Matrix5<_> = cs_l.into(); + assert_relative_eq!(l, cs_l_mat); +} diff --git a/tests/sparse/cs_construction.rs b/tests/sparse/cs_construction.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/sparse/cs_construction.rs @@ -0,0 +1 @@ + diff --git a/tests/sparse/cs_conversion.rs b/tests/sparse/cs_conversion.rs new file mode 100644 index 00000000..f08fe758 --- /dev/null +++ b/tests/sparse/cs_conversion.rs @@ -0,0 +1,89 @@ +use na::{CsMatrix, DMatrix, Matrix4x5}; + +#[test] +fn cs_from_to_matrix() { + #[cfg_attr(rustfmt, rustfmt_skip)] + let m = Matrix4x5::new( + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ); + + let cs: CsMatrix<_, _, _> = m.into(); + assert!(cs.is_sorted()); + + let m2: Matrix4x5<_> = cs.into(); + assert_eq!(m2, m); +} + +#[test] +fn cs_matrix_from_triplet() { + let mut irows = vec![0, 0, 0, 0, 1, 1, 1, 1, 2, 3, 3, 3]; + let mut icols = vec![0, 1, 3, 4, 0, 1, 2, 3, 2, 1, 2, 4]; + let mut vals = vec![ + 5.0, 6.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 13.0, 1.0, 4.0, 14.0, + ]; + + #[cfg_attr(rustfmt, rustfmt_skip)] + let expected = DMatrix::from_row_slice(4, 5, &[ + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ]); + let cs_expected = CsMatrix::from_parts( + 4, + 5, + vec![0, 2, 5, 8, 10], + vec![0, 1, 0, 1, 3, 1, 2, 3, 0, 1, 0, 3], + vec![ + 5.0, 9.0, 6.0, 10.0, 1.0, 11.0, 13.0, 4.0, 8.0, 12.0, 15.0, 14.0, + ], + ); + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected); + + /* + * Try again with some permutations. + */ + let permutations = [(2, 5), (0, 4), (8, 10), (1, 11)]; + + for (i, j) in &permutations { + irows.swap(*i, *j); + icols.swap(*i, *j); + vals.swap(*i, *j); + } + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected); + + /* + * Try again, duplicating all entries. + */ + let mut ir = irows.clone(); + let mut ic = icols.clone(); + let mut va = vals.clone(); + irows.append(&mut ir); + icols.append(&mut ic); + vals.append(&mut va); + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected * 2.0); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected * 2.0); +} diff --git a/tests/sparse/cs_matrix.rs b/tests/sparse/cs_matrix.rs new file mode 100644 index 00000000..b97260d4 --- /dev/null +++ b/tests/sparse/cs_matrix.rs @@ -0,0 +1,22 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{Matrix4x5, Matrix5x4, CsMatrix}; + +#[test] +fn cs_transpose() { + let m = Matrix4x5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 10.0 + ); + + let cs: CsMatrix<_, _, _> = m.into(); + assert!(cs.is_sorted()); + + let cs_transposed = cs.transpose(); + assert!(cs_transposed.is_sorted()); + + let cs_transposed_mat: Matrix5x4<_> = cs_transposed.into(); + assert_eq!(cs_transposed_mat, m.transpose()) +} diff --git a/tests/sparse/cs_matrix_market.rs b/tests/sparse/cs_matrix_market.rs new file mode 100644 index 00000000..12414b37 --- /dev/null +++ b/tests/sparse/cs_matrix_market.rs @@ -0,0 +1,55 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use na::io; +use na::DMatrix; + +#[test] +fn cs_matrix_market() { + let file_str = r#" + %%MatrixMarket matrix coordinate real general +%================================================================================= +% +% This ASCII file represents a sparse MxN matrix with L +% nonzeros in the following Matrix Market format: +% +% +----------------------------------------------+ +% |%%MatrixMarket matrix coordinate real general | <--- header line +% |% | <--+ +% |% comments | |-- 0 or more comment lines +% |% | <--+ +% | M N L | <--- rows, columns, entries +% | I1 J1 A(I1, J1) | <--+ +% | I2 J2 A(I2, J2) | | +% | I3 J3 A(I3, J3) | |-- L lines +% | . . . | | +% | IL JL A(IL, JL) | <--+ +% +----------------------------------------------+ +% +% Indices are 1-based, i.e. A(1,1) is the first element. +% +%================================================================================= + 5 5 8 + 1 1 1.000e+00 + 2 2 1.050e+01 + 3 3 1.500e-02 + 1 4 6.000e+00 + 4 2 2.505e+02 + 4 4 -2.800e+02 + 4 5 3.332e+01 + 5 5 1.200e+01 +"#; + + let cs_mat = io::cs_matrix_from_matrix_market_str(file_str).unwrap(); + println!("CS mat: {:?}", cs_mat); + let mat: DMatrix<_> = cs_mat.into(); + let expected = DMatrix::from_row_slice(5, 5, &[ + 1.0, 0.0, 0.0, 6.0, 0.0, + 0.0, 10.5, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.015, 0.0, 0.0, + 0.0, 250.5, 0.0, -280.0, 33.32, + 0.0, 0.0, 0.0, 0.0, 12.0, + ]); + + assert_eq!(mat, expected); +} diff --git a/tests/sparse/cs_ops.rs b/tests/sparse/cs_ops.rs new file mode 100644 index 00000000..49dfc2bc --- /dev/null +++ b/tests/sparse/cs_ops.rs @@ -0,0 +1,72 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use na::{Matrix3x4, Matrix4x5, Matrix3x5, CsMatrix, Vector5, CsVector}; + +#[test] +fn axpy_cs() { + let mut v1 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + let v2 = Vector5::new(10.0, 0.0, 30.0, 0.0, 50.0); + let expected = 5.0 * v2 + 10.0 * v1; + + let cs: CsVector<_, _> = v2.into(); + v1.axpy_cs(5.0, &cs, 10.0); + + assert!(cs.is_sorted()); + assert_eq!(v1, expected) +} + + +#[test] +fn cs_mat_mul() { + let m1 = Matrix3x4::new( + 0.0, 1.0, 4.0, 0.0, + 5.0, 6.0, 0.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + ); + + let m2 = Matrix4x5::new( + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ); + + let sm1: CsMatrix<_, _, _> = m1.into(); + let sm2: CsMatrix<_, _, _> = m2.into(); + + let mul = &sm1 * &sm2; + + assert!(sm1.is_sorted()); + assert!(sm2.is_sorted()); + assert!(mul.is_sorted()); + assert_eq!(Matrix3x5::from(mul), m1 * m2); +} + + +#[test] +fn cs_mat_add() { + let m1 = Matrix4x5::new( + 4.0, 1.0, 4.0, 0.0, 0.0, + 5.0, 6.0, 0.0, 8.0, 0.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 10.0 + ); + + let m2 = Matrix4x5::new( + 0.0, 1.0, 4.0, 0.0, 14.0, + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + ); + + let sm1: CsMatrix<_, _, _> = m1.into(); + let sm2: CsMatrix<_, _, _> = m2.into(); + + let sum = &sm1 + &sm2; + + assert!(sm1.is_sorted()); + assert!(sm2.is_sorted()); + assert!(sum.is_sorted()); + assert_eq!(Matrix4x5::from(sum), m1 + m2); +} diff --git a/tests/sparse/cs_solve.rs b/tests/sparse/cs_solve.rs new file mode 100644 index 00000000..b3415d79 --- /dev/null +++ b/tests/sparse/cs_solve.rs @@ -0,0 +1,106 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{CsMatrix, CsVector, Matrix5, Vector5}; + + +#[test] +fn cs_lower_triangular_solve() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert_eq!(cs_a.solve_lower_triangular(&b), a.solve_lower_triangular(&b)); +} + +#[test] +fn cs_tr_lower_triangular_solve() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.tr_solve_lower_triangular(&b).is_some()); + assert_eq!(cs_a.tr_solve_lower_triangular(&b), a.tr_solve_lower_triangular(&b)); + + // Singular case. + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 0.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.tr_solve_lower_triangular(&b).is_none()); +} + + +#[test] +fn cs_lower_triangular_solve_cs() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b1 = Vector5::zeros(); + let b2 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + let b3 = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0); + let b4 = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0); + let b5 = Vector5::x(); + let b6 = Vector5::y(); + let b7 = Vector5::z(); + let b8 = Vector5::w(); + let b9 = Vector5::a(); + + let cs_a: CsMatrix<_, _, _> = a.into(); + let cs_b1: CsVector<_, _> = Vector5::zeros().into(); + let cs_b2: CsVector<_, _> = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0).into(); + let cs_b3: CsVector<_, _> = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0).into(); + let cs_b4: CsVector<_, _> = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0).into(); + let cs_b5: CsVector<_, _> = Vector5::x().into(); + let cs_b6: CsVector<_, _> = Vector5::y().into(); + let cs_b7: CsVector<_, _> = Vector5::z().into(); + let cs_b8: CsVector<_, _> = Vector5::w().into(); + let cs_b9: CsVector<_, _> = Vector5::a().into(); + + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b1).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b1)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b5).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b5)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b6).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b6)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b7).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b7)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b8).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b8)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b9).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b9)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b2).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b2)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b3).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b3)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b4).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b4)); + + + // Singular case. + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 0.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 0.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.solve_lower_triangular_cs(&cs_b2).is_none()); + assert!(cs_a.solve_lower_triangular_cs(&cs_b3).is_none()); + assert!(cs_a.solve_lower_triangular_cs(&cs_b4).is_none()); +} diff --git a/tests/sparse/mod.rs b/tests/sparse/mod.rs new file mode 100644 index 00000000..df8e7e37 --- /dev/null +++ b/tests/sparse/mod.rs @@ -0,0 +1,8 @@ +mod cs_cholesky; +mod cs_construction; +mod cs_conversion; +mod cs_matrix; +#[cfg(feature = "io")] +mod cs_matrix_market; +mod cs_ops; +mod cs_solve;