vek 0.15.8

Generic 2D-3D math swiss army knife for game engines, with SIMD support and focus on convenience.
Documentation
//! Matrix types.

use std::mem;
use std::ptr;
use std::slice;
use std::ops::Add;
use std::fmt::{self, Display, Formatter, Debug};
use std::ops::*;
use num_traits::{Zero, One, real::Real, FloatConst, NumCast, AsPrimitive};
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use crate::ops::MulAdd;
use crate::vec;
use crate::geom::{Rect, FrustumPlanes}; // NOTE: Rect is therefore always repr_c here
use crate::quaternion;
use crate::transform;

// Needed because mem::transmute() isn't clever enough to figure out that e.g [T; 16] and [[T; 4]; 4]
// always have the exact same size and layout, regardless of T. The opposite is impossible.
// See https://github.com/rust-lang/rust/issues/47966
#[inline(always)]
unsafe fn transmute_unchecked<S, D>(s: S) -> D {
    debug_assert_eq!(mem::size_of::<S>(), mem::size_of::<D>());
    let d = ptr::read(&s as *const _ as *const D);
    mem::forget(s);
    d
}

macro_rules! mat_impl_mat {
    (rows $Mat:ident $MintRowMat:ident $MintColMat:ident $CVec:ident $Vec:ident ($nrows:tt x $ncols:tt) ($($get:tt)+)) => {

        mat_impl_mat!{common rows $Mat $CVec $Vec ($nrows x $ncols) ($($get)+)}

        use super::column_major::$Mat as Transpose;

        #[cfg(feature = "mint")]
        impl<T> From<mint::$MintRowMat<T>> for $Mat<T> {
            fn from(m: mint::$MintRowMat<T>) -> Self {
                Self {
                    rows: $CVec {
                        $($get : m.$get.into()),+
                    }
                }
            }
        }

        #[cfg(feature = "mint")]
        impl<T> Into<mint::$MintRowMat<T>> for $Mat<T> {
            fn into(self) -> mint::$MintRowMat<T> {
                mint::$MintRowMat {
                    $($get: self.rows.$get.into()),+
                }
            }
        }

        #[cfg(feature = "mint")]
        impl<T> From<mint::$MintColMat<T>> for $Mat<T> {
            fn from(m: mint::$MintColMat<T>) -> Self {
                Transpose::from(m).into()
            }
        }

        #[cfg(feature = "mint")]
        impl<T> Into<mint::$MintColMat<T>> for $Mat<T> {
            fn into(self) -> mint::$MintColMat<T> {
                Transpose::from(self).into()
            }
        }

        impl<T> $Mat<T> {
            /// Returns a row-wise-converted copy of this matrix, using the given conversion
            /// closure.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<f32>::new(
            ///     0.25, 1.25, 5.56, 8.66,
            ///     8.53, 2.92, 3.86, 9.36,
            ///     1.02, 0.28, 5.52, 6.06,
            ///     6.20, 7.01, 4.90, 5.26
            /// );
            /// let m = m.map_rows(|row| row.map(|x| x.round() as i32));
            /// assert_eq!(m, Mat4::new(
            ///     0, 1, 6, 9,
            ///     9, 3, 4, 9,
            ///     1, 0, 6, 6,
            ///     6, 7, 5, 5
            /// ));
            /// ```
            pub fn map_rows<D,F>(self, mut f: F) -> $Mat<D> where F: FnMut($Vec<T>) -> $Vec<D> {
                $Mat { rows: $CVec::new($(f(self.rows.$get)),+) }
            }

            /// Converts this matrix into a fixed-size array of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// ];
            /// assert_eq!(m.into_row_array(), array);
            /// ```
            pub fn into_row_array(self) -> [T; $nrows*$ncols] {
                let m = mem::ManuallyDrop::new(self);
                let mut cur = 0;
                unsafe {
                    let mut array: mem::MaybeUninit<[T; $nrows*$ncols]> = mem::MaybeUninit::uninit();
                    for i in 0..$nrows {
                        let row = m.rows.get_unchecked(i);
                        $(
                            mem::forget(mem::replace((&mut *array.as_mut_ptr()).get_unchecked_mut(cur), ptr::read(&row.$get)));
                            cur += 1;
                        )+
                    }
                    array.assume_init()
                }
            }
            /// Converts this matrix into a fixed-size array of fixed-size arrays of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [  0,  1,  2,  3, ],
            ///     [  4,  5,  6,  7, ],
            ///     [  8,  9, 10, 11, ],
            ///     [ 12, 13, 14, 15, ],
            /// ];
            /// assert_eq!(m.into_row_arrays(), array);
            /// ```
            pub fn into_row_arrays(self) -> [[T; $ncols]; $nrows] {
                unsafe {
                    transmute_unchecked(self.into_row_array())
                }
            }
            /// Converts a fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// ];
            /// assert_eq!(m, Mat4::from_row_array(array));
            /// ```
            pub fn from_row_array(array: [T; $nrows*$ncols]) -> Self {
                let array = mem::ManuallyDrop::new(array);
                let mut cur = 0;
                unsafe {
                    let mut m: mem::MaybeUninit<Self> = mem::MaybeUninit::uninit();
                    for i in 0..$nrows {
                        let row = (&mut *m.as_mut_ptr()).rows.get_unchecked_mut(i);
                        $(
                            mem::forget(mem::replace(&mut row.$get, ptr::read(array.get_unchecked(cur))));
                            cur += 1;
                        )+
                    }
                    m.assume_init()
                }
            }
            /// Converts a fixed-size array of fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [  0,  1,  2,  3, ],
            ///     [  4,  5,  6,  7, ],
            ///     [  8,  9, 10, 11, ],
            ///     [ 12, 13, 14, 15, ],
            /// ];
            /// assert_eq!(m, Mat4::from_row_arrays(array));
            /// ```
            pub fn from_row_arrays(array: [[T; $ncols]; $nrows]) -> Self {
                Self::from_row_array(unsafe { transmute_unchecked(array) })
            }
            /// Converts this matrix into a fixed-size array of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     0, 4, 8, 12,
            ///     1, 5, 9, 13,
            ///     2, 6, 10, 14,
            ///     3, 7, 11, 15
            /// ];
            /// assert_eq!(m.into_col_array(), array);
            /// ```
            pub fn into_col_array(self) -> [T; $nrows*$ncols] {
                let m = mem::ManuallyDrop::new(self);
                let mut cur = 0;
                unsafe {
                    let mut array: mem::MaybeUninit<[T; $nrows*$ncols]> = mem::MaybeUninit::uninit();
                    $(
                        for i in 0..$nrows {
                            mem::forget(mem::replace((&mut *array.as_mut_ptr()).get_unchecked_mut(cur), ptr::read(&m.rows.get_unchecked(i).$get)));
                            cur += 1;
                        }
                    )+
                    array.assume_init()
                }
            }
            /// Converts this matrix into a fixed-size array of fixed-size arrays of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [ 0, 4,  8, 12, ],
            ///     [ 1, 5,  9, 13, ],
            ///     [ 2, 6, 10, 14, ],
            ///     [ 3, 7, 11, 15, ],
            /// ];
            /// assert_eq!(m.into_col_arrays(), array);
            /// ```
            pub fn into_col_arrays(self) -> [[T; $nrows]; $ncols] {
                unsafe {
                    transmute_unchecked(self.into_col_array())
                }
            }
            /// Converts a fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     0, 4, 8, 12,
            ///     1, 5, 9, 13,
            ///     2, 6, 10, 14,
            ///     3, 7, 11, 15
            /// ];
            /// assert_eq!(m, Mat4::from_col_array(array));
            /// ```
            pub fn from_col_array(array: [T; $nrows*$ncols]) -> Self {
                let array = mem::ManuallyDrop::new(array);
                let mut cur = 0;
                unsafe {
                    let mut m: mem::MaybeUninit<Self> = mem::MaybeUninit::uninit();
                    $(
                        for i in 0..$nrows {
                            mem::forget(mem::replace(&mut (&mut *m.as_mut_ptr()).rows.get_unchecked_mut(i).$get, ptr::read(array.get_unchecked(cur))));
                            cur += 1;
                        }
                    )+
                    m.assume_init()
                }
            }
            /// Converts a fixed-size array of fixed-size arrays of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [ 0, 4,  8, 12, ],
            ///     [ 1, 5,  9, 13, ],
            ///     [ 2, 6, 10, 14, ],
            ///     [ 3, 7, 11, 15, ],
            /// ];
            /// assert_eq!(m, Mat4::from_col_arrays(array));
            /// ```
            pub fn from_col_arrays(array: [[T; $nrows]; $ncols]) -> Self {
                Self::from_col_array(unsafe { transmute_unchecked(array) })
            }
            /// Gets a const pointer to this matrix's elements.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_row_ptr(&self) -> *const T {
                debug_assert!(self.is_packed());
                self.rows.as_ptr() as *const _ as *const T
            }
            /// Gets a mut pointer to this matrix's elements.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_mut_row_ptr(&mut self) -> *mut T {
                debug_assert!(self.is_packed());
                self.rows.as_mut_ptr() as *mut _ as *mut T
            }
            /// View this matrix as an immutable slice.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_row_slice(&self) -> &[T] {
                unsafe {
                    slice::from_raw_parts(self.as_row_ptr(), $nrows*$ncols)
                }
            }
            /// View this matrix as a mutable slice.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_mut_row_slice(&mut self) -> &mut [T] {
                unsafe {
                    slice::from_raw_parts_mut(self.as_mut_row_ptr(), $nrows*$ncols)
                }
            }
        }


        /// Displays this matrix using the following format:
        ///
        /// (`i` being the number of rows and `j` the number of columns)
        ///
        /// ```text
        /// ( m00 ... m0j
        ///   ... ... ...
        ///   mi0 ... mij )
        /// ```
        ///
        /// Note that elements are not comma-separated.
        /// This format doesn't depend on the matrix's storage layout.
        impl<T: Display> Display for $Mat<T> {
            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                write!(f, "(")?;
                let mut rows = self.rows.iter();
                // first row goes after the opening paren
                if let Some(row) = rows.next(){
                    for elem in row {
                        write!(f, " ")?;
                        elem.fmt(f)?;
                    }
                }
                // subsequent rows start on a new line
                for row in rows{
                    write!(f, "\n ")?;
                    for elem in row {
                        write!(f, " ")?;
                        elem.fmt(f)?;
                    }
                }
                write!(f, " )")
            }
        }

        /// Index this matrix in a layout-agnostic way with an `(i, j)` (row index, column index) tuple.
        ///
        /// Matrices cannot be indexed by `Vec2`s because that would be likely to cause confusion:
        /// should `x` be the row index (because it's the first element) or the column index
        /// (because it's a horizontal position) ?
        impl<T> Index<(usize, usize)> for $Mat<T> {
            type Output = T;
            fn index(&self, t: (usize, usize)) -> &Self::Output {
                let (i, j) = t;
                &self.rows[i][j]
            }
        }
        impl<T> IndexMut<(usize, usize)> for $Mat<T> {
            fn index_mut(&mut self, t: (usize, usize)) -> &mut Self::Output {
                let (i, j) = t;
                &mut self.rows[i][j]
            }
        }

        impl<T> $Mat<T> {
            /// Gets the `transpose` parameter to pass to OpenGL `glUniformMatrix*()` functions.
            ///
            /// The return value is a plain `bool` which you may directly cast
            /// to a `GLboolean`.
            ///
            /// This takes `&self` to prevent surprises when changing the type
            /// of matrix you plan to send.
            pub fn gl_should_transpose(&self) -> bool {
                true
            }
            /// The `transpose` parameter to pass to OpenGL `glUniformMatrix*()` functions.
            pub const GL_SHOULD_TRANSPOSE: bool = true;
        }

        /// Multiplies a row vector with a row-major matrix, giving a row vector.
        ///
        /// With SIMD vectors, this is the most efficient way.
        ///
        /// ```
        /// use vek::mat::row_major::Mat4;
        /// use vek::vec::Vec4;
        ///
        /// let m = Mat4::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let v = Vec4::new(0, 1, 2, 3);
        /// let r = Vec4::new(26, 32, 18, 24);
        /// assert_eq!(v * m, r);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<$Mat<T>> for $Vec<T> {
            type Output = Self;
            fn mul(self, rhs: $Mat<T>) -> Self::Output {
                let mut out = rhs.rows[0] * $Vec::broadcast(self[0]);
                for i in 1..$nrows {
                    out = rhs.rows[i].mul_add($Vec::broadcast(self[i]), out);
                }
                out
            }
        }
        /// Multiplies a row-major matrix with a column vector, giving a column vector.
        ///
        /// ```
        /// use vek::mat::row_major::Mat4;
        /// use vek::vec::Vec4;
        ///
        /// let m = Mat4::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let v = Vec4::new(0, 1, 2, 3);
        /// let r = Vec4::new(14, 38, 12, 26);
        /// assert_eq!(m * v, r);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<$Vec<T>> for $Mat<T> {
            type Output = $Vec<T>;
            fn mul(self, v: $Vec<T>) -> Self::Output {
                // PERF: This transposes the matrix, but we could do better.
                Transpose::from(self) * v
            }
        }
        /// Multiplies a row-major matrix with another.
        ///
        /// ```
        /// use vek::mat::row_major::Mat4;
        ///
        /// let m = Mat4::<u32>::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let r = Mat4::<u32>::new(
        ///     26, 32, 18, 24,
        ///     82, 104, 66, 88,
        ///     38, 56, 74, 92,
        ///     54, 68, 42, 56
        /// );
        /// assert_eq!(m * m, r);
        /// assert_eq!(m, m * Mat4::<u32>::identity());
        /// assert_eq!(m, Mat4::<u32>::identity() * m);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul for $Mat<T> {
            type Output = Self;
            fn mul(self, rhs: Self) -> Self::Output {
                Self {
                    rows: $CVec {
                        $($get: self.rows.$get * rhs,)+
                    }
                }
            }
        }
        /// Multiplies a row-major matrix with a column-major matrix,
        /// producing a column-major matrix.
        ///
        /// ```
        /// use vek::mat::row_major::Mat4 as Rows4;
        /// use vek::mat::column_major::Mat4 as Cols4;
        ///
        /// let m = Rows4::<u32>::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let b = Cols4::from(m);
        /// let r = Cols4::<u32>::new(
        ///     26, 32, 18, 24,
        ///     82, 104, 66, 88,
        ///     38, 56, 74, 92,
        ///     54, 68, 42, 56
        /// );
        /// assert_eq!(m * b, r);
        /// assert_eq!(m * Cols4::<u32>::identity(), m.into());
        /// assert_eq!(Rows4::<u32>::identity() * b, m.into());
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<Transpose<T>> for $Mat<T> {
            type Output = Transpose<T>;
            fn mul(self, rhs: Transpose<T>) -> Self::Output {
                // PERF: This transposes the matrix, but we could do better.
                Transpose::from(self) * rhs
            }
        }
        impl<T> From<Transpose<T>> for $Mat<T> {
            fn from(m: Transpose<T>) -> Self {
                Self {
                    rows: m.transposed().cols
                }
            }
        }
    };
    (cols $Mat:ident $MintRowMat:ident $MintColMat:ident $CVec:ident $Vec:ident ($nrows:tt x $ncols:tt) ($($get:tt)+)) => {

        mat_impl_mat!{common cols $Mat $CVec $Vec ($nrows x $ncols) ($($get)+)}

        use super::row_major::$Mat as Transpose;

        #[cfg(feature = "mint")]
        impl<T> From<mint::$MintColMat<T>> for $Mat<T> {
            fn from(m: mint::$MintColMat<T>) -> Self {
                Self {
                    cols: $CVec {
                        $($get : m.$get.into()),+
                    }
                }
            }
        }

        #[cfg(feature = "mint")]
        impl<T> Into<mint::$MintColMat<T>> for $Mat<T> {
            fn into(self) -> mint::$MintColMat<T> {
                mint::$MintColMat {
                    $($get: self.cols.$get.into()),+
                }
            }
        }

        #[cfg(feature = "mint")]
        impl<T> From<mint::$MintRowMat<T>> for $Mat<T> {
            fn from(m: mint::$MintRowMat<T>) -> Self {
                Transpose::from(m).into()
            }
        }

        #[cfg(feature = "mint")]
        impl<T> Into<mint::$MintRowMat<T>> for $Mat<T> {
            fn into(self) -> mint::$MintRowMat<T> {
                Transpose::from(self).into()
            }
        }


        impl<T> $Mat<T> {
            /// Returns a column-wise-converted copy of this matrix, using the given conversion
            /// closure.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<f32>::new(
            ///     0.25, 1.25, 5.56, 8.66,
            ///     8.53, 2.92, 3.86, 9.36,
            ///     1.02, 0.28, 5.52, 6.06,
            ///     6.20, 7.01, 4.90, 5.26
            /// );
            /// let m = m.map_cols(|col| col.map(|x| x.round() as i32));
            /// assert_eq!(m, Mat4::new(
            ///     0, 1, 6, 9,
            ///     9, 3, 4, 9,
            ///     1, 0, 6, 6,
            ///     6, 7, 5, 5
            /// ));
            /// ```
            pub fn map_cols<D,F>(self, mut f: F) -> $Mat<D> where F: FnMut($Vec<T>) -> $Vec<D> {
                $Mat { cols: $CVec::new($(f(self.cols.$get)),+) }
            }


            /// Converts this matrix into a fixed-size array of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     0, 4, 8, 12,
            ///     1, 5, 9, 13,
            ///     2, 6, 10, 14,
            ///     3, 7, 11, 15
            /// ];
            /// assert_eq!(m.into_col_array(), array);
            /// ```
            pub fn into_col_array(self) -> [T; $nrows*$ncols] {
                let m = mem::ManuallyDrop::new(self);
                let mut cur = 0;
                unsafe {
                    let mut array: mem::MaybeUninit<[T; $nrows*$ncols]> = mem::MaybeUninit::uninit();
                    for i in 0..$ncols {
                        let col = m.cols.get_unchecked(i);
                        $(
                            mem::forget(mem::replace((&mut *array.as_mut_ptr()).get_unchecked_mut(cur), ptr::read(&col.$get)));
                            cur += 1;
                        )+
                    }
                    array.assume_init()
                }
            }
            /// Converts this matrix into a fixed-size array of fixed-size arrays of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [ 0, 4,  8, 12, ],
            ///     [ 1, 5,  9, 13, ],
            ///     [ 2, 6, 10, 14, ],
            ///     [ 3, 7, 11, 15, ],
            /// ];
            /// assert_eq!(m.into_col_arrays(), array);
            /// ```
            pub fn into_col_arrays(self) -> [[T; $nrows]; $ncols] {
                unsafe {
                    transmute_unchecked(self.into_col_array())
                }
            }
            /// Converts a fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     0, 4, 8, 12,
            ///     1, 5, 9, 13,
            ///     2, 6, 10, 14,
            ///     3, 7, 11, 15
            /// ];
            /// assert_eq!(m, Mat4::from_col_array(array));
            /// ```
            pub fn from_col_array(array: [T; $nrows*$ncols]) -> Self {
                let array = mem::ManuallyDrop::new(array);
                let mut cur = 0;
                unsafe {
                    let mut m: mem::MaybeUninit<Self> = mem::MaybeUninit::uninit();
                    for i in 0..$ncols {
                        let col = (&mut *m.as_mut_ptr()).cols.get_unchecked_mut(i);
                        $(
                            mem::forget(mem::replace(&mut col.$get, ptr::read(array.get_unchecked(cur))));
                            cur += 1;
                        )+
                    }
                    m.assume_init()
                }
            }
            /// Converts a fixed-size array of fixed-size arrays of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [ 0, 4,  8, 12, ],
            ///     [ 1, 5,  9, 13, ],
            ///     [ 2, 6, 10, 14, ],
            ///     [ 3, 7, 11, 15, ],
            /// ];
            /// assert_eq!(m, Mat4::from_col_arrays(array));
            /// ```
            pub fn from_col_arrays(array: [[T; $nrows]; $ncols]) -> Self {
                Self::from_col_array(unsafe { transmute_unchecked(array) })
            }
            /// Converts this matrix into a fixed-size array of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// ];
            /// assert_eq!(m.into_row_array(), array);
            /// ```
            pub fn into_row_array(self) -> [T; $nrows*$ncols] {
                let m = mem::ManuallyDrop::new(self);
                let mut cur = 0;
                unsafe {
                    let mut array: mem::MaybeUninit<[T; $nrows*$ncols]> = mem::MaybeUninit::uninit();
                    $(
                        for i in 0..$ncols {
                            mem::forget(mem::replace((&mut *array.as_mut_ptr()).get_unchecked_mut(cur), ptr::read(&m.cols.get_unchecked(i).$get)));
                            cur += 1;
                        }
                    )+
                    array.assume_init()
                }
            }
            /// Converts this matrix into a fixed-size array of fixed-size arrays of elements.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [  0,  1,  2,  3, ],
            ///     [  4,  5,  6,  7, ],
            ///     [  8,  9, 10, 11, ],
            ///     [ 12, 13, 14, 15, ],
            /// ];
            /// assert_eq!(m.into_row_arrays(), array);
            /// ```
            pub fn into_row_arrays(self) -> [[T; $ncols]; $nrows] {
                unsafe {
                    transmute_unchecked(self.into_row_array())
                }
            }
            /// Converts a fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// ];
            /// assert_eq!(m, Mat4::from_row_array(array));
            /// ```
            pub fn from_row_array(array: [T; $nrows*$ncols]) -> Self {
                let array = mem::ManuallyDrop::new(array);
                let mut cur = 0;
                unsafe {
                    let mut m: mem::MaybeUninit<Self> = mem::MaybeUninit::uninit();
                    $(
                        for i in 0..$ncols {
                            mem::forget(mem::replace(&mut (&mut *m.as_mut_ptr()).cols.get_unchecked_mut(i).$get, ptr::read(array.get_unchecked(cur))));
                            cur += 1;
                        }
                    )+
                    m.assume_init()
                }
            }
            /// Converts a fixed-size array of fixed-size array of elements into a matrix.
            ///
            /// ```
            /// use vek::mat::repr_c::column_major::Mat4;
            ///
            /// let m = Mat4::<u32>::new(
            ///      0,  1,  2,  3,
            ///      4,  5,  6,  7,
            ///      8,  9, 10, 11,
            ///     12, 13, 14, 15
            /// );
            /// let array = [
            ///     [  0,  1,  2,  3, ],
            ///     [  4,  5,  6,  7, ],
            ///     [  8,  9, 10, 11, ],
            ///     [ 12, 13, 14, 15, ],
            /// ];
            /// assert_eq!(m, Mat4::from_row_arrays(array));
            /// ```
            pub fn from_row_arrays(array: [[T; $ncols]; $nrows]) -> Self {
                Self::from_row_array(unsafe { transmute_unchecked(array) })
            }

            /// Gets a const pointer to this matrix's elements.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_col_ptr(&self) -> *const T {
                debug_assert!(self.is_packed());
                self.cols.as_ptr() as *const _ as *const T
            }
            /// Gets a mut pointer to this matrix's elements.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_mut_col_ptr(&mut self) -> *mut T {
                debug_assert!(self.is_packed());
                self.cols.as_mut_ptr() as *mut _ as *mut T
            }
            /// View this matrix as an immutable slice.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_col_slice(&self) -> &[T] {
                unsafe {
                    slice::from_raw_parts(self.as_col_ptr(), $nrows*$ncols)
                }
            }
            /// View this matrix as a mutable slice.
            ///
            /// # Panics
            /// Panics if the matrix's elements are not tightly packed in memory,
            /// which may be the case for matrices in the `repr_simd` module.
            /// You may check this with the `is_packed()` method.
            pub fn as_mut_col_slice(&mut self) -> &mut [T] {
                unsafe {
                    slice::from_raw_parts_mut(self.as_mut_col_ptr(), $nrows*$ncols)
                }
            }
        }


        /// Displays this matrix using the following format:
        ///
        /// (`i` being the number of rows and `j` the number of columns)
        ///
        /// ```text
        /// ( m00 ... m0j
        ///   ... ... ...
        ///   mi0 ... mij )
        /// ```
        ///
        /// Note that elements are not comma-separated.
        /// This format doesn't depend on the matrix's storage layout.
        impl<T: Display> Display for $Mat<T> {
            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                write!(f, "(")?;
                // first row goes after the opening paren
                for x in 0..$ncols {
                    write!(f, " ")?;
                    let elem = unsafe {
                        self.cols.get_unchecked(x).get_unchecked(0)
                    };
                    elem.fmt(f)?;
                }
                // subsequent rows start on a new line
                for y in 1..$nrows {
                    write!(f, "\n ")?;
                    for x in 0..$ncols {
                        write!(f, " ")?;
                        let elem = unsafe {
                            self.cols.get_unchecked(x).get_unchecked(y)
                        };
                        elem.fmt(f)?;
                    }
                }
                write!(f, " )")
            }
        }

        /// Index this matrix in a layout-agnostic way with an `(i, j)` (row index, column index) tuple.
        ///
        /// Matrices cannot be indexed by `Vec2`s because that would be likely to cause confusion:
        /// should `x` be the row index (because it's the first element) or the column index
        /// (because it's a horizontal position) ?
        impl<T> Index<(usize, usize)> for $Mat<T> {
            type Output = T;
            fn index(&self, t: (usize, usize)) -> &Self::Output {
                let (i, j) = t;
                &self.cols[j][i]
            }
        }
        impl<T> IndexMut<(usize, usize)> for $Mat<T> {
            fn index_mut(&mut self, t: (usize, usize)) -> &mut Self::Output {
                let (i, j) = t;
                &mut self.cols[j][i]
            }
        }


        impl<T> $Mat<T> {
            /// Gets the `transpose` parameter to pass to OpenGL `glUniformMatrix*()` functions.
            ///
            /// The return value is a plain `bool` which you may directly cast
            /// to a `GLboolean`.
            ///
            /// This takes `&self` to prevent surprises when changing the type
            /// of matrix you plan to send.
            pub fn gl_should_transpose(&self) -> bool {
                false
            }
            /// The `transpose` parameter to pass to OpenGL `glUniformMatrix*()` functions.
            pub const GL_SHOULD_TRANSPOSE: bool = false;
        }

        /// Multiplies a row vector with a column-major matrix, giving a row vector.
        ///
        /// ```
        /// use vek::mat::column_major::Mat4;
        /// use vek::vec::Vec4;
        ///
        /// let m = Mat4::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let v = Vec4::new(0, 1, 2, 3);
        /// let r = Vec4::new(26, 32, 18, 24);
        /// assert_eq!(v * m, r);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<$Mat<T>> for $Vec<T> {
            type Output = Self;
            fn mul(self, rhs: $Mat<T>) -> Self::Output {
                // PERF: This transposes the matrix, but we could do better.
                self * Transpose::from(rhs)
            }
        }
        /// Multiplies a column-major matrix with a column vector, giving a column vector.
        ///
        /// With SIMD vectors, this is the most efficient way.
        ///
        /// ```
        /// use vek::mat::column_major::Mat4;
        /// use vek::vec::Vec4;
        ///
        /// let m = Mat4::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let v = Vec4::new(0, 1, 2, 3);
        /// let r = Vec4::new(14, 38, 12, 26);
        /// assert_eq!(m * v, r);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<$Vec<T>> for $Mat<T> {
            type Output = $Vec<T>;
            fn mul(self, v: $Vec<T>) -> Self::Output {
                let mut out = self.cols[0] * $Vec::broadcast(v[0]);
                for i in 1..$ncols {
                    out = self.cols[i].mul_add($Vec::broadcast(v[i]), out);
                }
                out
            }
        }
        /// Multiplies a column-major matrix with another.
        ///
        /// ```
        /// use vek::mat::column_major::Mat4;
        ///
        /// let m = Mat4::<u32>::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let r = Mat4::<u32>::new(
        ///     26, 32, 18, 24,
        ///     82, 104, 66, 88,
        ///     38, 56, 74, 92,
        ///     54, 68, 42, 56
        /// );
        /// assert_eq!(m * m, r);
        /// assert_eq!(m, m * Mat4::<u32>::identity());
        /// assert_eq!(m, Mat4::<u32>::identity() * m);
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul for $Mat<T> {
            type Output = Self;
            fn mul(self, rhs: Self) -> Self::Output {
                Self {
                    cols: $CVec {
                        $($get: self * rhs.cols.$get,)+
                    }
                }
            }
        }
        /// Multiplies a column-major matrix with a row-major matrix.
        ///
        /// ```
        /// use vek::mat::row_major::Mat4 as Rows4;
        /// use vek::mat::column_major::Mat4 as Cols4;
        ///
        /// let m = Cols4::<u32>::new(
        ///     0, 1, 2, 3,
        ///     4, 5, 6, 7,
        ///     8, 9, 0, 1,
        ///     2, 3, 4, 5
        /// );
        /// let b = Rows4::from(m);
        /// let r = Rows4::<u32>::new(
        ///     26, 32, 18, 24,
        ///     82, 104, 66, 88,
        ///     38, 56, 74, 92,
        ///     54, 68, 42, 56
        /// );
        /// assert_eq!(m * b, r);
        /// assert_eq!(m * Rows4::<u32>::identity(), m.into());
        /// assert_eq!(Cols4::<u32>::identity() * b, m.into());
        /// ```
        impl<T: MulAdd<T,T,Output=T> + Mul<Output=T> + Copy> Mul<Transpose<T>> for $Mat<T> {
            type Output = Transpose<T>;
            fn mul(self, rhs: Transpose<T>) -> Self::Output {
                // PERF: This transposes the matrix, but we could do better.
                Transpose::from(self) * rhs
            }
        }
        impl<T> From<Transpose<T>> for $Mat<T> {
            fn from(m: Transpose<T>) -> Self {
                Self {
                    cols: m.transposed().rows
                }
            }
        }
    };
    (common $lines:ident $Mat:ident $CVec:ident $Vec:ident ($nrows:tt x $ncols:tt) ($($get:tt)+)) => {

        /// The default value for a square matrix is the identity.
        ///
        /// ```
        /// # use vek::mat::Mat4;
        /// assert_eq!(Mat4::<f32>::default(), Mat4::<f32>::identity());
        /// ```
        impl<T: Zero + One> Default for $Mat<T> {
            fn default() -> Self {
                Self::identity()
            }
        }
        // Welp, not using RelativeEq here - integers don't implement it :(
        impl<T: Zero + PartialEq> Zero for $Mat<T> {
            fn zero() -> Self { Self::zero() }
            fn is_zero(&self) -> bool { self == &Self::zero() }
        }
        impl<T: Zero + One + Copy + MulAdd<T,T,Output=T>> One for $Mat<T> {
            fn one() -> Self { Self::identity() }
        }
        impl<T> $Mat<T> {
            /// The identity matrix, which is also the default value for square matrices.
            ///
            /// ```
            /// # use vek::mat::Mat4;
            /// assert_eq!(Mat4::<f32>::default(), Mat4::<f32>::identity());
            /// ```
            pub fn identity() -> Self where T: Zero + One {
                let mut out = Self::zero();
                $(out.$lines.$get.$get = T::one();)+
                out
            }
            /// The matrix with all elements set to zero.
            pub fn zero() -> Self where T: Zero {
                Self {
                    $lines: $CVec {
                        $($get: $Vec::zero(),)+
                    }
                }
            }
            /// Applies the function f to each element of this matrix, in-place.
            ///
            /// For an example, see the `map()` method.
            pub fn apply<F>(&mut self, f: F) where T: Copy, F: FnMut(T) -> T {
                *self = self.map(f);
            }
            /// Applies the function f to each element of this matrix, in-place.
            ///
            /// For an example, see the `map2()` method.
            pub fn apply2<F, S>(&mut self, other: $Mat<S>, f: F) where T: Copy, F: FnMut(T, S) -> T {
                *self = self.map2(other, f);
            }
            /// Returns a memberwise-converted copy of this matrix, using `NumCast`.
            ///
            /// ```
            /// # use vek::Mat4;
            /// let m = Mat4::<f32>::identity();
            /// let m: Mat4<i32> = m.numcast().unwrap();
            /// assert_eq!(m, Mat4::identity());
            /// ```
            pub fn numcast<D>(self) -> Option<$Mat<D>> where T: NumCast, D: NumCast {
                // NOTE: Should use `?` for conciseness, but docs.rs uses rustc 1.22 and doesn't seem to like that.
                Some($Mat {
                    $lines: $CVec {
                        $($get: match self.$lines.$get.numcast() {
                            Some(x) => x,
                            None => return None,
                        },)+
                    }
                })
            }
            /// Initializes a new matrix with elements of the diagonal set to `val`
            /// and the other to zero.
            ///
            /// In a way, this is the same as single-argument matrix constructors
            /// in GLSL and GLM.
            ///
            /// ```
            /// # use vek::Mat4;
            /// assert_eq!(Mat4::broadcast_diagonal(0), Mat4::zero());
            /// assert_eq!(Mat4::broadcast_diagonal(1), Mat4::identity());
            /// assert_eq!(Mat4::broadcast_diagonal(2), Mat4::new(
            ///     2,0,0,0,
            ///     0,2,0,0,
            ///     0,0,2,0,
            ///     0,0,0,2,
            /// ));
            /// ```
            pub fn broadcast_diagonal(val: T) -> Self where T: Zero + Copy {
                let mut out = Self::zero();
                $(out.$lines.$get.$get = val;)+
                out
            }
            /// Initializes a matrix by its diagonal, setting other elements to zero.
            pub fn with_diagonal(d: $Vec<T>) -> Self where T: Zero + Copy {
                let mut out = Self::zero();
                $(out.$lines.$get.$get = d.$get;)+
                out
            }
            /// Gets the matrix's diagonal into a vector.
            ///
            /// ```
            /// # use vek::{Mat4, Vec4};
            /// assert_eq!(Mat4::<u32>::zero().diagonal(), Vec4::zero());
            /// assert_eq!(Mat4::<u32>::identity().diagonal(), Vec4::one());
            ///
            /// let mut m = Mat4::zero();
            /// m[(0, 0)] = 1;
            /// m[(1, 1)] = 2;
            /// m[(2, 2)] = 3;
            /// m[(3, 3)] = 4;
            /// assert_eq!(m.diagonal(), Vec4::new(1, 2, 3, 4));
            /// assert_eq!(m.diagonal(), Vec4::iota() + 1);
            pub fn diagonal(self) -> $Vec<T> {
                $Vec::new($(self.$lines.$get.$get),+)
            }
            /// The sum of the diagonal's elements.
            ///
            /// ```
            /// # use vek::Mat4;
            /// assert_eq!(Mat4::<u32>::zero().trace(), 0);
            /// assert_eq!(Mat4::<u32>::identity().trace(), 4);
            /// ```
            pub fn trace(self) -> T where T: Add<T, Output=T> {
                self.diagonal().sum()
            }
            /// Multiply elements of this matrix with another's.
            ///
            /// ```
            /// # use vek::mat::Mat4;
            ///
            /// let m = Mat4::new(
            ///     0, 1, 2, 3,
            ///     1, 2, 3, 4,
            ///     2, 3, 4, 5,
            ///     3, 4, 5, 6,
            /// );
            /// let r = Mat4::new(
            ///     0, 1, 4, 9,
            ///     1, 4, 9, 16,
            ///     4, 9, 16, 25,
            ///     9, 16, 25, 36,
            /// );
            /// assert_eq!(m.mul_memberwise(m), r);
            /// ```
            pub fn mul_memberwise(self, m: Self) -> Self where T: Mul<Output=T> {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get * m.$lines.$get),+
                    )
                }
            }
            /// Convenience for getting the number of rows of this matrix.
            pub fn row_count(&self) -> usize {
                $nrows
            }
            /// Convenience for getting the number of columns of this matrix.
            pub fn col_count(&self) -> usize {
                $ncols
            }
            /// Convenience constant representing the number of rows for matrices of this type.
            pub const ROW_COUNT: usize = $nrows;
            /// Convenience constant representing the number of columns for matrices of this type.
            pub const COL_COUNT: usize = $ncols;

            /// Are all elements of this matrix tightly packed together in memory ?
            ///
            /// This might not be the case for matrices in the `repr_simd` module
            /// (it depends on the target architecture).
            pub fn is_packed(&self) -> bool {
                mem::size_of::<Self>() == $nrows*$ncols*mem::size_of::<T>()
            }
        }

        impl<T> Mul<T> for $Mat<T>
            where T: Copy + Zero + Add<Output=T> + Mul<Output=T>
        {
            type Output = Self;
            fn mul(self, rhs: T) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get * rhs),+
                    )
                }
            }
        }

        impl<T> MulAssign for $Mat<T>
            where T: Copy + Zero + Add<Output=T> + Mul<Output=T> + MulAdd<T,T,Output=T>
        {
            fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
        }

        impl<T> MulAssign<T> for $Mat<T>
            where T: Copy + Zero + Add<Output=T> + Mul<Output=T>
        {
            fn mul_assign(&mut self, rhs: T) { *self = *self * rhs; }
        }


        impl<T> Add for $Mat<T> where T: Add<Output=T> {
            type Output = Self;
            fn add(self, rhs: Self) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get + rhs.$lines.$get),+
                    )
                }
            }
        }
        impl<T> Sub for $Mat<T> where T: Sub<Output=T> {
            type Output = Self;
            fn sub(self, rhs: Self) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get - rhs.$lines.$get),+
                    )
                }
            }
        }
        impl<T> Div for $Mat<T> where T: Div<Output=T> {
            type Output = Self;
            fn div(self, rhs: Self) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get / rhs.$lines.$get),+
                    )
                }
            }
        }
        impl<T> Rem for $Mat<T> where T: Rem<Output=T> {
            type Output = Self;
            fn rem(self, rhs: Self) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get % rhs.$lines.$get),+
                    )
                }
            }
        }
        impl<T> Neg for $Mat<T> where T: Neg<Output=T> {
            type Output = Self;
            fn neg(self) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(-self.$lines.$get),+
                    )
                }
            }
        }

        impl<T> Add<T> for $Mat<T> where T: Copy + Add<Output=T> {
            type Output = Self;
            fn add(self, rhs: T) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get + rhs),+
                    )
                }
            }
        }
        impl<T> Sub<T> for $Mat<T> where T: Copy + Sub<Output=T> {
            type Output = Self;
            fn sub(self, rhs: T) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get - rhs),+
                    )
                }
            }
        }
        impl<T> Div<T> for $Mat<T> where T: Copy + Div<Output=T> {
            type Output = Self;
            fn div(self, rhs: T) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get / rhs),+
                    )
                }
            }
        }
        impl<T> Rem<T> for $Mat<T> where T: Copy + Rem<Output=T> {
            type Output = Self;
            fn rem(self, rhs: T) -> Self::Output {
                Self {
                    $lines: $CVec::new(
                        $(self.$lines.$get % rhs),+
                    )
                }
            }
        }

        impl<T: Add<Output=T> + Copy> AddAssign    for $Mat<T> { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } }
        impl<T: Add<Output=T> + Copy> AddAssign<T> for $Mat<T> { fn add_assign(&mut self, rhs: T   ) { *self = *self + rhs; } }
        impl<T: Sub<Output=T> + Copy> SubAssign    for $Mat<T> { fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } }
        impl<T: Sub<Output=T> + Copy> SubAssign<T> for $Mat<T> { fn sub_assign(&mut self, rhs: T   ) { *self = *self - rhs; } }
        impl<T: Div<Output=T> + Copy> DivAssign    for $Mat<T> { fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; } }
        impl<T: Div<Output=T> + Copy> DivAssign<T> for $Mat<T> { fn div_assign(&mut self, rhs: T   ) { *self = *self / rhs; } }
        impl<T: Rem<Output=T> + Copy> RemAssign    for $Mat<T> { fn rem_assign(&mut self, rhs: Self) { *self = *self % rhs; } }
        impl<T: Rem<Output=T> + Copy> RemAssign<T> for $Mat<T> { fn rem_assign(&mut self, rhs: T   ) { *self = *self % rhs; } }

        impl<T: AbsDiffEq> AbsDiffEq for $Mat<T> where T::Epsilon: Copy {
            type Epsilon = T::Epsilon;

            fn default_epsilon() -> T::Epsilon {
                T::default_epsilon()
            }

            fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
                for (l, r) in self.$lines.iter().zip(other.$lines.iter()) {
                    if !AbsDiffEq::abs_diff_eq(l, r, epsilon) {
                        return false;
                    }
                }
                true
            }
        }

        impl<T: UlpsEq> UlpsEq for $Mat<T> where T::Epsilon: Copy {
            fn default_max_ulps() -> u32 {
                T::default_max_ulps()
            }

            fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
                for (l, r) in self.$lines.iter().zip(other.$lines.iter()) {
                    if !UlpsEq::ulps_eq(l, r, epsilon, max_ulps) {
                        return false;
                    }
                }
                true
            }
        }

        impl<T: RelativeEq> RelativeEq for $Mat<T> where T::Epsilon: Copy {
            fn default_max_relative() -> T::Epsilon {
                T::default_max_relative()
            }

            fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
                for (l, r) in self.$lines.iter().zip(other.$lines.iter()) {
                    if !RelativeEq::relative_eq(l, r, epsilon, max_relative) {
                        return false;
                    }
                }
                true
            }
        }

    };
}


