diff options
Diffstat (limited to 'vendor/smawk/src/monge.rs')
-rw-r--r-- | vendor/smawk/src/monge.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/vendor/smawk/src/monge.rs b/vendor/smawk/src/monge.rs new file mode 100644 index 0000000..dbc80e1 --- /dev/null +++ b/vendor/smawk/src/monge.rs @@ -0,0 +1,121 @@ +//! Functions for generating and checking Monge arrays. +//! +//! The functions here are mostly meant to be used for testing +//! correctness of the SMAWK implementation. + +use crate::Matrix; +use std::num::Wrapping; +use std::ops::Add; + +/// Verify that a matrix is a Monge matrix. +/// +/// A [Monge matrix] \(or array) is a matrix where the following +/// inequality holds: +/// +/// ```text +/// M[i, j] + M[i', j'] <= M[i, j'] + M[i', j] for all i < i', j < j' +/// ``` +/// +/// The inequality says that the sum of the main diagonal is less than +/// the sum of the antidiagonal. Checking this condition is done by +/// checking *n* ✕ *m* submatrices, so the running time is O(*mn*). +/// +/// [Monge matrix]: https://en.wikipedia.org/wiki/Monge_array +pub fn is_monge<T: Ord + Copy, M: Matrix<T>>(matrix: &M) -> bool +where + Wrapping<T>: Add<Output = Wrapping<T>>, +{ + /// Returns `Ok(a + b)` if the computation can be done without + /// overflow, otherwise `Err(a + b - T::MAX - 1)` is returned. + fn checked_add<T: Ord + Copy>(a: Wrapping<T>, b: Wrapping<T>) -> Result<T, T> + where + Wrapping<T>: Add<Output = Wrapping<T>>, + { + let sum = a + b; + if sum < a { + Err(sum.0) + } else { + Ok(sum.0) + } + } + + (0..matrix.nrows() - 1) + .flat_map(|row| (0..matrix.ncols() - 1).map(move |col| (row, col))) + .all(|(row, col)| { + let top_left = Wrapping(matrix.index(row, col)); + let top_right = Wrapping(matrix.index(row, col + 1)); + let bot_left = Wrapping(matrix.index(row + 1, col)); + let bot_right = Wrapping(matrix.index(row + 1, col + 1)); + + match ( + checked_add(top_left, bot_right), + checked_add(bot_left, top_right), + ) { + (Ok(a), Ok(b)) => a <= b, // No overflow. + (Err(a), Err(b)) => a <= b, // Double overflow. + (Ok(_), Err(_)) => true, // Anti-diagonal overflow. + (Err(_), Ok(_)) => false, // Main diagonal overflow. + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_monge_handles_overflow() { + // The x + y <= z + w computations will overflow for an u8 + // matrix unless is_monge is careful. + let matrix: Vec<Vec<u8>> = vec![ + vec![200, 200, 200, 200], + vec![200, 200, 200, 200], + vec![200, 200, 200, 200], + ]; + assert!(is_monge(&matrix)); + } + + #[test] + fn monge_constant_rows() { + let matrix = vec![ + vec![42, 42, 42, 42], + vec![0, 0, 0, 0], + vec![100, 100, 100, 100], + vec![1000, 1000, 1000, 1000], + ]; + assert!(is_monge(&matrix)); + } + + #[test] + fn monge_constant_cols() { + let matrix = vec![ + vec![42, 0, 100, 1000], + vec![42, 0, 100, 1000], + vec![42, 0, 100, 1000], + vec![42, 0, 100, 1000], + ]; + assert!(is_monge(&matrix)); + } + + #[test] + fn monge_upper_right() { + let matrix = vec![ + vec![10, 10, 42, 42, 42], + vec![10, 10, 42, 42, 42], + vec![10, 10, 10, 10, 10], + vec![10, 10, 10, 10, 10], + ]; + assert!(is_monge(&matrix)); + } + + #[test] + fn monge_lower_left() { + let matrix = vec![ + vec![10, 10, 10, 10, 10], + vec![10, 10, 10, 10, 10], + vec![42, 42, 42, 10, 10], + vec![42, 42, 42, 10, 10], + ]; + assert!(is_monge(&matrix)); + } +} |