summaryrefslogtreecommitdiffstats
path: root/third_party/rust/smawk/src/monge.rs
blob: dbc80e15170d05c76d0d6b5c4bada5133c2e4639 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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));
    }
}