macro_rules! mat_impl_mat4 {
    (rows) => {

        impl<T> Mat4<T> {
            /// Creates a new 4x4 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T, m02: T, m03: T,
                m10: T, m11: T, m12: T, m13: T,
                m20: T, m21: T, m22: T, m23: T,
                m30: T, m31: T, m32: T, m33: T
            ) -> Self {
                Self {
                    rows: CVec4::new(
                        Vec4::new(m00, m01, m02, m03),
                        Vec4::new(m10, m11, m12, m13),
                        Vec4::new(m20, m21, m22, m23),
                        Vec4::new(m30, m31, m32, m33)
                    )
                }
            }
        }
        mat_impl_mat4!{common rows}
    };
    (cols) => {
        impl<T> Mat4<T> {
            /// Creates a new 4x4 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T, m02: T, m03: T,
                m10: T, m11: T, m12: T, m13: T,
                m20: T, m21: T, m22: T, m23: T,
                m30: T, m31: T, m32: T, m33: T
            ) -> Self {
                Self {
                    cols: CVec4::new(
                        Vec4::new(m00, m10, m20, m30),
                        Vec4::new(m01, m11, m21, m31),
                        Vec4::new(m02, m12, m22, m32),
                        Vec4::new(m03, m13, m23, m33)
                    )
                }
            }
        }
        mat_impl_mat4!{common cols}
    };
    (common $lines:ident) => {

        use super::row_major::Mat4 as Rows4;
        use super::column_major::Mat4 as Cols4;

        impl<T> Mat4<T> {
            /// Returns an element-wise-converted copy of this matrix, using the given conversion
            /// closure.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<f32>::new(
            ///     0.25, 1.25, 5.56, 8.66,
            ///     8.53, 2.92, 3.86, 9.36,
            ///     1.02, 0.28, 5.52, 6.06,
            ///     6.20, 7.01, 4.90, 5.26
            /// );
            /// let m = m.map(|x| x.round() as i32);
            /// assert_eq!(m, Mat4::new(
            ///     0, 1, 6, 9,
            ///     9, 3, 4, 9,
            ///     1, 0, 6, 6,
            ///     6, 7, 5, 5
            /// ));
            /// ```
            pub fn map<D,F>(self, mut f: F) -> Mat4<D> where F: FnMut(T) -> D {
                let m = self.$lines;
                Mat4 {
                    $lines: CVec4::new(
                        Vec4::new(f(m.x.x), f(m.x.y), f(m.x.z), f(m.x.w)),
                        Vec4::new(f(m.y.x), f(m.y.y), f(m.y.z), f(m.y.w)),
                        Vec4::new(f(m.z.x), f(m.z.y), f(m.z.z), f(m.z.w)),
                        Vec4::new(f(m.w.x), f(m.w.y), f(m.w.z), f(m.w.w))
                    )
                }
            }

            /// Returns a memberwise-converted copy of this matrix, using `AsPrimitive`.
            ///
            /// # Examples
            ///
            /// ```
            /// # use vek::vec::Vec4;
            /// let v = Vec4::new(0_f32, 1., 2., 3.);
            /// let i: Vec4<i32> = v.as_();
            /// assert_eq!(i, Vec4::new(0, 1, 2, 3));
            /// ```
            ///
            /// # Safety
            ///
            /// **In Rust versions before 1.45.0**, some uses of the `as` operator were not entirely safe.
            /// In particular, it was undefined behavior if
            /// a truncated floating point value could not fit in the target integer
            /// type ([#10184](https://github.com/rust-lang/rust/issues/10184));
            ///
            /// ```ignore
            /// # use num_traits::AsPrimitive;
            /// let x: u8 = (1.04E+17).as_(); // UB
            /// ```
            pub fn as_<D>(self) -> Mat4<D> where T: AsPrimitive<D>, D: 'static + Copy {
                let m = self.$lines;
                Mat4 {
                    $lines: CVec4::new(
                        Vec4::new(m.x.x.as_(), m.x.y.as_(), m.x.z.as_(), m.x.w.as_()),
                        Vec4::new(m.y.x.as_(), m.y.y.as_(), m.y.z.as_(), m.y.w.as_()),
                        Vec4::new(m.z.x.as_(), m.z.y.as_(), m.z.z.as_(), m.z.w.as_()),
                        Vec4::new(m.w.x.as_(), m.w.y.as_(), m.w.z.as_(), m.w.w.as_())
                    )
                }
            }

            /// Applies the function f to each element of two matrices, pairwise, and returns the result.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let a = Mat4::<f32>::new(
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99
            /// );
            /// let b = Mat4::<i32>::new(
            ///     0, 1, 0, 0,
            ///     1, 0, 0, 0,
            ///     0, 0, 1, 0,
            ///     0, 0, 0, 1
            /// );
            /// let m = a.map2(b, |a, b| a.round() as i32 + b);
            /// assert_eq!(m, Mat4::new(
            ///     0, 2, 3, 3,
            ///     1, 1, 3, 3,
            ///     0, 1, 4, 3,
            ///     0, 1, 3, 4
            /// ));
            /// ```
            pub fn map2<D,F,S>(self, other: Mat4<S>, mut f: F) -> Mat4<D> where F: FnMut(T, S) -> D {
                let m = self.$lines;
                let o = other.$lines;
                Mat4 {
                    $lines: CVec4::new(
                        Vec4::new(f(m.x.x, o.x.x), f(m.x.y, o.x.y), f(m.x.z, o.x.z), f(m.x.w, o.x.w)),
                        Vec4::new(f(m.y.x, o.y.x), f(m.y.y, o.y.y), f(m.y.z, o.y.z), f(m.y.w, o.y.w)),
                        Vec4::new(f(m.z.x, o.z.x), f(m.z.y, o.z.y), f(m.z.z, o.z.z), f(m.z.w, o.z.w)),
                        Vec4::new(f(m.w.x, o.w.x), f(m.w.y, o.w.y), f(m.w.z, o.w.z), f(m.w.w, o.w.w))
                    )
                }
            }

            /// The matrix's transpose.
            ///
            /// For orthogonal matrices, the transpose is the same as the inverse.
            /// All pure rotation matrices are orthogonal, and therefore can be inverted
            /// faster by simply computing their transpose.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::Mat4;
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let m = Mat4::new(
            ///     0, 1, 2, 3,
            ///     4, 5, 6, 7,
            ///     8, 9, 0, 1,
            ///     2, 3, 4, 5
            /// );
            /// let t = Mat4::new(
            ///     0, 4, 8, 2,
            ///     1, 5, 9, 3,
            ///     2, 6, 0, 4,
            ///     3, 7, 1, 5
            /// );
            /// assert_eq!(m.transposed(), t);
            /// assert_eq!(m, m.transposed().transposed());
            ///
            /// // By the way, demonstrate ways to invert a rotation matrix,
            /// // from fastest (specific) to slowest (general-purpose).
            /// let m = Mat4::rotation_x(PI/7.);
            /// let id = Mat4::identity();
            /// assert_relative_eq!(id, m * m.transposed());
            /// assert_relative_eq!(id, m.transposed() * m);
            /// assert_relative_eq!(id, m * m.inverted_affine_transform_no_scale());
            /// assert_relative_eq!(id, m.inverted_affine_transform_no_scale() * m);
            /// assert_relative_eq!(id, m * m.inverted_affine_transform());
            /// assert_relative_eq!(id, m.inverted_affine_transform() * m);
            /// assert_relative_eq!(id, m * m.inverted());
            /// assert_relative_eq!(id, m.inverted() * m);
            /// # }
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transposed(self) -> Self {
                let s = self.$lines;
                Self {
                    $lines: CVec4::new(
                        Vec4::new(s.x.x, s.y.x, s.z.x, s.w.x),
                        Vec4::new(s.x.y, s.y.y, s.z.y, s.w.y),
                        Vec4::new(s.x.z, s.y.z, s.z.z, s.w.z),
                        Vec4::new(s.x.w, s.y.w, s.z.w, s.w.w)
                    )
                }
            }
            /// Transpose this matrix.
            ///
            /// ```
            /// # use vek::mat::Mat4;
            ///
            /// let mut m = Mat4::new(
            ///     0, 1, 2, 3,
            ///     4, 5, 6, 7,
            ///     8, 9, 0, 1,
            ///     2, 3, 4, 5
            /// );
            /// let t = Mat4::new(
            ///     0, 4, 8, 2,
            ///     1, 5, 9, 3,
            ///     2, 6, 0, 4,
            ///     3, 7, 1, 5
            /// );
            /// m.transpose();
            /// assert_eq!(m, t);
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transpose(&mut self) {
                mem::swap(&mut self.$lines.x.y, &mut self.$lines.y.x);
                mem::swap(&mut self.$lines.x.z, &mut self.$lines.z.x);
                mem::swap(&mut self.$lines.x.w, &mut self.$lines.w.x);
                mem::swap(&mut self.$lines.y.z, &mut self.$lines.z.y);
                mem::swap(&mut self.$lines.y.w, &mut self.$lines.w.y);
                mem::swap(&mut self.$lines.z.w, &mut self.$lines.w.z);
            }

            /// Get this matrix's determinant.
            ///
            /// A matrix is invertible if its determinant is non-zero.
            pub fn determinant(self) -> T where T: Copy + Mul<T,Output=T> + Sub<T,Output=T> + Add<T, Output=T> {
                // http://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/fourD/index.htm
                let CVec4 {
                    x: Vec4 { x: m00, y: m01, z: m02, w: m03 },
                    y: Vec4 { x: m10, y: m11, z: m12, w: m13 },
                    z: Vec4 { x: m20, y: m21, z: m22, w: m23 },
                    w: Vec4 { x: m30, y: m31, z: m32, w: m33 },
                } = Rows4::from(self).rows;
                m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 -
                m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
                m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 -
                m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
                m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 -
                m02 * m10 * m23 * m31 + m00 * m12 * m23 * m31 +
                m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 -
                m03 * m10 * m21 * m32 + m00 * m13 * m21 * m32 +
                m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 -
                m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
                m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 -
                m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33
            }

            //
            // BASIC
            //

            /// Inverts this matrix, blindly assuming that it is invertible.
            /// See `inverted()` for more info.
            pub fn invert(&mut self) where T: Real {
                *self = self.inverted()
            }
            /// Returns this matrix's inverse, blindly assuming that it is invertible.
            ///
            /// All affine matrices have inverses; Your matrices may be affine
            /// as long as they consist of any combination of pure rotations,
            /// translations, scales and shears.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// use vek::vec::repr_c::Vec3;
            /// use vek::mat::repr_c::row_major::Mat4 as Rows4;
            /// use vek::mat::repr_c::column_major::Mat4 as Cols4;
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let a = Rows4::scaling_3d(1.77_f32)
            ///     .rotated_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted();
            /// assert_relative_eq!(a*b, Rows4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Rows4::identity(), epsilon = 0.000001);
            ///
            /// let a = Cols4::scaling_3d(1.77_f32)
            ///     .rotated_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted();
            /// assert_relative_eq!(a*b, Cols4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Cols4::identity(), epsilon = 0.000001);
            ///
            /// // Beware, projection matrices are not invertible!
            /// // Notice that we assert _inequality_ below.
            /// let a = Cols4::perspective_rh_zo(60_f32.to_radians(), 16./9., 0.001, 1000.) * a;
            /// let b = a.inverted();
            /// assert_relative_ne!(a*b, Cols4::identity(), epsilon = 0.000001);
            /// assert_relative_ne!(b*a, Cols4::identity(), epsilon = 0.000001);
            /// # }
            /// ```
            // NOTE: Stolen from
            // https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html
            pub fn inverted(self) -> Self where T: Real
            {
                // NOTE: The VecShuffle_2323() macro in the article swaps its arguments
                let m = self.$lines;
                let a = Vec4::shuffle_lo_hi_0101(m.x, m.y);
                let b = Vec4::shuffle_hi_lo_2323(m.y, m.x);
                let c = Vec4::shuffle_lo_hi_0101(m.z, m.w);
                let d = Vec4::shuffle_hi_lo_2323(m.w, m.z);

                let det_a = Vec4::broadcast(m.x.x * m.y.y - m.x.y * m.y.x);
                let det_b = Vec4::broadcast(m.x.z * m.y.w - m.x.w * m.y.z);
                let det_c = Vec4::broadcast(m.z.x * m.w.y - m.z.y * m.w.x);
                let det_d = Vec4::broadcast(m.z.z * m.w.w - m.z.w * m.w.z);

                let d_c = d.mat2_rows_adj_mul(c);
                let a_b = a.mat2_rows_adj_mul(b);
                let x_ = det_d * a - b.mat2_rows_mul(d_c);
                let w_ = det_a * d - c.mat2_rows_mul(a_b);
                let y_ = det_b * c - d.mat2_rows_mul_adj(a_b);
                let z_ = det_c * b - a.mat2_rows_mul_adj(d_c);

                let tr = a_b * d_c.shuffled((0,2,1,3));
                let tr = tr.hadd(tr);
                let tr = tr.hadd(tr);

                let det_m = det_a * det_d + det_b * det_c - tr;

                let adj_sign_mask = Vec4::new(T::one(), -T::one(), -T::one(), T::one());
                let r_det_m = adj_sign_mask / det_m;

                let x_ = x_ * r_det_m;
                let y_ = y_ * r_det_m;
                let z_ = z_ * r_det_m;
                let w_ = w_ * r_det_m;

                Self {
                    $lines: CVec4::new(
                        Vec4::shuffle_lo_hi(x_, y_, (3,1,3,1)),
                        Vec4::shuffle_lo_hi(x_, y_, (2,0,2,0)),
                        Vec4::shuffle_lo_hi(z_, w_, (3,1,3,1)),
                        Vec4::shuffle_lo_hi(z_, w_, (2,0,2,0))
                    )
                }
            }

            /// Returns this matrix's inverse, blindly assuming that it is an invertible transform
            /// matrix which scale is 1.
            ///
            /// See `inverted_affine_transform_no_scale()` for more info.
            pub fn invert_affine_transform_no_scale(&mut self) where T: Real {
                *self = self.inverted_affine_transform_no_scale()
            }
            /// Returns this matrix's inverse, blindly assuming that it is an invertible transform
            /// matrix which scale is 1.
            ///
            /// A transform matrix is invertible this way as long as it consists
            /// of translations, rotations, and shears.
            /// **It's not guaranteed to work if the scale is not 1.**
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// use vek::vec::repr_c::Vec3;
            /// use vek::mat::repr_c::row_major::Mat4 as Rows4;
            /// use vek::mat::repr_c::column_major::Mat4 as Cols4;
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let a = Rows4::rotation_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted_affine_transform_no_scale();
            /// assert_relative_eq!(a*b, Rows4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Rows4::identity(), epsilon = 0.000001);
            ///
            /// let a = Cols4::rotation_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted_affine_transform_no_scale();
            /// assert_relative_eq!(a*b, Cols4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Cols4::identity(), epsilon = 0.000001);
            ///
            /// // Look! It stops working as soon as we add a scale.
            /// // Notice that we assert _inequality_ below.
            /// let a = Rows4::scaling_3d(5_f32)
            ///     .rotated_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted_affine_transform_no_scale();
            /// assert_relative_ne!(a*b, Rows4::identity(), epsilon = 0.000001);
            /// assert_relative_ne!(b*a, Rows4::identity(), epsilon = 0.000001);
            /// # }
            /// ```
            // NOTE: Stolen from
            // https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html
            pub fn inverted_affine_transform_no_scale(self) -> Self where T: Real
            {
                // NOTE: The VecShuffle_2323() macro in the article swaps its arguments
                let m = Cols4::from(self).cols;
                let t0 = Vec4::shuffle_lo_hi_0101(m.x, m.y);
                let t1 = Vec4::shuffle_hi_lo_2323(m.y, m.x);
                let r0 = Vec4::shuffle_lo_hi(t0, m.z, (0,2,0,3));
                let r1 = Vec4::shuffle_lo_hi(t0, m.z, (1,3,1,3));
                let r2 = Vec4::shuffle_lo_hi(t1, m.z, (0,2,2,3));
                let r3 = Vec4::unit_w()
                       - r0 * m.w.shuffled(0)
                       - r1 * m.w.shuffled(1)
                       - r2 * m.w.shuffled(2);
                Cols4 { cols: CVec4::new(r0, r1, r2, r3) }.into()
            }
            /// Inverts this matrix, blindly assuming that it is an invertible transform matrix.
            /// See `inverted_affine_transform()` for more info.
            pub fn invert_affine_transform(&mut self) where T: Real {
                *self = self.inverted_affine_transform()
            }
            /// Returns this matrix's inverse, blindly assuming that it is an invertible transform
            /// matrix.
            ///
            /// A transform matrix is invertible this way as long as it consists
            /// of translations, rotations, scales and shears.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// use vek::vec::repr_c::Vec3;
            /// use vek::mat::repr_c::row_major::Mat4 as Rows4;
            /// use vek::mat::repr_c::column_major::Mat4 as Cols4;
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let a = Rows4::scaling_3d(1.77_f32)
            ///     .rotated_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted_affine_transform();
            /// assert_relative_eq!(a*b, Rows4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Rows4::identity(), epsilon = 0.000001);
            ///
            /// let a = Cols4::scaling_3d(1.77_f32)
            ///     .rotated_3d(PI*4./5., Vec3::new(5., 8., 10.))
            ///     .translated_3d(Vec3::new(1., 2., 3.));
            /// let b = a.inverted_affine_transform();
            /// assert_relative_eq!(a*b, Cols4::identity(), epsilon = 0.000001);
            /// assert_relative_eq!(b*a, Cols4::identity(), epsilon = 0.000001);
            /// # }
            /// ```
            // NOTE: Stolen from
            // https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html
            pub fn inverted_affine_transform(self) -> Self where T: Real
            {
                // NOTE: The VecShuffle_2323() macro in the article swaps its arguments
                let m = Cols4::from(self).cols;
                let t0 = Vec4::shuffle_lo_hi_0101(m.x, m.y);
                let t1 = Vec4::shuffle_hi_lo_2323(m.y, m.x);
                let r0 = Vec4::shuffle_lo_hi(t0, m.z, (0,2,0,3));
                let r1 = Vec4::shuffle_lo_hi(t0, m.z, (1,3,1,3));
                let r2 = Vec4::shuffle_lo_hi(t1, m.z, (0,2,2,3));
                // PERF: Could use mul_add()
                let size_sqr = r0 * r0 + r1 * r1 + r2 * r2;
                let epsilon = T::epsilon(); // XXX: Might prefer the one from RelativeEq ???
                // PERF: Could use _mm_blendv_ps(), like in this part of the article's source code.
                let size_sqr = size_sqr.map(|x| if x.abs() > epsilon { x } else { T::one() });
                let r0 = r0 / size_sqr;
                let r1 = r1 / size_sqr;
                let r2 = r2 / size_sqr;
                let r3 = Vec4::unit_w()
                       - r0 * m.w.shuffled(0)
                       - r1 * m.w.shuffled(1)
                       - r2 * m.w.shuffled(2);
                Cols4 { cols: CVec4::new(r0, r1, r2, r3) }.into()
            }




            #[allow(dead_code)]
            /// XXX I don't know exactly what this does. Make it public when I do.
            fn orthonormalize(&mut self) where T: Real + Add<T, Output=T> + SubAssign {
                *self = self.orthonormalized();
            }
            #[allow(dead_code)]
            /// XXX I don't know exactly what this does. Make it public when I do.
            // Taken verbatim from linmath.h - I don't know exactly what it does.
            fn orthonormalized(self) -> Self where T: Real + Add<T, Output=T> + SubAssign {
                let mut r = Rows4::from(self).rows;

                r[2] = r[2].normalized();

                let s = r[1].dot(r[2]);
                let h = r[2] * s;
                r[1] -= h;

                r[1] -= h;
                r[1] = r[1].normalized();

                let s = r[0].dot(r[1]);
                let h = r[1] * s;
                r[0] -= h;
                r[0] = r[0].normalized();

                Rows4 { rows: r }.into()
            }


            //
            // MULTIPLY BY
            //

            /// Shortcut for `self * Vec4::from_point(rhs)`.
            pub fn mul_point<V: Into<Vec3<T>> + From<Vec4<T>>>(self, rhs: V) -> V
                where T: Real + MulAdd<T,T,Output=T>
            {
                V::from(self * Vec4::from_point(rhs))
            }
            /// Shortcut for `self * Vec4::from_direction(rhs)`.
            pub fn mul_direction<V: Into<Vec3<T>> + From<Vec4<T>>>(self, rhs: V) -> V
                where T: Real + MulAdd<T,T,Output=T>
            {
                V::from(self * Vec4::from_direction(rhs))
            }

            //
            // TRANSFORMS
            //

            /// Translates this matrix in 2D.
            pub fn translate_2d<V: Into<Vec2<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.translated_2d(v);
            }
            /// Returns this matrix translated in 2D.
            pub fn translated_2d<V: Into<Vec2<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::translation_2d(v) * self
            }
            /// Creates a 2D translation matrix.
            pub fn translation_2d<V: Into<Vec2<T>>>(v: V) -> Self where T: Zero + One {
                let Vec2 { x, y } = v.into();
                Self::new(
                    T::one() , T::zero(), T::zero(), x,
                    T::zero(), T::one() , T::zero(), y,
                    T::zero(), T::zero(), T::one() , T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one(),
                )
            }
            /// Translates this matrix in 3D.
            pub fn translate_3d<V: Into<Vec3<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.translated_3d(v);
            }
            /// Returns this matrix translated in 3D.
            pub fn translated_3d<V: Into<Vec3<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::translation_3d(v) * self
            }
            /// Creates a 3D translation matrix.
            pub fn translation_3d<V: Into<Vec3<T>>>(v: V) -> Self where T: Zero + One {
                let Vec3 { x, y, z } = v.into();
                Self::new(
                    T::one() , T::zero(), T::zero(), x,
                    T::zero(), T::one() , T::zero(), y,
                    T::zero(), T::zero(), T::one() , z,
                    T::zero(), T::zero(), T::zero(), T::one(),
                )
            }
            #[allow(dead_code)]
            /// XXX: This was from linmath.h. I'm not confident what this does. Make it pub when I do.
            fn translate_in_place_3d<V: Into<Vec3<T>>>(&mut self, v: V) where T: Copy + Zero + One + AddAssign + Add<T, Output=T> {
                let Vec3 { x, y, z } = v.into();
                let t = Vec4 { x, y, z, w: T::zero() };
                let mut rows = Rows4::from(*self).rows;
                rows[3] = [
                    rows[0].dot(t),
                    rows[1].dot(t),
                    rows[2].dot(t),
                    rows[3].dot(t),
                ].into();
                *self = Rows4 { rows }.into();
            }

            /// Scales this matrix in 3D.
            pub fn scale_3d<V: Into<Vec3<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.scaled_3d(v);
            }
            /// Returns this matrix scaled in 3D.
            pub fn scaled_3d<V: Into<Vec3<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::scaling_3d(v) * self
            }
            /// Creates a 3D scaling matrix.
            pub fn scaling_3d<V: Into<Vec3<T>>>(v: V) -> Self where T: Zero + One {
                let Vec3 { x, y, z } = v.into();
                Self::new(
                    x, T::zero(), T::zero(), T::zero(),
                    T::zero(), y, T::zero(), T::zero(),
                    T::zero(), T::zero(), z, T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
            /// Rotates this matrix around the X axis.
            pub fn rotate_x(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_x(angle_radians);
            }
            /// Returns this matrix rotated around the X axis.
            pub fn rotated_x(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_x(angle_radians) * self
            }
            /// Creates a matrix that rotates around the X axis.
            pub fn rotation_x(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    T::one(), T::zero(), T::zero(), T::zero(),
                    T::zero(), c, -s, T::zero(),
                    T::zero(), s, c, T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
            /// Rotates this matrix around the Y axis.
            pub fn rotate_y(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_y(angle_radians);
            }
            /// Returns this matrix rotated around the Y axis.
            pub fn rotated_y(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_y(angle_radians) * self
            }
            /// Creates a matrix that rotates around the Y axis.
            pub fn rotation_y(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    c, T::zero(), s, T::zero(),
                    T::zero(), T::one(), T::zero(), T::zero(),
                    -s, T::zero(), c, T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
            /// Rotates this matrix around the Z axis.
            pub fn rotate_z(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_z(angle_radians);
            }
            /// Returns this matrix rotated around the Z axis.
            pub fn rotated_z(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_z(angle_radians) * self
            }
            /// Creates a matrix that rotates around the Z axis.
            pub fn rotation_z(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    c, -s, T::zero(), T::zero(),
                    s,  c, T::zero(), T::zero(),
                    T::zero(), T::zero(), T::one(), T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
            /// Rotates this matrix around a 3D axis.
            /// The axis is not required to be normalized.
            pub fn rotate_3d<V: Into<Vec3<T>>>(&mut self, angle_radians: T, axis: V)
                where T: Real + MulAdd<T,T,Output=T> + Add<T, Output=T>
            {
                *self = self.rotated_3d(angle_radians, axis);
            }
            /// Returns this matrix rotated around a 3D axis.
            /// The axis is not required to be normalized.
            pub fn rotated_3d<V: Into<Vec3<T>>>(self, angle_radians: T, axis: V) -> Self
                where T: Real + MulAdd<T,T,Output=T> + Add<T, Output=T>
            {
                Self::rotation_3d(angle_radians, axis) * self
            }
            /// Creates a matrix that rotates around a 3D axis.
            /// The axis is not required to be normalized.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec4};
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let v = Vec4::unit_x();
            ///
            /// let m = Mat4::rotation_z(PI);
            /// assert_relative_eq!(m * v, -v);
            ///
            /// let m = Mat4::rotation_z(PI * 0.5);
            /// assert_relative_eq!(m * v, Vec4::unit_y());
            ///
            /// let m = Mat4::rotation_z(PI * 1.5);
            /// assert_relative_eq!(m * v, -Vec4::unit_y());
            ///
            /// let angles = 32;
            /// for i in 0..angles {
            ///     let theta = PI * 2. * (i as f32) / (angles as f32);
            ///
            ///     // See what rotating unit vectors do for most angles between 0 and 2*PI.
            ///     // It's helpful to picture this as a right-handed coordinate system.
            ///
            ///     let v = Vec4::unit_y();
            ///     let m = Mat4::rotation_x(theta);
            ///     assert_relative_eq!(m * v, Vec4::new(0., theta.cos(), theta.sin(), 0.));
            ///
            ///     let v = Vec4::unit_z();
            ///     let m = Mat4::rotation_y(theta);
            ///     assert_relative_eq!(m * v, Vec4::new(theta.sin(), 0., theta.cos(), 0.));
            ///
            ///     let v = Vec4::unit_x();
            ///     let m = Mat4::rotation_z(theta);
            ///     assert_relative_eq!(m * v, Vec4::new(theta.cos(), theta.sin(), 0., 0.));
            ///
            ///     assert_relative_eq!(Mat4::rotation_x(theta), Mat4::rotation_3d(theta, Vec4::unit_x()));
            ///     assert_relative_eq!(Mat4::rotation_y(theta), Mat4::rotation_3d(theta, Vec4::unit_y()));
            ///     assert_relative_eq!(Mat4::rotation_z(theta), Mat4::rotation_3d(theta, Vec4::unit_z()));
            /// }
            /// # }
            /// ```
            pub fn rotation_3d<V: Into<Vec3<T>>>(angle_radians: T, axis: V) -> Self where T: Real + Add<T, Output=T> {
                let Vec3 { x, y, z } = axis.into().normalized();
                let s = angle_radians.sin();
                let c = angle_radians.cos();
                let oc = T::one() - c;
                Self::new(
                    oc*x*x + c  , oc*x*y - z*s, oc*z*x + y*s, T::zero(),
                    oc*x*y + z*s, oc*y*y + c  , oc*y*z - x*s, T::zero(),
                    oc*z*x - y*s, oc*y*z + x*s, oc*z*z + c  , T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }


            /// Creates a matrix that would rotate a `from` direction to `to`.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Vec4, Mat4};
            ///
            /// # fn main() {
            /// let (from, to) = (Vec4::<f32>::unit_x(), Vec4::<f32>::unit_z());
            /// let m = Mat4::<f32>::rotation_from_to_3d(from, to);
            /// assert_relative_eq!(m * from, to);
            ///
            /// let (from, to) = (Vec4::<f32>::unit_x(), -Vec4::<f32>::unit_x());
            /// let m = Mat4::<f32>::rotation_from_to_3d(from, to);
            /// assert_relative_eq!(m * from, to);
            /// # }
            /// ```
            pub fn rotation_from_to_3d<V: Into<Vec3<T>>>(from: V, to: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                Self::from(Quaternion::rotation_from_to_3d(from, to))
            }

            //
            // CHANGE OF BASIS
            //

            /// Builds a change of basis matrix that transforms points and directions from
            /// any space to the canonical one.
            ///
            /// `origin` is the origin of the child space.
            /// `i`, `j` and `k` are all required to be normalized;
            /// They are the unit basis vector along the target space x-axis, y-axis and z-axis
            /// respectively, expressed in canonical-space coordinates.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec3};
            /// # fn main() {
            ///     let origin = Vec3::new(1_f32, 2., 3.);
            ///     let i = Vec3::unit_z();
            ///     let j = Vec3::unit_y();
            ///     let k = Vec3::unit_x();
            ///     let m = Mat4::basis_to_local(origin, i, j, k);
            ///     assert_relative_eq!(m.mul_point(origin), Vec3::zero());
            ///     assert_relative_eq!(m.mul_point(origin+i), Vec3::unit_x());
            ///     assert_relative_eq!(m.mul_point(origin+j), Vec3::unit_y());
            ///     assert_relative_eq!(m.mul_point(origin+k), Vec3::unit_z());
            ///
            ///     // `local_to_basis` and `basis_to_local` undo each other
            ///     let a = Mat4::<f32>::basis_to_local(origin, i, j, k);
            ///     let b = Mat4::<f32>::local_to_basis(origin, i, j, k);
            ///     assert_relative_eq!(a*b, Mat4::identity());
            ///     assert_relative_eq!(b*a, Mat4::identity());
            /// # }
            /// ```
            ///
            /// Slightly more contrived example:
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec3};
            /// # fn main() {
            ///     let origin = Vec3::new(1_f32, 2., 3.);
            ///     let r = Mat4::rotation_3d(3., Vec3::new(2_f32, 1., 3.));
            ///     let i = r.mul_direction(Vec3::unit_x());
            ///     let j = r.mul_direction(Vec3::unit_y());
            ///     let k = r.mul_direction(Vec3::unit_z());
            ///     let m = Mat4::basis_to_local(origin, i, j, k);
            ///     assert_relative_eq!(m.mul_point(origin), Vec3::zero(), epsilon = 0.000001);
            ///     assert_relative_eq!(m.mul_point(origin+i), Vec3::unit_x(), epsilon = 0.000001);
            ///     assert_relative_eq!(m.mul_point(origin+j), Vec3::unit_y(), epsilon = 0.000001);
            ///     assert_relative_eq!(m.mul_point(origin+k), Vec3::unit_z(), epsilon = 0.000001);
            /// # }
            /// ```
            pub fn basis_to_local<V: Into<Vec3<T>>>(origin: V, i: V, j: V, k: V) -> Self
                where T: Zero + One + Neg<Output=T> + Real + Add<T, Output=T>
            {
                let (origin, i, j, k) = (origin.into(), i.into(), j.into(), k.into());
                Self::new(
                    i.x, i.y, i.z, -i.dot(origin),
                    j.x, j.y, j.z, -j.dot(origin),
                    k.x, k.y, k.z, -k.dot(origin),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
            /// Builds a change of basis matrix that transforms points and directions from
            /// canonical space to another space.
            ///
            /// `origin` is the origin of the child space.
            /// `i`, `j` and `k` are all required to be normalized;
            /// They are the unit basis vector along the target space x-axis, y-axis and z-axis
            /// respectively, expressed in canonical-space coordinates.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec3};
            /// # fn main() {
            ///     let origin = Vec3::new(1_f32, 2., 3.);
            ///     let i = Vec3::unit_z();
            ///     let j = Vec3::unit_y();
            ///     let k = Vec3::unit_x();
            ///     let m = Mat4::local_to_basis(origin, i, j, k);
            ///     assert_relative_eq!(origin,   m.mul_point(Vec3::zero()));
            ///     assert_relative_eq!(origin+i, m.mul_point(Vec3::unit_x()));
            ///     assert_relative_eq!(origin+j, m.mul_point(Vec3::unit_y()));
            ///     assert_relative_eq!(origin+k, m.mul_point(Vec3::unit_z()));
            ///
            ///     // `local_to_basis` and `basis_to_local` undo each other
            ///     let a = Mat4::<f32>::local_to_basis(origin, i, j, k);
            ///     let b = Mat4::<f32>::basis_to_local(origin, i, j, k);
            ///     assert_relative_eq!(a*b, Mat4::identity());
            ///     assert_relative_eq!(b*a, Mat4::identity());
            /// # }
            /// ```
            ///
            /// Slightly more contrived example:
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec3};
            /// # fn main() {
            ///     // Sanity test
            ///     let origin = Vec3::new(1_f32, 2., 3.);
            ///     let r = Mat4::rotation_3d(3., Vec3::new(2_f32, 1., 3.));
            ///     let i = r.mul_direction(Vec3::unit_x());
            ///     let j = r.mul_direction(Vec3::unit_y());
            ///     let k = r.mul_direction(Vec3::unit_z());
            ///     let m = Mat4::local_to_basis(origin, i, j, k);
            ///     assert_relative_eq!(origin,   m.mul_point(Vec3::zero()));
            ///     assert_relative_eq!(origin+i, m.mul_point(Vec3::unit_x()));
            ///     assert_relative_eq!(origin+j, m.mul_point(Vec3::unit_y()));
            ///     assert_relative_eq!(origin+k, m.mul_point(Vec3::unit_z()));
            /// # }
            /// ```
            pub fn local_to_basis<V: Into<Vec3<T>>>(origin: V, i: V, j: V, k: V) -> Self where T: Zero + One {
                let (origin, i, j, k) = (origin.into(), i.into(), j.into(), k.into());
                Self::new(
                    i.x, j.x, k.x, origin.x,
                    i.y, j.y, k.y, origin.y,
                    i.z, j.z, k.z, origin.z,
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }


            //
            // VIEW
            //

            /// Builds a "look at" view transform
            /// from an eye position, a target position, and up vector.
            /// Commonly used for cameras - in short, it maps points from
            /// world-space to eye-space.
            #[deprecated(since = "0.9.7", note = "Use look_at_lh() or look_at_rh() instead depending on your space's handedness")]
            pub fn look_at<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                Self::look_at_lh(eye, target, up)
            }

            /// Builds a "look at" view transform for left-handed spaces
            /// from an eye position, a target position, and up vector.
            /// Commonly used for cameras - in short, it maps points from
            /// world-space to eye-space.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec4};
            /// # fn main() {
            /// let eye = Vec4::new(1_f32, 0., 1., 1.);
            /// let target = Vec4::new(2_f32, 0., 2., 1.);
            /// let view = Mat4::<f32>::look_at_lh(eye, target, Vec4::unit_y());
            /// assert_relative_eq!(view * eye, Vec4::unit_w());
            /// assert_relative_eq!(view * target, Vec4::new(0_f32, 0., 2_f32.sqrt(), 1.));
            /// # }
            /// ```
            pub fn look_at_lh<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                // From GLM
                let (eye, target, up) = (eye.into(), target.into(), up.into());
                let f = (target - eye).normalized();
                let s = up.cross(f).normalized();
                let u = f.cross(s);
                Self::new(
                    s.x, s.y, s.z, -s.dot(eye),
                    u.x, u.y, u.z, -u.dot(eye),
                    f.x, f.y, f.z, -f.dot(eye),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }

            /// Builds a "look at" view transform for right-handed spaces
            /// from an eye position, a target position, and up vector.
            /// Commonly used for cameras - in short, it maps points from
            /// world-space to eye-space.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec4};
            /// # fn main() {
            /// let eye = Vec4::new(1_f32, 0., 1., 1.);
            /// let target = Vec4::new(2_f32, 0., 2., 1.);
            /// let view = Mat4::<f32>::look_at_rh(eye, target, Vec4::unit_y());
            /// assert_relative_eq!(view * eye, Vec4::unit_w());
            /// assert_relative_eq!(view * target, Vec4::new(0_f32, 0., -2_f32.sqrt(), 1.));
            /// # }
            /// ```
            pub fn look_at_rh<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                // From GLM
                let (eye, target, up) = (eye.into(), target.into(), up.into());
                let f = (target - eye).normalized();
                let s = f.cross(up).normalized();
                let u = s.cross(f);
                Self::new(
                     s.x,  s.y,  s.z, -s.dot(eye),
                     u.x,  u.y,  u.z, -u.dot(eye),
                    -f.x, -f.y, -f.z,  f.dot(eye),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }

            /// Builds a "look at" model transform
            /// from an eye position, a target position, and up vector.
            /// Preferred for transforming objects.
            #[deprecated(since = "0.9.7", note = "Use model_look_at_lh() or model_look_at_rh() instead depending on your space's handedness")]
            pub fn model_look_at<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                Self::model_look_at_lh(eye, target, up)
            }

            /// Builds a "look at" model transform for left-handed spaces
            /// from an eye position, a target position, and up vector.
            /// Preferred for transforming objects.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec4};
            /// # fn main() {
            /// let eye = Vec4::new(1_f32, 0., 1., 1.);
            /// let target = Vec4::new(2_f32, 0., 2., 1.);
            /// let model = Mat4::<f32>::model_look_at_lh(eye, target, Vec4::unit_y());
            /// assert_relative_eq!(model * Vec4::unit_w(), eye);
            /// let d = 2_f32.sqrt();
            /// assert_relative_eq!(model * Vec4::new(0_f32, 0., d, 1.), target);
            ///
            /// // A "model" look-at essentially undoes a "view" look-at
            /// let view = Mat4::look_at_lh(eye, target, Vec4::unit_y());
            /// assert_relative_eq!(view * model, Mat4::identity());
            /// assert_relative_eq!(model * view, Mat4::identity());
            /// # }
            /// ```
            pub fn model_look_at_lh<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                // Advanced 3D Game Programming with DirectX 10.0, p. 173
                let (eye, target, up) = (eye.into(), target.into(), up.into());
                let f = (target - eye).normalized();
                let s = up.cross(f).normalized();
                let u = f.cross(s);
                Self::new(
                    s.x, u.x, f.x, eye.x,
                    s.y, u.y, f.y, eye.y,
                    s.z, u.z, f.z, eye.z,
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }

            /// Builds a "look at" model transform for right-handed spaces
            /// from an eye position, a target position, and up vector.
            /// Preferred for transforming objects.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Vec4};
            /// # fn main() {
            /// let eye = Vec4::new(1_f32, 0., -1., 1.);
            /// let forward = Vec4::new(0_f32, 0., -1., 0.);
            /// let model = Mat4::<f32>::model_look_at_rh(eye, eye + forward, Vec4::unit_y());
            /// assert_relative_eq!(model * Vec4::unit_w(), eye);
            /// assert_relative_eq!(model * forward, forward);
            ///
            /// // A "model" look-at essentially undoes a "view" look-at
            /// let view = Mat4::look_at_rh(eye, eye + forward, Vec4::unit_y());
            /// assert_relative_eq!(view * model, Mat4::identity());
            /// assert_relative_eq!(model * view, Mat4::identity());
            /// # }
            /// ```
            pub fn model_look_at_rh<V: Into<Vec3<T>>>(eye: V, target: V, up: V) -> Self
                where T: Real + Add<T, Output=T> + MulAdd<T,T,Output=T>
            {
                let (eye, target, up) = (eye.into(), target.into(), up.into());
                let f = (target - eye).normalized();
                let s = f.cross(up).normalized();
                let u = s.cross(f);
                Self::new(
                    s.x, u.x, -f.x, eye.x,
                    s.y, u.y, -f.y, eye.y,
                    s.z, u.z, -f.z, eye.z,
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }


            //
            // PROJECTIONS
            //

            /// Returns an orthographic projection matrix that doesn't use near and far planes.
            pub fn orthographic_without_depth_planes (o: FrustumPlanes<T>) -> Self where T: Real {
                let two = T::one() + T::one();
                let FrustumPlanes { left, right, top, bottom, .. } = o;
                let mut m = Self::identity();
                m[(0, 0)] = two / (right - left);
                m[(1, 1)] = two / (top - bottom);
                m[(0, 3)] = - (right + left) / (right - left);
                m[(1, 3)] = - (top + bottom) / (top - bottom);
                m
            }
            /// Returns an orthographic projection matrix for left-handed spaces,
            /// for a depth clip space ranging from 0 to 1 (`GL_DEPTH_ZERO_TO_ONE`,
            /// hence the `_zo` suffix).
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, FrustumPlanes, Vec4};
            /// # fn main() {
            /// let m = Mat4::orthographic_lh_zo(FrustumPlanes {
            ///     left: -1_f32, right: 1., bottom: -1., top: 1.,
            ///     near: 0., far: 1.
            /// });
            /// let v = Vec4::new(0_f32, 0., 1., 1.); // "forward"
            /// assert_relative_eq!(m * v, Vec4::new(0., 0., 1., 1.)); // "far"
            /// # }
            /// ```
            pub fn orthographic_lh_zo (o: FrustumPlanes<T>) -> Self where T: Real {
                let FrustumPlanes { near, far, .. } = o;
                let mut m = Self::orthographic_without_depth_planes(o);
                m[(2, 2)] = T::one() / (far - near);
                m[(2, 3)] = - near / (far - near);
                m
            }
            /// Returns an orthographic projection matrix for left-handed spaces,
            /// for a depth clip space ranging from -1 to 1 (`GL_DEPTH_NEGATIVE_ONE_TO_ONE`,
            /// hence the `_no` suffix).
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, FrustumPlanes, Vec4};
            /// # fn main() {
            /// let m = Mat4::orthographic_lh_no(FrustumPlanes {
            ///     left: -1_f32, right: 1., bottom: -1., top: 1.,
            ///     near: 0., far: 1.
            /// });
            /// let v = Vec4::new(0_f32, 0., 1., 1.); // "forward"
            /// assert_relative_eq!(m * v, Vec4::new(0., 0., 1., 1.)); // "far"
            /// # }
            /// ```
            pub fn orthographic_lh_no (o: FrustumPlanes<T>) -> Self where T: Real {
                let two = T::one() + T::one();
                let FrustumPlanes { near, far, .. } = o;
                let mut m = Self::orthographic_without_depth_planes(o);
                m[(2, 2)] = two / (far - near);
                m[(2, 3)] = - (far + near) / (far - near);
                m
            }
            /// Returns an orthographic projection matrix for right-handed spaces,
            /// for a depth clip space ranging from 0 to 1 (`GL_DEPTH_ZERO_TO_ONE`,
            /// hence the `_zo` suffix).
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, FrustumPlanes, Vec4};
            /// # fn main() {
            /// let m = Mat4::orthographic_rh_zo(FrustumPlanes {
            ///     left: -1_f32, right: 1., bottom: -1., top: 1.,
            ///     near: 0., far: 1.
            /// });
            /// let v = Vec4::new(0_f32, 0., -1., 1.); // "forward"
            /// assert_relative_eq!(m * v, Vec4::new(0., 0., 1., 1.)); // "far"
            /// # }
            /// ```
            pub fn orthographic_rh_zo (o: FrustumPlanes<T>) -> Self where T: Real {
                let FrustumPlanes { near, far, .. } = o;
                let mut m = Self::orthographic_without_depth_planes(o);
                m[(2, 2)] = - T::one() / (far - near);
                m[(2, 3)] = - near / (far - near);
                m
            }
            /// Returns an orthographic projection matrix for right-handed spaces,
            /// for a depth clip space ranging from -1 to 1 (`GL_DEPTH_NEGATIVE_ONE_TO_ONE`,
            /// hence the `_no` suffix).
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, FrustumPlanes, Vec4};
            /// # fn main() {
            /// let m = Mat4::orthographic_rh_no(FrustumPlanes {
            ///     left: -1_f32, right: 1., bottom: -1., top: 1.,
            ///     near: 0., far: 1.
            /// });
            /// let v = Vec4::new(0_f32, 0., -1., 1.); // "forward"
            /// assert_relative_eq!(m * v, Vec4::new(0., 0., 1., 1.)); // "far"
            /// # }
            /// ```
            pub fn orthographic_rh_no (o: FrustumPlanes<T>) -> Self where T: Real {
                let two = T::one() + T::one();
                let FrustumPlanes { near, far, .. } = o;
                let mut m = Self::orthographic_without_depth_planes(o);
                m[(2, 2)] = - two / (far - near);
                m[(2, 3)] = - (far + near) / (far - near);
                m
            }

            /// Creates a perspective projection matrix from a frustum
            /// (left-handed, zero-to-one depth clip planes).
            pub fn frustum_lh_zo (o: FrustumPlanes<T>) -> Self where T: Real {
                let two = T::one() + T::one();
                let FrustumPlanes { left, right, top, bottom, near, far } = o;
                let mut m = Self::zero();
                m[(0, 0)] = (two * near) / (right - left);
                m[(1, 1)] = (two * near) / (top - bottom);
                m[(0, 2)] = (right + left) / (right - left);
                m[(1, 2)] = (top + bottom) / (top - bottom);
                m[(2, 2)] = far / (far - near);
                m[(3, 2)] = T::one();
                m[(2, 3)] = -(far * near) / (far - near);
                m
            }
            /// Creates a perspective projection matrix from a frustum
            /// (left-handed, negative-one-to-one depth clip planes).
            pub fn frustum_lh_no (o: FrustumPlanes<T>) -> Self where T: Real {
                let two = T::one() + T::one();
                let FrustumPlanes { near, far, .. } = o;
                let mut m = Self::frustum_lh_zo(o);
                m[(2, 2)] = (far + near) / (far - near);
                m[(2, 3)] = -(two * far * near) / (far - near);
                m
            }
            /// Creates a perspective projection matrix from a frustum
            /// (right-handed, zero-to-one depth clip planes).
            pub fn frustum_rh_zo (o: FrustumPlanes<T>) -> Self where T: Real {
                let mut m = Self::frustum_lh_zo(o);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }
            /// Creates a perspective projection matrix from a frustum
            /// (right-handed, negative-one-to-one depth clip planes).
            pub fn frustum_rh_no (o: FrustumPlanes<T>) -> Self where T: Real {
                let mut m = Self::frustum_lh_no(o);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }

            /// Creates a perspective projection matrix for right-handed spaces, with zero-to-one depth clip planes.
            pub fn perspective_rh_zo (fov_y_radians: T, aspect_ratio: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                // Assertions from cgmath
                debug_assert!(fov_y_radians > T::zero(), "The vertical field of view cannot be below zero, found: {:?}", fov_y_radians);
                debug_assert!(fov_y_radians < (T::PI()+T::PI()), "The vertical field of view cannot be greater than a half turn, found: {:?} radians", fov_y_radians);
                debug_assert!(aspect_ratio > T::zero(), "The aspect ratio cannot be below zero, found: {:?}", aspect_ratio);
                debug_assert!(near > T::zero(), "The near plane distance cannot be below zero, found: {:?}", near);
                debug_assert!(far  > T::zero(), "The far plane distance cannot be below zero, found: {:?}", far);
                debug_assert!(far  > near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", far, near);
                let two = T::one() + T::one();
                let tan_half_fovy = (fov_y_radians / two).tan();
                let m00 = T::one() / (aspect_ratio * tan_half_fovy);
                let m11 = T::one() / tan_half_fovy;
                let m22 = far / (near - far);
                let m23 = -(far*near) / (far-near);
                let m32 = -T::one();
                Self::new(
                    m00, T::zero(), T::zero(), T::zero(),
                    T::zero(), m11, T::zero(), T::zero(),
                    T::zero(), T::zero(), m22, m23,
                    T::zero(), T::zero(), m32, T::zero()
                )
            }
            /// Creates a perspective projection matrix for left-handed spaces, with zero-to-one depth clip planes.
            pub fn perspective_lh_zo (fov_y_radians: T, aspect_ratio: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                let mut m = Self::perspective_rh_zo(fov_y_radians, aspect_ratio, near, far);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }

            /// Creates a perspective projection matrix for right-handed spaces, with negative-one-to-one depth clip planes.
            pub fn perspective_rh_no (fov_y_radians: T, aspect_ratio: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                // Assertions from cgmath
                debug_assert!(fov_y_radians > T::zero(), "The vertical field of view cannot be below zero, found: {:?}", fov_y_radians);
                debug_assert!(fov_y_radians < (T::PI()+T::PI()), "The vertical field of view cannot be greater than a half turn, found: {:?} radians", fov_y_radians);
                debug_assert!(aspect_ratio > T::zero(), "The aspect ratio cannot be below zero, found: {:?}", aspect_ratio);
                debug_assert!(near > T::zero(), "The near plane distance cannot be below zero, found: {:?}", near);
                debug_assert!(far  > T::zero(), "The far plane distance cannot be below zero, found: {:?}", far);
                debug_assert!(far  > near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", far, near);
                let two = T::one() + T::one();
                let tan_half_fovy = (fov_y_radians / two).tan();
                let m00 = T::one() / (aspect_ratio * tan_half_fovy);
                let m11 = T::one() / tan_half_fovy;
                let m22 = -(far + near) / (far - near);
                let m23 = -(two*far*near) / (far-near);
                let m32 = -T::one();
                Self::new(
                    m00, T::zero(), T::zero(), T::zero(),
                    T::zero(), m11, T::zero(), T::zero(),
                    T::zero(), T::zero(), m22, m23,
                    T::zero(), T::zero(), m32, T::zero()
                )
            }
            /// Creates a perspective projection matrix for left-handed spaces, with negative-one-to-one depth clip planes.
            pub fn perspective_lh_no (fov_y_radians: T, aspect_ratio: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                let mut m = Self::perspective_rh_no(fov_y_radians, aspect_ratio, near, far);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }

            /// Creates a perspective projection matrix for right-handed spaces, with zero-to-one depth clip planes.
            ///
            /// # Panics
            /// `width`, `height` and `fov_y_radians` must all be strictly greater than zero.
            pub fn perspective_fov_rh_zo (fov_y_radians: T, width: T, height: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                debug_assert!(width > T::zero(), "viewport width cannot be below zero, found: {:?}", width);
                debug_assert!(height > T::zero(), "viewport height cannot be below zero, found: {:?}", height);
                // Assertions from cgmath
                debug_assert!(fov_y_radians > T::zero(), "The vertical field of view cannot be below zero, found: {:?}", fov_y_radians);
                debug_assert!(fov_y_radians < (T::PI()+T::PI()), "The vertical field of view cannot be greater than a half turn, found: {:?} radians", fov_y_radians);
                debug_assert!(near > T::zero(), "The near plane distance cannot be below zero, found: {:?}", near);
                debug_assert!(far  > T::zero(), "The far plane distance cannot be below zero, found: {:?}", far);
                debug_assert!(far  > near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", far, near);

                let two = T::one() + T::one();
                let rad = fov_y_radians;
                let h = (rad/two).cos() / (rad/two).sin();
                let w = h * height / width;

                let m00 = w;
                let m11 = h;
                let m22 = far / (near - far);
                let m23 = -(far * near) / (far - near);
                let m32 = -T::one();
                Self::new(
                    m00, T::zero(), T::zero(), T::zero(),
                    T::zero(), m11, T::zero(), T::zero(),
                    T::zero(), T::zero(), m22, m23,
                    T::zero(), T::zero(), m32, T::zero()
                )
            }

            /// Creates a perspective projection matrix for left-handed spaces, with zero-to-one depth clip planes.
            ///
            /// # Panics
            /// `width`, `height` and `fov_y_radians` must all be strictly greater than zero.
            pub fn perspective_fov_lh_zo (fov_y_radians: T, width: T, height: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                let mut m = Self::perspective_fov_rh_zo(fov_y_radians, width, height, near, far);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }

            /// Creates a perspective projection matrix for right-handed spaces, with negative-one-to-one depth clip planes.
            ///
            /// # Panics
            /// `width`, `height` and `fov_y_radians` must all be strictly greater than zero.
            pub fn perspective_fov_rh_no (fov_y_radians: T, width: T, height: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                debug_assert!(width > T::zero(), "viewport width cannot be below zero, found: {:?}", width);
                debug_assert!(height > T::zero(), "viewport height cannot be below zero, found: {:?}", height);
                // Assertions from cgmath
                debug_assert!(fov_y_radians > T::zero(), "The vertical field of view cannot be below zero, found: {:?}", fov_y_radians);
                debug_assert!(fov_y_radians < (T::PI()+T::PI()), "The vertical field of view cannot be greater than a half turn, found: {:?} radians", fov_y_radians);
                debug_assert!(near > T::zero(), "The near plane distance cannot be below zero, found: {:?}", near);
                debug_assert!(far  > T::zero(), "The far plane distance cannot be below zero, found: {:?}", far);
                debug_assert!(far  > near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", far, near);

                let two = T::one() + T::one();
                let rad = fov_y_radians;
                let h = (rad/two).cos() / (rad/two).sin();
                let w = h * height / width;

                let m00 = w;
                let m11 = h;
                let m22 = -(far + near) / (far - near);
                let m23 = -(two * far * near) / (far - near);
                let m32 = -T::one();
                Self::new(
                    m00, T::zero(), T::zero(), T::zero(),
                    T::zero(), m11, T::zero(), T::zero(),
                    T::zero(), T::zero(), m22, m23,
                    T::zero(), T::zero(), m32, T::zero()
                )
            }
            /// Creates a perspective projection matrix for left-handed spaces, with negative-one-to-one depth clip planes.
            ///
            /// # Panics
            /// `width`, `height` and `fov_y_radians` must all be strictly greater than zero.
            pub fn perspective_fov_lh_no (fov_y_radians: T, width: T, height: T, near: T, far: T) -> Self
                where T: Real + FloatConst + Debug
            {
                let mut m = Self::perspective_fov_rh_no(fov_y_radians, width, height, near, far);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }


            /// Creates an infinite perspective projection matrix for right-handed spaces.
            ///
            /// [Link to PDF](http://www.terathon.com/gdc07_lengyel.pdf)
            // From GLM
            pub fn tweaked_infinite_perspective_rh (fov_y_radians: T, aspect_ratio: T, near: T, epsilon: T) -> Self
                where T: Real + FloatConst + Debug
            {
                // Assertions from cgmath
                debug_assert!(fov_y_radians > T::zero(), "The vertical field of view cannot be below zero, found: {:?}", fov_y_radians);
                debug_assert!(fov_y_radians < (T::PI()+T::PI()), "The vertical field of view cannot be greater than a half turn, found: {:?} radians", fov_y_radians);
                debug_assert!(aspect_ratio > T::zero(), "The aspect ratio cannot be below zero, found: {:?}", aspect_ratio);
                debug_assert!(near > T::zero(), "The near plane distance cannot be below zero, found: {:?}", near);

                let two = T::one() + T::one();
                let range = (fov_y_radians / two).tan() * near;
                let left = -range * aspect_ratio;
                let right = range * aspect_ratio;
                let bottom = -range;
                let top = range;

                let m00 = (two * near) / (right - left);
                let m11 = (two * near) / (top - bottom);
                let m22 = epsilon - T::one();
                let m23 = (epsilon - two) * near;
                let m32 = -T::one();
                Self::new(
                    m00, T::zero(), T::zero(), T::zero(),
                    T::zero(), m11, T::zero(), T::zero(),
                    T::zero(), T::zero(), m22, m23,
                    T::zero(), T::zero(), m32, T::zero()
                )
            }

            /// Creates an infinite perspective projection matrix for left-handed spaces.
            pub fn tweaked_infinite_perspective_lh (fov_y_radians: T, aspect_ratio: T, near: T, epsilon: T) -> Self
                where T: Real + FloatConst + Debug
            {
                let mut m = Self::tweaked_infinite_perspective_rh(fov_y_radians, aspect_ratio, near, epsilon);
                m[(2, 2)] = -m[(2, 2)];
                m[(3, 2)] = -m[(3, 2)];
                m
            }

            /// Creates an infinite perspective projection matrix for right-handed spaces.
            pub fn infinite_perspective_rh (fov_y_radians: T, aspect_ratio: T, near: T) -> Self
                where T: Real + FloatConst + Debug
            {
                Self::tweaked_infinite_perspective_rh(fov_y_radians, aspect_ratio, near, T::zero())
            }

            /// Creates an infinite perspective projection matrix for left-handed spaces.
            pub fn infinite_perspective_lh (fov_y_radians: T, aspect_ratio: T, near: T) -> Self
                where T: Real + FloatConst + Debug
            {
                Self::tweaked_infinite_perspective_lh(fov_y_radians, aspect_ratio, near, T::zero())
            }

            //
            // PICKING
            //

            /// GLM's pickMatrix. Creates a projection matrix that can be
            /// used to restrict drawing to a small region of the viewport.
            ///
            /// # Panics
            /// `delta`'s `x` and `y` are required to be strictly greater than zero.
            pub fn picking_region<V2: Into<Vec2<T>>>(center: V2, delta: V2, viewport: Rect<T, T>) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                let (center, delta) = (center.into(), delta.into());
                assert!(delta.x > T::zero());
                assert!(delta.y > T::zero());
                let two = T::one() + T::one();

                let tr = Vec3::new(
                    (viewport.w - two * (center.x - viewport.x)) / delta.x,
                    (viewport.h - two * (center.y - viewport.y)) / delta.y,
                    T::zero()
                );
                let sc = Vec3::new(
                    viewport.w / delta.x,
                    viewport.h / delta.y,
                    T::one()
                );
                Self::scaling_3d(sc) * Self::translation_3d(tr)
            }

            /// Projects a world-space coordinate into screen space,
            /// for a depth clip space ranging from -1 to 1 (`GL_DEPTH_NEGATIVE_ONE_TO_ONE`,
            /// hence the `_no` suffix).
            pub fn world_to_viewport_no<V3>(obj: V3, modelview: Self, proj: Self, viewport: Rect<T, T>) -> Vec3<T>
                where T: Real + MulAdd<T,T,Output=T>,
                      V3: Into<Vec3<T>>
            {
                // GLM's projectNO()
                let mut tmp = Vec4::from_point(obj.into());
                tmp = modelview * tmp;
                tmp = proj * tmp;

                let two = T::one() + T::one();
                let half_one = T::one() / two;

                tmp = tmp / tmp.w; // Real doesn't imply DivAssign
                tmp = tmp / two + half_one;
                tmp.x = tmp.x * viewport.w + viewport.x;
                tmp.y = tmp.y * viewport.h + viewport.y;

                tmp.into()
            }

            /// Projects a world-space coordinate into screen space,
            /// for a depth clip space ranging from 0 to 1 (`GL_DEPTH_ZERO_TO_ONE`,
            /// hence the `_zo` suffix).
            pub fn world_to_viewport_zo<V3>(obj: V3, modelview: Self, proj: Self, viewport: Rect<T, T>) -> Vec3<T>
                where T: Real + MulAdd<T,T,Output=T>,
                      V3: Into<Vec3<T>>
            {
                // GLM's projectZO()
                let mut tmp = Vec4::from_point(obj.into());
                tmp = modelview * tmp;
                tmp = proj * tmp;

                let two = T::one() + T::one();
                let half_one = T::one() / two;

                tmp = tmp / tmp.w; // Real doesn't imply DivAssign
                tmp.x = tmp.x / two + half_one;
                tmp.y = tmp.y / two + half_one;
                tmp.x = tmp.x * viewport.w + viewport.x;
                tmp.y = tmp.y * viewport.h + viewport.y;

                tmp.into()
            }

            /// Projects a screen-space coordinate into world space,
            /// for a depth clip space ranging from 0 to 1 (`GL_DEPTH_ZERO_TO_ONE`,
            /// hence the `_zo` suffix).
            pub fn viewport_to_world_zo<V3>(ray: V3, modelview: Self, proj: Self, viewport: Rect<T, T>) -> Vec3<T>
                where T: Real + MulAdd<T,T,Output=T>,
                      V3: Into<Vec3<T>>
            {
                let inverse = (proj * modelview).inverted();
                let two = T::one() + T::one();

                let mut tmp = Vec4::from_point(ray.into());
                tmp.x = (tmp.x - viewport.x) / viewport.w;
                tmp.y = (tmp.y - viewport.y) / viewport.h;
                tmp.x = tmp.x * two - T::one();
                tmp.y = tmp.y * two - T::one();

                let mut obj = inverse * tmp;
                obj = obj / obj.w; // Real doesn't imply DivAssign

                obj.into()
            }
            /// Projects a screen-space coordinate into world space,
            /// for a depth clip space ranging from -1 to 1 (`GL_DEPTH_NEGATIVE_ONE_TO_ONE`,
            /// hence the `_no` suffix).
            pub fn viewport_to_world_no<V3>(ray: V3, modelview: Self, proj: Self, viewport: Rect<T, T>) -> Vec3<T>
                where T: Real + MulAdd<T,T,Output=T>,
                      V3: Into<Vec3<T>>
            {
                let inverse = (proj * modelview).inverted();
                let two = T::one() + T::one();

                let mut tmp = Vec4::from_point(ray.into());
                tmp.x = (tmp.x - viewport.x) / viewport.w;
                tmp.y = (tmp.y - viewport.y) / viewport.h;
                tmp = tmp * two - T::one();

                let mut obj = inverse * tmp;
                obj = obj / obj.w; // Real doesn't imply DivAssign

                obj.into()
            }
        }
        use super::mat3::Mat3;
        impl<T> From<Mat3<T>> for Mat4<T> where T: Zero + One {
            fn from(m: Mat3<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec4::new(
                        m.x.into(),
                        m.y.into(),
                        m.z.into(),
                        Vec4::new(T::zero(), T::zero(), T::zero(), T::one())
                    )
                }
            }
        }
        use super::mat2::Mat2;
        impl<T> From<Mat2<T>> for Mat4<T> where T: Zero + One {
            fn from(m: Mat2<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec4::new(
                        m.x.into(),
                        m.y.into(),
                        Vec4::new(T::zero(), T::zero(), T::one(), T::zero()),
                        Vec4::new(T::zero(), T::zero(), T::zero(), T::one())
                    )
                }
            }
        }

        /// A `Mat4` can be obtained from a `Transform`, by rotating, then scaling, then
        /// translating.
        impl<T> From<Transform<T,T,T>> for Mat4<T>
            where T: Real + MulAdd<T,T,Output=T>
        {
            fn from(xform: Transform<T,T,T>) -> Self {
                let Transform { position, orientation, scale } = xform;
                Mat4::from(orientation).scaled_3d(scale).translated_3d(position)
            }
        }

        /// Rotation matrices can be obtained from quaternions.
        /// **This implementation only works properly if the quaternion is normalized**.
        ///
        /// ```
        /// # extern crate vek;
        /// # #[macro_use] extern crate approx;
        /// # use vek::{Quaternion, Mat4, Vec4};
        /// use std::f32::consts::PI;
        ///
        /// # fn main() {
        /// let angles = 32;
        /// for i in 0..angles {
        ///     let theta = PI * 2. * (i as f32) / (angles as f32);
        ///
        ///     assert_relative_eq!(Mat4::rotation_x(theta), Mat4::from(Quaternion::rotation_x(theta)), epsilon = 0.000001);
        ///     assert_relative_eq!(Mat4::rotation_y(theta), Mat4::from(Quaternion::rotation_y(theta)), epsilon = 0.000001);
        ///     assert_relative_eq!(Mat4::rotation_z(theta), Mat4::from(Quaternion::rotation_z(theta)), epsilon = 0.000001);
        ///
        ///     assert_relative_eq!(Mat4::rotation_x(theta), Mat4::rotation_3d(theta, Vec4::unit_x()));
        ///     assert_relative_eq!(Mat4::rotation_y(theta), Mat4::rotation_3d(theta, Vec4::unit_y()));
        ///     assert_relative_eq!(Mat4::rotation_z(theta), Mat4::rotation_3d(theta, Vec4::unit_z()));
        ///
        ///     // See what rotating unit vectors do for most angles between 0 and 2*PI.
        ///     // It's helpful to picture this as a right-handed coordinate system.
        ///
        ///     let v = Vec4::unit_y();
        ///     let m = Mat4::rotation_x(theta);
        ///     assert_relative_eq!(m * v, Vec4::new(0., theta.cos(), theta.sin(), 0.));
        ///
        ///     let v = Vec4::unit_z();
        ///     let m = Mat4::rotation_y(theta);
        ///     assert_relative_eq!(m * v, Vec4::new(theta.sin(), 0., theta.cos(), 0.));
        ///
        ///     let v = Vec4::unit_x();
        ///     let m = Mat4::rotation_z(theta);
        ///     assert_relative_eq!(m * v, Vec4::new(theta.cos(), theta.sin(), 0., 0.));
        /// }
        /// # }
        /// ```
        // NOTE: Logically, this conversion should be implemented for Mat3,
        // and Mat4 would do it by converting itself from a Mat3.
        // Here, we have the other way round, and I'm fine with this, because
        // at best it's better, SIMD-wise, and at worst we don't care that much, seriously.
        impl<T> From<Quaternion<T>> for Mat4<T>
            where T: Copy + Zero + One + Mul<Output=T> + Add<Output=T> + Sub<Output=T>
        {
            fn from(q: Quaternion<T>) -> Self {
                // From both GEA and Matrix FAQ
                let Quaternion { x, y, z, w } = q;
                let one = T::one();
                let two = one+one;
                let (x2, y2, z2) = (x*x, y*y, z*z);
                let m00 = one - two*y2 - two*z2;
                let m11 = one - two*x2 - two*z2;
                let m22 = one - two*x2 - two*y2;
                let m01 = two*x*y + two*z*w;
                let m10 = two*x*y - two*z*w;
                let m02 = two*x*z - two*y*w;
                let m20 = two*x*z + two*y*w;
                let m12 = two*y*z + two*x*w;
                let m21 = two*y*z - two*x*w;
                // NOTE: transpose, because otherwise the rotation goes the opposite way.
                // See tests.
                Self::new(
                    m00, m10, m20, T::zero(),
                    m01, m11, m21, T::zero(),
                    m02, m12, m22, T::zero(),
                    T::zero(), T::zero(), T::zero(), T::one()
                )
            }
        }

        /* NOTE: Commented until I find an implementation that actually works
        /// A quaternion may be obtained from a rotation matrix.
        ///
        /// ```
        /// # extern crate vek;
        /// # #[macro_use] extern crate approx;
        /// # use vek::{Mat4, Quaternion, Vec3};
        /// # fn main() {
        /// let (angle, axis) = (3_f32, Vec3::new(1_f32, 3., 7.));
        /// let a = Quaternion::rotation_3d(angle, axis);
        /// let b = Quaternion::from(Mat4::rotation_3d(angle, axis));
        /// assert_relative_eq!(a, b);
        /// # }
        /// ```
        impl<T> From<Mat4<T>> for Quaternion<T>
            where T: Real + Zero + One + Mul<Output=T> + Add<Output=T> + Sub<Output=T>
        {
            fn from(m: Mat4<T>) -> Self {
                // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
                // Using the most branchy one, because if you're willing to convert a matrix to
                // quaternion in the first place, surely you don't mind this extra cost.
                let m = Rows4::from(m);
                let Rows4 {
                    rows: CVec4 {
                        x: Vec4 { x: m00, y: m01, z: m02, w: _ },
                        y: Vec4 { x: m10, y: m11, z: m12, w: _ },
                        z: Vec4 { x: m20, y: m21, z: m22, w: _ },
                        w: Vec4 { x:   _, y:   _, z:   _, w: _ },
                    }
                } = m;
                let tr = m00 + m11 + m22;

                let one = T::one();
                let two = one + one;
                let four = two + two;
                if tr > T::zero() {
                    let s = (tr+one).sqrt() * two; // S=4*qw
                    Self {
                        w: s / four,
                        x: (m21 - m12) / s,
                        y: (m02 - m20) / s,
                        z: (m10 - m01) / s,
                    }
                } else if (m00 > m11) && (m00 > m22) {
                    let s = (one + m00 - m11 - m22).sqrt() * two; // S=4*qx
                    Self {
                        w: (m21 - m12) / s,
                        x: s / four,
                        y: (m01 + m10) / s,
                        z: (m02 + m20) / s,
                    }
                } else if m11 > m22 {
                    let s = (one + m11 - m00 - m22).sqrt() * two; // S=4*qy
                    Self {
                        w: (m02 - m20) / s,
                        x: (m01 + m10) / s,
                        y: s / four,
                        z: (m12 + m21) / s,
                    }
                } else {
                    let s = (one + m22 - m00 - m11).sqrt() * two; // S=4*qz
                    Self {
                        w: (m10 - m01) / s,
                        x: (m02 + m20) / s,
                        y: (m12 + m21) / s,
                        z: s / four,
                    }
                }
            }
        } */
    };
}

