// See http://en.wikipedia.org/wiki/Dancing_Links. #include "dlx.hpp" #include namespace dlx { template struct CellView; struct Cell { Cell *U, *D, *L, *R; std::size_t n; union { Cell* c; std::size_t s; }; auto LR_self() -> void; auto UD_self() -> void; auto LR_delete() -> void; auto UD_delete() -> void; auto UD_restore() -> void; auto LR_restore() -> void; auto LR_insert(Cell* k) -> void; auto UD_insert(Cell* k) -> void; auto CoverCol() -> void; auto UncoverCol() -> void; auto rightwards() -> CellView<&Cell::R>; auto leftwards() -> CellView<&Cell::L>; auto upwards() -> CellView<&Cell::U>; auto downwards() -> CellView<&Cell::D>; auto ColAdd(std::size_t row_id) -> Cell*; static Cell* ColNew(); }; template struct CellView { Cell* cell; auto operator==(CellView const&) const -> bool = default; auto begin() const -> CellView; auto end() const -> CellView; auto operator*() -> Cell*; auto operator++() -> CellView&; }; template auto CellView::begin() const -> CellView { return {cell->*direction}; } template auto CellView::end() const -> CellView { return {cell}; } template auto CellView::operator*() -> Cell* { return cell; } template auto CellView::operator++() -> CellView& { cell = cell->*direction; return *this; } auto Cell::LR_self() -> void { L = R = this; } auto Cell::UD_self() -> void { U = D = this; } auto Cell::LR_delete() -> void { L->R = R; R->L = L; } auto Cell::UD_delete() -> void { U->D = D; D->U = U; } auto Cell::UD_restore() -> void { U->D = D->U = this; } auto Cell::LR_restore() -> void { L->R = R->L = this; } auto Cell::LR_insert(Cell* k) -> void { L = k->L; R = k; k->L = k->L->R = this; } auto Cell::UD_insert(Cell* k) -> void { U = k->U, D = k, k->U = k->U->D = this; } auto Cell::ColNew() -> Cell* { auto c = new Cell; c->UD_self(); c->s = 0; return c; } auto Cell::ColAdd(std::size_t row_id) -> Cell* { auto n = new Cell; n->n = row_id; n->c = this; this->s++; n->UD_insert(this); return n; } auto Cell::CoverCol() -> void { LR_delete(); for (auto const i : downwards()) { for (auto const j : i->rightwards()) { j->UD_delete(); j->c->s--; } } } auto Cell::UncoverCol() -> void { for (auto const i : upwards()) { for (auto const j : i->leftwards()) { j->c->s++; j->UD_restore(); } } LR_restore(); } auto Cell::rightwards() -> CellView<&Cell::R> { return CellView<&Cell::R>{this}; } auto Cell::leftwards() -> CellView<&Cell::L> { return CellView<&Cell::L>{this}; } auto Cell::upwards() -> CellView<&Cell::U> { return CellView<&Cell::U>{this}; } auto Cell::downwards() -> CellView<&Cell::D> { return CellView<&Cell::D>{this}; } Dlx::Dlx() { root_ = Cell::ColNew(); root_->LR_self(); } Dlx::~Dlx() { for (auto const row : rtab_) { if (row) { auto cursor = row->R; // manual iteration because we have to save next _before_ deleting cursor while (cursor != row) { auto tmp = cursor; cursor = cursor->R; delete tmp; } delete row; } } for (auto const col : ctab_) { delete col; } delete root_; } Dlx::Dlx(Dlx && dlx) : ctab_{}, rtab_{}, root_{} { std::swap(ctab_, dlx.ctab_); std::swap(rtab_, dlx.rtab_); std::swap(root_, dlx.root_); } auto Dlx::operator=(Dlx && dlx) -> Dlx& { if (this != &dlx) { this->~Dlx(); new (this) Dlx(std::move(dlx)); } return *this; } auto Dlx::Rows() const -> std::size_t { return rtab_.size(); } auto Dlx::Cols() const -> std::size_t { return ctab_.size(); } auto Dlx::AllocCol(std::size_t n) -> void { while(Cols() <= n) { auto c = Cell::ColNew(); c->LR_insert(root_); c->n = Cols(); ctab_.push_back(c); } } auto Dlx::AllocRow(std::size_t n) -> void { while (Rows() <= n) { rtab_.push_back(nullptr); } } auto Dlx::MarkOptional(std::size_t col) -> void { AllocCol(col); auto c = ctab_[col]; // Prevent undeletion by self-linking. c->LR_delete(); c->LR_self(); } auto Dlx::Set(std::size_t row, std::size_t col) -> void { AllocRow(row); AllocCol(col); auto c = ctab_[col]; auto & r = rtab_[row]; if (!r) { r = c->ColAdd(row); r->LR_self(); return; } // Ignore duplicates. if (r->c->n == col) return; for (auto const cursor : r->rightwards()) { if (cursor->c->n == col) return; } // Otherwise insert at end of LR list. c->ColAdd(row)->LR_insert(r); } auto Dlx::PickRow(std::size_t i) -> void { if (auto r = rtab_.at(i)) { r->c->CoverCol(); for (auto const j : r->rightwards()) { j->c->CoverCol(); } } } auto Dlx::RemoveRow(std::size_t i) -> void { if (auto & r = rtab_.at(i)) { r->UD_delete(); r->c->s--; for (auto const j : r->rightwards()) { j->UD_delete(); j->c->s--; } r = nullptr; } } auto Dlx::Solve( std::function try_cb, std::function undo_cb, std::function found_cb, std::function stuck_cb) -> void { auto const recurse = [&](auto const& self) -> bool { auto c = root_->R; if (c == root_) { return found_cb(); } auto s = c->s; for (auto const i : root_->rightwards()) { if (i->s < s) { s = (c = i)->s; } } if (s == 0) { return stuck_cb(c->n); } c->CoverCol(); for (auto const r : c->downwards()) { if (try_cb(c->n, s, r->n)) [[unlikely]] return true; for (auto const j : r->rightwards()) { j->c->CoverCol(); } if (self(self)) [[unlikely]] return true; if (undo_cb()) [[unlikely]] return true; for (auto const j : r->leftwards()) { j->c->UncoverCol(); } } c->UncoverCol(); return false; }; recurse(recurse); } } // namespace