exact_covers/
dl.rs

1use crate::indices::{InstIndex, ItemIndex, NodeIndex};
2use crate::Solution;
3use std::iter;
4use std::marker::PhantomData;
5use std::mem::MaybeUninit;
6use std::ops::ControlFlow;
7
8/// An item in an XCC problem.
9pub(crate) struct Item<'l, L> {
10    /// A unique identifier assigned to this item.
11    ///
12    /// This field roughly corresponds to the `NAME` member in Knuth's
13    /// data structure.
14    ///
15    /// # Invariant
16    ///
17    /// This variable is initialized if and only if this item does not represent
18    /// the special header node in a horizontal list of a [`Solver`].
19    label: MaybeUninit<&'l L>,
20    /// Possibly the previous item in a (horizontal) list of active items,
21    /// in cyclic order. The contents of this variable are preserved when
22    /// the item is removed from such linked list. This property makes it
23    /// possible to apply the dancing links technique on a list of active
24    /// items.
25    ///
26    /// This field corresponds to the `LLINK` pointer in Knuth's data structure.
27    ///
28    /// # Invariant
29    ///
30    /// If `left == right`, then this item represents the special header node
31    /// in an empty horizontal list of a [`Solver`].
32    left: ItemIndex,
33    /// Possibly the next item in a (horizontal) list of active items,
34    /// in cyclic order. The contents of this variable are preserved
35    /// when the item is removed from such linked list. (See `self.left`
36    /// for details.)
37    ///
38    /// This field corresponds to the `RLINK` pointer in Knuth's data structure.
39    right: ItemIndex,
40    /// The node of the first active option that contains this item, if any.
41    /// In other words, the first node in the vertical list for this item.
42    ///
43    /// This field corresponds to the `DLINK` pointer in Knuth's data structure.
44    ///
45    /// # Invariant
46    ///
47    /// `first_option` is [`None`] if and only if `last_option` is [`None`].
48    first_option: Option<InstIndex>,
49    /// The node of the last active option that contains this item, if any.
50    /// In other words, the last node in the vertical list for this item.
51    ///
52    /// This field corresponds to the `ULINK` pointer in Knuth's data structure.
53    last_option: Option<InstIndex>,
54    /// The number of elements in the vertical list for this item.
55    ///
56    /// # Invariants
57    ///
58    /// - `len == 0` if and only if `first_option` and `last_option` are [`None`].
59    /// - `len == 1` if and only if `first_option == last_option`.
60    len: usize,
61    /// Whether this item is secondary and appears in a chosen option that
62    /// specifies its color explicitly. This field is true if and only if
63    /// $\text{COLOR}(i)\ne0$ in Knuth's Algorithm C, and it helps us to
64    /// determine if a solution intersects every purely secondary option
65    /// in method [`is_valid_solution`].
66    ///
67    /// [`is_valid_solution`]: Solver::is_valid_solution
68    purified: bool,
69}
70
71impl<'l, L> Item<'l, L> {
72    /// Creates the head for an active list of items.
73    fn header(left: ItemIndex, right: ItemIndex) -> Self {
74        Self {
75            label: MaybeUninit::uninit(),
76            left,
77            right,
78            first_option: None,
79            last_option: None,
80            len: 0,
81            purified: false,
82        }
83    }
84
85    /// Creates an item that points to its predecessor and successor
86    /// in a horizontal list, and whose vertical list is empty.
87    fn new(label: &'l L, left: ItemIndex, right: ItemIndex) -> Self {
88        Self {
89            label: MaybeUninit::new(label),
90            left,
91            right,
92            first_option: None,
93            last_option: None,
94            len: 0,
95            purified: false,
96        }
97    }
98}
99
100/// The position of the special node in the `items` table of a [`Solver`]
101/// that serves as the head of the list of active _primary_ items; Knuth
102/// called this the _root_ in the paper "Dancing links", [arXiv:cs/0011047][dl]
103/// [cs.DS] (2000).
104///
105/// The list of active secondary items has its own header node, namely the last
106/// element in `items`. Its position thus depends on the number of items in
107/// the exact cover problem, so this constant has no secondary counterpart.
108///
109/// [dl]: https://arxiv.org/pdf/cs/0011047.pdf
110pub(crate) const PRIMARY_HEADER: ItemIndex = ItemIndex::new(0);
111
112/// An instance of some [item](`Item`) in an option, represented as
113/// an internal node in the toroidal data structures of [`Solver`].
114pub(crate) struct Instance<C> {
115    /// The item associated with this node.
116    ///
117    /// This field corresponds to the `TOP` pointer in Knuth's data structure.
118    item: ItemIndex,
119    /// The previous node in the vertical list for `item`, if any.
120    ///
121    /// This field corresponds to the `ULINK` pointer in Knuth's data structure,
122    /// except that it equals [`None`] instead of `item` when a node belongs
123    /// to the first option that contains `item`.
124    above: Option<InstIndex>,
125    /// The next node in the vertical list for `item`, if any.
126    ///
127    /// This field corresponds to the `DLINK` pointer in Knuth's data structure,
128    /// except that it equals [`None`] instead of `item` when a node belongs
129    /// to the last option that contains `item`.
130    below: Option<InstIndex>,
131    /// The color assigned to `item` by this option, if any. Otherwise
132    /// the solver implicitly assigns a unique color to this instance
133    /// that is incompatible with the colors of any other option,
134    /// provided that `item` is secondary.
135    ///
136    /// This field corresponds to the `COLOR` member in Knuth's data structure.
137    ///
138    /// # Invariant
139    ///
140    /// If `item` is a primary item, then this variable is [`None`].
141    color: Option<C>,
142    /// If this instance appears in the vertical list of a purified secondary
143    /// item, this field indicates whether the instance wants the color chosen
144    /// for the item or not. The purpose of this field, which is true if and
145    /// only if $\text{COLOR}(x)=-1$ in Knuth's Algorithm C, is to avoid
146    /// repeatedly purifying an item; see methods [`purify`] and [`unpurify`]
147    /// for details.
148    ///
149    /// [`purify`]: Solver::purify
150    /// [`unpurify`]: Solver::unpurify
151    wants_color: bool,
152}
153
154/// A node in the sequential table of a [`Solver`] that either is a separator
155/// between the items of two options, or it refers to one of these items.
156pub(crate) enum Node<C> {
157    /// A spacer node between options.
158    Spacer {
159        /// The first node in the preceding option, or [`None`] if this is
160        /// the spacer that comes before the first option.
161        ///
162        /// This field is an aid to traversing such option in cyclic order,
163        /// from left to right. It corresponds to the `ULINK` pointer in
164        /// Knuth's data structure.
165        first_in_prev: Option<InstIndex>,
166        /// The last node in the succeeding option, or [`None`] if this is
167        /// the spacer that comes after the last option.
168        ///
169        /// This field is an aid to traversing such option in cyclic order,
170        /// from right to left.
171        last_in_next: Option<InstIndex>,
172    },
173    /// An instance of an item in some option.
174    Instance(Instance<C>),
175}
176
177/// Visits all solutions to a given XCC problem by means of dancing links.
178///
179/// More precisely, this structure embodies an implementation of Algorithm C,
180/// as presented by D. E. Knuth in Section 7.2.2.1 of [_TAOCP_ **4B**][taocp4b],
181/// part 2, pages 87–91.
182///
183/// [XCC solver]: `crate::Solver`
184/// [taocp4b]: https://www-cs-faculty.stanford.edu/~knuth/taocp.html#vol4
185pub struct Solver<'i, I, C> {
186    /// The $N=N_1+N_2$ items, some of which are uncovered and consequently
187    /// appear in the currently active lists.
188    items: Vec<Item<'i, I>>,
189    /// The [item instances] within the vertical lists, with [spacers] between
190    /// them.
191    ///
192    /// [item instances]: `Node::Instance`
193    /// [spacers]: `Node::Spacer`
194    nodes: Vec<Node<C>>,
195    /// A stack of item instance pointers used for backtracking.
196    pointers: Vec<InstIndex>,
197}
198
199impl<'i, I: Eq, C: Eq + Copy> Solver<'i, I, C> {
200    // Problem setup routines.
201
202    /// Appends a new node to the vertical list of the specified item.
203    ///
204    /// If the item is secondary, `color` may specify the color assigned to
205    /// the item by the option (if any). Otherwise `color` must be [`None`].
206    fn append_inst(&mut self, item_ix: ItemIndex, ix: InstIndex, color: Option<C>) {
207        let item = self.item_mut(item_ix);
208        item.len += 1;
209        let above = if let Some(prev_last_ix) = item.last_option.replace(ix) {
210            // Update the `below` link of the new node's predecessor
211            // in the vertical list of `item`.
212            let prev = self.instance_mut(prev_last_ix);
213            prev.below = Some(ix);
214            Some(prev_last_ix)
215        } else {
216            // This is the first option that involves `item`.
217            item.first_option = Some(ix);
218            None
219        };
220        self.nodes.push(Node::Instance(Instance {
221            item: item_ix,
222            above,
223            below: None,
224            color,
225            wants_color: false,
226        }));
227    }
228
229    // Algorithm C routines.
230
231    /// Marks an item as covered by deleting it from the list of items remaining
232    /// to be covered (the horizontal list), and by deleting all of the options
233    /// that contain the item from the database of currently active options.
234    fn cover(&mut self, ix: ItemIndex) {
235        let item = self.item(ix);
236        let mut inst_ix = item.first_option;
237
238        // Delete `item` from the horizontal list.
239        let (left_ix, right_ix) = (item.left, item.right);
240        self.item_mut(left_ix).right = right_ix;
241        self.item_mut(right_ix).left = left_ix;
242
243        // Hide all options containing `item`, from top to bottom.
244        while let Some(ix) = inst_ix {
245            self.hide(ix);
246            inst_ix = self.instance(ix).below;
247        }
248    }
249
250    /// Hides an option that cannot appear in an exact cover for the items
251    /// remaining in the horizontal list. This step traverses the colored
252    /// siblings both to the left and to the right of the node with index `ix`,
253    /// and deletes them from their corresponding vertical lists.
254    fn hide(&mut self, ix: InstIndex) {
255        // Proceed cyclically through the nodes of the option associated with
256        // the given node, from left to right. We store the nodes of an option
257        // contiguously in the `self.nodes` arena, so their indices form a
258        // sequence of consecutive integers. The end of this sublist is
259        // delimited by a spacer node, whose `prev` link points to the node
260        // associated with the first item of the option. Thus, to visit the
261        // relevant nodes we can start from the node at index `ix` until
262        // reaching a spacer node; then we return back to the first item in
263        // the option and continue removing the nodes from their vertical lists
264        // until reaching the given index `ix`.
265        let mut cur_ix = ix.increment();
266        while cur_ix != ix {
267            cur_ix = match *self.node(cur_ix.get()) {
268                Node::Spacer { first_in_prev, .. } => {
269                    // Return to the first item in the option.
270                    first_in_prev.unwrap()
271                }
272                Node::Instance(Instance {
273                    item,
274                    above,
275                    below,
276                    wants_color,
277                    ..
278                }) => {
279                    // Ignore the node if it already has the "correct" color.
280                    if !wants_color {
281                        if let Some(above) = above {
282                            self.instance_mut(above).below = below;
283                        } else {
284                            self.item_mut(item).first_option = below;
285                        }
286                        if let Some(below) = below {
287                            self.instance_mut(below).above = above;
288                        } else {
289                            self.item_mut(item).last_option = above;
290                        }
291                        // Update the length of the vertical list.
292                        self.item_mut(item).len -= 1;
293                    }
294                    // Continue to go rightwards.
295                    cur_ix.increment()
296                }
297            };
298        }
299    }
300
301    /// Undoes the updates made by the last [covering](`Self::cover`) operation.
302    /// This step puts the item at index `ix` back into the list of items remaining
303    /// to be covered (the horizontal list), and reinserts all of the options
304    /// that contain the item into the database of currently active options.
305    fn uncover(&mut self, ix: ItemIndex) {
306        let item = self.item(ix);
307        let mut node_ix = item.first_option;
308
309        // Put back `item` into the horizontal list.
310        let (left_ix, right_ix) = (item.left, item.right);
311        self.item_mut(left_ix).right = ix;
312        self.item_mut(right_ix).left = ix;
313
314        // Unhide all options containing `item`, from top to bottom. This order
315        // may appear to be incorrect at first glance, because covering is also
316        // done from top to bottom. But the answer to exercise 7.2.2.1–2 of
317        // TAOCP shows that it is completely trustworthy.
318        while let Some(ix) = node_ix {
319            self.unhide(ix);
320            node_ix = self.instance(ix).below;
321        }
322    }
323
324    /// Undoes the updates made by the last [hiding](`Self::hide`) operation.
325    /// This step visits the colored siblings both to the left and to the right
326    /// of the node at index `ix`, and puts them back into their corresponding
327    /// vertical lists.
328    fn unhide(&mut self, ix: InstIndex) {
329        // See `Self::hide` for an explanation. There is an important difference
330        // between these two methods, however: since the first spacer cannot
331        // be referenced using a `NodeIndex` and we traverse the table of nodes
332        // in reverse order, we need to use raw indices.
333        let ix = ix.get();
334        let mut cur_ix = ix - 1;
335        while cur_ix != ix {
336            cur_ix = match self.nodes[cur_ix] {
337                Node::Spacer { last_in_next, .. } => {
338                    // Return to the last item in the option.
339                    last_in_next
340                        .expect("spacer should have a last_in_next link")
341                        .get()
342                }
343                Node::Instance(Instance {
344                    item,
345                    above,
346                    below,
347                    wants_color,
348                    ..
349                }) => {
350                    // Ignore the node if we know that it has the correct color.
351                    if !wants_color {
352                        // Reinsert `inst` into its vertical list.
353                        // SAFETY: `inst` is not a spacer, so `cur_ix > 0`.
354                        let wrapped_ix = Some(InstIndex::new(cur_ix));
355                        if let Some(above) = above {
356                            self.instance_mut(above).below = wrapped_ix;
357                        } else {
358                            self.item_mut(item).first_option = wrapped_ix;
359                        }
360                        if let Some(below) = below {
361                            self.instance_mut(below).above = wrapped_ix;
362                        } else {
363                            self.item_mut(item).last_option = wrapped_ix;
364                        }
365                        // Update the length of the vertical list.
366                        self.item_mut(item).len += 1;
367                    }
368                    // Continue to go leftwards.
369                    cur_ix - 1
370                }
371            };
372        }
373    }
374
375    /// [Covers](`Self::cover`) the item of an option, if it has no color
376    /// preference. Otherwise the given node has a color control, and we
377    /// [purify](`Self::purify`) it to remove all options with conflicting
378    /// colors from the relevant vertical lists.
379    fn commit(&mut self, ix: InstIndex) {
380        let inst = self.instance(ix);
381        if let Some(color) = inst.color {
382            // Don't purify a vertical list that has already been culled.
383            if !inst.wants_color {
384                self.purify(ix, color);
385            }
386        } else {
387            self.cover(inst.item);
388        }
389    }
390
391    /// Removes all options that are incompatible with the color constraint
392    /// imposed by the given secondary item instance (if it were present in a
393    /// solution). The items of all compatible options in the relevant vertical
394    /// list temporarily have their `wants_color` field set to `true` in order
395    /// to prevent them from being repeatedly purified (because they already
396    /// have the "correct" color).
397    fn purify(&mut self, ix: InstIndex, color: C) {
398        // We cannot use `debug_assert_eq` because `C` need not implement `Debug`.
399        debug_assert!(
400            self.instance(ix).color == Some(color),
401            "item instance has unexpected or missing color control"
402        );
403        let item_ix = self.instance(ix).item;
404        let item = self.item_mut(item_ix);
405        item.purified = true;
406        // Hide all options that contain the given item but with a color other
407        // than `color`, from top to bottom. If an item in the vertical list
408        // has the "correct" color, then mark it to avoid its repurification
409        // in the future.
410        let mut cur_ix = item.first_option;
411        while let Some(ix) = cur_ix {
412            let inst = self.instance_mut(ix);
413            if inst.color == Some(color) {
414                inst.wants_color = true; // $\texttt{COLOR}(q)\gets-1$.
415            } else {
416                self.hide(ix);
417            }
418            cur_ix = self.instance(ix).below;
419        }
420    }
421
422    /// Undoes the updates made by the last ["commit"](`Self::commit`) operation.
423    /// If the given item is primary, we cover it as in Algorithm X. Otherwise
424    /// the item instance has a color control, and we [unpurify](`Self::unpurify`)
425    /// by putting back all options with conflicting colors into the relevant
426    /// vertical lists.
427    fn uncommit(&mut self, ix: InstIndex) {
428        let inst = self.instance(ix);
429        if let Some(color) = inst.color {
430            // Don't unpurify an item that's already known to have the
431            // correct color.
432            if !inst.wants_color {
433                self.unpurify(ix, color);
434            }
435        } else {
436            self.uncover(inst.item);
437        }
438    }
439
440    /// Undoes the updates made by the last ["purify"](`Self::purify`) operation.
441    /// Puts back all the options incompatible with the given secondary item
442    /// into the corresponding vertical list, and sets back the color field
443    /// of every compatible item instance.
444    fn unpurify(&mut self, ix: InstIndex, color: C) {
445        // Again, we use `debug_assert` rather than `debug_assert_eq` because
446        // `C` might not implement `Debug`.
447        debug_assert!(
448            self.instance(ix).color == Some(color),
449            "item instance has unexpected or missing color control"
450        );
451        let item_ix = self.instance(ix).item;
452        let item = self.item_mut(item_ix);
453        item.purified = false;
454        // Unhide all options that contain the given item, from bottom to top.
455        // If a node in the vertical list has its `wants_color` field set to
456        // `true`, we need to reset it.
457        let mut cur_ix = item.last_option;
458        while let Some(ix) = cur_ix {
459            let inst = self.instance_mut(ix);
460            if inst.wants_color {
461                // $\texttt{COLOR}(q)<0$ in Knuth's description.
462                inst.wants_color = false; // $\texttt{COLOR}(q)\gets c`.
463            } else {
464                self.unhide(ix);
465            }
466            cur_ix = self.instance(ix).above;
467        }
468    }
469
470    /// Finds an active primary item $i$ for which $h(i)$ is minimum, where
471    /// $h$ is usually a heuristic function intended to reduce the amount of
472    /// branching performed by Algorithm C. In case of equality, ties are
473    /// broken by using the position of $i$ within the horizontal list of
474    /// active primary items.
475    ///
476    /// Returns `None` if all primary items have been covered.
477    fn choose_item<H>(&self, heuristic: H) -> Option<ItemIndex>
478    where
479        H: Fn(&Item<'i, I>) -> usize,
480    {
481        let mut min_h = usize::MAX;
482        let mut min_ix = None;
483        let mut cur_ix = self.primary_head().right;
484        while cur_ix != PRIMARY_HEADER {
485            let item = self.item(cur_ix);
486            let h = heuristic(item);
487            if h < min_h {
488                // If $h(i)=0$, then $i$ is surely the result.
489                if h == 0 {
490                    return Some(cur_ix);
491                }
492                min_h = h;
493                min_ix = Some(cur_ix);
494            }
495            cur_ix = item.right;
496        }
497        min_ix
498    }
499
500    /// Given a node corresponding to the covering of a particular item $i$
501    /// with some option $o$, commits all the items $\neq i$ in $o$ from
502    /// the lists of active items.
503    ///
504    /// This function is part of step C5 in Knuth's Algorithm C.
505    fn commit_items_of(&mut self, ix: InstIndex) {
506        // Commit the items cyclically from left to right.
507        let mut cur_ix = ix.increment();
508        while cur_ix != ix {
509            cur_ix = match self.node(cur_ix.get()) {
510                Node::Spacer { first_in_prev, .. } => {
511                    first_in_prev.expect("spacer should have a first_in_prev link")
512                }
513                Node::Instance(_) => {
514                    self.commit(cur_ix);
515                    cur_ix.increment()
516                }
517            }
518        }
519    }
520
521    /// Given a node corresponding to the covering of a particular item $i$
522    /// with some option $o$, uncommits all the items $\neq i$ in $o$ back
523    /// into the list of items that need to be covered.
524    ///
525    /// This function is part of step C6 in Knuth's Algorithm C.
526    fn uncommit_items_of(&mut self, ix: InstIndex) {
527        // Uncommit the items cyclically, in the opposite order as `commit_items_of`.
528        // As in `Self::unhide`, we must use raw node indices in case we visit
529        // the first spacer.
530        let ix = ix.get();
531        let mut cur_ix = ix - 1;
532        while cur_ix != ix {
533            cur_ix = match self.node(cur_ix) {
534                Node::Spacer { last_in_next, .. } => last_in_next
535                    .expect("spacer should have a last_in_next link")
536                    .get(),
537                Node::Instance(_) => {
538                    // SAFETY: `node` is not the first spacer.
539                    self.uncommit(unsafe { InstIndex::new_unchecked(cur_ix) });
540                    cur_ix - 1
541                }
542            }
543        }
544    }
545}
546
547impl<'i, I: Eq, C: Eq + Copy> crate::Solver<'i, I, C> for Solver<'i, I, C> {
548    fn new(primary: &'i [I], secondary: &'i [I]) -> Self {
549        // Construct the horizontal list.
550        let n_1 = primary.len();
551        let n = n_1 + secondary.len();
552        let last_primary_ix = ItemIndex::new(n_1);
553        let primary_head = Item::header(last_primary_ix, ItemIndex::new(1));
554        let first_secondary_ix = last_primary_ix.increment();
555        let last_secondary_ix = ItemIndex::new(if secondary.is_empty() { n + 1 } else { n });
556        let secondary_head = Item::header(last_secondary_ix, first_secondary_ix);
557        let items = primary
558            .iter()
559            .chain(secondary)
560            .enumerate()
561            .map(|(prev_ix, label)| {
562                let cur_ix = ItemIndex::new(prev_ix + 1);
563                // Ensure that no item appears twice in the horizontal list, but
564                // only check this invariant in debug mode: We don't require `T`
565                // to be bound by `Ord` nor `Hash`, so this operation needs $O(N)$
566                // steps per item.
567                debug_assert!(
568                    // We cannot use `primary[..prev_ix].contains(label)` since
569                    // `prev_ix` is out of bounds when the current item is
570                    // secondary.
571                    !primary.iter().take(prev_ix).any(|o| o == label),
572                    "item at index {cur_ix:?} is already in the primary item list"
573                );
574                debug_assert!(
575                    !secondary[..prev_ix.saturating_sub(n_1)].contains(label),
576                    "item at index {cur_ix:?} is already in the secondary item list"
577                );
578                // SAFETY: `cur_ix` does not refer to the header.
579                Item::new(label, cur_ix.decrement(), cur_ix.increment())
580            });
581        let mut items: Vec<Item<'i, I>> = iter::once(primary_head)
582            .chain(items)
583            .chain(iter::once(secondary_head))
584            .collect();
585        // Only the primary items appear in the active list:
586        if !secondary.is_empty() {
587            // 1. Link the first secondary item to the secondary header.
588            items[n_1 + 1].left = ItemIndex::new(n + 1);
589            // `items[n].right` is already $n_1+1$ by construction.
590        }
591        // 2. Link the last primary item to the primary header.
592        items[n_1].right = PRIMARY_HEADER;
593        Self {
594            items,
595            // Create the node arena, and insert the first spacer.
596            nodes: vec![Node::Spacer {
597                first_in_prev: None,
598                last_in_next: None,
599            }],
600            pointers: Vec::new(),
601        }
602    }
603
604    fn add_option<P, S>(&mut self, primary: P, secondary: S)
605    where
606        P: AsRef<[I]>,
607        S: AsRef<[(I, Option<C>)]>,
608    {
609        let primary = primary.as_ref();
610        let secondary = secondary.as_ref();
611        // We will create one item instance per item in `primary` and `secondary`,
612        // followed by a trailing spacer node.
613        self.nodes.reserve(primary.len() + secondary.len() + 1);
614        let first_inst_ix = InstIndex::new(self.nodes.len());
615        let mut inst_ix = first_inst_ix;
616        for (ix, label) in primary.iter().enumerate() {
617            // Fail if an item label appears more than once in the option.
618            debug_assert!(
619                !primary[..ix].contains(label),
620                "primary item at index {ix} can only appear once in the option"
621            );
622            let item_ix = self.find_item(label).unwrap_or_else(|| {
623                panic!("primary item at index {ix} must be in the problem's item list");
624            });
625            // Append the new node to the vertical list of `item`.
626            self.append_inst(item_ix, inst_ix, None);
627            inst_ix = inst_ix.increment();
628        }
629        for (ix, inst) in secondary.iter().enumerate() {
630            // Fail if an item label appears more than one in the option.
631            let (label, color) = inst;
632            debug_assert!(
633                !primary.contains(label) && !secondary[..ix].contains(inst),
634                "secondary item at index {ix} can only appear once in the option"
635            );
636            let item_ix = self.find_item(label).unwrap_or_else(|| {
637                panic!("secondary item at index {ix} must be in the problem's item list");
638            });
639            // Append the new node to the vertical list of `item`.
640            self.append_inst(item_ix, inst_ix, *color);
641            inst_ix = inst_ix.increment();
642        }
643        assert_ne!(first_inst_ix, inst_ix, "option must have at least one item");
644        // Link the previous spacer to the last node in the option.
645        // The first spacer cannot be referenced directly; see `InstIndex`.
646        let prev_spacer = &mut self.nodes[first_inst_ix.get() - 1];
647        if let Node::Spacer { last_in_next, .. } = prev_spacer {
648            *last_in_next = inst_ix.decrement();
649        } else {
650            panic!("the record before the first node should be a spacer");
651        }
652        // Create the next spacer, and link it to the first node in the option.
653        self.nodes.push(Node::Spacer {
654            first_in_prev: Some(first_inst_ix),
655            last_in_next: None,
656        })
657    }
658
659    fn solve<F>(mut self, mut visit: F)
660    where
661        F: FnMut(Solution<'_, 'i, I, C, Self>) -> ControlFlow<()>,
662    {
663        // The heuristic function used in step C3 to choose an active item
664        // for branching. Knuth found that selecting an item whose vertical
665        // list is of minimum length often works well in practice; this strategy
666        // is called the "minimum remaining values" (MRV) heuristic. See
667        // Section 7.2.2.3 in [_The Art of Computer Programming_, Pre-Fascicle 7A]
668        // for numerous empirical results on standard exact cover problems.
669        let branch_heuristic = |i: &Item<'i, I>| i.len;
670        'outer: loop {
671            // Try to cover as many items as possible, without backtracking.
672            loop {
673                // C3: Select an item $i$ that needs to be covered.
674                if let Some(item_ix) = self.choose_item(branch_heuristic) {
675                    // C4: Cover item $i$ using the first action option $x_l$
676                    //     in its vertical list.
677                    let item = self.item(item_ix);
678                    if let Some(inst_ix) = item.first_option {
679                        self.cover(item_ix);
680                        self.pointers.push(inst_ix);
681                        // C5: Commit the items $\neq i$ in the option that
682                        //     contains $x_l$, cyclically from left to right.
683                        self.commit_items_of(inst_ix);
684                    } else {
685                        // C7: There are no options left to cover $i$; backtrack.
686                        // Optimization: we only cover an item once we know that
687                        // there is an active option that contains it. Therefore
688                        // the original step in Knuth's algorithm translates to
689                        // a no-op here.
690                        break;
691                    }
692                } else {
693                    // C2: All primary items have been covered. Visit the solution
694                    //     given by the nodes in `self.pointers` and leave the
695                    //     current recursion level.
696                    if self.is_valid_solution() {
697                        let control_flow = visit(Solution {
698                            solver: &mut self,
699                            level: 0,
700                            _phantom: PhantomData,
701                        });
702                        if control_flow.is_break() {
703                            // The user needs no more solutions.
704                            return;
705                        }
706                    }
707                    break;
708                }
709            }
710            // C8: Leave the current recursion level $l$ until we find an item
711            //     that can be covered.
712            while let Some(inst_ix) = self.pointers.pop() {
713                // C6: In order to cover $i$ (the item associated with $x_l$)
714                //     using another option, we need to undo the covering done
715                //     by operation C5 above. This amounts to uncommiting the
716                //     items $\neq i$ in the option that contains $x_l$,
717                //     cyclically from right to left.
718                self.uncommit_items_of(inst_ix);
719                // Also set the current pointer to the next active option
720                // in the vertical list for $i$, if any.
721                let inst = self.instance(inst_ix);
722                if let Some(below_ix) = inst.below {
723                    self.pointers.push(below_ix);
724                    // C5: Try to uncommit item $i$ with this new option and
725                    //     go back to C2.
726                    self.commit_items_of(below_ix);
727                    continue 'outer;
728                } else {
729                    // C5, C7: We have tried all options for $i$; backtrack.
730                    self.uncover(inst.item);
731                }
732            }
733            // C8: We have explored the entire search tree; terminate.
734            return;
735        }
736    }
737}
738
739impl<'i, I: Eq, C> Solver<'i, I, C> {
740    /// Returns whether [`solve`] should visit the current solution.
741    ///
742    /// A solution is valid only if the (secondary) items in the purely
743    /// secondary options are already covered by options containing at least
744    /// one primary item. This happens if the vertical lists of all active
745    /// nonpurified secondary items are empty, which in turn occurs as
746    /// the result of [`hide`] operations during the commiting of items
747    /// $\ne i$ in step C5 of Algorithm C.
748    ///
749    /// [`solve`]: crate::Solver::solve
750    /// [`hide`]: Self::hide
751    fn is_valid_solution(&self) -> bool {
752        // Traverse the horizontal list of active secondary items, checking
753        // that their corresponding vertical lists are empty (and thus have
754        // already been dealt with by primary covering). Exercise 7.2.2.1–19
755        // of TAOCP explains this procedure in more detail.
756        let secondary_head_ix = ItemIndex::new(self.items.len() - 1);
757        let secondary_head = self.secondary_head();
758        let first_active_secondary_ix = secondary_head.right;
759        let mut cur_ix = first_active_secondary_ix;
760        while cur_ix != secondary_head_ix {
761            let item = self.item(cur_ix);
762            if !item.purified && item.len > 0 {
763                return false; // Skip the solution.
764            }
765            cur_ix = item.right;
766        }
767        // We have traversed the entire list of active secondary items;
768        // visit the solution.
769        true
770    }
771
772    // Accessor methods.
773
774    /// Returns a reference to the item at the given position.
775    ///
776    /// # Panics
777    ///
778    /// This function panics if the index is out of bounds.
779    fn item(&self, ix: ItemIndex) -> &Item<'i, I> {
780        &self.items[ix.get()]
781    }
782
783    /// Returns a mutable reference to the item at the given position.
784    ///
785    /// # Panics
786    ///
787    /// This function panics if the index is out of bounds.
788    fn item_mut(&mut self, ix: ItemIndex) -> &mut Item<'i, I> {
789        &mut self.items[ix.get()]
790    }
791
792    /// Returns a reference to the head of the list of primary items
793    /// that need to be covered.
794    fn primary_head(&self) -> &Item<'i, I> {
795        self.item(PRIMARY_HEADER)
796    }
797
798    /// Returns a reference to the head of the list of secondary items
799    /// that can still be covered.
800    fn secondary_head(&self) -> &Item<'i, I> {
801        self.items.last().unwrap()
802    }
803
804    /// Search through the table of primary and secondary items to find
805    /// the node with the given label, if any.
806    fn find_item(&self, label: &I) -> Option<ItemIndex> {
807        // Remember that `T` is not bound by `Ord` nor `Hash`, so we need to
808        // search through the whole `items` list in order to find the node
809        // corresponding to `label` in the worst case.
810        let mut nodes = self.items.iter().enumerate();
811        // Skip the heads of the active item lists.
812        nodes.next();
813        nodes.next_back();
814        nodes
815            .find(|(_, item)| {
816                // SAFETY: `item` is not a header, so its label is initialized.
817                let other_label = unsafe { item.label.assume_init() };
818                label == other_label
819            })
820            .map(|(ix, _)| ItemIndex::new(ix))
821    }
822
823    /// Returns a reference to the node at the given position.
824    ///
825    /// # Panics
826    ///
827    /// This function panics if the index is out of bounds.
828    fn node(&self, ix: NodeIndex) -> &Node<C> {
829        &self.nodes[ix]
830    }
831
832    /// Returns a reference to the item instance at the given position.
833    ///
834    /// # Panics
835    ///
836    /// This function panics if the index is out of bounds, or if the node
837    /// referenced is a [spacer](`Node::Spacer`) rather than an instance.
838    fn instance(&self, ix: InstIndex) -> &Instance<C> {
839        if let Node::Instance(inst) = self.node(ix.get()) {
840            inst
841        } else {
842            panic!("node at index {ix:?} is not an item instance")
843        }
844    }
845
846    /// Returns a mutable reference to the item instance at the given position.
847    ///
848    /// # Panics
849    ///
850    /// This function panics if the index is out of bounds, or if the node
851    /// referenced is a [spacer](`Node::Spacer`) rather than an instance.
852    fn instance_mut(&mut self, ix: InstIndex) -> &mut Instance<C> {
853        if let Node::Instance(inst) = &mut self.nodes[ix.get()] {
854            inst
855        } else {
856            panic!("node at index {ix:?} is not an item instance")
857        }
858    }
859}
860
861impl<'i, I: Eq, C: Copy> crate::private::Solver<'i, I, C> for Solver<'i, I, C> {
862    fn pointer(&self, level: usize) -> Option<InstIndex> {
863        self.pointers.get(level).copied()
864    }
865
866    fn level(&self) -> usize {
867        self.pointers.len()
868    }
869
870    fn option_of(&self, ix: InstIndex, result: &mut Vec<(&'i I, Option<C>)>) {
871        result.clear();
872        let mut cur_ix = ix;
873        loop {
874            cur_ix = match self.node(cur_ix.get()) {
875                Node::Spacer { first_in_prev, .. } => {
876                    first_in_prev.expect("spacer should have a first_in_prev link")
877                }
878                Node::Instance(Instance { item, color, .. }) => {
879                    let item = self.item(*item);
880                    // SAFETY: `item` is a nonheader node in the item table,
881                    // so its label is initialized.
882                    let label = unsafe { item.label.assume_init() };
883                    result.push((label, *color));
884                    cur_ix.increment()
885                }
886            };
887            if cur_ix == ix {
888                break;
889            }
890        }
891    }
892}
893
894#[cfg(test)]
895mod tests {
896    use super::*;
897    use crate::indices::ItemIndex;
898    use crate::{DlSolver, Solver};
899    use std::fmt::Debug;
900    use std::ops::ControlFlow;
901
902    fn assert_eq_item<'i, I>(item: &Item<'i, I>, label: &I, left: ItemIndex, right: ItemIndex)
903    where
904        I: Debug + Eq,
905    {
906        assert_eq!(unsafe { item.label.assume_init() }, label);
907        assert_eq!(item.left, left);
908        assert_eq!(item.right, right);
909    }
910
911    #[test]
912    fn new_exact_cover_with_primary_only() {
913        let solver: DlSolver<u8, ()> = DlSolver::new(&[1, 2, 3], &[]);
914        assert_eq!(solver.items.len(), 5); // 2 headers + 3 items
915
916        let primary_header = solver.primary_head();
917        assert_eq!(primary_header.left, ItemIndex::new(3));
918        assert_eq!(primary_header.right, ItemIndex::new(1));
919
920        let one = solver.item(ItemIndex::new(1));
921        assert_eq_item(one, &1, PRIMARY_HEADER, ItemIndex::new(2));
922
923        let two = solver.item(ItemIndex::new(2));
924        assert_eq_item(two, &2, ItemIndex::new(1), ItemIndex::new(3));
925
926        let three = solver.item(ItemIndex::new(3));
927        assert_eq_item(three, &3, ItemIndex::new(2), PRIMARY_HEADER);
928
929        let secondary_header = solver.secondary_head();
930        assert_eq!(secondary_header.left, ItemIndex::new(4));
931        assert_eq!(secondary_header.right, ItemIndex::new(4));
932    }
933
934    #[test]
935    fn new_exact_cover_with_primary_and_secondary() {
936        let solver: DlSolver<char, ()> = DlSolver::new(&['a', 'b', 'c'], &['d', 'e', 'f']);
937        assert_eq!(solver.items.len(), 8); // 2 headers + 6 items
938
939        // The left link of this header points to the last primary item,
940        // because the secondary items do not appear in the active list
941        // of primary items.
942        let primary_header = solver.primary_head();
943        assert_eq!(primary_header.left, ItemIndex::new(3));
944        assert_eq!(primary_header.right, ItemIndex::new(1));
945
946        let a = solver.item(ItemIndex::new(1));
947        assert_eq_item(a, &'a', PRIMARY_HEADER, ItemIndex::new(2));
948
949        let b = solver.item(ItemIndex::new(2));
950        assert_eq_item(b, &'b', ItemIndex::new(1), ItemIndex::new(3));
951
952        // The right link of the last primary item points to the primary header.
953        let c = solver.item(ItemIndex::new(3));
954        assert_eq_item(c, &'c', ItemIndex::new(2), PRIMARY_HEADER);
955
956        // The left link of the first secondary item points to the secondary header.
957        let d = solver.item(ItemIndex::new(4));
958        assert_eq_item(d, &'d', ItemIndex::new(7), ItemIndex::new(5));
959
960        let e = solver.item(ItemIndex::new(5));
961        assert_eq_item(e, &'e', ItemIndex::new(4), ItemIndex::new(6));
962
963        // The right link of the last secondary item points to the secondary header.
964        let a = solver.item(ItemIndex::new(6));
965        assert_eq_item(a, &'f', ItemIndex::new(5), ItemIndex::new(7));
966
967        // The right link of this header points to the first secondary item.
968        let secondary_header = solver.secondary_head();
969        assert_eq!(secondary_header.left, ItemIndex::new(6));
970        assert_eq!(secondary_header.right, ItemIndex::new(4));
971    }
972
973    #[test]
974    fn new_exact_cover_without_primary_has_no_solution() {
975        // We implement the "second death" method to support purely secondary
976        // options; see `DlSolver::is_valid_solution` for details. Of course,
977        // Algorithm C visits no solutions because they are all purely secondary.
978        let mut solver = DlSolver::new(&[], &[1, 2, 3]);
979        solver.add_option([], [(1, None)]);
980        solver.add_option([], [(2, Some('A'))]);
981        solver.solve(|_| panic!("found solution with purely secondary option"));
982    }
983
984    #[test]
985    fn find_item() {
986        let solver: DlSolver<char, ()> = DlSolver::new(&['a', 'b'], &['c', 'd']);
987        assert_eq!(solver.find_item(&'a'), Some(ItemIndex::new(1)));
988        assert_eq!(solver.find_item(&'b'), Some(ItemIndex::new(2)));
989        assert_eq!(solver.find_item(&'c'), Some(ItemIndex::new(3)));
990        assert_eq!(solver.find_item(&'d'), Some(ItemIndex::new(4)));
991    }
992
993    #[test]
994    fn toy_problem_with_colors() {
995        // Example problem taken from Section 7.2.2.1 of TAOCP.
996        let primary = ['p', 'q', 'r'];
997        let secondary = ['x', 'y'];
998        let mut solver = DlSolver::new(&primary, &secondary);
999        solver.add_option(['p', 'q'], [('x', None), ('y', Some('A'))]);
1000        solver.add_option(['p', 'r'], [('x', Some('A')), ('y', None)]);
1001        solver.add_option(['p'], [('x', Some('B'))]);
1002        solver.add_option(['q'], [('x', Some('A'))]);
1003        solver.add_option(['r'], [('y', Some('B'))]);
1004
1005        let mut count = 0;
1006        let mut option = Vec::new();
1007        solver.solve(|mut solution| {
1008            assert!(solution.next(&mut option));
1009            assert_eq!(option, &[(&'q', None), (&'x', Some('A'))]);
1010            assert!(solution.next(&mut option));
1011            assert_eq!(
1012                option,
1013                &[(&'p', None), (&'r', None), (&'x', Some('A')), (&'y', None)]
1014            );
1015            assert!(!solution.next(&mut option));
1016            count += 1;
1017            ControlFlow::Continue(())
1018        });
1019        assert_eq!(count, 1);
1020    }
1021
1022    #[test]
1023    fn another_toy_problem_with_colors() {
1024        // Example taken from Program DLX2 by D. E. Knuth, available
1025        // at https://cs.stanford.edu/~knuth/programs.html.
1026        let primary = ['a', 'b', 'c'];
1027        let secondary = ['x', 'y'];
1028        let mut solver = DlSolver::new(&primary, &secondary);
1029        solver.add_option(['a', 'b'], [('x', Some(0)), ('y', Some(0))]);
1030        solver.add_option(['a', 'c'], [('x', Some(1)), ('y', Some(1))]);
1031        solver.add_option([], [('x', Some(0)), ('y', Some(1))]);
1032        solver.add_option(['b'], [('x', Some(1))]);
1033        solver.add_option(['c'], [('y', Some(1))]);
1034
1035        let mut count = 0;
1036        let mut option = Vec::new();
1037        solver.solve(|mut solution| {
1038            assert!(solution.next(&mut option));
1039            assert_eq!(
1040                option,
1041                &[(&'a', None), (&'c', None), (&'x', Some(1)), (&'y', Some(1))]
1042            );
1043            assert!(solution.next(&mut option));
1044            assert_eq!(option, &[(&'b', None), (&'x', Some(1))]);
1045            count += 1;
1046            ControlFlow::Continue(())
1047        });
1048        assert_eq!(count, 1);
1049    }
1050
1051    #[test]
1052    fn skips_solutions_with_purely_secondary_options() {
1053        // A colored variant of the problem from Exercise 7.2.2.1–19 of TAOCP.
1054        let primary = ['a', 'b'];
1055        let secondary = ['c', 'd', 'e', 'f', 'g'];
1056        let mut solver: DlSolver<char, ()> = DlSolver::new(&primary, &secondary);
1057        solver.add_option([], [('c', None), ('e', None)]); // purely secondary
1058        solver.add_option(['a'], [('d', None), ('g', None)]);
1059        solver.add_option(['b'], [('c', None), ('f', None)]);
1060        solver.add_option(['a'], [('d', None), ('f', None)]);
1061        solver.add_option(['b'], [('g', None)]);
1062        solver.add_option([], [('d', None), ('e', None), ('g', None)]); // purely secondary
1063
1064        let mut count = 0;
1065        let mut option = Vec::new();
1066        solver.solve(|mut solution| {
1067            assert!(solution.next(&mut option));
1068            assert_eq!(option, &[(&'a', None), (&'d', None), (&'g', None)]);
1069            assert!(solution.next(&mut option));
1070            assert_eq!(option, &[(&'b', None), (&'c', None), (&'f', None)]);
1071            assert!(!solution.next(&mut option));
1072            count += 1;
1073            ControlFlow::Continue(())
1074        });
1075        // Note that 'a d g', 'b g' is not a valid solution, because it does not
1076        // intersect the purely secondary option 'c e'.
1077        assert_eq!(count, 1);
1078    }
1079
1080    #[test]
1081    fn solutions_intersect_item_sets_of_purely_secondary_options() {
1082        let mut solver = DlSolver::new(&['a'], &['b', 'c']);
1083        solver.add_option(['a'], [('b', Some(1)), ('c', Some(2))]);
1084        solver.add_option([], [('b', Some(1))]);
1085        solver.add_option([], [('b', Some(2))]);
1086        solver.add_option([], [('c', Some(3))]);
1087        // The three purely secondary options do not appear in any solution. The
1088        // only solution to the XCC problem consists of option $'a\\;b:1\\;c:2'$
1089        // alone, because it intersects every purely secondary option.
1090        let mut count = 0;
1091        let mut option = Vec::new();
1092        solver.solve(|mut solution| {
1093            assert!(solution.next(&mut option));
1094            assert_eq!(option, &[(&'a', None), (&'b', Some(1)), (&'c', Some(2))]);
1095            assert!(!solution.next(&mut option));
1096            count += 1;
1097            ControlFlow::Continue(())
1098        });
1099        assert_eq!(count, 1);
1100    }
1101
1102    #[test]
1103    fn skips_solutions_that_do_not_intersect_a_purely_secondary_option() {
1104        let mut solver = DlSolver::new(&['a'], &['b']);
1105        solver.add_option(['a'], []);
1106        solver.add_option([], [('b', Some(1))]);
1107        // Even if option $'a'$ covers every primary item exactly once, it does
1108        // not intersect the purely secondary option $'b'$.
1109        solver.solve(|_| panic!("found solution with purely secondary option"));
1110    }
1111}