#[allow(unused_macros)]
macro_rules! mat_impl_mat3 {
    (rows) => {

        mat_impl_mat3!{common rows}

        impl<T> Mat3<T> {
            /// Creates a new 3x3 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T, m02: T,
                m10: T, m11: T, m12: T,
                m20: T, m21: T, m22: T,
            ) -> Self {
                Self {
                    rows: CVec3::new(
                        Vec3::new(m00, m01, m02),
                        Vec3::new(m10, m11, m12),
                        Vec3::new(m20, m21, m22)
                    )
                }
            }
        }
    };
    (cols) => {

        mat_impl_mat3!{common cols}

        impl<T> Mat3<T> {
            /// Creates a new 3x3 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T, m02: T,
                m10: T, m11: T, m12: T,
                m20: T, m21: T, m22: T
            ) -> Self {
                Self {
                    cols: CVec3::new(
                        Vec3::new(m00, m10, m20),
                        Vec3::new(m01, m11, m21),
                        Vec3::new(m02, m12, m22)
                    )
                }
            }
        }
    };
    (common $lines:ident) => {
        impl<T> Mat3<T> {
            /// Returns an element-wise-converted copy of this matrix, using the given conversion
            /// closure.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<f32>::new(
            ///     0.25, 1.25, 5.56, 8.66,
            ///     8.53, 2.92, 3.86, 9.36,
            ///     1.02, 0.28, 5.52, 6.06,
            ///     6.20, 7.01, 4.90, 5.26
            /// );
            /// let m = m.map(|x| x.round() as i32);
            /// assert_eq!(m, Mat4::new(
            ///     0, 1, 6, 9,
            ///     9, 3, 4, 9,
            ///     1, 0, 6, 6,
            ///     6, 7, 5, 5
            /// ));
            /// ```
            pub fn map<D,F>(self, mut f: F) -> Mat3<D> where F: FnMut(T) -> D {
                let m = self.$lines;
                Mat3 {
                    $lines: CVec3::new(
                        Vec3::new(f(m.x.x), f(m.x.y), f(m.x.z)),
                        Vec3::new(f(m.y.x), f(m.y.y), f(m.y.z)),
                        Vec3::new(f(m.z.x), f(m.z.y), f(m.z.z)),
                    )
                }
            }

            /// Returns a memberwise-converted copy of this matrix, using `AsPrimitive`.
            ///
            /// # Examples
            ///
            /// ```
            /// # use vek::vec::Vec4;
            /// let v = Vec4::new(0_f32, 1., 2., 3.);
            /// let i: Vec4<i32> = v.as_();
            /// assert_eq!(i, Vec4::new(0, 1, 2, 3));
            /// ```
            ///
            /// # Safety
            ///
            /// **In Rust versions before 1.45.0**, some uses of the `as` operator were not entirely safe.
            /// In particular, it was undefined behavior if
            /// a truncated floating point value could not fit in the target integer
            /// type ([#10184](https://github.com/rust-lang/rust/issues/10184));
            ///
            /// ```ignore
            /// # use num_traits::AsPrimitive;
            /// let x: u8 = (1.04E+17).as_(); // UB
            /// ```
            pub fn as_<D>(self) -> Mat3<D> where T: AsPrimitive<D>, D: 'static + Copy {
                let m = self.$lines;
                Mat3 {
                    $lines: CVec3::new(
                        Vec3::new(m.x.x.as_(), m.x.y.as_(), m.x.z.as_()),
                        Vec3::new(m.y.x.as_(), m.y.y.as_(), m.y.z.as_()),
                        Vec3::new(m.z.x.as_(), m.z.y.as_(), m.z.z.as_()),
                    )
                }
            }

            /// Applies the function f to each element of two matrices, pairwise, and returns the result.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let a = Mat4::<f32>::new(
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99
            /// );
            /// let b = Mat4::<i32>::new(
            ///     0, 1, 0, 0,
            ///     1, 0, 0, 0,
            ///     0, 0, 1, 0,
            ///     0, 0, 0, 1
            /// );
            /// let m = a.map2(b, |a, b| a.round() as i32 + b);
            /// assert_eq!(m, Mat4::new(
            ///     0, 2, 3, 3,
            ///     1, 1, 3, 3,
            ///     0, 1, 4, 3,
            ///     0, 1, 3, 4
            /// ));
            /// ```
            pub fn map2<D,F,S>(self, other: Mat3<S>, mut f: F) -> Mat3<D> where F: FnMut(T, S) -> D {
                let m = self.$lines;
                let o = other.$lines;
                Mat3 {
                    $lines: CVec3::new(
                        Vec3::new(f(m.x.x, o.x.x), f(m.x.y, o.x.y), f(m.x.z, o.x.z)),
                        Vec3::new(f(m.y.x, o.y.x), f(m.y.y, o.y.y), f(m.y.z, o.y.z)),
                        Vec3::new(f(m.z.x, o.z.x), f(m.z.y, o.z.y), f(m.z.z, o.z.z))
                    )
                }
            }

            /// The matrix's transpose.
            ///
            /// For orthogonal matrices, the transpose is the same as the inverse.
            /// All pure rotation matrices are orthogonal, and therefore can be inverted
            /// faster by simply computing their transpose.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::Mat3;
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let m = Mat3::new(
            ///     0, 1, 2,
            ///     4, 5, 6,
            ///     8, 9, 0
            /// );
            /// let t = Mat3::new(
            ///     0, 4, 8,
            ///     1, 5, 9,
            ///     2, 6, 0
            /// );
            /// assert_eq!(m.transposed(), t);
            /// assert_eq!(m, m.transposed().transposed());
            ///
            /// let m = Mat3::rotation_x(PI/7.);
            /// assert_relative_eq!(m * m.transposed(), Mat3::identity());
            /// assert_relative_eq!(m.transposed() * m, Mat3::identity());
            /// # }
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transposed(self) -> Self {
                let s = self.$lines;
                Self {
                    $lines: CVec3::new(
                        Vec3::new(s.x.x, s.y.x, s.z.x),
                        Vec3::new(s.x.y, s.y.y, s.z.y),
                        Vec3::new(s.x.z, s.y.z, s.z.z)
                    )
                }
            }
            /// Transpose this matrix.
            ///
            /// ```
            /// # use vek::Mat3;
            ///
            /// let mut m = Mat3::new(
            ///     0, 1, 2,
            ///     4, 5, 6,
            ///     8, 9, 0
            /// );
            /// let t = Mat3::new(
            ///     0, 4, 8,
            ///     1, 5, 9,
            ///     2, 6, 0
            /// );
            /// m.transpose();
            /// assert_eq!(m, t);
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transpose(&mut self) {
                mem::swap(&mut self.$lines.x.y, &mut self.$lines.y.x);
                mem::swap(&mut self.$lines.x.z, &mut self.$lines.z.x);
                mem::swap(&mut self.$lines.y.z, &mut self.$lines.z.y);
            }

            /// Get this matrix's determinant.
            ///
            /// A matrix is invertible if its determinant is non-zero.
            pub fn determinant(self) -> T where T: Copy + Mul<T,Output=T> + Sub<T,Output=T> + Add<T, Output=T> {
                // https://www.mathsisfun.com/algebra/matrix-determinant.html
                use super::row_major::Mat3 as Rows3;
                let CVec3 {
                    x: Vec3 { x: a, y: b, z: c },
                    y: Vec3 { x: d, y: e, z: f },
                    z: Vec3 { x: g, y: h, z: i },
                } = Rows3::from(self).rows;
                a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g)
            }


            //
            // MULTIPLY BY
            //

            /// Shortcut for `self * Vec3::from_point_2d(rhs)`.
            pub fn mul_point_2d<V: Into<Vec2<T>> + From<Vec3<T>>>(self, rhs: V) -> V
                where T: Real + MulAdd<T,T,Output=T>
            {
                V::from(self * Vec3::from_point_2d(rhs))
            }
            /// Shortcut for `self * Vec3::from_direction_2d(rhs)`.
            pub fn mul_direction_2d<V: Into<Vec2<T>> + From<Vec3<T>>>(self, rhs: V) -> V
                where T: Real + MulAdd<T,T,Output=T>
            {
                V::from(self * Vec3::from_direction_2d(rhs))
            }


            /// Translates this matrix in 2D.
            pub fn translate_2d<V: Into<Vec2<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.translated_2d(v);
            }
            /// Returns this matrix translated in 2D.
            pub fn translated_2d<V: Into<Vec2<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::translation_2d(v) * self
            }
            /// Creates a 2D translation matrix.
            pub fn translation_2d<V: Into<Vec2<T>>>(v: V) -> Self where T: Zero + One {
                let v = v.into();
                Self::new(
                    T::one() , T::zero(), v.x,
                    T::zero(), T::one() , v.y,
                    T::zero(), T::zero(), T::one()
                )
            }
            /// Scales this matrix in 3D.
            pub fn scale_3d<V: Into<Vec3<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.scaled_3d(v);
            }
            /// Returns this matrix scaled in 3D.
            pub fn scaled_3d<V: Into<Vec3<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::scaling_3d(v) * self
            }
            /// Creates a 3D scaling matrix.
            pub fn scaling_3d<V: Into<Vec3<T>>>(v: V) -> Self where T: Zero {
                let Vec3 { x, y, z } = v.into();
                Self::new(
                    x, T::zero(), T::zero(),
                    T::zero(), y, T::zero(),
                    T::zero(), T::zero(), z
                )
            }
            /// Rotates this matrix around the X axis.
            pub fn rotate_x(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_x(angle_radians);
            }
            /// Returns this matrix rotated around the X axis.
            pub fn rotated_x(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_x(angle_radians) * self
            }
            /// Creates a matrix that rotates around the X axis.
            pub fn rotation_x(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    T::one(), T::zero(), T::zero(),
                    T::zero(), c, -s,
                    T::zero(), s, c
                )
            }
            /// Rotates this matrix around the Y axis.
            pub fn rotate_y(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_y(angle_radians);
            }
            /// Returns this matrix rotated around the Y axis.
            pub fn rotated_y(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_y(angle_radians) * self
            }
            /// Creates a matrix that rotates around the Y axis.
            pub fn rotation_y(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    c, T::zero(), s,
                    T::zero(), T::one(), T::zero(),
                    -s, T::zero(), c
                )
            }
            /// Rotates this matrix around the Z axis.
            pub fn rotate_z(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_z(angle_radians);
            }
            /// Returns this matrix rotated around the Z axis.
            pub fn rotated_z(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_z(angle_radians) * self
            }
            /// Creates a matrix that rotates around the Z axis.
            pub fn rotation_z(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    c, -s, T::zero(),
                    s,  c, T::zero(),
                    T::zero(), T::zero(), T::one()
                )
            }

            /// Rotates this matrix around a 3D axis.
            /// The axis is not required to be normalized.
            pub fn rotate_3d<V: Into<Vec3<T>>>(&mut self, angle_radians: T, axis: V)
                where T: Real + MulAdd<T,T,Output=T> + Add<T, Output=T>
            {
                *self = self.rotated_3d(angle_radians, axis);
            }
            /// Returns this matrix rotated around a 3D axis.
            /// The axis is not required to be normalized.
            pub fn rotated_3d<V: Into<Vec3<T>>>(self, angle_radians: T, axis: V) -> Self
                where T: Real + MulAdd<T,T,Output=T> + Add<T, Output=T>
            {
                Self::rotation_3d(angle_radians, axis) * self
            }
            /// Creates a matrix that rotates around a 3D axis.
            /// The axis is not required to be normalized.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Mat4, Mat3, Vec3, Quaternion};
            /// use std::f32::consts::PI;
            ///
            /// # fn main() {
            /// let angles = 32;
            /// for i in 0..angles {
            ///     let theta = PI * 2. * (i as f32) / (angles as f32);
            ///
            ///     assert_relative_eq!(Mat3::rotation_x(theta), Mat3::from(Quaternion::rotation_x(theta)), epsilon = 0.000001);
            ///     assert_relative_eq!(Mat3::rotation_y(theta), Mat3::from(Quaternion::rotation_y(theta)), epsilon = 0.000001);
            ///     assert_relative_eq!(Mat3::rotation_z(theta), Mat3::from(Quaternion::rotation_z(theta)), epsilon = 0.000001);
            ///
            ///     assert_relative_eq!(Mat3::rotation_x(theta), Mat3::rotation_3d(theta, Vec3::unit_x()));
            ///     assert_relative_eq!(Mat3::rotation_y(theta), Mat3::rotation_3d(theta, Vec3::unit_y()));
            ///     assert_relative_eq!(Mat3::rotation_z(theta), Mat3::rotation_3d(theta, Vec3::unit_z()));
            ///
            ///     assert_relative_eq!(Mat3::rotation_x(theta), Mat3::from(Mat4::rotation_3d(theta, Vec3::unit_x())));
            ///     assert_relative_eq!(Mat3::rotation_y(theta), Mat3::from(Mat4::rotation_3d(theta, Vec3::unit_y())));
            ///     assert_relative_eq!(Mat3::rotation_z(theta), Mat3::from(Mat4::rotation_3d(theta, Vec3::unit_z())));
            ///
            ///     assert_relative_eq!(Mat4::rotation_x(theta), Mat4::from(Mat3::rotation_3d(theta, Vec3::unit_x())));
            ///     assert_relative_eq!(Mat4::rotation_y(theta), Mat4::from(Mat3::rotation_3d(theta, Vec3::unit_y())));
            ///     assert_relative_eq!(Mat4::rotation_z(theta), Mat4::from(Mat3::rotation_3d(theta, Vec3::unit_z())));
            ///
            ///     // See what rotating unit vectors do for most angles between 0 and 2*PI.
            ///     // It's helpful to picture this as a right-handed coordinate system.
            ///
            ///     let v = Vec3::unit_y();
            ///     let m = Mat3::rotation_x(theta);
            ///     assert_relative_eq!(m * v, Vec3::new(0., theta.cos(), theta.sin()));
            ///
            ///     let v = Vec3::unit_z();
            ///     let m = Mat3::rotation_y(theta);
            ///     assert_relative_eq!(m * v, Vec3::new(theta.sin(), 0., theta.cos()));
            ///
            ///     let v = Vec3::unit_x();
            ///     let m = Mat3::rotation_z(theta);
            ///     assert_relative_eq!(m * v, Vec3::new(theta.cos(), theta.sin(), 0.));
            /// }
            /// # }
            /// ```
            pub fn rotation_3d<V: Into<Vec3<T>>>(angle_radians: T, axis: V) -> Self where T: Real + Add<T, Output=T> {
                let Vec3 { x, y, z } = axis.into().normalized();
                let s = angle_radians.sin();
                let c = angle_radians.cos();
                let oc = T::one() - c;
                Self::new(
                    oc*x*x + c  , oc*x*y - z*s, oc*z*x + y*s,
                    oc*x*y + z*s, oc*y*y + c  , oc*y*z - x*s,
                    oc*z*x - y*s, oc*y*z + x*s, oc*z*z + c
                )
            }
            /// Creates a matrix that would rotate a `from` direction to `to`.
            ///
            /// ```
            /// # extern crate vek;
            /// # #[macro_use] extern crate approx;
            /// # use vek::{Vec3, Mat3};
            ///
            /// # fn main() {
            /// let (from, to) = (Vec3::<f32>::unit_x(), Vec3::<f32>::unit_z());
            /// let m = Mat3::<f32>::rotation_from_to_3d(from, to);
            /// assert_relative_eq!(m * from, to);
            ///
            /// let (from, to) = (Vec3::<f32>::unit_x(), -Vec3::<f32>::unit_x());
            /// let m = Mat3::<f32>::rotation_from_to_3d(from, to);
            /// assert_relative_eq!(m * from, to);
            /// # }
            /// ```
            pub fn rotation_from_to_3d<V: Into<Vec3<T>>>(from: V, to: V) -> Self
                where T: Real + Add<T, Output=T>
            {
                Self::from(Quaternion::rotation_from_to_3d(from, to))
            }
        }

        use super::mat4::Mat4;
        impl<T> From<Mat4<T>> for Mat3<T> {
            fn from(m: Mat4<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec3::new(
                        m.x.into(),
                        m.y.into(),
                        m.z.into()
                    )
                }
            }
        }
        use super::mat2::Mat2;
        impl<T> From<Mat2<T>> for Mat3<T> where T: Zero + One {
            fn from(m: Mat2<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec3::new(
                        m.x.into(),
                        m.y.into(),
                        Vec3::new(T::zero(), T::zero(), T::one())
                    )
                }
            }
        }
        impl<T> From<Quaternion<T>> for Mat3<T>
            where T: Copy + Zero + One + Mul<Output=T> + Add<Output=T> + Sub<Output=T>
        {
            fn from(q: Quaternion<T>) -> Self {
                Mat4::from(q).into()
            }
        }
        /* NOTE: Blocked by From<Mat4<T>> for Quaternion
        /// A quaternion may be obtained from a rotation matrix.
        ///
        /// ```
        /// # extern crate vek;
        /// # #[macro_use] extern crate approx;
        /// # use vek::{Mat3, Quaternion, Vec3};
        /// # fn main() {
        /// let (angle, axis) = (3_f32, Vec3::new(1_f32, 3., 7.));
        /// let a = Quaternion::rotation_3d(angle, axis);
        /// let b = Quaternion::from(Mat3::rotation_3d(angle, axis));
        /// assert_relative_eq!(a, b);
        /// # }
        /// ```
        impl<T> From<Mat3<T>> for Quaternion<T>
            where T: Zero + One + Mul<Output=T> + Add<Output=T> + Sub<Output=T> + Real
        {
            fn from(m: Mat3<T>) -> Self {
                Mat4::from(m).into()
            }
        }
        */

    };
}

