#![macro_use] macro_rules! matrix_impl( ($t: ident, $dimension: expr, $vector: ident, $dvector: ident, $($compN: ident),+) => ( impl $t { #[inline] pub fn new($($compN: N ),+) -> $t { $t { $($compN: $compN ),+ } } } /* * * Conversions (AsRef, AsMut, From) * */ impl AsRef<[[N; $dimension]; $dimension]> for $t { #[inline] fn as_ref(&self) -> &[[N; $dimension]; $dimension] { unsafe { mem::transmute(self) } } } impl AsMut<[[N; $dimension]; $dimension]> for $t { #[inline] fn as_mut(&mut self) -> &mut [[N; $dimension]; $dimension] { unsafe { mem::transmute(self) } } } impl<'a, N> From<&'a [[N; $dimension]; $dimension]> for &'a $t { #[inline] fn from(arr: &'a [[N; $dimension]; $dimension]) -> &'a $t { unsafe { mem::transmute(arr) } } } impl<'a, N> From<&'a mut [[N; $dimension]; $dimension]> for &'a mut $t { #[inline] fn from(arr: &'a mut [[N; $dimension]; $dimension]) -> &'a mut $t { unsafe { mem::transmute(arr) } } } impl<'a, N: Clone> From<&'a [[N; $dimension]; $dimension]> for $t { #[inline] fn from(arr: &'a [[N; $dimension]; $dimension]) -> $t { let tref: &$t = From::from(arr); tref.clone() } } /* * * Unsafe indexing. * */ impl $t { #[inline] pub unsafe fn at_fast(&self, (i, j): (usize, usize)) -> N { (*mem::transmute::<&$t, &[N; $dimension * $dimension]>(self) .get_unchecked(i + j * $dimension)) } #[inline] pub unsafe fn set_fast(&mut self, (i, j): (usize, usize), val: N) { (*mem::transmute::<&mut $t, &mut [N; $dimension * $dimension]>(self) .get_unchecked_mut(i + j * $dimension)) = val } } /* * * Cast * */ impl> Cast<$t> for $t { #[inline] fn from(v: $t) -> $t { $t::new($(Cast::from(v.$compN)),+) } } /* * * Iterable * */ impl Iterable for $t { #[inline] fn iter(&self) -> Iter { unsafe { mem::transmute::<&$t, &[N; $dimension * $dimension]>(self).iter() } } } impl IterableMut for $t { #[inline] fn iter_mut(& mut self) -> IterMut { unsafe { mem::transmute::<&mut $t, &mut [N; $dimension * $dimension]>(self).iter_mut() } } } /* * * Shape/Indexable/Index * */ impl Shape<(usize, usize)> for $t { #[inline] fn shape(&self) -> (usize, usize) { ($dimension, $dimension) } } impl Indexable<(usize, usize), N> for $t { #[inline] fn swap(&mut self, (i1, j1): (usize, usize), (i2, j2): (usize, usize)) { unsafe { mem::transmute::<&mut $t, &mut [N; $dimension * $dimension]>(self) .swap(i1 + j1 * $dimension, i2 + j2 * $dimension) } } #[inline] unsafe fn unsafe_at(&self, (i, j): (usize, usize)) -> N { (*mem::transmute::<&$t, &[N; $dimension * $dimension]>(self).get_unchecked(i + j * $dimension)) } #[inline] unsafe fn unsafe_set(&mut self, (i, j): (usize, usize), val: N) { (*mem::transmute::<&mut $t, &mut [N; $dimension * $dimension]>(self).get_unchecked_mut(i + j * $dimension)) = val } } impl Index<(usize, usize)> for $t { type Output = N; fn index(&self, (i, j): (usize, usize)) -> &N { unsafe { &mem::transmute::<&$t, & [N; $dimension * $dimension]>(self)[i + j * $dimension] } } } impl IndexMut<(usize, usize)> for $t { fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut N { unsafe { &mut mem::transmute::<&mut $t, &mut [N; $dimension * $dimension]>(self)[i + j * $dimension] } } } /* * * Row/Column * */ impl Column<$vector> for $t { #[inline] fn ncols(&self) -> usize { Dimension::dimension(None::<$t>) } #[inline] fn set_column(&mut self, column: usize, v: $vector) { for (i, e) in v.iter().enumerate() { self[(i, column)] = *e; } } #[inline] fn column(&self, column: usize) -> $vector { let mut res: $vector = ::zero(); for (i, e) in res.iter_mut().enumerate() { *e = self[(i, column)]; } res } } impl ColumnSlice<$dvector> for $t { fn column_slice(&self, cid: usize, rstart: usize, rend: usize) -> $dvector { let column = self.column(cid); $dvector::from_slice(rend - rstart, &column.as_ref()[rstart .. rend]) } } impl Row<$vector> for $t { #[inline] fn nrows(&self) -> usize { Dimension::dimension(None::<$t>) } #[inline] fn set_row(&mut self, row: usize, v: $vector) { for (i, e) in v.iter().enumerate() { self[(row, i)] = *e; } } #[inline] fn row(&self, row: usize) -> $vector { let mut res: $vector = ::zero(); for (i, e) in res.iter_mut().enumerate() { *e = self[(row, i)]; } res } } impl RowSlice<$dvector> for $t { fn row_slice(&self, rid: usize, cstart: usize, cend: usize) -> $dvector { let row = self.row(rid); $dvector::from_slice(cend - cstart, &row.as_ref()[cstart .. cend]) } } /* * * Transpose * */ impl Transpose for $t { #[inline] fn transpose(&self) -> $t { let mut res = *self; res.transpose_mut(); res } #[inline] fn transpose_mut(&mut self) { for i in 1 .. $dimension { for j in 0 .. i { self.swap((i, j), (j, i)) } } } } /* * * ApproxEq * */ impl> ApproxEq for $t { #[inline] fn approx_epsilon(_: Option<$t>) -> N { ApproxEq::approx_epsilon(None::) } #[inline] fn approx_ulps(_: Option<$t>) -> u32 { ApproxEq::approx_ulps(None::) } #[inline] fn approx_eq_eps(&self, other: &$t, epsilon: &N) -> bool { let mut zip = self.iter().zip(other.iter()); zip.all(|(a, b)| ApproxEq::approx_eq_eps(a, b, epsilon)) } #[inline] fn approx_eq_ulps(&self, other: &$t, ulps: u32) -> bool { let mut zip = self.iter().zip(other.iter()); zip.all(|(a, b)| ApproxEq::approx_eq_ulps(a, b, ulps)) } } /* * * Mean * */ impl + Clone> Mean<$vector> for $t { fn mean(&self) -> $vector { let mut res: $vector = ::zero(); let normalizer: N = Cast::from(1.0f64 / $dimension as f64); for i in 0 .. $dimension { for j in 0 .. $dimension { unsafe { let acc = res.unsafe_at(j) + self.unsafe_at((i, j)) * normalizer; res.unsafe_set(j, acc); } } } res } } /* * * Diagonal * */ impl Diagonal<$vector> for $t { #[inline] fn from_diagonal(diagonal: &$vector) -> $t { let mut res: $t = ::zero(); res.set_diagonal(diagonal); res } #[inline] fn diagonal(&self) -> $vector { let mut diagonal: $vector = ::zero(); for i in 0 .. $dimension { unsafe { diagonal.unsafe_set(i, self.unsafe_at((i, i))) } } diagonal } } impl DiagonalMut<$vector> for $t { #[inline] fn set_diagonal(&mut self, diagonal: &$vector) { for i in 0 .. $dimension { unsafe { self.unsafe_set((i, i), diagonal.unsafe_at(i)) } } } } /* * * Outer * */ impl + Zero> Outer for $vector { type OuterProductType = $t; #[inline] fn outer(&self, other: &$vector) -> $t { let mut res: $t = ::zero(); for i in 0 .. ::dimension::<$vector>() { for j in 0 .. ::dimension::<$vector>() { res[(i, j)] = self[i] * other[j] } } res } } /* * * Componentwise unary operations. * */ componentwise_repeat!($t, $($compN),+); componentwise_absolute!($t, $($compN),+); componentwise_zero!($t, $($compN),+); /* * * Pointwise binary operations. * */ pointwise_add!($t, $($compN),+); pointwise_sub!($t, $($compN),+); pointwise_scalar_add!($t, $($compN),+); pointwise_scalar_sub!($t, $($compN),+); pointwise_scalar_div!($t, $($compN),+); pointwise_scalar_mul!($t, $($compN),+); ) ); macro_rules! mat_mul_mat_impl( ($t: ident, $dimension: expr) => ( impl Mul<$t> for $t { type Output = $t; #[inline] fn mul(self, right: $t) -> $t { let mut res: $t = ::zero(); for i in 0 .. $dimension { for j in 0 .. $dimension { let mut acc: N = ::zero(); unsafe { for k in 0 .. $dimension { acc = acc + self.at_fast((i, k)) * right.at_fast((k, j)); } res.set_fast((i, j), acc); } } } res } } impl MulAssign<$t> for $t { #[inline] fn mul_assign(&mut self, right: $t) { // NOTE: there is probably not any useful optimization to perform here compaired to the // version without assignment.. *self = *self * right } } ) ); macro_rules! vec_mul_mat_impl( ($t: ident, $v: ident, $dimension: expr, $zero: expr) => ( impl Mul<$t> for $v { type Output = $v; #[inline] fn mul(self, right: $t) -> $v { let mut res : $v = $zero(); for i in 0..$dimension { for j in 0..$dimension { unsafe { let val = res.at_fast(i) + self.at_fast(j) * right.at_fast((j, i)); res.set_fast(i, val) } } } res } } impl MulAssign<$t> for $v { #[inline] fn mul_assign(&mut self, right: $t) { // NOTE: there is probably not any useful optimization to perform here compaired to the // version without assignment.. *self = *self * right } } ) ); macro_rules! mat_mul_vec_impl( ($t: ident, $v: ident, $dimension: expr, $zero: expr) => ( impl Mul<$v> for $t { type Output = $v; #[inline] fn mul(self, right: $v) -> $v { let mut res : $v = $zero(); for i in 0 .. $dimension { for j in 0 .. $dimension { unsafe { let val = res.at_fast(i) + self.at_fast((i, j)) * right.at_fast(j); res.set_fast(i, val) } } } res } } ) ); macro_rules! point_mul_mat_impl( ($t: ident, $v: ident, $dimension: expr, $zero: expr) => ( vec_mul_mat_impl!($t, $v, $dimension, $zero); ) ); macro_rules! mat_mul_point_impl( ($t: ident, $v: ident, $dimension: expr, $zero: expr) => ( mat_mul_vec_impl!($t, $v, $dimension, $zero); ) ); macro_rules! inverse_impl( ($t: ident, $dimension: expr) => ( impl Inverse for $t { #[inline] fn inverse(&self) -> Option<$t> { let mut res : $t = *self; if res.inverse_mut() { Some(res) } else { None } } fn inverse_mut(&mut self) -> bool { let mut res: $t = ::one(); // inversion using Gauss-Jordan elimination for k in 0..$dimension { // search a non-zero value on the k-th column // FIXME: would it be worth it to spend some more time searching for the // max instead? let mut n0 = k; // index of a non-zero entry while n0 != $dimension { if self[(n0, k)] != ::zero() { break; } n0 = n0 + 1; } if n0 == $dimension { return false } // swap pivot line if n0 != k { for j in 0..$dimension { self.swap((n0, j), (k, j)); res.swap((n0, j), (k, j)); } } let pivot = self[(k, k)]; for j in k..$dimension { let selfval = self[(k, j)] / pivot; self[(k, j)] = selfval; } for j in 0..$dimension { let resval = res[(k, j)] / pivot; res[(k, j)] = resval; } for l in 0..$dimension { if l != k { let normalizer = self[(l, k)]; for j in k..$dimension { let selfval = self[(l, j)] - self[(k, j)] * normalizer; self[(l, j)] = selfval; } for j in 0..$dimension { let resval = res[(l, j)] - res[(k, j)] * normalizer; res[(l, j)] = resval; } } } } *self = res; true } } ) ); macro_rules! to_homogeneous_impl( ($t: ident, $t2: ident, $dimension: expr, $dim2: expr) => ( impl ToHomogeneous<$t2> for $t { #[inline] fn to_homogeneous(&self) -> $t2 { let mut res: $t2 = ::one(); for i in 0 .. $dimension { for j in 0 .. $dimension { res[(i, j)] = self[(i, j)] } } res } } ) ); macro_rules! from_homogeneous_impl( ($t: ident, $t2: ident, $dimension: expr, $dim2: expr) => ( impl FromHomogeneous<$t2> for $t { #[inline] fn from(m: &$t2) -> $t { let mut res: $t = ::one(); for i in 0 .. $dimension { for j in 0 .. $dimension { res[(i, j)] = m[(i, j)] } } // FIXME: do we have to deal the lost components // (like if the 1 is not a 1… do we have to divide?) res } } ) ); macro_rules! eigen_qr_impl( ($t: ident, $v: ident) => ( impl EigenQR> for $t where N: BaseFloat + ApproxEq + Clone { fn eigen_qr(&self, eps: &N, niter: usize) -> ($t, $v) { linalg::eigen_qr(self, eps, niter) } } ) ); macro_rules! mat_display_impl( ($t: ident, $dimension: expr) => ( impl fmt::Display for $t { // XXX: will will not always work correctly due to rounding errors. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn integral_length(val: &N) -> usize { let mut res = 1; let mut curr: N = ::cast(10.0f64); while curr <= *val { curr = curr * ::cast(10.0f64); res = res + 1; } if val.is_sign_negative() { res + 1 } else { res } } let mut max_decimal_length = 0; let mut decimal_lengths: $t = ::zero(); for i in 0 .. $dimension { for j in 0 .. $dimension { decimal_lengths[(i, j)] = integral_length(&self[(i, j)].clone()); max_decimal_length = ::max(max_decimal_length, decimal_lengths[(i, j)]); } } let precision = f.precision().unwrap_or(3); let max_number_length = max_decimal_length + precision + 1; try!(writeln!(f, " ┌ {:>width$} ┐", "", width = max_number_length * $dimension + $dimension - 1)); for i in 0 .. $dimension { try!(write!(f, " │")); for j in 0 .. $dimension { let number_length = decimal_lengths[(i, j)] + precision + 1; let pad = max_number_length - number_length; try!(write!(f, " {:>thepad$}", "", thepad = pad)); try!(write!(f, "{:.*}", precision, (*self)[(i, j)])); } try!(writeln!(f, " │")); } writeln!(f, " └ {:>width$} ┘", "", width = max_number_length * $dimension + $dimension - 1) } } ) ); macro_rules! one_impl( ($t: ident, $($valueN: expr),+ ) => ( impl One for $t { #[inline] fn one() -> $t { $t::new($($valueN() ),+) } } ) ); macro_rules! eye_impl( ($t: ident, $dimension: expr, $($comp_diagN: ident),+) => ( impl Eye for $t { fn new_identity(dimension: usize) -> $t { assert!(dimension == $dimension); let mut eye: $t = ::zero(); $(eye.$comp_diagN = ::one();)+ eye } } ) ); macro_rules! dim_impl( ($t: ident, $dimension: expr) => ( impl Dimension for $t { #[inline] fn dimension(_: Option<$t>) -> usize { $dimension } } ) );