type Cell = (usize, usize);
type CutScore = (f64, f64);
const HAMILTONIAN_BUDGET: u64 = 400_000;
#[cfg(test)]
const SOLVE_BUDGET: u64 = 50_000;
const MAX_ATTEMPTS: u32 = 500;
const CUT_ATTEMPTS: u32 = 500;
const PAIR_DISTANCE_FLOOR: usize = 3;
const CONTENTION_BUDGET: u64 = 500_000;
const PAIRWISE_CONTENTION_BUDGET: u64 = 50_000;
fn to_idx(cell: Cell, width: usize) -> usize {
cell.1 * width + cell.0
}
fn neighbors(cell: Cell, width: usize, height: usize) -> [Option<Cell>; 4] {
let (x, y) = cell;
[
(x > 0).then(|| (x - 1, y)),
(x + 1 < width).then_some((x + 1, y)),
(y > 0).then(|| (x, y - 1)),
(y + 1 < height).then_some((x, y + 1)),
]
}
pub(crate) fn is_adjacent(a: Cell, b: Cell) -> bool {
let dx = a.0.abs_diff(b.0);
let dy = a.1.abs_diff(b.1);
(dx == 1 && dy == 0) || (dx == 0 && dy == 1)
}
fn manhattan(a: Cell, b: Cell) -> usize {
a.0.abs_diff(b.0) + a.1.abs_diff(b.1)
}
fn is_periphery(width: usize, height: usize, cell: Cell) -> bool {
let (x, y) = cell;
x == 0 || y == 0 || x == width - 1 || y == height - 1
}
fn random_hamiltonian_path(
width: usize,
height: usize,
rng: &mut fastrand::Rng,
) -> Option<Vec<Cell>> {
let n = width * height;
let start = (rng.usize(..width), rng.usize(..height));
let mut visited = vec![false; n];
visited[to_idx(start, width)] = true;
let mut path = vec![start];
let mut frames: Vec<Vec<Cell>> = vec![ranked_neighbors(start, width, height, &visited, rng)];
let mut budget = HAMILTONIAN_BUDGET;
while !frames.is_empty() {
if path.len() == n {
return Some(path);
}
if budget == 0 {
return None;
}
budget -= 1;
let frame = frames.last_mut().expect("loop condition ensures non-empty");
let mut advanced = false;
while let Some(candidate) = frame.pop() {
let ci = to_idx(candidate, width);
if !visited[ci] {
visited[ci] = true;
path.push(candidate);
frames.push(ranked_neighbors(candidate, width, height, &visited, rng));
advanced = true;
break;
}
}
if !advanced {
frames.pop();
let removed = path.pop().expect("frames/path stay in lockstep");
visited[to_idx(removed, width)] = false;
}
}
None
}
fn ranked_neighbors(
cell: Cell,
width: usize,
height: usize,
visited: &[bool],
rng: &mut fastrand::Rng,
) -> Vec<Cell> {
let mut candidates: Vec<Cell> = neighbors(cell, width, height)
.into_iter()
.flatten()
.filter(|&c| !visited[to_idx(c, width)])
.collect();
rng.shuffle(&mut candidates);
let degree = |c: Cell| -> usize {
neighbors(c, width, height)
.into_iter()
.flatten()
.filter(|&n| !visited[to_idx(n, width)])
.count()
};
candidates.sort_by_key(|&c| std::cmp::Reverse(degree(c)));
candidates
}
fn random_cut_points(path_len: usize, pair_count: usize, rng: &mut fastrand::Rng) -> Vec<usize> {
debug_assert!(path_len >= pair_count * 2);
loop {
let mut cuts: Vec<usize> = (0..pair_count - 1)
.map(|_| rng.usize(1..path_len))
.collect();
cuts.sort_unstable();
cuts.dedup();
if cuts.len() != pair_count - 1 {
continue; }
let mut boundary = 0;
let mut ok = true;
for &cut in &cuts {
if cut - boundary < 2 {
ok = false;
break;
}
boundary = cut;
}
if ok && path_len - boundary >= 2 {
return cuts;
}
}
}
fn score_cuts(width: usize, height: usize, segments: &[Vec<Cell>]) -> CutScore {
let fractions: Vec<f64> = segments
.iter()
.map(|s| {
let first = s[0];
let last = *s.last().expect("segment has >= 2 cells");
let dist = manhattan(first, last);
if dist < PAIR_DISTANCE_FLOOR {
return 0.0;
}
if is_periphery(width, height, first) && is_periphery(width, height, last) {
return 0.0;
}
dist as f64 / (s.len() - 1) as f64
})
.collect();
if fractions.contains(&0.0) {
return (0.0, 0.0);
}
let endpoints = to_endpoints(segments);
let mut budget = CONTENTION_BUDGET;
if !has_forced_contention(width, height, &endpoints, &mut budget) {
return (0.0, 0.0);
}
let covered = pairwise_conflicts(width, height, &endpoints, PAIRWISE_CONTENTION_BUDGET);
if covered < endpoints.len() {
return (0.0, 0.0);
}
let min = fractions.iter().copied().fold(f64::INFINITY, f64::min);
let mean = fractions.iter().sum::<f64>() / fractions.len() as f64;
(min, mean)
}
fn to_endpoints(segments: &[Vec<Cell>]) -> Vec<(Cell, Cell)> {
segments
.iter()
.map(|s| (s[0], *s.last().expect("segment has >= 2 cells")))
.collect()
}
fn cut_into_segments(
width: usize,
height: usize,
path: &[Cell],
pair_count: usize,
rng: &mut fastrand::Rng,
) -> Vec<Vec<Cell>> {
let mut best: Option<(Vec<Vec<Cell>>, CutScore)> = None;
for _ in 0..CUT_ATTEMPTS {
let cuts = random_cut_points(path.len(), pair_count, rng);
let mut segments = Vec::with_capacity(pair_count);
let mut start = 0;
for &cut in &cuts {
segments.push(path[start..cut].to_vec());
start = cut;
}
segments.push(path[start..].to_vec());
let score = score_cuts(width, height, &segments);
let is_better = match &best {
Some((_, best_score)) => score > *best_score,
None => true,
};
if is_better {
best = Some((segments, score));
}
}
best.expect("CUT_ATTEMPTS > 0, so at least one attempt was made")
.0
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum SolveOutcome {
Count(u32),
BudgetExceeded,
}
struct Solver {
width: usize,
height: usize,
owner: Vec<Option<usize>>,
blocked: Vec<bool>,
heads: Vec<Cell>,
targets: Vec<Cell>,
target_of: Vec<Option<usize>>,
done: Vec<bool>,
filled: usize,
usable_cells: usize,
require_full_coverage: bool,
shortest_paths_only: bool,
}
impl Solver {
fn new(
width: usize,
height: usize,
endpoints: &[(Cell, Cell)],
blocked_cells: &[Cell],
) -> Self {
let mut owner = vec![None; width * height];
let mut blocked = vec![false; width * height];
for &c in blocked_cells {
blocked[to_idx(c, width)] = true;
}
let mut target_of = vec![None; width * height];
let mut heads = Vec::with_capacity(endpoints.len());
let mut targets = Vec::with_capacity(endpoints.len());
for (n, &(a, b)) in endpoints.iter().enumerate() {
owner[to_idx(a, width)] = Some(n);
target_of[to_idx(b, width)] = Some(n);
heads.push(a);
targets.push(b);
}
let usable_cells = width * height - blocked.iter().filter(|&&b| b).count();
Self {
width,
height,
owner,
blocked,
heads,
targets,
target_of,
done: vec![false; endpoints.len()],
filled: endpoints.len(),
usable_cells,
require_full_coverage: true,
shortest_paths_only: false,
}
}
#[cfg(test)]
fn without_full_coverage_requirement(mut self) -> Self {
self.require_full_coverage = false;
self
}
fn shortest_paths_only(mut self) -> Self {
self.shortest_paths_only = true;
self.require_full_coverage = false;
self
}
fn enterable(&self, number: usize, cell: Cell) -> bool {
let idx = to_idx(cell, self.width);
if self.blocked[idx] || self.owner[idx].is_some() {
return false;
}
if self.shortest_paths_only
&& manhattan(cell, self.targets[number])
>= manhattan(self.heads[number], self.targets[number])
{
return false;
}
!matches!(self.target_of[idx], Some(other) if other != number && !self.done[other])
}
fn legal_moves(&self, number: usize) -> Vec<Cell> {
neighbors(self.heads[number], self.width, self.height)
.into_iter()
.flatten()
.filter(|&c| self.enterable(number, c))
.collect()
}
fn apply(&mut self, number: usize, cell: Cell) {
self.owner[to_idx(cell, self.width)] = Some(number);
self.heads[number] = cell;
self.filled += 1;
if cell == self.targets[number] {
self.done[number] = true;
}
}
fn undo(&mut self, number: usize, cell: Cell, prev_head: Cell) {
self.owner[to_idx(cell, self.width)] = None;
self.heads[number] = prev_head;
self.filled -= 1;
self.done[number] = false;
}
fn is_feasible(&self) -> bool {
if self.shortest_paths_only {
return true;
}
for y in 0..self.height {
for x in 0..self.width {
let cell = (x, y);
let idx = to_idx(cell, self.width);
if self.blocked[idx] || self.owner[idx].is_some() {
continue;
}
let is_pending_target = self
.targets
.iter()
.zip(&self.done)
.any(|(&t, &done)| !done && t == cell);
if !self.require_full_coverage && !is_pending_target {
continue;
}
let usable = neighbors(cell, self.width, self.height)
.into_iter()
.flatten()
.filter(|&n| {
let ni = to_idx(n, self.width);
!self.blocked[ni]
&& (self.owner[ni].is_none()
|| self
.heads
.iter()
.zip(&self.done)
.any(|(&h, &done)| !done && h == n))
})
.count();
let required = if is_pending_target { 1 } else { 2 };
if usable < required {
return false;
}
}
}
true
}
fn count_solutions(&mut self, limit: u32, budget: &mut u64) -> SolveOutcome {
let mut count = 0u32;
if self.dfs(limit, budget, &mut count) {
SolveOutcome::Count(count)
} else {
SolveOutcome::BudgetExceeded
}
}
fn dfs(&mut self, limit: u32, budget: &mut u64, count: &mut u32) -> bool {
if *budget == 0 {
return false;
}
*budget -= 1;
if !self.require_full_coverage && self.done.iter().all(|&d| d) {
*count += 1;
return true;
}
if self.filled == self.usable_cells {
if self.done.iter().all(|&d| d) {
*count += 1;
}
return true;
}
let mut chosen: Option<(usize, Vec<Cell>)> = None;
for n in 0..self.heads.len() {
if self.done[n] {
continue;
}
let moves = self.legal_moves(n);
if moves.is_empty() {
return true; }
if chosen.as_ref().map_or(true, |(_, m)| moves.len() < m.len()) {
chosen = Some((n, moves));
}
}
let Some((number, moves)) = chosen else {
return true;
};
for mv in moves {
let prev_head = self.heads[number];
self.apply(number, mv);
let feasible = self.is_feasible();
let keep_going = if feasible {
self.dfs(limit, budget, count)
} else {
true };
self.undo(number, mv, prev_head);
if !keep_going || *count >= limit {
return keep_going;
}
}
true
}
}
fn has_forced_contention(
width: usize,
height: usize,
endpoints: &[(Cell, Cell)],
budget: &mut u64,
) -> bool {
let mut solver = Solver::new(width, height, endpoints, &[]).shortest_paths_only();
matches!(solver.count_solutions(1, budget), SolveOutcome::Count(0))
}
fn pairwise_conflicts(
width: usize,
height: usize,
endpoints: &[(Cell, Cell)],
budget_per_check: u64,
) -> usize {
let mut conflicted = vec![false; endpoints.len()];
for i in 0..endpoints.len() {
for j in (i + 1)..endpoints.len() {
if conflicted[i] && conflicted[j] {
continue; }
let others_blocked: Vec<Cell> = endpoints
.iter()
.enumerate()
.filter(|&(n, _)| n != i && n != j)
.flat_map(|(_, &(a, b))| [a, b])
.collect();
let pair = [endpoints[i], endpoints[j]];
let mut budget = budget_per_check;
let mut solver =
Solver::new(width, height, &pair, &others_blocked).shortest_paths_only();
if matches!(
solver.count_solutions(1, &mut budget),
SolveOutcome::Count(0)
) {
conflicted[i] = true;
conflicted[j] = true;
}
}
}
conflicted.iter().filter(|&&c| c).count()
}
pub(crate) fn generate(
width: usize,
height: usize,
pair_count: usize,
seed: u64,
) -> Vec<(Cell, Cell)> {
assert!(
width * height >= pair_count * 2,
"not enough cells for {pair_count} pairs on a {width}x{height} board"
);
let mut rng = fastrand::Rng::with_seed(seed);
let mut fallback: Option<(Vec<(Cell, Cell)>, CutScore)> = None;
for _ in 0..MAX_ATTEMPTS {
let Some(path) = random_hamiltonian_path(width, height, &mut rng) else {
continue;
};
let segments = cut_into_segments(width, height, &path, pair_count, &mut rng);
let score = score_cuts(width, height, &segments);
let is_better = match &fallback {
Some((_, best)) => score > *best,
None => true,
};
if is_better {
fallback = Some((to_endpoints(&segments), score));
}
if score.0 == 0.0 {
continue;
}
return to_endpoints(&segments);
}
fallback
.expect(
"random_hamiltonian_path succeeds within a handful of attempts on any reasonable board",
)
.0
}
#[cfg(test)]
mod tests {
use super::*;
fn count_solutions_for(
width: usize,
height: usize,
endpoints: &[(Cell, Cell)],
limit: u32,
) -> SolveOutcome {
let mut solver = Solver::new(width, height, endpoints, &[]);
let mut budget = SOLVE_BUDGET;
solver.count_solutions(limit, &mut budget)
}
#[test]
fn is_adjacent_only_true_for_orthogonal_neighbors() {
assert!(is_adjacent((1, 1), (1, 2)));
assert!(is_adjacent((1, 1), (2, 1)));
assert!(!is_adjacent((1, 1), (2, 2)));
assert!(!is_adjacent((1, 1), (1, 1)));
}
#[test]
fn is_periphery_only_true_on_the_outer_border() {
assert!(is_periphery(5, 5, (0, 0)));
assert!(is_periphery(5, 5, (4, 4)));
assert!(is_periphery(5, 5, (0, 2)));
assert!(is_periphery(5, 5, (2, 0)));
assert!(is_periphery(5, 5, (4, 2)));
assert!(is_periphery(5, 5, (2, 4)));
assert!(!is_periphery(5, 5, (1, 1)));
assert!(!is_periphery(5, 5, (2, 2)));
assert!(!is_periphery(5, 5, (3, 3)));
}
#[test]
fn generate_produces_the_requested_shape() {
for seed in 0..8 {
let endpoints = generate(4, 4, 4, seed);
assert_eq!(endpoints.len(), 4);
let mut seen = std::collections::HashSet::new();
for &(a, b) in &endpoints {
assert_ne!(a, b);
assert!(seen.insert(a));
assert!(seen.insert(b));
}
}
}
#[test]
fn generate_succeeds_at_the_web_demos_difficulty_presets() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
for seed in 0..3 {
let endpoints = generate(width, height, pairs, seed);
assert_eq!(endpoints.len(), pairs);
}
}
}
#[test]
fn generated_pairs_are_never_trivially_adjacent() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
for seed in 0..10 {
let endpoints = generate(width, height, pairs, seed);
for &(a, b) in &endpoints {
assert!(
!is_adjacent(a, b),
"pair {a:?}-{b:?} is directly adjacent on a {width}x{height}/{pairs} \
board (seed {seed})"
);
}
}
}
}
#[test]
fn generated_pairs_never_have_both_endpoints_on_the_periphery() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
for seed in 0..10 {
let endpoints = generate(width, height, pairs, seed);
for &(a, b) in &endpoints {
assert!(
!(is_periphery(width, height, a) && is_periphery(width, height, b)),
"pair {a:?}-{b:?} has both endpoints on the periphery of a \
{width}x{height}/{pairs} board (seed {seed})"
);
}
}
}
}
#[test]
fn generate_is_verifiably_unique() {
let endpoints = generate(4, 4, 3, 0);
let mut solver = Solver::new(4, 4, &endpoints, &[]);
let mut budget = SOLVE_BUDGET;
assert_eq!(
solver.count_solutions(2, &mut budget),
SolveOutcome::Count(1)
);
}
#[test]
fn cut_into_segments_partitions_the_whole_path() {
let mut rng = fastrand::Rng::with_seed(7);
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
let mut path = None;
for _ in 0..MAX_ATTEMPTS {
if let Some(p) = random_hamiltonian_path(width, height, &mut rng) {
path = Some(p);
break;
}
}
let path = path.expect("small boards find a Hamiltonian path within a few attempts");
let segments = cut_into_segments(width, height, &path, pairs, &mut rng);
assert_eq!(segments.len(), pairs);
let mut rebuilt = Vec::with_capacity(path.len());
for segment in &segments {
assert!(
segment.len() >= 2,
"segment {segment:?} has fewer than 2 cells"
);
rebuilt.extend(segment.iter().copied());
}
assert_eq!(
rebuilt, path,
"segments must concatenate back to the original path in order"
);
}
}
#[test]
fn cut_into_segments_finds_well_spread_endpoints_across_seeds() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
for seed in 0..20 {
let mut rng = fastrand::Rng::with_seed(seed);
let mut segments = None;
for _ in 0..MAX_ATTEMPTS {
let Some(path) = random_hamiltonian_path(width, height, &mut rng) else {
continue;
};
let candidate = cut_into_segments(width, height, &path, pairs, &mut rng);
if score_cuts(width, height, &candidate).0 > 0.0 {
segments = Some(candidate);
break;
}
}
let segments = segments
.expect("a non-degenerate cut should be found within MAX_ATTEMPTS fresh paths");
let (min_fraction, _mean) = score_cuts(width, height, &segments);
assert!(
min_fraction > 0.0,
"{width}x{height}/{pairs} seed {seed}: every segment should clear \
PAIR_DISTANCE_FLOOR, got min_fraction {min_fraction}"
);
for segment in &segments {
let first = segment[0];
let last = *segment.last().unwrap();
let dist = manhattan(first, last);
assert!(
dist >= PAIR_DISTANCE_FLOOR,
"{width}x{height}/{pairs} seed {seed}: segment {first:?}-{last:?} only \
{dist} apart, expected at least {PAIR_DISTANCE_FLOOR}"
);
}
}
}
}
#[test]
fn a_two_by_two_board_with_two_diagonal_pairs_is_unique() {
let endpoints = [((0, 0), (1, 0)), ((0, 1), (1, 1))];
assert_eq!(
count_solutions_for(2, 2, &endpoints, 2),
SolveOutcome::Count(1)
);
}
#[test]
fn a_two_by_two_board_with_a_crossing_pair_layout_is_not_unique_or_impossible() {
let endpoints = [((0, 0), (1, 1)), ((1, 0), (0, 1))];
match count_solutions_for(2, 2, &endpoints, 4) {
SolveOutcome::Count(_) => {}
SolveOutcome::BudgetExceeded => panic!("solver should finish instantly on a 2x2 board"),
}
}
#[test]
fn blocked_cells_are_never_entered() {
let endpoints = [((0, 0), (2, 0))];
let mut solver = Solver::new(3, 1, &endpoints, &[(1, 0)]);
let mut budget = SOLVE_BUDGET;
assert_eq!(
solver.count_solutions(2, &mut budget),
SolveOutcome::Count(0)
);
}
#[test]
fn without_full_coverage_requirement_finds_many_solutions_when_space_allows() {
let endpoints = [((0, 0), (2, 2))];
let mut solver = Solver::new(3, 3, &endpoints, &[]).without_full_coverage_requirement();
let mut budget = SOLVE_BUDGET;
assert_eq!(
solver.count_solutions(2, &mut budget),
SolveOutcome::Count(2)
);
}
#[test]
fn without_full_coverage_requirement_still_finds_a_genuinely_forced_route() {
let endpoints = [((0, 0), (0, 2))];
let mut solver = Solver::new(1, 3, &endpoints, &[]).without_full_coverage_requirement();
let mut budget = SOLVE_BUDGET;
assert_eq!(
solver.count_solutions(2, &mut budget),
SolveOutcome::Count(1)
);
}
#[test]
fn pairwise_conflicts_is_zero_when_nothing_is_forced() {
let endpoints = [((0, 0), (1, 1)), ((3, 3), (4, 4))];
assert_eq!(pairwise_conflicts(5, 5, &endpoints, SOLVE_BUDGET), 0);
}
#[test]
fn pairwise_conflicts_isolates_just_the_entangled_numbers() {
let pair_a = ((0, 1), (2, 1));
let pair_b = ((1, 0), (1, 2));
let pair_c = ((3, 3), (4, 4));
let endpoints = [pair_a, pair_b, pair_c];
assert_eq!(
pairwise_conflicts(5, 5, &endpoints, SOLVE_BUDGET),
2,
"only pair_a/pair_b should be entangled, pair_c stays free"
);
}
#[test]
fn pairwise_conflicts_counts_every_entangled_number_across_multiple_clusters() {
let endpoints = [
((0, 1), (2, 1)), ((1, 0), (1, 2)),
((4, 1), (6, 1)), ((5, 0), (5, 2)),
];
assert_eq!(pairwise_conflicts(7, 3, &endpoints, SOLVE_BUDGET), 4);
}
#[test]
fn generate_produces_genuinely_contended_puzzles() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
for seed in 0..10 {
let endpoints = generate(width, height, pairs, seed);
let mut budget = 2_000_000u64;
assert!(
has_forced_contention(width, height, &endpoints, &mut budget),
"{width}x{height}/{pairs} seed {seed}: {endpoints:?} has no forced \
contention — a naive independent-shortest-path assignment solves it"
);
let covered = pairwise_conflicts(width, height, &endpoints, 2_000_000);
assert_eq!(
covered,
pairs,
"{width}x{height}/{pairs} seed {seed}: {endpoints:?} only has {covered}/{pairs} \
numbers entangled in a forced conflict, not all of them"
);
}
}
}
}
#[cfg(test)]
mod difficulty_survey {
use super::*;
const PRESETS: [(usize, usize, usize); 3] = [(5, 5, 4), (7, 7, 7), (9, 9, 9)];
const SURVEY_SOLVE_BUDGET: u64 = 2_000_000;
fn env_count(var: &str, default: usize) -> usize {
std::env::var(var)
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(default)
}
fn sample_count() -> usize {
env_count("SURVEY_SAMPLES", 200)
}
#[test]
#[ignore]
fn survey_contention_hit_rate_and_cost() {
let samples = sample_count();
for &(width, height, pairs) in &PRESETS {
let data: Vec<(bool, u64)> = parallel_samples(samples, |count, rng| {
let mut out = Vec::with_capacity(count);
while out.len() < count {
let Some(path) = random_hamiltonian_path(width, height, rng) else {
continue;
};
for _ in 0..10.min(count - out.len()) {
let cuts = random_cut_points(path.len(), pairs, rng);
let mut segments = Vec::with_capacity(pairs);
let mut start = 0;
for &cut in &cuts {
segments.push(path[start..cut].to_vec());
start = cut;
}
segments.push(path[start..].to_vec());
let too_close = segments
.iter()
.any(|s| manhattan(s[0], *s.last().unwrap()) < PAIR_DISTANCE_FLOOR);
if too_close {
continue;
}
let endpoints = to_endpoints(&segments);
let mut budget = SURVEY_SOLVE_BUDGET;
let contends =
has_forced_contention(width, height, &endpoints, &mut budget);
let cost = SURVEY_SOLVE_BUDGET - budget;
out.push((contends, cost));
}
}
out
});
let hits = data.iter().filter(|&&(c, _)| c).count();
let costs: Vec<u64> = data.iter().map(|&(_, c)| c).collect();
let mean_cost = costs.iter().sum::<u64>() as f64 / costs.len() as f64;
let max_cost = costs.iter().copied().max().unwrap_or(0);
println!(
"{width}x{height}/{pairs}: n={} genuine_contention={hits}/{} ({:.1}%) \
mean_cost={mean_cost:.1} max_cost={max_cost}",
data.len(),
data.len(),
100.0 * hits as f64 / data.len() as f64
);
}
}
#[test]
#[ignore]
fn survey_pairwise_conflict_coverage() {
let samples = sample_count();
for &(width, height, pairs) in &PRESETS {
let coverage: Vec<usize> = parallel_samples(samples, |count, rng| {
let mut out = Vec::with_capacity(count);
while out.len() < count {
let Some(path) = random_hamiltonian_path(width, height, rng) else {
continue;
};
for _ in 0..10.min(count - out.len()) {
let cuts = random_cut_points(path.len(), pairs, rng);
let mut segments = Vec::with_capacity(pairs);
let mut start = 0;
for &cut in &cuts {
segments.push(path[start..cut].to_vec());
start = cut;
}
segments.push(path[start..].to_vec());
let too_close = segments
.iter()
.any(|s| manhattan(s[0], *s.last().unwrap()) < PAIR_DISTANCE_FLOOR);
if too_close {
continue;
}
let endpoints = to_endpoints(&segments);
let mut budget = SURVEY_SOLVE_BUDGET;
if !has_forced_contention(width, height, &endpoints, &mut budget) {
continue; }
let covered =
pairwise_conflicts(width, height, &endpoints, SURVEY_SOLVE_BUDGET);
out.push(covered);
}
}
out
});
let n = coverage.len() as f64;
let mean = coverage.iter().sum::<usize>() as f64 / n;
let max = coverage.iter().copied().max().unwrap_or(0);
let min = coverage.iter().copied().min().unwrap_or(0);
let fully_entangled = coverage.iter().filter(|&&c| c == pairs).count();
println!(
"{width}x{height}/{pairs}: n={} covered_numbers mean={mean:.2}/{pairs} \
min={min} max={max} fully_entangled={fully_entangled}/{}",
coverage.len(),
coverage.len()
);
}
}
fn parallel_samples<T: Send>(
total: usize,
work: impl Fn(usize, &mut fastrand::Rng) -> Vec<T> + Sync,
) -> Vec<T> {
let threads = std::thread::available_parallelism()
.map(std::num::NonZeroUsize::get)
.unwrap_or(1)
.min(total.max(1));
let per_thread = total.div_ceil(threads);
std::thread::scope(|scope| {
let work = &work;
let handles: Vec<_> = (0..threads)
.map(|i| {
scope.spawn(move || {
let mut rng = fastrand::Rng::with_seed(0x9E37_79B9 ^ i as u64);
work(per_thread, &mut rng)
})
})
.collect();
handles
.into_iter()
.flat_map(|h| h.join().unwrap())
.collect()
})
}
#[test]
#[ignore]
fn measure_generate_latency_and_contention() {
for (width, height, pairs) in [(5, 5, 4), (7, 7, 7), (9, 9, 9)] {
let n = 30u64;
let mut total = std::time::Duration::ZERO;
let mut max = std::time::Duration::ZERO;
let mut contended = 0u32;
for seed in 0..n {
let start = std::time::Instant::now();
let endpoints = generate(width, height, pairs, seed);
let elapsed = start.elapsed();
total += elapsed;
max = max.max(elapsed);
let mut budget = SURVEY_SOLVE_BUDGET;
if has_forced_contention(width, height, &endpoints, &mut budget) {
contended += 1;
}
}
println!(
"{width}x{height}/{pairs}: avg={:?} max={:?} contended={contended}/{n}",
total / n as u32,
max
);
}
}
}