#[allow(unused_macros)]
macro_rules! mat_impl_mat2 {
    (rows) => {

        mat_impl_mat2!{common rows}

        impl<T> Mat2<T> {
            /// Creates a new 2x2 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T,
                m10: T, m11: T
            ) -> Self {
                Self {
                    rows: CVec2::new(
                        Vec2::new(m00, m01),
                        Vec2::new(m10, m11)
                    )
                }
            }
        }
    };
    (cols) => {

        mat_impl_mat2!{common cols}

        impl<T> Mat2<T> {
            /// Creates a new 2x2 matrix from elements in a layout-agnostic way.
            ///
            /// The parameters are named `mij` where `i` is the row index and
            /// `j` the column index. Their order is always the same regardless
            /// of the matrix's layout.
            pub fn new(
                m00: T, m01: T,
                m10: T, m11: T
            ) -> Self {
                Self {
                    cols: CVec2::new(
                        Vec2::new(m00, m10),
                        Vec2::new(m01, m11),
                    )
                }
            }
        }
    };
    (common $lines:ident) => {
        impl<T> Mat2<T> {
            /// Returns an element-wise-converted copy of this matrix, using the given conversion
            /// closure.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let m = Mat4::<f32>::new(
            ///     0.25, 1.25, 5.56, 8.66,
            ///     8.53, 2.92, 3.86, 9.36,
            ///     1.02, 0.28, 5.52, 6.06,
            ///     6.20, 7.01, 4.90, 5.26
            /// );
            /// let m = m.map(|x| x.round() as i32);
            /// assert_eq!(m, Mat4::new(
            ///     0, 1, 6, 9,
            ///     9, 3, 4, 9,
            ///     1, 0, 6, 6,
            ///     6, 7, 5, 5
            /// ));
            /// ```
            pub fn map<D,F>(self, mut f: F) -> Mat2<D> where F: FnMut(T) -> D {
                let m = self.$lines;
                Mat2 {
                    $lines: CVec2::new(
                        Vec2::new(f(m.x.x), f(m.x.y)),
                        Vec2::new(f(m.y.x), f(m.y.y)),
                    )
                }
            }

            /// Returns a memberwise-converted copy of this matrix, using `AsPrimitive`.
            ///
            /// # Examples
            ///
            /// ```
            /// # use vek::vec::Vec4;
            /// let v = Vec4::new(0_f32, 1., 2., 3.);
            /// let i: Vec4<i32> = v.as_();
            /// assert_eq!(i, Vec4::new(0, 1, 2, 3));
            /// ```
            ///
            /// # Safety
            ///
            /// **In Rust versions before 1.45.0**, some uses of the `as` operator were not entirely safe.
            /// In particular, it was undefined behavior if
            /// a truncated floating point value could not fit in the target integer
            /// type ([#10184](https://github.com/rust-lang/rust/issues/10184));
            ///
            /// ```ignore
            /// # use num_traits::AsPrimitive;
            /// let x: u8 = (1.04E+17).as_(); // UB
            /// ```
            pub fn as_<D>(self) -> Mat2<D> where T: AsPrimitive<D>, D: 'static + Copy {
                let m = self.$lines;
                Mat2 {
                    $lines: CVec2::new(
                        Vec2::new(m.x.x.as_(), m.x.y.as_()),
                        Vec2::new(m.y.x.as_(), m.y.y.as_()),
                    )
                }
            }

            /// Applies the function f to each element of two matrices, pairwise, and returns the result.
            ///
            /// ```
            /// use vek::mat::repr_c::row_major::Mat4;
            ///
            /// let a = Mat4::<f32>::new(
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99,
            ///     0.25, 1.25, 2.52, 2.99
            /// );
            /// let b = Mat4::<i32>::new(
            ///     0, 1, 0, 0,
            ///     1, 0, 0, 0,
            ///     0, 0, 1, 0,
            ///     0, 0, 0, 1
            /// );
            /// let m = a.map2(b, |a, b| a.round() as i32 + b);
            /// assert_eq!(m, Mat4::new(
            ///     0, 2, 3, 3,
            ///     1, 1, 3, 3,
            ///     0, 1, 4, 3,
            ///     0, 1, 3, 4
            /// ));
            /// ```
            pub fn map2<D,F,S>(self, other: Mat2<S>, mut f: F) -> Mat2<D> where F: FnMut(T, S) -> D {
                let m = self.$lines;
                let o = other.$lines;
                Mat2 {
                    $lines: CVec2::new(
                        Vec2::new(f(m.x.x, o.x.x), f(m.x.y, o.x.y)),
                        Vec2::new(f(m.y.x, o.y.x), f(m.y.y, o.y.y))
                    )
                }
            }

            /// The matrix's transpose.
            ///
            /// ```
            /// # extern crate vek;
            /// # use vek::Mat2;
            ///
            /// # fn main() {
            /// let m = Mat2::new(
            ///     0, 1,
            ///     4, 5
            /// );
            /// let t = Mat2::new(
            ///     0, 4,
            ///     1, 5
            /// );
            /// assert_eq!(m.transposed(), t);
            /// assert_eq!(m, m.transposed().transposed());
            /// # }
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transposed(self) -> Self {
                let s = self.$lines;
                Self {
                    $lines: CVec2::new(
                        Vec2::new(s.x.x, s.y.x),
                        Vec2::new(s.x.y, s.y.y)
                    )
                }
            }
            /// Transpose this matrix.
            ///
            /// ```
            /// # use vek::Mat2;
            ///
            /// let mut m = Mat2::new(
            ///     0, 1,
            ///     4, 5
            /// );
            /// let t = Mat2::new(
            ///     0, 4,
            ///     1, 5
            /// );
            /// m.transpose();
            /// assert_eq!(m, t);
            /// ```
            // NOTE: Implemented on a per-matrix basis to avoid a Clone bound on T
            pub fn transpose(&mut self) {
                mem::swap(&mut self.$lines.x.y, &mut self.$lines.y.x);
            }

            /// Get this matrix's determinant.
            ///
            /// A matrix is invertible if its determinant is non-zero.
            ///
            /// ```
            /// # use vek::Mat2;
            /// let (a,b,c,d) = (0,1,2,3);
            /// let m = Mat2::new(a,b,c,d);
            /// assert_eq!(m.determinant(), a*d - c*b);
            /// ```
            pub fn determinant(self) -> T where T: Mul<T,Output=T> + Sub<T,Output=T> {
                // NOTE: This works on row-major as well as column-major.
                let CVec2 {
                    x: Vec2 { x: a, y: b },
                    y: Vec2 { x: c, y: d },
                } = self.$lines;
                a*d - c*b
            }

            /// Rotates this matrix around the Z axis (counter-clockwise rotation in 2D).
            pub fn rotate_z(&mut self, angle_radians: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.rotated_z(angle_radians);
            }
            /// Rotates this matrix around the Z axis (counter-clockwise rotation in 2D).
            pub fn rotated_z(self, angle_radians: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::rotation_z(angle_radians) * self
            }
            /// Creates a matrix that rotates around the Z axis (counter-clockwise rotation in 2D).
            pub fn rotation_z(angle_radians: T) -> Self where T: Real {
                let c = angle_radians.cos();
                let s = angle_radians.sin();
                Self::new(
                    c, -s,
                    s,  c
                )
            }
            /// Scales this matrix in 2D.
            pub fn scale_2d<V: Into<Vec2<T>>>(&mut self, v: V)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.scaled_2d(v);
            }
            /// Returns this matrix scaled in 2D.
            pub fn scaled_2d<V: Into<Vec2<T>>>(self, v: V) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::scaling_2d(v) * self
            }
            /// Creates a 2D scaling matrix.
            pub fn scaling_2d<V: Into<Vec2<T>>>(v: V) -> Self where T: Zero {
                let Vec2 { x, y } = v.into();
                Self::new(
                    x, T::zero(),
                    T::zero(), y
                )
            }
            /// Shears this matrix along the X axis.
            pub fn shear_x(&mut self, k: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.sheared_x(k);
            }
            /// Returns this matrix sheared along the X axis.
            pub fn sheared_x(self, k: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::shearing_x(k) * self
            }
            /// Creates a 2D shearing matrix along the X axis.
            pub fn shearing_x(k: T) -> Self where T: Zero + One {
                Self::new(
                    T::one(), k,
                    T::zero(), T::one()
                )
            }
            /// Shears this matrix along the Y axis.
            pub fn shear_y(&mut self, k: T)
                where T: Real + MulAdd<T,T,Output=T>
            {
                *self = self.sheared_y(k);
            }
            /// Returns this matrix sheared along the Y axis.
            pub fn sheared_y(self, k: T) -> Self
                where T: Real + MulAdd<T,T,Output=T>
            {
                Self::shearing_y(k) * self
            }
            /// Creates a 2D shearing matrix along the Y axis.
            pub fn shearing_y(k: T) -> Self where T: Zero + One {
                Self::new(
                    T::one(), T::zero(),
                    k, T::one()
                )
            }
        }
        use super::mat3::Mat3;
        impl<T> From<Mat3<T>> for Mat2<T> {
            fn from(m: Mat3<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec2::new(
                        m.x.into(),
                        m.y.into(),
                    )
                }
            }
        }
        use super::mat4::Mat4;
        impl<T> From<Mat4<T>> for Mat2<T> {
            fn from(m: Mat4<T>) -> Self {
                let m = m.$lines;
                Self {
                    $lines: CVec2::new(
                        m.x.into(),
                        m.y.into(),
                    )
                }
            }
        }

    };
}




