diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /third_party/rust/smawk/src/recursive.rs | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/smawk/src/recursive.rs')
-rw-r--r-- | third_party/rust/smawk/src/recursive.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/third_party/rust/smawk/src/recursive.rs b/third_party/rust/smawk/src/recursive.rs new file mode 100644 index 0000000000..9df8b9c824 --- /dev/null +++ b/third_party/rust/smawk/src/recursive.rs @@ -0,0 +1,191 @@ +//! Recursive algorithm for finding column minima. +//! +//! The functions here are mostly meant to be used for testing +//! correctness of the SMAWK implementation. +//! +//! **Note: this module is only available if you enable the `ndarray` +//! Cargo feature.** + +use ndarray::{s, Array2, ArrayView2, Axis}; + +/// Compute row minima in O(*m* + *n* log *m*) time. +/// +/// This function computes row minima in a totally monotone matrix +/// using a recursive algorithm. +/// +/// Running time on an *m* ✕ *n* matrix: O(*m* + *n* log *m*). +/// +/// # Examples +/// +/// ``` +/// let matrix = ndarray::arr2(&[[4, 2, 4, 3], +/// [5, 3, 5, 3], +/// [5, 3, 3, 1]]); +/// assert_eq!(smawk::recursive::row_minima(&matrix), +/// vec![1, 1, 3]); +/// ``` +/// +/// # Panics +/// +/// It is an error to call this on a matrix with zero columns. +pub fn row_minima<T: Ord>(matrix: &Array2<T>) -> Vec<usize> { + let mut minima = vec![0; matrix.nrows()]; + recursive_inner(matrix.view(), &|| Direction::Row, 0, &mut minima); + minima +} + +/// Compute column minima in O(*n* + *m* log *n*) time. +/// +/// This function computes column minima in a totally monotone matrix +/// using a recursive algorithm. +/// +/// Running time on an *m* ✕ *n* matrix: O(*n* + *m* log *n*). +/// +/// # Examples +/// +/// ``` +/// let matrix = ndarray::arr2(&[[4, 2, 4, 3], +/// [5, 3, 5, 3], +/// [5, 3, 3, 1]]); +/// assert_eq!(smawk::recursive::column_minima(&matrix), +/// vec![0, 0, 2, 2]); +/// ``` +/// +/// # Panics +/// +/// It is an error to call this on a matrix with zero rows. +pub fn column_minima<T: Ord>(matrix: &Array2<T>) -> Vec<usize> { + let mut minima = vec![0; matrix.ncols()]; + recursive_inner(matrix.view(), &|| Direction::Column, 0, &mut minima); + minima +} + +/// The type of minima (row or column) we compute. +enum Direction { + Row, + Column, +} + +/// Compute the minima along the given direction (`Direction::Row` for +/// row minima and `Direction::Column` for column minima). +/// +/// The direction is given as a generic function argument to allow +/// monomorphization to kick in. The function calls will be inlined +/// and optimized away and the result is that the compiler generates +/// differnet code for finding row and column minima. +fn recursive_inner<T: Ord, F: Fn() -> Direction>( + matrix: ArrayView2<'_, T>, + dir: &F, + offset: usize, + minima: &mut [usize], +) { + if matrix.is_empty() { + return; + } + + let axis = match dir() { + Direction::Row => Axis(0), + Direction::Column => Axis(1), + }; + let mid = matrix.len_of(axis) / 2; + let min_idx = crate::brute_force::lane_minimum(matrix.index_axis(axis, mid)); + minima[mid] = offset + min_idx; + + if mid == 0 { + return; // Matrix has a single row or column, so we're done. + } + + let top_left = match dir() { + Direction::Row => matrix.slice(s![..mid, ..(min_idx + 1)]), + Direction::Column => matrix.slice(s![..(min_idx + 1), ..mid]), + }; + let bot_right = match dir() { + Direction::Row => matrix.slice(s![(mid + 1).., min_idx..]), + Direction::Column => matrix.slice(s![min_idx.., (mid + 1)..]), + }; + recursive_inner(top_left, dir, offset, &mut minima[..mid]); + recursive_inner(bot_right, dir, offset + min_idx, &mut minima[mid + 1..]); +} + +#[cfg(test)] +mod tests { + use super::*; + use ndarray::arr2; + + #[test] + fn recursive_1x1() { + let matrix = arr2(&[[2]]); + let minima = vec![0]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_2x1() { + let matrix = arr2(&[ + [3], // + [2], + ]); + let minima = vec![0, 0]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_1x2() { + let matrix = arr2(&[[2, 1]]); + let minima = vec![1]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_2x2() { + let matrix = arr2(&[ + [3, 2], // + [2, 1], + ]); + let minima = vec![1, 1]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_3x3() { + let matrix = arr2(&[ + [3, 4, 4], // + [3, 4, 4], + [2, 3, 3], + ]); + let minima = vec![0, 0, 0]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_4x4() { + let matrix = arr2(&[ + [4, 5, 5, 5], // + [2, 3, 3, 3], + [2, 3, 3, 3], + [2, 2, 2, 2], + ]); + let minima = vec![0, 0, 0, 0]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } + + #[test] + fn recursive_5x5() { + let matrix = arr2(&[ + [3, 2, 4, 5, 6], + [2, 1, 3, 3, 4], + [2, 1, 3, 3, 4], + [3, 2, 4, 3, 4], + [4, 3, 2, 1, 1], + ]); + let minima = vec![1, 1, 1, 1, 3]; + assert_eq!(row_minima(&matrix), minima); + assert_eq!(column_minima(&matrix.reversed_axes()), minima); + } +} |