1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Graph helpers.

use crate::bit::BitIterator;
use crate::number::UnsignedInteger;
use std::marker::PhantomData;

/// Explore all hamiltonian paths/cycles in a graph.
///
/// # Panics
/// This function panics if the number of vertices exceeds the maximum supported, currently 32.
///
/// # Examples
///
/// Shortest hamiltonian path and cycle starting at vertex 0:
/// ```
/// # use utils::graph::explore_hamiltonian_paths;
/// # use std::ops::Add;
/// let distance_matrix = vec![
///     0, 7, 2, 8, 6,
///     7, 0, 1, 3, 5,
///     2, 1, 0, 9, 4,
///     8, 3, 9, 0, 9,
///     6, 5, 4, 9, 0,
/// ];
///
/// let mut min_hamiltonian_path = u32::MAX;
/// let mut min_hamiltonian_cycle = u32::MAX;
/// explore_hamiltonian_paths(
///     5,                                                              // Number of vertices
///     0,                                                              // Start vertex
///     0,                                                              // Initial path cost
///     |a, b| distance_matrix[a as usize * 5 + b as usize],            // Distance function
///     u32::add,                                                       // Accumulate function
///     |path, loop_edge| {                                             // Callback function
///         min_hamiltonian_path = min_hamiltonian_path.min(path);
///         min_hamiltonian_cycle = min_hamiltonian_cycle.min(path + loop_edge);
///     }
/// );
///
/// assert_eq!(min_hamiltonian_path, 14);  // 0 =[6]=> 4 =[4]=> 2 =[1]=> 1 =[3]=> 3
/// assert_eq!(min_hamiltonian_cycle, 21); // 0 =[2]=> 2 =[1]=> 1 =[3]=> 3 =[9]=> 4 =[6]=> 0
/// ```
///
/// Shortest and longest hamilton paths starting at any vertex:
/// ```
/// # use utils::graph::explore_hamiltonian_paths;
/// let distance_matrix = vec![
///     0, 7, 2, 8, 6,
///     7, 0, 1, 3, 5,
///     2, 1, 0, 9, 4,
///     8, 3, 9, 0, 9,
///     6, 5, 4, 9, 0,
/// ];
///
/// let mut min_path = u32::MAX;
/// let mut max_path = 0;
/// explore_hamiltonian_paths(
///     5,                                                              // Number of vertices
///     0,                                                              // Start vertex
///     (0, u32::MAX, 0),                                               // Initial path cost
///     |a, b| distance_matrix[a as usize * 5 + b as usize],            // Distance function
///     |(total, min_edge, max_edge), edge| {                           // Accumulate function
///         (total + edge, min_edge.min(edge), max_edge.max(edge))
///     },
///     |(total, min_edge, max_edge), loop_edge| {                      // Callback function
///         let loop_total = total + loop_edge;
///         min_path = min_path.min(loop_total - max_edge.max(loop_edge));
///         max_path = max_path.max(loop_total - min_edge.min(loop_edge));
///     }
/// );
///
/// assert_eq!(min_path, 12); // 3 =[3]=> 1 =[1]=> 2 =[2]=> 0 =[6]=> 4
/// assert_eq!(max_path, 31); // 1 =[7]=> 0 =[6]=> 4 =[9]=> 3 =[9]=> 2
/// ```
#[inline]
pub fn explore_hamiltonian_paths<E, P: Copy>(
    vertices: u32,
    start_vertex: u32,
    initial_path: P,
    distance_fn: impl Fn(u32, u32) -> E,
    accumulate_fn: impl Fn(P, E) -> P,
    callback_fn: impl FnMut(P, E),
) {
    // Rust doesn't allow recursive FnMut closures, so move state into struct
    struct Visitor<P, F, G, H> {
        start_vertex: u32,
        phantom: PhantomData<P>,
        distance_fn: F,
        accumulate_fn: G,
        callback_fn: H,
    }

    impl<E, P: Copy, F: Fn(u32, u32) -> E, G: Fn(P, E) -> P, H: FnMut(P, E)> Visitor<P, F, G, H> {
        #[inline]
        fn visit<V: UnsignedInteger>(&mut self, current_vertex: u32, visited: V, path: P) {
            if visited == V::MAX {
                let loop_edge = (self.distance_fn)(current_vertex, self.start_vertex);
                (self.callback_fn)(path, loop_edge);
                return;
            }

            for (next_vertex, next_bit) in BitIterator::zeroes(visited) {
                let new_edge = (self.distance_fn)(current_vertex, next_vertex);
                let new_path = (self.accumulate_fn)(path, new_edge);
                self.visit(next_vertex, visited | next_bit, new_path);
            }
        }
    }

    // visit can take any unsigned integer type as a bitmask, so the code could switch the
    // implementation based on the number of vertices. However, it seems unlikely that an AoC
    // problem would require this as generating that many permutations would be very slow, so for
    // now assume u32 is fine.
    assert!(vertices <= 32, "too many vertices");

    let mut visitor = Visitor {
        start_vertex,
        distance_fn,
        accumulate_fn,
        callback_fn,
        phantom: PhantomData,
    };
    visitor.visit(
        start_vertex,
        !(u32::MAX >> (u32::BITS - vertices)) | (1 << start_vertex),
        initial_path,
    );
}