macro_rules! mat_impl_all_mats {
    ($lines:ident) => {
        /// 4x4 matrix.
        pub mod mat4 {
            use super::*;
            /// 4x4 matrix.
            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq/* , Ord, PartialOrd */)]
            #[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
            #[repr(C)]
            pub struct Mat4<T> {
                #[allow(missing_docs)]
                pub $lines: CVec4<Vec4<T>>,
            }
            mat_impl_mat!{$lines Mat4 RowMatrix4 ColumnMatrix4 CVec4 Vec4 (4 x 4) (x y z w)}
            mat_impl_mat4!{$lines}
        }
        pub use self::mat4::Mat4;

        /// 3x3 matrix.
        pub mod mat3 {
            use super::*;
            /// 3x3 matrix.
            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq/* , Ord, PartialOrd */)]
            #[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
            #[repr(C)]
            pub struct Mat3<T> {
                #[allow(missing_docs)]
                pub $lines: CVec3<Vec3<T>>,
            }
            mat_impl_mat!{$lines Mat3 RowMatrix3 ColumnMatrix3 CVec3 Vec3 (3 x 3) (x y z)}
            mat_impl_mat3!{$lines}
        }
        pub use self::mat3::Mat3;

        /// 2x2 matrix.
        pub mod mat2 {
            use super::*;
            /// 2x2 matrix.
            #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq/* , Ord, PartialOrd */)]
            #[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
            #[repr(C)]
            pub struct Mat2<T> {
                #[allow(missing_docs)]
                pub $lines: CVec2<Vec2<T>>,
            }
            mat_impl_mat!{$lines Mat2 RowMatrix2 ColumnMatrix2 CVec2 Vec2 (2 x 2) (x y)}
            mat_impl_mat2!{$lines}
        }
        pub use self::mat2::Mat2;

    }
}

