Skip to content

Commit

Permalink
Merge #200 #201 #202 #203
Browse files Browse the repository at this point in the history
200: Extend range of MatrixFormatError r=samueltardieu a=samueltardieu



201: Add read-only row iterator to Matrix r=samueltardieu a=samueltardieu



202: Use writeln! when appropriate r=samueltardieu a=samueltardieu



203: Implement Deref/DerefMut instead of AsRef/AsMut r=samueltardieu a=samueltardieu

This allows the removal of `to_vec` which now comes as part of the slice
`Deref` operation.

Co-authored-by: Samuel Tardieu <sam@rfc1149.net>
  • Loading branch information
bors[bot] and samueltardieu committed Jan 8, 2019
5 parents 11df60c + 05bc6ff + 98c32bc + a27c432 + daead4b commit 1a07ae3
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 96 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -6,7 +6,7 @@ keywords = ["shortest-path", "astar", "dijkstra", "flow", "graph"]
license = "Apache-2.0/MIT"
homepage = "https://rfc1149.net/devel/pathfinding.html"
documentation = "https://docs.rs/pathfinding/"
version = "1.1.11-pre"
version = "2.0.0-pre"
authors = ["Samuel Tardieu <sam@rfc1149.net>"]
categories = ["algorithms"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -40,7 +40,7 @@ In your `Cargo.toml`, put:

``` ini
[dependencies]
pathfinding = "1.1"
pathfinding = "2"
```

You can then pull your preferred algorithm (BFS in this example) using:
Expand Down
2 changes: 1 addition & 1 deletion src/directed/edmonds_karp.rs
Expand Up @@ -125,7 +125,7 @@ pub trait EdmondsKarp<C: Copy + Zero + Signed + Ord + Bounded> {
where
Self: Sized,
{
Self::from_matrix(source, sink, Matrix::square_from_vec(capacities))
Self::from_matrix(source, sink, Matrix::square_from_vec(capacities).unwrap())
}

/// Common data.
Expand Down
2 changes: 1 addition & 1 deletion src/grid.rs
Expand Up @@ -481,7 +481,7 @@ impl fmt::Debug for Grid {
write!(f, "{}", if self.has_vertex(&(x, y)) { '#' } else { '.' })?;
}
if y != self.height - 1 {
write!(f, "\n")?;
writeln!(f)?;
}
}
Ok(())
Expand Down
153 changes: 98 additions & 55 deletions src/matrix.rs
Expand Up @@ -4,7 +4,7 @@ use itertools::iproduct;
use num_traits::Signed;
use std::error::Error;
use std::fmt;
use std::ops::{Index, IndexMut, Neg, Range};
use std::ops::{Deref, DerefMut, Index, IndexMut, Neg, Range};

/// Matrix of an arbitrary type. Data are stored consecutively in
/// memory, by rows. Raw data can be accessed using `as_ref()`
Expand Down Expand Up @@ -41,9 +41,19 @@ impl<C: Clone> Matrix<C> {
self.data.resize(self.rows * self.columns, value);
}

/// Return a copy of a sub-matrix.
/// Return a copy of a sub-matrix, or return an error if the
/// ranges are outside the original matrix.
#[allow(clippy::needless_pass_by_value)]
pub fn slice(&self, rows: Range<usize>, columns: Range<usize>) -> Matrix<C> {
pub fn slice(
&self,
rows: Range<usize>,
columns: Range<usize>,
) -> Result<Matrix<C>, MatrixFormatError> {
if rows.end > self.rows || columns.end > self.columns {
return Err(MatrixFormatError {
message: "slice far end points outside the matrix".to_owned(),
});
}
let height = rows.end - rows.start;
let width = columns.end - columns.start;
let mut v = Vec::with_capacity(height * width);
Expand Down Expand Up @@ -117,29 +127,31 @@ impl<C: Clone> Matrix<C> {
}
}

/// Extend the matrix in place by adding one full row.
///
/// # Panics
///
/// This function panics if the row does not have the expected
/// number of elements.
pub fn extend(&mut self, row: &[C]) {
assert_eq!(
self.columns,
row.len(),
"new row has {} columns intead of expected {}",
row.len(),
self.columns
);
/// Extend the matrix in place by adding one full row. An error
/// is returned if the row does not have the expected number of
/// elements.
pub fn extend(&mut self, row: &[C]) -> Result<(), MatrixFormatError> {
if self.columns != row.len() {
return Err(MatrixFormatError {
message: format!(
"new row has {} columns intead of expected {}",
row.len(),
self.columns
),
});
}
self.rows += 1;
for e in row {
self.data.push(e.clone());
}
Ok(())
}
}

impl<C: Copy> Matrix<C> {
/// Replace a slice of the current matrix with the content of another one.
/// Only the relevant cells will be extracted if the slice goes outside the
/// original matrix.
pub fn set_slice(&mut self, pos: &(usize, usize), slice: &Matrix<C>) {
let &(ref row, ref column) = pos;
let height = (self.rows - row).min(slice.rows);
Expand All @@ -166,39 +178,34 @@ impl<C: Clone + Signed> Neg for Matrix<C> {
impl<C> Matrix<C> {
/// Create new matrix from vector values. The first value
/// will be assigned to index (0, 0), the second one to index (0, 1),
/// and so on.
///
/// # Panics
///
/// This function will panic if the number of values does not correspond
/// to the announced size.
pub fn from_vec(rows: usize, columns: usize, values: Vec<C>) -> Matrix<C> {
assert_eq!(
rows * columns,
values.len(),
"length of vector does not correspond to announced dimensions"
);
Matrix {
/// and so on. An error is returned instead if data length does not
/// correspond to the announced size.
pub fn from_vec(
rows: usize,
columns: usize,
values: Vec<C>,
) -> Result<Matrix<C>, MatrixFormatError> {
if rows * columns != values.len() {
return Err(MatrixFormatError { message: format!("length of vector does not correspond to announced dimensions ({} instead of {}×{}={})", values.len(), rows, columns, rows*columns)});
}
Ok(Matrix {
rows,
columns,
data: values,
}
})
}

/// Create new square matrix from vector values. The first value
/// will be assigned to index (0, 0), the second one to index (0, 1),
/// and so on.
///
/// # Panics
///
/// This function will panic if the number of values is not a square number.
pub fn square_from_vec(values: Vec<C>) -> Matrix<C> {
/// and so on. An error is returned if the number of values is not a
/// square number.
pub fn square_from_vec(values: Vec<C>) -> Result<Matrix<C>, MatrixFormatError> {
let size = (values.len() as f32).sqrt().round() as usize;
assert_eq!(
size * size,
values.len(),
"length of vector is not a square number"
);
if size * size != values.len() {
return Err(MatrixFormatError {
message: format!("length of vector ({}) is not a square number", values.len()),
});
}
Self::from_vec(size, size, values)
}

Expand Down Expand Up @@ -255,18 +262,12 @@ impl<C> Matrix<C> {
});
}
}
Ok(Matrix::from_vec(nrows, ncols, data))
Matrix::from_vec(nrows, ncols, data)
} else {
Ok(Matrix::new_empty(0))
}
}

/// Retrieve the content of the matrix as a vector. The content starts
/// with the first row, then the second one, and so on.
pub fn to_vec(self) -> Vec<C> {
self.data
}

/// Check if a matrix is a square one.
pub fn is_square(&self) -> bool {
self.rows == self.columns
Expand Down Expand Up @@ -380,6 +381,11 @@ impl<C> Matrix<C> {
.filter(move |&(dr, dc)| (diagonals && dr != 0 && dc != 0) || dr.abs() + dc.abs() == 1)
.map(move |(dr, dc)| ((r as isize + dr) as usize, (c as isize + dc) as usize))
}

/// Return an iterator on rows of the matrix.
pub fn iter(&self) -> RowIterator<C> {
(&self).into_iter()
}
}

impl<'a, C> Index<&'a (usize, usize)> for Matrix<C> {
Expand All @@ -397,14 +403,16 @@ impl<'a, C> IndexMut<&'a (usize, usize)> for Matrix<C> {
}
}

impl<C> AsRef<[C]> for Matrix<C> {
fn as_ref(&self) -> &[C] {
impl<C> Deref for Matrix<C> {
type Target = [C];

fn deref(&self) -> &[C] {
&self.data
}
}

impl<C> AsMut<[C]> for Matrix<C> {
fn as_mut(&mut self) -> &mut [C] {
impl<C> DerefMut for Matrix<C> {
fn deref_mut(&mut self) -> &mut [C] {
&mut self.data
}
}
Expand All @@ -431,14 +439,14 @@ impl<C> AsMut<[C]> for Matrix<C> {
macro_rules! matrix {
($a:expr) => {{
let mut m = pathfinding::matrix::Matrix::new_empty($a.len());
m.extend(&$a);
m.extend(&$a).unwrap();
m
}};
($a:expr, $($b: expr),+) => {{
let mut m = matrix!($a);
let mut r = 0;
$(
m.extend(&$b);
m.extend(&$b).unwrap();
)+
m
}};
Expand All @@ -458,3 +466,38 @@ impl fmt::Display for MatrixFormatError {
write!(f, "matrix format error: {}", self.message)
}
}

/// Row iterator returned by `iter()` on a matrix.
pub struct RowIterator<'a, C> {
matrix: &'a Matrix<C>,
row: usize,
}

impl<'a, C> Iterator for RowIterator<'a, C> {
type Item = &'a [C];

fn next(&mut self) -> Option<Self::Item> {
if self.row < self.matrix.rows {
let r = Some(
&self.matrix.data
[self.row * self.matrix.columns..(self.row + 1) * self.matrix.columns],
);
self.row += 1;
r
} else {
None
}
}
}

impl<'a, C> IntoIterator for &'a Matrix<C> {
type IntoIter = RowIterator<'a, C>;
type Item = &'a [C];

fn into_iter(self) -> RowIterator<'a, C> {
RowIterator {
matrix: self,
row: 0,
}
}
}

0 comments on commit 1a07ae3

Please sign in to comment.