#![allow(dead_code)]
use std::collections::{HashMap, HashSet};
use togo::prelude::*;
const EPS_CONNECT: f64 = 1e-7;
#[doc(hidden)]
pub fn offset_reconnect_arcs(arcs: &Arcline) -> Vec<Arcline> {
println!(
"DEBUG: offset_reconnect_arcs called with {} arcs",
arcs.len()
);
let mut result = Vec::new();
let len = arcs.len();
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new(); let mut merge: Vec<(usize, usize)> = Vec::new(); let mut k = 1000; for i in 0..len {
arc_map.insert(i, (k, k + 1));
k += 2;
}
for i in 0..arcs.len() {
for j in 0..arcs.len() {
if i == j {
continue; }
if arcs[i].a.close_enough(arcs[j].a, EPS_CONNECT) {
let x = arc_map.get(&i).unwrap().0;
let y = arc_map.get(&j).unwrap().0;
merge.push((x, y));
}
if arcs[i].a.close_enough(arcs[j].b, EPS_CONNECT) {
let x = arc_map.get(&i).unwrap().0;
let y = arc_map.get(&j).unwrap().1;
merge.push((x, y));
}
if arcs[i].b.close_enough(arcs[j].a, EPS_CONNECT) {
let x = arc_map.get(&i).unwrap().1;
let y = arc_map.get(&j).unwrap().0;
merge.push((x, y));
}
if arcs[i].b.close_enough(arcs[j].b, EPS_CONNECT) {
let x = arc_map.get(&i).unwrap().1;
let y = arc_map.get(&j).unwrap().1;
merge.push((x, y));
}
}
}
merge_points(&mut arc_map, &merge);
println!("DEBUG: Arc map after merge: {:?}", arc_map);
let graph: Vec<(usize, usize)> = arc_map.values().cloned().collect();
println!("DEBUG: Graph edges after merge: {:?}", graph);
println!("DEBUG: Merge operations count: {}", merge.len());
let components = find_connected_components(&graph);
println!("DEBUG: Found {} components", components.len());
for (i, component) in components.iter().enumerate() {
println!("DEBUG: Component {}: {:?}", i, component);
}
for component in components.iter() {
println!(
"DEBUG: Processing component with {} vertices: {:?}",
component.len(),
component
);
if component.len() >= 2 {
println!("DEBUG: Converting component {:?} to arcs", component);
let arc_sequence = vertex_path_to_arcs(&component, &arcs, &arc_map);
println!("DEBUG: Arc sequence length: {}", arc_sequence.len());
if !arc_sequence.is_empty() {
result.push(arc_sequence);
} else {
println!(
"DEBUG: Arc sequence was empty for component {:?}",
component
);
}
} else {
println!(
"DEBUG: Skipping component {:?} - too short (len={})",
component,
component.len()
);
}
}
println!(
"DEBUG: offset_reconnect_arcs returning {} components",
result.len()
);
result
}
#[doc(hidden)]
pub fn find_middle_points(arcs: &mut Arcline) {
for i in 0..arcs.len() {
for j in 0..arcs.len() {
if i == j {
continue; }
if arcs[i].a.close_enough(arcs[j].a, EPS_CONNECT) {
let mid = middle_point(&arcs[i].a, &arcs[j].a);
arcs[i].a = mid;
arcs[j].a = mid;
}
if arcs[i].a.close_enough(arcs[j].b, EPS_CONNECT) {
let mid = middle_point(&arcs[i].a, &arcs[j].b);
arcs[i].a = mid;
arcs[j].b = mid;
}
if arcs[i].b.close_enough(arcs[j].a, EPS_CONNECT) {
let mid = middle_point(&arcs[i].b, &arcs[j].a);
arcs[i].b = mid;
arcs[j].a = mid;
}
if arcs[i].b.close_enough(arcs[j].b, EPS_CONNECT) {
let mid = middle_point(&arcs[i].b, &arcs[j].b);
arcs[i].b = mid;
arcs[j].b = mid;
}
}
}
}
fn middle_point(a: &Point, b: &Point) -> Point {
Point {
x: (a.x + b.x) / 2.0,
y: (a.y + b.y) / 2.0,
}
}
fn merge_points(arc_map: &mut HashMap<usize, (usize, usize)>, merge: &Vec<(usize, usize)>) {
use std::collections::HashMap;
let mut parent: HashMap<usize, usize> = HashMap::new();
for (_, (start, end)) in arc_map.iter() {
parent.insert(*start, *start);
parent.insert(*end, *end);
}
fn find(parent: &mut HashMap<usize, usize>, x: usize) -> usize {
if parent[&x] != x {
let root = find(parent, parent[&x]);
parent.insert(x, root);
}
parent[&x]
}
fn union(parent: &mut HashMap<usize, usize>, x: usize, y: usize) {
let root_x = find(parent, x);
let root_y = find(parent, y);
if root_x != root_y {
if root_x < root_y {
parent.insert(root_y, root_x);
} else {
parent.insert(root_x, root_y);
}
}
}
for &(vertex1, vertex2) in merge {
union(&mut parent, vertex1, vertex2);
}
for (_arc_id, (start, end)) in arc_map.iter_mut() {
*start = find(&mut parent, *start);
*end = find(&mut parent, *end);
}
}
fn vertex_path_to_arcs(
vertex_path: &[usize],
arcs: &[Arc],
arc_map: &HashMap<usize, (usize, usize)>,
) -> Vec<Arc> {
let mut result = Vec::new();
let mut used_arcs = HashSet::new();
for i in 0..vertex_path.len() {
let current_vertex = vertex_path[i];
let next_vertex = vertex_path[(i + 1) % vertex_path.len()];
let arc_idx = find_connecting_arc_by_vertices(current_vertex, next_vertex, arc_map);
if let Some(idx) = arc_idx {
if !used_arcs.contains(&idx) {
if idx < arcs.len() {
let arc = &arcs[idx];
result.push(arc.clone());
used_arcs.insert(idx);
}
}
} else {
}
}
result
}
fn find_connecting_arc_by_vertices(
vertex1: usize,
vertex2: usize,
arc_map: &HashMap<usize, (usize, usize)>,
) -> Option<usize> {
for (&arc_idx, &(start_vertex, end_vertex)) in arc_map {
if (start_vertex == vertex1 && end_vertex == vertex2)
|| (start_vertex == vertex2 && end_vertex == vertex1)
{
return Some(arc_idx);
}
}
None
}
fn find_connecting_arc(vertex1: usize, vertex2: usize, len: usize) -> Option<usize> {
let arc1_idx = if vertex1 < len {
vertex1
} else {
vertex1 - len
};
let arc2_idx = if vertex2 < len {
vertex2
} else {
vertex2 - len
};
if arc1_idx == arc2_idx {
Some(arc1_idx)
} else {
None
}
}
fn should_use_forward_direction(from_vertex: usize, to_vertex: usize, len: usize) -> bool {
let arc_idx = if from_vertex < len {
from_vertex
} else {
from_vertex - len
};
let from_is_start = from_vertex < len;
let to_is_start = to_vertex < len;
let to_arc_idx = if to_vertex < len {
to_vertex
} else {
to_vertex - len
};
if arc_idx == to_arc_idx {
from_is_start && !to_is_start
} else {
true
}
}
const EPS_BRIDGE: f64 = 1e-6;
#[doc(hidden)]
pub fn remove_bridge_arcs(arcs: &mut Arcline) {
let mut to_remove = Vec::new(); let mut to_add = Vec::new(); for i in 0..arcs.len() {
for j in (i + 1)..arcs.len() {
let arc0 = &arcs[i];
let arc1 = &arcs[j];
if arc0.a.close_enough(arc1.a, EPS_BRIDGE)
&& arc0.b.close_enough(arc1.b, EPS_BRIDGE)
&& (arc0.is_arc() && arc1.is_arc() || arc0.is_line() && arc1.is_line())
{
if arc0.is_arc() && arc1.is_arc() && !close_enough(arc0.r, arc1.r, EPS_BRIDGE) {
continue;
}
to_remove.push(i);
to_remove.push(j);
let new0 = arcseg(arc0.a, arc1.a);
let new1 = arcseg(arc0.b, arc1.b);
if new0.is_valid(1e-8) {
to_add.push(new0);
}
if new1.is_valid(1e-8) {
to_add.push(new1);
}
continue;
}
if arc0.a.close_enough(arc1.b, EPS_BRIDGE)
&& arc0.b.close_enough(arc1.a, EPS_BRIDGE)
&& (arc0.is_arc() && arc1.is_arc() || arc0.is_line() && arc1.is_line())
{
if arc0.is_arc() && arc1.is_arc() && !close_enough(arc0.r, arc1.r, EPS_BRIDGE) {
continue;
}
let new0 = arcseg(arc0.a, arc1.b);
let new1 = arcseg(arc0.b, arc1.a);
to_remove.push(i);
to_remove.push(j);
if new0.is_valid(1e-8) {
to_add.push(new0);
}
if new1.is_valid(1e-8) {
to_add.push(new1);
}
continue;
}
}
}
to_remove.sort_unstable();
to_remove.dedup();
for i in to_remove.iter().rev() {
arcs.remove(*i);
}
arcs.extend(to_add);
}
#[doc(hidden)]
pub fn find_connected_components(graph: &[(usize, usize)]) -> Vec<Vec<usize>> {
use std::collections::{HashMap, HashSet};
if graph.is_empty() {
return Vec::new();
}
let mut adj_list: HashMap<usize, Vec<usize>> = HashMap::new();
let mut all_vertices = HashSet::new();
for &(u, v) in graph {
if u != v {
adj_list.entry(u).or_insert_with(Vec::new).push(v);
adj_list.entry(v).or_insert_with(Vec::new).push(u);
}
all_vertices.insert(u);
all_vertices.insert(v);
}
for neighbors in adj_list.values_mut() {
neighbors.sort();
}
println!("DEBUG: Adjacency list: {:?}", adj_list);
println!("DEBUG: All vertices: {:?}", all_vertices);
let mut visited = HashSet::new();
let mut cycles = Vec::new();
for &start_vertex in &all_vertices {
if visited.contains(&start_vertex) {
continue;
}
println!(
"DEBUG: Finding component starting from vertex {}",
start_vertex
);
let component_vertices = find_component_vertices(start_vertex, &adj_list, &mut visited);
println!(
"DEBUG: Found component with {} vertices: {:?}",
component_vertices.len(),
component_vertices
);
if component_vertices.len() >= 3 {
println!(
"DEBUG: Looking for cycles in component with {} vertices",
component_vertices.len()
);
let component_cycles = find_cycles_iterative(&component_vertices, &adj_list);
println!(
"DEBUG: Found {} cycles in component",
component_cycles.len()
);
cycles.extend(component_cycles);
} else {
println!(
"DEBUG: Skipping component with {} vertices (need >= 3)",
component_vertices.len()
);
}
}
cycles
}
#[doc(hidden)]
fn find_cycles_iterative(
component: &[usize],
adj_list: &HashMap<usize, Vec<usize>>,
) -> Vec<Vec<usize>> {
use std::collections::HashSet;
if component.len() < 3 {
return Vec::new();
}
println!(
"DEBUG: find_cycles_iterative called with {} vertices",
component.len()
);
let mut cycles = Vec::new();
let component_set: HashSet<usize> = component.iter().cloned().collect();
for &start in component {
println!("DEBUG: Looking for cycles starting from vertex {}", start);
let found_cycles = find_cycles_from_vertex_iterative(start, adj_list, &component_set);
println!(
"DEBUG: Found {} cycles starting from vertex {}",
found_cycles.len(),
start
);
for cycle in found_cycles {
let mut normalized_cycle = cycle;
if let Some(min_pos) = normalized_cycle
.iter()
.position(|&x| x == *normalized_cycle.iter().min().unwrap())
{
normalized_cycle.rotate_left(min_pos);
}
if !is_duplicate_cycle(&normalized_cycle, &cycles) {
println!(
"DEBUG: Adding new cycle of length {}: {:?}",
normalized_cycle.len(),
normalized_cycle
);
cycles.push(normalized_cycle);
} else {
println!(
"DEBUG: Skipping duplicate cycle of length {}",
normalized_cycle.len()
);
}
}
} cycles.sort_by(|a, b| a.len().cmp(&b.len()));
cycles
}
#[doc(hidden)]
fn find_cycles_from_vertex_iterative(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component_set: &HashSet<usize>,
) -> Vec<Vec<usize>> {
let mut cycles = Vec::new();
#[derive(Clone)]
struct DfsState {
current: usize,
path: Vec<usize>,
visited: HashSet<usize>,
}
let mut stack = Vec::new();
stack.push(DfsState {
current: start,
path: Vec::new(),
visited: HashSet::new(),
});
while let Some(mut state) = stack.pop() {
if state.path.len() > 10 {
continue;
}
state.path.push(state.current);
state.visited.insert(state.current);
if let Some(neighbors) = adj_list.get(&state.current) {
for &neighbor in neighbors {
if !component_set.contains(&neighbor) {
continue;
}
if neighbor == start && state.path.len() >= 3 {
cycles.push(state.path.clone());
} else if !state.visited.contains(&neighbor) {
let new_state = DfsState {
current: neighbor,
path: state.path.clone(),
visited: state.visited.clone(),
};
stack.push(new_state);
}
}
}
}
cycles
}
#[doc(hidden)]
fn find_component_with_cycles(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
visited: &mut HashSet<usize>,
) -> Vec<usize> {
let mut component = Vec::new();
let mut stack = vec![start];
let mut local_visited = HashSet::new();
while let Some(vertex) = stack.pop() {
if local_visited.contains(&vertex) {
continue;
}
local_visited.insert(vertex);
visited.insert(vertex);
component.push(vertex);
if let Some(neighbors) = adj_list.get(&vertex) {
for &neighbor in neighbors {
if !local_visited.contains(&neighbor) {
stack.push(neighbor);
}
}
}
}
component
}
#[doc(hidden)]
fn extract_shortest_cycle(
component: &[usize],
adj_list: &HashMap<usize, Vec<usize>>,
) -> Option<Vec<usize>> {
if component.len() < 3 {
return None; }
let mut shortest_cycle: Option<Vec<usize>> = None;
for &start in component {
if let Some(mut cycle) = find_cycle_from_vertex(start, adj_list, component) {
if let Some(min_pos) = cycle
.iter()
.position(|&x| x == *cycle.iter().min().unwrap())
{
cycle.rotate_left(min_pos);
}
let should_replace = if let Some(ref current) = shortest_cycle {
cycle.len() < current.len() || (cycle.len() == current.len() && cycle < *current)
} else {
true
};
if should_replace {
shortest_cycle = Some(cycle);
}
}
}
shortest_cycle
}
#[doc(hidden)]
fn find_cycle_from_vertex(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component: &[usize],
) -> Option<Vec<usize>> {
let component_set: HashSet<usize> = component.iter().cloned().collect();
fn dfs_shortest_cycle(
current: usize,
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component_set: &HashSet<usize>,
path: &mut Vec<usize>,
visited: &mut HashSet<usize>,
min_cycle_len: &mut usize,
) -> Option<Vec<usize>> {
if path.len() >= *min_cycle_len {
return None; }
path.push(current);
visited.insert(current);
if let Some(neighbors) = adj_list.get(¤t) {
for &neighbor in neighbors {
if !component_set.contains(&neighbor) {
continue;
}
if neighbor == start && path.len() >= 3 {
if path.len() < *min_cycle_len {
*min_cycle_len = path.len();
let result = path.clone();
path.pop();
visited.remove(¤t);
return Some(result);
}
} else if !visited.contains(&neighbor) {
if let Some(cycle) = dfs_shortest_cycle(
neighbor,
start,
adj_list,
component_set,
path,
visited,
min_cycle_len,
) {
path.pop();
visited.remove(¤t);
return Some(cycle);
}
}
}
}
path.pop();
visited.remove(¤t);
None
}
let mut path = Vec::new();
let mut visited = HashSet::new();
let mut min_cycle_len = usize::MAX;
dfs_shortest_cycle(
start,
start,
adj_list,
&component_set,
&mut path,
&mut visited,
&mut min_cycle_len,
)
}
#[doc(hidden)]
fn reconstruct_cycle(u: usize, v: usize, parent: &HashMap<usize, Option<usize>>) -> Vec<usize> {
let mut cycle = Vec::new();
let mut path_u = Vec::new();
let mut current = u;
path_u.push(current);
while let Some(Some(p)) = parent.get(¤t) {
current = *p;
path_u.push(current);
}
let mut path_v = Vec::new();
current = v;
path_v.push(current);
while let Some(Some(p)) = parent.get(¤t) {
current = *p;
path_v.push(current);
}
let path_u_set: HashSet<usize> = path_u.iter().cloned().collect();
let mut lca = v;
for &vertex in &path_v {
if path_u_set.contains(&vertex) {
lca = vertex;
break;
}
}
for &vertex in path_u.iter().take_while(|&&x| x != lca) {
cycle.push(vertex);
}
cycle.push(lca);
for &vertex in path_v
.iter()
.take_while(|&&x| x != lca)
.collect::<Vec<_>>()
.iter()
.rev()
{
cycle.push(*vertex);
}
cycle
}
#[doc(hidden)]
fn find_component_vertices(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
visited: &mut HashSet<usize>,
) -> Vec<usize> {
let mut component = Vec::new();
let mut stack = vec![start];
let mut local_visited = HashSet::new();
while let Some(vertex) = stack.pop() {
if local_visited.contains(&vertex) {
continue;
}
local_visited.insert(vertex);
visited.insert(vertex);
component.push(vertex);
if let Some(neighbors) = adj_list.get(&vertex) {
for &neighbor in neighbors {
if !local_visited.contains(&neighbor) {
stack.push(neighbor);
}
}
}
}
component
}
#[doc(hidden)]
fn find_cycles_optimized(
component: &[usize],
adj_list: &HashMap<usize, Vec<usize>>,
vertex_degrees: &HashMap<usize, usize>,
) -> Vec<Vec<usize>> {
use std::collections::HashSet;
if component.len() < 3 {
return Vec::new(); }
let mut cycles = Vec::new();
let component_set: HashSet<usize> = component.iter().cloned().collect();
if component.len() <= 10 {
for &start in component {
let found_cycles =
find_cycles_from_vertex_optimized(start, adj_list, &component_set, vertex_degrees);
for cycle in found_cycles {
let mut normalized_cycle = cycle;
if let Some(min_pos) = normalized_cycle
.iter()
.position(|&x| x == *normalized_cycle.iter().min().unwrap())
{
normalized_cycle.rotate_left(min_pos);
}
if !is_duplicate_cycle(&normalized_cycle, &cycles) {
cycles.push(normalized_cycle);
}
}
}
} else {
cycles = find_cycles_by_degree_analysis(component, adj_list, vertex_degrees);
}
cycles.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)));
cycles
}
#[doc(hidden)]
fn find_cycles_from_vertex_optimized(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component_set: &HashSet<usize>,
_vertex_degrees: &HashMap<usize, usize>,
) -> Vec<Vec<usize>> {
let mut cycles = Vec::new();
#[derive(Clone)]
struct SearchState {
current: usize,
path: Vec<usize>,
visited: HashSet<usize>,
}
let mut stack = Vec::new();
stack.push(SearchState {
current: start,
path: Vec::new(),
visited: HashSet::new(),
});
while let Some(mut state) = stack.pop() {
state.path.push(state.current);
state.visited.insert(state.current);
if let Some(neighbors) = adj_list.get(&state.current) {
for &neighbor in neighbors {
if !component_set.contains(&neighbor) {
continue;
}
if neighbor == start && state.path.len() >= 3 {
cycles.push(state.path.clone());
} else if !state.visited.contains(&neighbor) {
let new_state = SearchState {
current: neighbor,
path: state.path.clone(),
visited: state.visited.clone(),
};
stack.push(new_state);
}
}
}
}
cycles
}
#[doc(hidden)]
fn find_cycles_by_degree_analysis(
component: &[usize],
adj_list: &HashMap<usize, Vec<usize>>,
vertex_degrees: &HashMap<usize, usize>,
) -> Vec<Vec<usize>> {
let mut cycles = Vec::new();
let component_set: HashSet<usize> = component.iter().cloned().collect();
if let Some(&start) = component
.iter()
.find(|&&v| vertex_degrees.get(&v).unwrap_or(&0) >= &2)
{
let found_cycles =
find_cycles_from_vertex_optimized(start, adj_list, &component_set, vertex_degrees);
for cycle in found_cycles {
let mut normalized_cycle = cycle;
if let Some(min_pos) = normalized_cycle
.iter()
.position(|&x| x == *normalized_cycle.iter().min().unwrap())
{
normalized_cycle.rotate_left(min_pos);
}
if !is_duplicate_cycle(&normalized_cycle, &cycles) {
cycles.push(normalized_cycle);
}
}
}
cycles
}
#[doc(hidden)]
fn find_all_cycles_in_component(
component: &[usize],
adj_list: &HashMap<usize, Vec<usize>>,
) -> Vec<Vec<usize>> {
use std::collections::HashSet;
if component.len() < 3 {
return Vec::new(); }
let mut cycles = Vec::new();
let component_set: HashSet<usize> = component.iter().cloned().collect();
for &start in component {
let found_cycles = find_cycles_from_vertex(start, adj_list, &component_set);
for cycle in found_cycles {
let mut normalized_cycle = cycle;
if let Some(min_pos) = normalized_cycle
.iter()
.position(|&x| x == *normalized_cycle.iter().min().unwrap())
{
normalized_cycle.rotate_left(min_pos);
}
if !is_duplicate_cycle(&normalized_cycle, &cycles) {
cycles.push(normalized_cycle);
}
}
}
cycles.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)));
cycles
}
#[doc(hidden)]
fn find_cycles_from_vertex(
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component_set: &HashSet<usize>,
) -> Vec<Vec<usize>> {
let mut cycles = Vec::new();
fn dfs_find_cycles(
current: usize,
start: usize,
adj_list: &HashMap<usize, Vec<usize>>,
component_set: &HashSet<usize>,
path: &mut Vec<usize>,
visited: &mut HashSet<usize>,
cycles: &mut Vec<Vec<usize>>,
) {
if path.len() > 10 {
return; }
path.push(current);
visited.insert(current);
if let Some(neighbors) = adj_list.get(¤t) {
for &neighbor in neighbors {
if !component_set.contains(&neighbor) {
continue;
}
if neighbor == start && path.len() >= 3 {
cycles.push(path.clone());
} else if !visited.contains(&neighbor) {
dfs_find_cycles(
neighbor,
start,
adj_list,
component_set,
path,
visited,
cycles,
);
}
}
}
path.pop();
visited.remove(¤t);
}
let mut path = Vec::new();
let mut visited = HashSet::new();
dfs_find_cycles(
start,
start,
adj_list,
component_set,
&mut path,
&mut visited,
&mut cycles,
);
cycles
}
#[doc(hidden)]
fn is_duplicate_cycle(new_cycle: &[usize], existing_cycles: &[Vec<usize>]) -> bool {
for existing in existing_cycles {
if is_same_cycle(new_cycle, existing) {
return true;
}
}
false
}
#[doc(hidden)]
fn is_same_cycle(cycle1: &[usize], cycle2: &[usize]) -> bool {
if cycle1.len() != cycle2.len() {
return false;
}
let len = cycle1.len();
for start in 0..len {
let mut matches = true;
for i in 0..len {
if cycle1[i] != cycle2[(start + i) % len] {
matches = false;
break;
}
}
if matches {
return true;
}
matches = true;
for i in 0..len {
if cycle1[i] != cycle2[(start + len - i) % len] {
matches = false;
break;
}
}
if matches {
return true;
}
}
false
}
#[cfg(test)]
mod test_remove_bridge_arcs {
use super::remove_bridge_arcs;
use togo::prelude::*;
#[test]
fn test_remove_bridge_arcs_duplicate_arcs() {
let mut arcs = vec![
arc(point(0.0, 0.0), point(1.0, 1.0), point(0.5, 0.5), 0.5),
arc(point(1.0, 1.0), point(2.0, 2.0), point(1.5, 1.5), 0.5),
arc(point(0.0, 0.0), point(1.0, 1.0), point(0.5, 0.5), 0.5),
];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_remove_bridge_arcs_duplicate_lines() {
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(2.0, 2.0)),
arcseg(point(0.0, 0.0), point(1.0, 1.0)), ];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_remove_bridge_arcs_duplicate_lines_reversed() {
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(0.0, 0.0)), arcseg(point(2.0, 2.0), point(3.0, 3.0)),
];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_remove_bridge_arcs_no_duplicates() {
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(2.0, 2.0)),
arc(point(2.0, 2.0), point(3.0, 1.0), point(2.5, 1.5), 0.5),
];
let original_len = arcs.len();
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), original_len);
}
#[test]
fn test_remove_bridge_arcs_duplicate_arcs_same_params() {
let mut arcs = vec![
arc(point(0.0, 0.0), point(2.0, 0.0), point(1.0, 1.0), 1.0),
arc(point(0.0, 0.0), point(2.0, 0.0), point(1.0, 1.0), 1.0), arcseg(point(3.0, 3.0), point(4.0, 4.0)),
];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_remove_bridge_arcs_mixed_arc_and_line() {
let mut arcs = vec![
arc(point(0.0, 0.0), point(2.0, 0.0), point(1.0, 1.0), 1.0),
arcseg(point(0.0, 0.0), point(2.0, 0.0)), arcseg(point(3.0, 3.0), point(4.0, 4.0)),
];
let original_len = arcs.len();
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), original_len); }
#[test]
fn test_remove_bridge_arcs_multiple_duplicates() {
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 1.0)),
arcseg(point(0.0, 0.0), point(1.0, 1.0)), arcseg(point(0.0, 0.0), point(1.0, 1.0)), arc(point(2.0, 2.0), point(4.0, 2.0), point(3.0, 3.0), 1.0),
arc(point(2.0, 2.0), point(4.0, 2.0), point(3.0, 3.0), 1.0), arcseg(point(5.0, 5.0), point(6.0, 6.0)),
];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1); }
#[test]
fn test_remove_bridge_arcs_empty_input() {
let mut arcs: Vec<Arc> = vec![];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 0);
}
#[test]
fn test_remove_bridge_arcs_single_element() {
let mut arcs = vec![arcseg(point(0.0, 0.0), point(1.0, 1.0))];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_remove_bridge_arcs_close_but_not_equal() {
let eps = super::EPS_CONNECT;
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 1.0)),
arcseg(point(0.0, 0.0), point(1.0 + eps * 0.5, 1.0 + eps * 0.5)), ];
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), 1); }
#[test]
fn test_remove_bridge_arcs_different_radius() {
let mut arcs = vec![
arc(point(0.0, 0.0), point(2.0, 0.0), point(1.0, 1.0), 1.0),
arc(point(0.0, 0.0), point(2.0, 0.0), point(1.0, 1.0), 1.5), arcseg(point(3.0, 3.0), point(4.0, 4.0)),
];
let original_len = arcs.len();
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), original_len); }
#[test]
fn test_remove_bridge_arcs_two_connected_rectangles() {
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 0.0)),
arcseg(point(1.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(2.0, 1.0)),
arcseg(point(2.0, 1.0), point(2.0, 0.0)),
arcseg(point(2.0, 0.0), point(3.0, 0.0)),
arcseg(point(3.0, 0.0), point(3.0, 2.0)),
arcseg(point(3.0, 2.0), point(2.0, 2.0)),
arcseg(point(2.0, 2.0), point(2.0, 1.0)),
arcseg(point(2.0, 1.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(1.0, 2.0)),
arcseg(point(1.0, 2.0), point(0.0, 2.0)),
];
let original_len = arcs.len();
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), original_len-2);
}
#[test]
fn test_remove_bridge_arcs_two_connected_rectangles_new_arc() {
let eps = super::EPS_BRIDGE;
let mut arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 0.0)),
arcseg(point(1.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(2.0, 1.0)),
arcseg(point(2.0, 1.0), point(2.0, 0.0)),
arcseg(point(2.0, 0.0), point(3.0, 0.0)),
arcseg(point(3.0, 0.0), point(3.0, 2.0)),
arcseg(point(3.0, 2.0), point(2.0, 2.0)),
arcseg(point(2.0, 2.0), point(2.0, 1.0 + eps)),
arcseg(point(2.0, 1.0 + eps), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(1.0, 2.0)),
arcseg(point(1.0, 2.0), point(0.0, 2.0)),
];
let original_len = arcs.len();
remove_bridge_arcs(&mut arcs);
assert_eq!(arcs.len(), original_len-1);
}
}
#[cfg(test)]
mod test_merge_points {
#[test]
fn test_merge_points() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(5, (1005, 1006)); arc_map.insert(7, (1006, 1007));
let merge = vec![];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&5], (1005, 1006)); assert_eq!(arc_map[&7], (1006, 1007)); }
#[test]
fn test_merge_points_multiple_merges() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
let merge = vec![(1001, 1002), (1003, 1004)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001));
assert_eq!(arc_map[&1], (1001, 1003));
assert_eq!(arc_map[&2], (1003, 1005));
}
#[test]
fn test_merge_points_empty_merge() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
let original_arc_map = arc_map.clone();
let merge = vec![];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map, original_arc_map);
}
#[test]
fn test_merge_points_self_merge() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
let original_arc_map = arc_map.clone();
let merge = vec![(1000, 1000), (1001, 1001)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map, original_arc_map);
}
#[test]
fn test_merge_points_transitive_closure() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
let merge = vec![(1000, 1002), (1002, 1004)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1000, 1003)); assert_eq!(arc_map[&2], (1000, 1005)); }
#[test]
fn test_merge_points_both_endpoints_merge() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
let merge = vec![(1000, 1002), (1001, 1003)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1000, 1001)); }
#[test]
fn test_merge_points_complex_graph() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
arc_map.insert(3, (1006, 1007));
arc_map.insert(4, (1008, 1009));
let merge = vec![
(1000, 1002), (1002, 1004), (1006, 1008), ];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1000, 1003)); assert_eq!(arc_map[&2], (1000, 1005)); assert_eq!(arc_map[&3], (1006, 1007)); assert_eq!(arc_map[&4], (1006, 1009)); }
#[test]
fn test_merge_points_duplicate_merges() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
let merge = vec![(1000, 1002), (1002, 1000), (1000, 1002)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1000, 1003)); }
#[test]
fn test_merge_points_cycle_formation() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1000));
let merge = vec![(1001, 1002), (1002, 1004), (1004, 1000)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1000)); assert_eq!(arc_map[&1], (1000, 1003)); assert_eq!(arc_map[&2], (1000, 1000)); }
#[test]
fn test_merge_points_large_numbers() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (100000, 100001));
arc_map.insert(1, (200000, 200001));
let merge = vec![(100001, 200000)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (100000, 100001)); assert_eq!(arc_map[&1], (100001, 200001)); }
#[test]
fn test_merge_points_single_arc() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
let merge = vec![(1000, 1001)];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1000));
}
#[test]
fn test_merge_points_reverse_order() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map1: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map1.insert(0, (1000, 1001));
arc_map1.insert(1, (1002, 1003));
let mut arc_map2 = arc_map1.clone();
let merge1 = vec![(1000, 1002), (1001, 1003)];
let merge2 = vec![(1003, 1001), (1002, 1000)];
merge_points(&mut arc_map1, &merge1);
merge_points(&mut arc_map2, &merge2);
assert_eq!(arc_map1, arc_map2);
}
#[test]
fn test_merge_points_simple_loop() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
let merge = vec![
(1001, 1002), (1003, 1004), (1005, 1000), ];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001));
assert_eq!(arc_map[&1], (1001, 1003));
assert_eq!(arc_map[&2], (1003, 1000));
}
#[test]
fn test_merge_points_multiple_loops() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
arc_map.insert(3, (2000, 2001));
arc_map.insert(4, (2002, 2003));
let merge = vec![
(1001, 1002),
(1003, 1004),
(1005, 1000),
(2001, 2002),
(2003, 2000),
];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1001, 1003)); assert_eq!(arc_map[&2], (1003, 1000));
assert_eq!(arc_map[&3], (2000, 2001)); assert_eq!(arc_map[&4], (2001, 2000)); }
#[test]
fn test_merge_points_nested_loops() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001)); arc_map.insert(1, (1002, 1003)); arc_map.insert(2, (1004, 1005)); arc_map.insert(3, (1006, 1007)); arc_map.insert(4, (1008, 1009));
let merge = vec![
(1001, 1002),
(1003, 1000), (1005, 1006),
(1007, 1008),
(1009, 1004), (1000, 1004), ];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1001, 1000)); assert_eq!(arc_map[&2], (1000, 1005)); assert_eq!(arc_map[&3], (1005, 1007)); assert_eq!(arc_map[&4], (1007, 1000)); }
#[test]
fn test_merge_points_many_small_loops() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
let mut merge = vec![];
for loop_id in 0..5 {
let base = loop_id * 1000 + 1000; let arc1_id = loop_id * 2;
let arc2_id = loop_id * 2 + 1;
arc_map.insert(arc1_id, (base, base + 1));
arc_map.insert(arc2_id, (base + 2, base + 3));
merge.push((base + 1, base + 2)); merge.push((base + 3, base)); }
merge_points(&mut arc_map, &merge);
for loop_id in 0..5 {
let base = loop_id * 1000 + 1000;
let arc1_id = loop_id * 2;
let arc2_id = loop_id * 2 + 1;
assert_eq!(arc_map[&arc1_id], (base, base + 1)); assert_eq!(arc_map[&arc2_id], (base + 1, base)); }
}
#[test]
fn test_merge_points_long_chain_to_loop() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
let mut merge = vec![];
for i in 0..8 {
arc_map.insert(i, (1000 + i * 2, 1000 + i * 2 + 1));
if i < 7 {
merge.push((1000 + i * 2 + 1, 1000 + (i + 1) * 2));
}
}
merge.push((1000 + 7 * 2 + 1, 1000));
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1001, 1003)); assert_eq!(arc_map[&2], (1003, 1005)); assert_eq!(arc_map[&3], (1005, 1007)); assert_eq!(arc_map[&4], (1007, 1009)); assert_eq!(arc_map[&5], (1009, 1011)); assert_eq!(arc_map[&6], (1011, 1013)); assert_eq!(arc_map[&7], (1013, 1000)); }
#[test]
fn test_merge_points_figure_eight() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001)); arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
arc_map.insert(3, (1006, 1007));
arc_map.insert(4, (1008, 1009));
arc_map.insert(5, (1010, 1000));
let merge = vec![
(1001, 1002),
(1003, 1004),
(1005, 1000),
(1000, 1006),
(1007, 1008),
(1009, 1010),
];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1001, 1003)); assert_eq!(arc_map[&2], (1003, 1000)); assert_eq!(arc_map[&3], (1000, 1007)); assert_eq!(arc_map[&4], (1007, 1009)); assert_eq!(arc_map[&5], (1009, 1000)); }
#[test]
fn test_merge_points_spiral_pattern() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
let mut merge = vec![];
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (1004, 1005));
arc_map.insert(3, (1006, 1007));
arc_map.insert(4, (1008, 1009));
merge.extend(vec![
(1001, 1004), (1005, 1008), (1009, 1002), (1003, 1006), (1007, 1000), ]);
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1002, 1003)); assert_eq!(arc_map[&2], (1001, 1005)); assert_eq!(arc_map[&3], (1003, 1000)); assert_eq!(arc_map[&4], (1005, 1002)); }
#[test]
fn test_merge_points_disconnected_components_with_loops() {
use super::merge_points;
use std::collections::HashMap;
let mut arc_map: HashMap<usize, (usize, usize)> = HashMap::new();
arc_map.insert(0, (1000, 1001));
arc_map.insert(1, (1002, 1003));
arc_map.insert(2, (2000, 2001));
arc_map.insert(3, (2002, 2003));
arc_map.insert(4, (3000, 3001));
arc_map.insert(5, (4000, 4001));
arc_map.insert(6, (4002, 4003));
arc_map.insert(7, (4004, 4005));
let merge = vec![
(1001, 1002),
(1003, 1000),
(2001, 2002),
(3001, 3000),
(4001, 4002),
(4003, 4004),
(4005, 4000),
];
merge_points(&mut arc_map, &merge);
assert_eq!(arc_map[&0], (1000, 1001)); assert_eq!(arc_map[&1], (1001, 1000));
assert_eq!(arc_map[&2], (2000, 2001)); assert_eq!(arc_map[&3], (2001, 2003));
assert_eq!(arc_map[&4], (3000, 3000));
assert_eq!(arc_map[&5], (4000, 4001)); assert_eq!(arc_map[&6], (4001, 4003)); assert_eq!(arc_map[&7], (4003, 4000)); }
}
#[cfg(test)]
mod test_find_connected_components {
use super::find_connected_components;
#[test]
fn test_find_connected_components_empty_graph() {
let graph = vec![];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 0);
}
#[test]
fn test_find_connected_components_simple_triangle() {
let graph = vec![(0, 1), (1, 2), (2, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
let cycle = &components[0];
assert_eq!(cycle.len(), 3);
assert!(cycle.contains(&0));
assert!(cycle.contains(&1));
assert!(cycle.contains(&2));
}
#[test]
fn test_find_connected_components_square() {
let graph = vec![(0, 1), (1, 2), (2, 3), (3, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
let cycle = &components[0];
assert_eq!(cycle.len(), 4);
for i in 0..4 {
assert!(cycle.contains(&i));
}
}
#[test]
fn test_find_connected_components_multiple_cycles() {
let graph = vec![
(0, 1),
(1, 2),
(2, 0), (3, 4),
(4, 5),
(5, 3), ];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 2);
for cycle in &components {
assert_eq!(cycle.len(), 3);
}
let mut all_vertices = std::collections::HashSet::new();
for cycle in &components {
for &vertex in cycle {
all_vertices.insert(vertex);
}
}
assert_eq!(all_vertices.len(), 6);
for i in 0..6 {
assert!(all_vertices.contains(&i));
}
}
#[test]
fn test_find_connected_components_isolated_vertices() {
let graph = vec![(0, 1), (1, 2), (2, 0), (3, 4)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 3);
}
#[test]
fn test_find_connected_components_complex_graph() {
let graph = vec![
(0, 1),
(1, 2),
(2, 0), (0, 3),
(3, 4),
(4, 0), ];
let components = find_connected_components(&graph);
assert!(!components.is_empty());
for cycle in &components {
assert!(cycle.len() >= 3);
}
}
#[test]
fn test_find_connected_components_line_graph() {
let graph = vec![(0, 1), (1, 2), (2, 3)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 0);
}
#[test]
fn test_find_connected_components_self_loop() {
let graph = vec![(0, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 0);
}
#[test]
fn test_find_connected_components_duplicate_elimination() {
let graph = vec![(0, 1), (1, 2), (2, 0), (0, 2), (2, 1), (1, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 3);
}
#[test]
fn test_find_connected_components_pentagon() {
let graph = vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 5);
for i in 0..5 {
assert!(components[0].contains(&i));
}
}
#[test]
fn test_find_connected_components_wheel_graph() {
let graph = vec![
(1, 2),
(2, 3),
(3, 1), (0, 1),
(0, 2),
(0, 3), ];
let components = find_connected_components(&graph);
assert!(!components.is_empty());
let has_valid_cycle = components
.iter()
.any(|cycle| cycle.len() == 3 || cycle.len() == 4);
assert!(
has_valid_cycle,
"Expected to find either a 3-cycle or 4-cycle, but found: {:?}",
components
);
}
#[test]
fn test_find_connected_components_degree_constraints() {
let graph = vec![(0, 1), (1, 2), (2, 3), (3, 0)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 4);
let cycle = &components[0];
for i in 0..4 {
assert!(cycle.contains(&i));
}
}
#[test]
fn test_find_connected_components_degree_1_endpoints() {
let graph = vec![(0, 1), (1, 2)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 0);
}
#[test]
fn test_find_connected_components_degree_3_branch() {
let graph = vec![(0, 1), (1, 2), (1, 3), (2, 3)];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 3);
let cycle = &components[0];
assert!(cycle.contains(&1) && cycle.contains(&2) && cycle.contains(&3));
}
#[test]
fn test_find_connected_components_degree_4_intersection() {
let graph = vec![
(0, 1),
(0, 2),
(0, 3),
(0, 4), (1, 2),
(3, 4), ];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 2);
for component in &components {
assert_eq!(component.len(), 3);
assert!(component.contains(&0)); }
}
#[test]
fn test_find_connected_components_mixed_degrees() {
let graph = vec![
(0, 1),
(1, 2),
(2, 0), (2, 3), (3, 4),
(3, 5), (4, 5), ];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 2);
let mut found_triangle_1 = false;
let mut found_triangle_2 = false;
for component in &components {
assert_eq!(component.len(), 3);
if component.contains(&0) && component.contains(&1) && component.contains(&2) {
found_triangle_1 = true;
}
if component.contains(&3) && component.contains(&4) && component.contains(&5) {
found_triangle_2 = true;
}
}
assert!(found_triangle_1, "Should find triangle [0,1,2]");
assert!(found_triangle_2, "Should find triangle [3,4,5]");
}
#[test]
fn test_find_connected_components_performance_constraints() {
let mut graph = Vec::new();
for i in 0..5 {
let base = i * 2;
graph.push((base, base + 1));
graph.push((base + 1, base + 2));
graph.push((base + 2, base));
if i < 4 {
graph.push((base + 2, base + 3));
}
}
let components = find_connected_components(&graph);
assert_eq!(components.len(), 5);
for component in &components {
assert_eq!(component.len(), 3, "Each component should be a triangle");
}
}
#[test]
fn test_find_connected_components_bowtie_graph() {
let graph = vec![
(0, 1),
(1, 2),
(2, 0), (0, 3),
(3, 4),
(4, 0), ];
let components = find_connected_components(&graph);
assert!(components.len() >= 1);
for cycle in &components {
assert!(cycle.len() >= 3);
}
}
#[test]
fn test_find_connected_components_large_cycle() {
let mut graph = vec![];
for i in 0..8 {
graph.push((i, (i + 1) % 8));
}
let components = find_connected_components(&graph);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 8);
for i in 0..8 {
assert!(components[0].contains(&i));
}
}
#[test]
fn test_find_connected_components_mixed_components() {
let graph = vec![
(0, 1),
(1, 2),
(2, 0), (3, 4),
(4, 5),
(5, 6),
(6, 3), (7, 8),
(8, 9), ];
let components = find_connected_components(&graph);
assert_eq!(components.len(), 2);
let mut cycle_sizes: Vec<usize> = components.iter().map(|c| c.len()).collect();
cycle_sizes.sort();
assert_eq!(cycle_sizes, vec![3, 4]);
}
}