macro_rules! mat_declare_modules {
    () => {
        pub mod column_major {
            //! Matrices stored in column-major layout.
            //!
            //! Multiplying a column-major matrix by one or more column vectors is fast
            //! due to the way it is implemented in SIMD
            //! (a `matrix * vector` mutliply is four broadcasts, one SIMD product and three fused-multiply-adds).
            //!
            //! Because `matrix * matrix` and `matrix * vector` products are fastest with this layout,
            //! it's the preferred one for most computer graphics libraries and application.

            use super::*;
            mat_impl_all_mats!{cols}
        }
        pub mod row_major {
            //! Matrices stored in row-major layout.
            //!
            //! Unlike the column-major layout, row-major matrices are good at being the
            //! right-hand-side of a product with one or more row vectors.
            //!
            //! Also, their natural indexing order matches the existing mathematical conventions.

            use super::*;
            mat_impl_all_mats!{rows}
        }
        /// Column-major layout is the default.
        ///
        /// Rationale:
        /// - (Matrix * Vector) multiplications are more efficient;
        /// - This is the layout expected by OpenGL;
        pub use self::column_major::*;
    }
}

pub mod repr_c {
    //! Matrix types which use `#[repr(C)]` vectors exclusively.
    //!
    //! See also the `repr_simd` neighbour module, which is available on Nightly
    //! with the `repr_simd` feature enabled.

