Refactor control flow of algorithm

* Get rid of enum Step
* Instead more explicity encode the control flow.
This commit is contained in:
Michael Neumann 2019-04-07 17:30:57 +02:00
parent bd8728ce2f
commit 44026fb680

View File

@ -48,32 +48,19 @@ pub enum Error {
MatrixNotSolvable, MatrixNotSolvable,
} }
#[derive(Debug, Eq, PartialEq)]
enum Step {
Step1,
Step2,
Step3,
Step4(Option<usize>),
Step5(Position),
Step6,
Failure(Error),
Done,
}
/// For each row of the matrix, find the smallest element and /// For each row of the matrix, find the smallest element and
/// subtract it from every element in its row. Go to Step 2. /// subtract it from every element in its row. Go to Step 2.
fn step1<W>(c: &mut W) -> Step fn step1<W>(c: &mut W)
where where
W: Weights, W: Weights,
{ {
c.sub_min_of_each_row(); c.sub_min_of_each_row();
return Step::Step2;
} }
/// Find a zero (Z) in the resulting matrix. If there is no starred /// Find a zero (Z) in the resulting matrix. If there is no starred
/// zero in its row or column, star Z. Repeat for each element in the /// zero in its row or column, star Z. Repeat for each element in the
/// matrix. Go to Step 3. /// matrix. Go to Step 3.
fn step2<W>(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step fn step2<W>(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage)
where where
W: Weights, W: Weights,
{ {
@ -94,14 +81,18 @@ where
// clear covers // clear covers
cov.clear(); cov.clear();
}
return Step::Step3; #[derive(Debug, Eq, PartialEq)]
enum Step3 {
Done,
ContinueWithStep4 { star_count: usize },
} }
/// Cover each column containing a starred zero. If K columns are /// Cover each column containing a starred zero. If K columns are
/// covered, the starred zeros describe a complete set of unique /// covered, the starred zeros describe a complete set of unique
/// assignments. In this case, Go to DONE, otherwise, Go to Step 4. /// assignments. In this case, Go to DONE, otherwise, Go to Step 4.
fn step3<W>(c: &W, marks: &MarkMatrix, cov: &mut Coverage) -> Step fn step3<W>(c: &W, marks: &MarkMatrix, cov: &mut Coverage) -> Step3
where where
W: Weights, W: Weights,
{ {
@ -110,27 +101,33 @@ where
assert!(marks.n() == n); assert!(marks.n() == n);
assert!(cov.n() == n); assert!(cov.n() == n);
let mut count: usize = 0; let mut star_count: usize = 0;
marks.each_star(|Position { column, .. }| { marks.each_star(|Position { column, .. }| {
cov.cover_column(column); cov.cover_column(column);
count += 1; star_count += 1;
}); });
if count >= n { if star_count >= n {
assert!(count == n); assert!(star_count == n);
Step::Done Step3::Done
} else { } else {
Step::Step4(Some(count)) Step3::ContinueWithStep4 { star_count }
} }
} }
#[derive(Debug, Eq, PartialEq)]
enum Step4 {
ContinueWithStep5 { z0_pos: Position },
ContinueWithStep6,
}
/// Find a noncovered zero and prime it. If there is no starred zero /// Find a noncovered zero and prime it. If there is no starred zero
/// in the row containing this primed zero, Go to Step 5. Otherwise, /// in the row containing this primed zero, Go to Step 5. Otherwise,
/// cover this row and uncover the column containing the starred /// cover this row and uncover the column containing the starred
/// zero. Continue in this manner until there are no uncovered zeros /// zero. Continue in this manner until there are no uncovered zeros
/// left. Save the smallest uncovered value and Go to Step 6. /// left. Save the smallest uncovered value and Go to Step 6.
fn step4<W>(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step fn step4<W>(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step4
where where
W: Weights, W: Weights,
{ {
@ -142,9 +139,6 @@ where
loop { loop {
// find uncovered zero element // find uncovered zero element
match cov.find_uncovered_cell_column_row_order(|pos| c.is_element_zero(pos)) { match cov.find_uncovered_cell_column_row_order(|pos| c.is_element_zero(pos)) {
None => {
return Step::Step6;
}
Some(pos) => { Some(pos) => {
marks.prime(pos); marks.prime(pos);
match marks.find_first_star_in_row(pos.row) { match marks.find_first_star_in_row(pos.row) {
@ -154,14 +148,22 @@ where
} }
None => { None => {
// in Python: self.Z0_r, self.Z0_c // in Python: self.Z0_r, self.Z0_c
return Step::Step5(pos); return Step4::ContinueWithStep5 { z0_pos: pos };
} }
} }
} }
None => {
return Step4::ContinueWithStep6;
}
} }
} }
} }
#[derive(Debug, Eq, PartialEq)]
enum Step5 {
ContinueWithStep3,
}
/// Construct a series of alternating primed and starred zeros as /// Construct a series of alternating primed and starred zeros as
/// follows. Let Z0 represent the uncovered primed zero found in Step 4. /// follows. Let Z0 represent the uncovered primed zero found in Step 4.
/// Let Z1 denote the starred zero in the column of Z0 (if any). /// Let Z1 denote the starred zero in the column of Z0 (if any).
@ -175,7 +177,7 @@ fn step5(
cov: &mut Coverage, cov: &mut Coverage,
z0_pos: Position, z0_pos: Position,
path: &mut Vec<Position>, path: &mut Vec<Position>,
) -> Step { ) -> Result<Step5, Error> {
let n = cov.n(); let n = cov.n();
assert!(marks.n() == n); assert!(marks.n() == n);
@ -198,7 +200,7 @@ fn step5(
prev_col = column; prev_col = column;
} else { } else {
// XXX: Can this really happen? // XXX: Can this really happen?
return Step::Failure(Error::NoPrimeInRow); return Err(Error::NoPrimeInRow);
} }
} }
None => { None => {
@ -214,14 +216,19 @@ fn step5(
cov.clear(); cov.clear();
marks.clear_primes(); marks.clear_primes();
return Step::Step3; Ok(Step5::ContinueWithStep3)
}
#[derive(Debug, Eq, PartialEq)]
enum Step6 {
ContinueWithStep4,
} }
/// Add the value found in Step 4 to every element of each covered /// Add the value found in Step 4 to every element of each covered
/// row, and subtract it from every element of each uncovered column. /// row, and subtract it from every element of each uncovered column.
/// Return to Step 4 without altering any stars, primes, or covered /// Return to Step 4 without altering any stars, primes, or covered
/// lines. /// lines.
fn step6<W>(c: &mut W, cov: &Coverage) -> Step fn step6<W>(c: &mut W, cov: &Coverage) -> Result<Step6, Error>
where where
W: Weights, W: Weights,
{ {
@ -252,9 +259,9 @@ where
} }
} }
Step::Step4(None) Ok(Step6::ContinueWithStep4)
} else { } else {
Step::Failure(Error::MatrixNotSolvable) Err(Error::MatrixNotSolvable)
} }
} }
@ -272,46 +279,36 @@ where
let mut coverage = Coverage::new(n); let mut coverage = Coverage::new(n);
let mut path = Vec::with_capacity(n); let mut path = Vec::with_capacity(n);
let mut step = Step::Step1; step1(weights);
loop { step2(weights, &mut marks, &mut coverage);
match step { 'step3: loop {
Step::Step1 => step = step1(weights), match step3(weights, &marks, &mut coverage) {
Step::Step2 => { Step3::ContinueWithStep4 { .. } => 'step4: loop {
step = step2(weights, &mut marks, &mut coverage); match step4(weights, &mut marks, &mut coverage) {
} Step4::ContinueWithStep5 { z0_pos } => {
Step::Step3 => { match step5(&mut marks, &mut coverage, z0_pos, &mut path)? {
step = step3(weights, &marks, &mut coverage); Step5::ContinueWithStep3 => {
} continue 'step3;
Step::Step4(_) => { }
step = step4(weights, &mut marks, &mut coverage); }
} }
Step::Step5(z0_pos) => { Step4::ContinueWithStep6 => match step6(weights, &coverage)? {
step = step5(&mut marks, &mut coverage, z0_pos, &mut path); Step6::ContinueWithStep4 => {
} continue 'step4;
Step::Step6 => { }
step = step6(weights, &coverage); },
} }
Step::Failure(err) => { },
return Err(err); Step3::Done => {
} break 'step3;
Step::Done => {
break;
} }
} }
} }
// now look for the starred elements // now look for the starred elements
let mut matching = Vec::with_capacity(n); let mut matching = Vec::with_capacity(n);
for row in 0..n { marks.each_star(|pos| matching.push(pos));
for column in 0..n {
let pos = Position { row, column };
if marks.is_star(pos) {
matching.push(pos);
}
}
}
assert!(matching.len() == n); assert!(matching.len() == n);
return Ok(matching); return Ok(matching);
} }
@ -327,8 +324,7 @@ fn test_step1() {
let mut weights: WeightMatrix<i32> = WeightMatrix::from_row_vec(N, c); let mut weights: WeightMatrix<i32> = WeightMatrix::from_row_vec(N, c);
let next_step = step1(&mut weights); step1(&mut weights);
assert_eq!(Step::Step2, next_step);
let exp = &[0, 150, 100, 50, 250, 0, 0, 200, 50]; let exp = &[0, 150, 100, 50, 250, 0, 0, 200, 50];
@ -344,8 +340,7 @@ fn test_step2() {
let mut marks = MarkMatrix::new(N); let mut marks = MarkMatrix::new(N);
let mut coverage = Coverage::new(N); let mut coverage = Coverage::new(N);
let next_step = step2(&weights, &mut marks, &mut coverage); step2(&weights, &mut marks, &mut coverage);
assert_eq!(Step::Step3, next_step);
assert_eq!(true, marks.is_star(pos(0, 0))); assert_eq!(true, marks.is_star(pos(0, 0)));
assert_eq!(false, marks.is_star(pos(0, 1))); assert_eq!(false, marks.is_star(pos(0, 1)));
@ -381,7 +376,7 @@ fn test_step3() {
marks.star(pos(1, 2)); marks.star(pos(1, 2));
let next_step = step3(&weights, &marks, &mut coverage); let next_step = step3(&weights, &marks, &mut coverage);
assert_eq!(Step::Step4(Some(2)), next_step); assert_eq!(Step3::ContinueWithStep4 { star_count: 2 }, next_step);
assert_eq!(true, coverage.is_column_covered(0)); assert_eq!(true, coverage.is_column_covered(0));
assert_eq!(false, coverage.is_column_covered(1)); assert_eq!(false, coverage.is_column_covered(1));
@ -408,7 +403,7 @@ fn test_step4_case1() {
let next_step = step4(&weights, &mut marks, &mut coverage); let next_step = step4(&weights, &mut marks, &mut coverage);
assert_eq!(Step::Step6, next_step); assert_eq!(Step4::ContinueWithStep6, next_step);
// coverage did not change. // coverage did not change.
assert_eq!(true, coverage.is_column_covered(0)); assert_eq!(true, coverage.is_column_covered(0));
@ -446,7 +441,7 @@ fn test_step6() {
let next_step = step6(&mut weights, &coverage); let next_step = step6(&mut weights, &coverage);
assert_eq!(Step::Step4(None), next_step); assert_eq!(Ok(Step6::ContinueWithStep4), next_step);
let exp = &[0, 0, 100, 50, 100, 0, 0, 50, 50]; let exp = &[0, 0, 100, 50, 100, 0, 0, 50, 50];
@ -469,7 +464,7 @@ fn test_step4_case2() {
let next_step = step4(&weights, &mut marks, &mut coverage); let next_step = step4(&weights, &mut marks, &mut coverage);
assert_eq!(Step::Step5(pos(2, 0)), next_step); assert_eq!(Step4::ContinueWithStep5 { z0_pos: pos(2, 0) }, next_step);
// coverage DID CHANGE! // coverage DID CHANGE!
assert_eq!(false, coverage.is_column_covered(0)); assert_eq!(false, coverage.is_column_covered(0));
@ -507,7 +502,7 @@ fn test_step5() {
let mut path = Vec::new(); let mut path = Vec::new();
let next_step = step5(&mut marks, &mut coverage, pos(2, 0), &mut path); let next_step = step5(&mut marks, &mut coverage, pos(2, 0), &mut path);
assert_eq!(Step::Step3, next_step); assert_eq!(Ok(Step5::ContinueWithStep3), next_step);
// coverage DID CHANGE! // coverage DID CHANGE!
assert_eq!(false, coverage.is_column_covered(0)); assert_eq!(false, coverage.is_column_covered(0));
@ -560,16 +555,14 @@ fn test_solve_equal_rows_stepwise() {
// step 1 // step 1
let next_step = step1(&mut weights); step1(&mut weights);
assert_eq!(Step::Step2, next_step);
assert_eq!(&[0, 0, 0, 0], weights.as_slice()); assert_eq!(&[0, 0, 0, 0], weights.as_slice());
// step 2 // step 2
let mut marks = MarkMatrix::new(N); let mut marks = MarkMatrix::new(N);
let mut coverage = Coverage::new(N); let mut coverage = Coverage::new(N);
let next_step = step2(&weights, &mut marks, &mut coverage); step2(&weights, &mut marks, &mut coverage);
assert_eq!(Step::Step3, next_step);
assert!(coverage.all_uncovered()); assert!(coverage.all_uncovered());
assert!(marks.is_star(pos(0, 0))); assert!(marks.is_star(pos(0, 0)));
@ -579,7 +572,7 @@ fn test_solve_equal_rows_stepwise() {
// step 3 // step 3
let next_step = step3(&weights, &mut marks, &mut coverage); let next_step = step3(&weights, &mut marks, &mut coverage);
assert_eq!(Step::Done, next_step); assert_eq!(Step3::Done, next_step);
} }
#[cfg(test)] #[cfg(test)]