fn compare_and_replace_tag_opt<const N: usize, Cmp>(
tags: &mut [Option<([usize; N], usize)>],
idx: &mut usize,
tag: ([usize; N], usize),
cmp: Cmp,
) where
Cmp: Fn(&([usize; N], usize), &([usize; N], usize)) -> bool,
{
match &mut tags[*idx] {
Some(existing_tag) => {
if cmp(&tag, existing_tag) {
*existing_tag = tag;
}
}
None => {
tags[*idx] = Some(tag);
}
}
*idx += 1;
}
pub fn get_dop_tags<const N: usize>(tags: &[([usize; N], usize)]) -> Vec<([usize; N], usize)> {
let mut dop_tags: Vec<Option<([usize; N], usize)>> = vec![None; 2 * (N + 1) * (N + 1)];
for &tag in tags {
let mut idx = 0;
for i in 0..N {
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| a.0[i] < b.0[i]);
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| a.0[i] > b.0[i]);
}
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| a.1 < b.1);
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| a.1 > b.1);
for i in 0..N {
for j in (i + 1)..N {
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + a.0[j] < b.0[i] + b.0[j]
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + a.0[j] > b.0[i] + b.0[j]
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + b.0[j] < b.0[i] + a.0[j]
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + b.0[j] > b.0[i] + a.0[j]
});
}
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + a.1 < b.0[i] + b.1
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + a.1 > b.0[i] + b.1
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + b.1 < b.0[i] + a.1
});
compare_and_replace_tag_opt(&mut dop_tags, &mut idx, tag, |a, b| {
a.0[i] + b.1 > b.0[i] + a.1
});
}
}
dop_tags
.into_iter()
.flatten()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect()
}
fn compare_and_replace_coord_opt<const N: usize, Cmp>(
coords: &mut [Option<[usize; N]>],
idx: &mut usize,
coord: [usize; N],
cmp: Cmp,
) where
Cmp: Fn(&[usize; N], &[usize; N]) -> bool,
{
match &mut coords[*idx] {
Some(existing_coord) => {
if cmp(&coord, existing_coord) {
*existing_coord = coord;
}
}
None => {
coords[*idx] = Some(coord);
}
}
*idx += 1;
}
pub fn get_dop_coords<const N: usize>(coords: &[[usize; N]]) -> Vec<[usize; N]> {
let mut dop_coords: Vec<Option<[usize; N]>> = vec![None; 2 * N * N];
for &coord in coords {
let mut idx = 0;
for i in 0..N {
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| a[i] < b[i]);
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| a[i] > b[i]);
}
for i in 0..N {
for j in (i + 1)..N {
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| {
a[i] + a[j] < b[i] + b[j]
});
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| {
a[i] + a[j] > b[i] + b[j]
});
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| {
a[i] + b[j] < b[i] + a[j]
});
compare_and_replace_coord_opt(&mut dop_coords, &mut idx, coord, |a, b| {
a[i] + b[j] > b[i] + a[j]
});
}
}
}
dop_coords
.into_iter()
.flatten()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect()
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
fn set_of<T: std::hash::Hash + Eq>(v: Vec<T>) -> HashSet<T> {
v.into_iter().collect()
}
#[test]
fn dop_coords_empty() {
let coords: Vec<[usize; 2]> = vec![];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected: HashSet<[usize; 2]> = HashSet::new();
assert_eq!(result_set, expected);
}
#[test]
fn dop_coords_single_point() {
let coords = vec![[3, 7]];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected = set_of(vec![[3, 7]]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_coords_two_points_axis_extrema() {
let coords = vec![[0, 10], [10, 0]];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected = set_of(vec![[0, 10], [10, 0]]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_coords_square_2d() {
let coords = vec![[0, 0], [0, 10], [10, 0], [10, 10]];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected = set_of(vec![[0, 0], [0, 10], [10, 0], [10, 10]]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_coords_interior_removed() {
let coords = vec![[0, 0], [0, 10], [10, 0], [10, 10], [5, 5]];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected = set_of(vec![[0, 0], [0, 10], [10, 0], [10, 10]]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_coords_duplicates() {
let coords = vec![[1, 2], [1, 2], [1, 2]];
let result = get_dop_coords::<2>(&coords);
let result_set = set_of(result);
let expected = set_of(vec![[1, 2]]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_tags_empty() {
let tags: Vec<([usize; 2], usize)> = vec![];
let result = get_dop_tags::<2>(&tags);
let result_set = set_of(result);
let expected: HashSet<([usize; 2], usize)> = HashSet::new();
assert_eq!(result_set, expected);
}
#[test]
fn dop_tags_single_point() {
let tags = vec![([3, 7], 5)];
let result = get_dop_tags::<2>(&tags);
let result_set = set_of(result);
let expected = set_of(vec![([3, 7], 5)]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_tags_extrema() {
let tags = vec![([0, 0], 0), ([10, 0], 0), ([0, 10], 0), ([10, 10], 0)];
let result = get_dop_tags::<2>(&tags);
let result_set = set_of(result);
let expected = set_of(vec![([0, 0], 0), ([10, 0], 0), ([0, 10], 0), ([10, 10], 0)]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_tags_interior_removed() {
let tags = vec![
([0, 0], 0),
([10, 0], 0),
([0, 10], 0),
([10, 10], 0),
([5, 5], 0), ];
let result = get_dop_tags::<2>(&tags);
let result_set = set_of(result);
let expected = set_of(vec![([0, 0], 0), ([10, 0], 0), ([0, 10], 0), ([10, 10], 0)]);
assert_eq!(result_set, expected);
}
#[test]
fn dop_tags_spawn_dimension_extrema() {
let tags = vec![([5, 5], 0), ([5, 5], 10), ([5, 5], 20)];
let result = get_dop_tags::<2>(&tags);
let result_set = set_of(result);
let expected = set_of(vec![([5, 5], 0), ([5, 5], 20)]);
assert_eq!(result_set, expected);
}
}