    use super::*;
    #[allow(unused_imports)]
    use super::vec::repr_c::{Vec2, Vec2 as CVec2};
    #[allow(unused_imports)]
    use super::vec::repr_c::{Vec3, Vec3 as CVec3};
    use super::vec::repr_c::{Vec4, Vec4 as CVec4};

    use super::quaternion::repr_c::Quaternion;
    use super::transform::repr_c::Transform;

    mat_declare_modules!{}
}

#[cfg(all(nightly, feature="repr_simd"))]
pub mod repr_simd {
    //! Matrix types which use a `#[repr(C)]` vector of `#[repr(simd)]` vectors.

    use super::*;
    #[allow(unused_imports)]
    use super::vec::repr_simd::{Vec2};
    #[allow(unused_imports)]
    use super::vec::repr_c::{Vec2 as CVec2};
    #[allow(unused_imports)]
    use super::vec::repr_simd::{Vec3};
    #[allow(unused_imports)]
    use super::vec::repr_c::{Vec3 as CVec3};
    use super::vec::repr_simd::{Vec4};
    use super::vec::repr_c::{Vec4 as CVec4};

    use super::quaternion::repr_simd::Quaternion;
    use super::transform::repr_simd::Transform;

    mat_declare_modules!{}
}

pub use self::repr_c::*;


