use crate::Matrix;
use std::num::Wrapping;
use std::ops::Add;
pub fn is_monge<T: Ord + Copy, M: Matrix<T>>(matrix: &M) -> bool
where
Wrapping<T>: Add<Output = Wrapping<T>>,
{
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,
(Err(a), Err(b)) => a <= b,
(Ok(_), Err(_)) => true,
(Err(_), Ok(_)) => false,
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_monge_handles_overflow() {
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));
}
}