use crate::types::Position;
type Dimensions = (usize, usize, usize);
#[derive(Debug, Clone, PartialEq)]
pub enum PatternElement {
Wildcard, Skip, Exclude, Exact(i32), }
impl PatternElement {
pub fn from_value(value: &str) -> Self {
match value {
"*" => PatternElement::Wildcard,
"?" => PatternElement::Skip,
"!" => PatternElement::Exclude,
_ => {
if let Ok(num) = value.parse::<i32>() {
PatternElement::Exact(num)
} else {
PatternElement::Wildcard }
}
}
}
pub fn from_int(value: i32) -> Self {
if value == -1 {
PatternElement::Wildcard
} else if value == -2 {
PatternElement::Skip
} else if value == -3 {
PatternElement::Exclude
} else {
PatternElement::Exact(value)
}
}
}
pub type Pattern3D = (PatternElement, PatternElement, PatternElement);
pub fn match_pattern_element(element: &PatternElement, coordinate: i32, src_coord: i32) -> bool {
match element {
PatternElement::Wildcard => true,
PatternElement::Skip => coordinate == src_coord,
PatternElement::Exclude => coordinate != src_coord,
PatternElement::Exact(val) => coordinate == *val,
}
}
pub fn find_destination_coordinates(
dst_dimensions: Dimensions,
src_coordinate: Position,
_src_pattern: &Pattern3D,
dst_pattern: &Pattern3D,
) -> Vec<Position> {
let mut results = Vec::new();
let (dst_width, dst_height, dst_depth) = dst_dimensions;
let (src_x, src_y, src_z) = src_coordinate;
let x_range: Vec<u32> = match &dst_pattern.0 {
PatternElement::Wildcard => (0..dst_width as u32).collect(),
PatternElement::Skip => {
if (src_x as usize) < dst_width {
vec![src_x]
} else {
vec![]
}
}
PatternElement::Exclude => (0..dst_width as u32).filter(|&x| x != src_x).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < dst_width {
vec![*val as u32]
} else {
vec![]
}
}
};
let y_range: Vec<u32> = match &dst_pattern.1 {
PatternElement::Wildcard => (0..dst_height as u32).collect(),
PatternElement::Skip => {
if (src_y as usize) < dst_height {
vec![src_y]
} else {
vec![]
}
}
PatternElement::Exclude => (0..dst_height as u32).filter(|&y| y != src_y).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < dst_height {
vec![*val as u32]
} else {
vec![]
}
}
};
let z_range: Vec<u32> = match &dst_pattern.2 {
PatternElement::Wildcard => (0..dst_depth as u32).collect(),
PatternElement::Skip => {
if (src_z as usize) < dst_depth {
vec![src_z]
} else {
vec![]
}
}
PatternElement::Exclude => (0..dst_depth as u32).filter(|&z| z != src_z).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < dst_depth {
vec![*val as u32]
} else {
vec![]
}
}
};
for x in &x_range {
for y in &y_range {
for z in &z_range {
results.push((*x, *y, *z));
}
}
}
results
}
pub fn find_source_coordinates(
src_pattern: &Pattern3D,
src_dimensions: Dimensions,
) -> Vec<Position> {
let mut results = Vec::new();
let (src_width, src_height, src_depth) = src_dimensions;
let x_range: Vec<u32> = match &src_pattern.0 {
PatternElement::Wildcard => (0..src_width as u32).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < src_width {
vec![*val as u32]
} else {
vec![]
}
}
_ => (0..src_width as u32).collect(), };
let y_range: Vec<u32> = match &src_pattern.1 {
PatternElement::Wildcard => (0..src_height as u32).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < src_height {
vec![*val as u32]
} else {
vec![]
}
}
_ => (0..src_height as u32).collect(),
};
let z_range: Vec<u32> = match &src_pattern.2 {
PatternElement::Wildcard => (0..src_depth as u32).collect(),
PatternElement::Exact(val) => {
if *val >= 0 && (*val as usize) < src_depth {
vec![*val as u32]
} else {
vec![]
}
}
_ => (0..src_depth as u32).collect(),
};
for x in &x_range {
for y in &y_range {
for z in &z_range {
results.push((*x, *y, *z));
}
}
}
results
}
pub fn match_patterns_batch(
src_coordinate: Position,
patterns: &[(Pattern3D, Pattern3D)], _src_dimensions: Dimensions,
dst_dimensions: Dimensions,
) -> Vec<Position> {
let mut all_results = Vec::new();
for (src_pattern, dst_pattern) in patterns {
let (src_x, src_y, src_z) = src_coordinate;
let x_match = match &src_pattern.0 {
PatternElement::Wildcard => true,
PatternElement::Exact(val) => src_x == (*val as u32),
_ => true, };
let y_match = match &src_pattern.1 {
PatternElement::Wildcard => true,
PatternElement::Exact(val) => src_y == (*val as u32),
_ => true,
};
let z_match = match &src_pattern.2 {
PatternElement::Wildcard => true,
PatternElement::Exact(val) => src_z == (*val as u32),
_ => true,
};
if x_match && y_match && z_match {
let mut results = find_destination_coordinates(
dst_dimensions,
src_coordinate,
src_pattern,
dst_pattern,
);
all_results.append(&mut results);
}
}
all_results.sort_unstable();
all_results.dedup();
all_results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wildcard_pattern() {
let src_pattern = (
PatternElement::Wildcard,
PatternElement::Wildcard,
PatternElement::Exact(0),
);
let dst_pattern = (
PatternElement::Skip,
PatternElement::Skip,
PatternElement::Exact(1),
);
let results =
find_destination_coordinates((10, 10, 10), (5, 5, 0), &src_pattern, &dst_pattern);
assert_eq!(results.len(), 1);
assert_eq!(results[0], (5, 5, 1)); }
#[test]
fn test_exact_pattern() {
let src_pattern = (
PatternElement::Exact(0),
PatternElement::Exact(0),
PatternElement::Exact(0),
);
let dst_pattern = (
PatternElement::Exact(1),
PatternElement::Exact(2),
PatternElement::Exact(3),
);
let results =
find_destination_coordinates((10, 10, 10), (0, 0, 0), &src_pattern, &dst_pattern);
assert_eq!(results.len(), 1);
assert_eq!(results[0], (1, 2, 3));
}
#[test]
fn test_exclude_pattern() {
let src_pattern = (
PatternElement::Wildcard,
PatternElement::Wildcard,
PatternElement::Wildcard,
);
let dst_pattern = (
PatternElement::Exclude,
PatternElement::Exact(0),
PatternElement::Exact(0),
);
let results =
find_destination_coordinates((3, 1, 1), (1, 0, 0), &src_pattern, &dst_pattern);
assert_eq!(results.len(), 2);
assert!(results.contains(&(0, 0, 0)));
assert!(results.contains(&(2, 0, 0)));
}
}