#[cfg(test)]
mod tests {
    macro_rules! for_each_type {
        ($mat:ident $Mat:ident $($T:ident)+) => {
            mod $mat {
                // repr_c matrices are expected to be packed.
                // repr_simd matrices are not necessarily expected to be packed in the first place.
                mod repr_c {
                    $(mod $T {
                        use $crate::mat::repr_c::column_major::$Mat;
                        use $crate::vtest::Rc;

                        #[test]
                        fn is_actually_packed() {
                            let m = $Mat::<$T>::identity();
                            let a = m.clone().into_row_array(); // same as into_col_array(), because identity.
                            let mp = unsafe {
                                ::std::slice::from_raw_parts(&m as *const _ as *const $T, a.len())
                            };
                            assert_eq!(mp, &a);
                        }
                        #[test]
                        fn is_actually_packed_refcell() {
                            let m = $Mat::<$T>::identity().map(::std::cell::RefCell::new);
                            let a = m.clone().into_row_array(); // same as into_col_array(), because identity.
                            let mp = unsafe {
                                ::std::slice::from_raw_parts(&m as *const _ as *const ::std::cell::RefCell<$T>, a.len())
                            };
                            assert_eq!(mp, &a);
                        }
                        #[test] fn claims_to_be_packed() { assert!($Mat::<$T>::default().is_packed()); }
                        #[test] fn claims_to_be_packed_refcell() { assert!($Mat::<$T>::default().map(::std::cell::RefCell::new).is_packed()); }

                        #[test]
                        fn rc_col_array_conversions() {
                            let m: $Mat<Rc<i32>> = $Mat::default().map(Rc::new);
                            let mut a = m.into_col_array();
                            assert_eq!(Rc::strong_count(&a[0]), 1);
                            *Rc::make_mut(&mut a[0]) = 2; // Try to write. If there's a double free, this is supposed to crash.
                            let mut m = $Mat::from_col_array(a);
                            assert_eq!(Rc::strong_count(&m[(0, 0)]), 1);
                            *Rc::make_mut(&mut m[(0, 0)]) = 3; // Try to write. If there's a double free, this is supposed to crash.
                        }
                        #[test]
                        fn rc_row_array_conversions() {
                            let m: $Mat<Rc<i32>> = $Mat::default().map(Rc::new);
                            let mut a = m.into_row_array();
                            assert_eq!(Rc::strong_count(&a[0]), 1);
                            *Rc::make_mut(&mut a[0]) = 2; // Try to write. If there's a double free, this is supposed to crash.
                            let mut m = $Mat::from_row_array(a);
                            assert_eq!(Rc::strong_count(&m[(0, 0)]), 1);
                            *Rc::make_mut(&mut m[(0, 0)]) = 3; // Try to write. If there's a double free, this is supposed to crash.
                        }
                    })+
                }
                #[cfg(all(nightly, feature="repr_simd"))]
                mod repr_simd {
                    mod bool {
                        use $crate::mat::repr_simd::column_major::$Mat;
                        #[test]
                        fn can_monomorphize() {
                            let _: $Mat<bool> = $Mat::<u32>::identity().map(|x| x != 0);
                        }
                    }
                }
            }
        };
    }
    // Vertical editing helps here :)
    for_each_type!{mat2 Mat2 i8 u8 i16 u16 i32 u32 i64 u64 f32 f64}
    for_each_type!{mat3 Mat3 i8 u8 i16 u16 i32 u32 i64 u64 f32 f64}
    for_each_type!{mat4 Mat4 i8 u8 i16 u16 i32 u32 i64 u64 f32 f64}

    use super::Mat4;
    use super::super::vec::Vec4;


    #[test] fn simple_mat2() {
        use crate::vec::Vec2;
        use crate::mat::row_major::Mat2 as Rows2;
        use crate::mat::column_major::Mat2 as Cols2;
        let a = Rows2::new(
            1, 2,
            3, 4
        );
        let b = Cols2::new(
            1, 2,
            3, 4
        );
        let v = Vec2::new(2, 3);

        // Not available on no_std, but don't worry, it's been long proven.
        // assert_eq!(format!("{}", a), format!("{}", b));
        
        assert_eq!(v * a, v * b);
        assert_eq!(a * v, b * v);
    }

    #[test]
    fn test_model_look_at_rh() {
        let eye = Vec4::new(1_f32, 0., 1., 1.);
        let target = Vec4::new(2_f32, 0., 2., 1.);
        let model = Mat4::<f32>::model_look_at_rh(eye, target, Vec4::unit_y());
        assert_relative_eq!(model * Vec4::unit_w(), Vec4::new(1_f32, 0., 1., 1.));
        assert_relative_eq!(model * Vec4::new(0_f32, 0., -2_f32.sqrt(), 1.), Vec4::new(2_f32, 0., 2., 1.));

        // A "model" look-at essentially undoes a "view" look-at
        let view = Mat4::<f32>::look_at_rh(eye, target, Vec4::unit_y());
        assert_relative_eq!(view * model, Mat4::identity());
        assert_relative_eq!(model * view, Mat4::identity());
    }

    #[test]
    fn test_model_look_at_lh() {
        let eye = Vec4::new(1_f32, 0., 1., 1.);
        let target = Vec4::new(2_f32, 0., 2., 1.);
        let model = Mat4::<f32>::model_look_at_lh(eye, target, Vec4::unit_y());
        assert_relative_eq!(model * Vec4::unit_w(), eye);
        let d = 2_f32.sqrt();
        assert_relative_eq!(model * Vec4::new(0_f32, 0., d, 1.), target);

        // A "model" look-at essentially undoes a "view" look-at
        let view = Mat4::look_at_lh(eye, target, Vec4::unit_y());
        assert_relative_eq!(view * model, Mat4::identity());
        assert_relative_eq!(model * view, Mat4::identity());
    }

    #[test]
    fn test_look_at_rh() {
        let eye = Vec4::new(1_f32, 0., 1., 1.);
        let target = Vec4::new(2_f32, 0., 2., 1.);
        let view = Mat4::<f32>::look_at_rh(eye, target, Vec4::unit_y());
        assert_relative_eq!(view * eye, Vec4::unit_w());
        assert_relative_eq!(view * target, Vec4::new(0_f32, 0., -2_f32.sqrt(), 1.));
    }

    #[test]
    fn test_look_at_lh() {
        let eye = Vec4::new(1_f32, 0., 1., 1.);
        let target = Vec4::new(2_f32, 0., 2., 1.);
        let view = Mat4::<f32>::look_at_lh(eye, target, Vec4::unit_y());
        assert_relative_eq!(view * eye, Vec4::unit_w());
        assert_relative_eq!(view * target, Vec4::new(0_f32, 0., 2_f32.sqrt(), 1.));
    }

}