aocpp/dlx/src/dlx.cpp

271 lines
5.9 KiB
C++
Raw Normal View History

2022-11-19 14:59:21 -08:00
// See http://en.wikipedia.org/wiki/Dancing_Links.
#include "dlx.hpp"
#include <limits>
2022-11-20 10:09:23 -08:00
namespace dlx {
2022-11-20 11:15:15 -08:00
template <Cell* Cell::* direction> 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 <Cell* Cell::* direction>
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 <Cell* Cell::* direction>
auto CellView<direction>::begin() const -> CellView { return {cell->*direction}; }
template <Cell* Cell::* direction>
auto CellView<direction>::end() const -> CellView { return {cell}; }
template <Cell* Cell::* direction>
auto CellView<direction>::operator*() -> Cell* { return cell; }
template <Cell* Cell::* direction>
auto CellView<direction>::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*
2022-11-20 10:09:23 -08:00
{
auto c = new Cell;
c->UD_self();
c->s = 0;
return c;
}
2022-11-20 11:15:15 -08:00
auto Cell::ColAdd(std::size_t row_id) -> Cell*
2022-11-20 10:09:23 -08:00
{
auto n = new Cell;
n->n = row_id;
n->c = this;
this->s++;
n->UD_insert(this);
return n;
}
2022-11-20 11:15:15 -08:00
auto Cell::CoverCol() -> void {
2022-11-20 10:09:23 -08:00
LR_delete();
2022-11-20 11:15:15 -08:00
for (auto const i : downwards()) {
for (auto const j : i->rightwards()) {
2022-11-20 10:09:23 -08:00
j->UD_delete();
j->c->s--;
}
}
}
2022-11-20 11:15:15 -08:00
auto Cell::UncoverCol() -> void {
for (auto const i : upwards()) {
for (auto const j : i->leftwards()) {
2022-11-20 10:09:23 -08:00
j->c->s++;
j->UD_restore();
}
}
LR_restore();
}
2022-11-19 14:59:21 -08:00
2022-11-20 11:15:15 -08:00
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}; }
2022-11-19 14:59:21 -08:00
Dlx::Dlx() {
root_ = Cell::ColNew();
root_->LR_self();
}
Dlx::~Dlx() {
for (auto const row : rtab_) {
if (row) {
2022-11-20 11:15:15 -08:00
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;
2022-11-19 14:59:21 -08:00
}
delete row;
}
}
for (auto const col : ctab_) {
2022-11-20 11:15:15 -08:00
delete col;
2022-11-19 14:59:21 -08:00
}
delete root_;
}
2022-11-20 10:09:23 -08:00
Dlx::Dlx(Dlx && dlx) : ctab_{}, rtab_{}, root_{}
{
std::swap(ctab_, dlx.ctab_);
std::swap(rtab_, dlx.rtab_);
std::swap(root_, dlx.root_);
2022-11-19 14:59:21 -08:00
}
2022-11-20 10:09:23 -08:00
auto Dlx::operator=(Dlx && dlx) -> Dlx& {
if (this != &dlx) {
this->~Dlx();
new (this) Dlx(std::move(dlx));
}
return *this;
2022-11-19 14:59:21 -08:00
}
2022-11-20 10:09:23 -08:00
auto Dlx::Rows() const -> std::size_t { return rtab_.size(); }
auto Dlx::Cols() const -> std::size_t { return ctab_.size(); }
2022-11-19 14:59:21 -08:00
auto Dlx::AllocCol(std::size_t n) -> void {
2022-11-20 10:09:23 -08:00
while(Cols() <= n) {
auto c = Cell::ColNew();
c->LR_insert(root_);
c->n = Cols();
ctab_.push_back(c);
}
2022-11-19 14:59:21 -08:00
}
auto Dlx::AllocRow(std::size_t n) -> void {
2022-11-20 10:09:23 -08:00
while (Rows() <= n) {
rtab_.push_back(nullptr);
}
2022-11-19 14:59:21 -08:00
}
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);
2022-11-20 11:15:15 -08:00
auto c = ctab_[col];
2022-11-19 14:59:21 -08:00
auto & r = rtab_[row];
2022-11-20 11:15:15 -08:00
2022-11-19 14:59:21 -08:00
if (!r) {
2022-11-20 10:09:23 -08:00
r = c->ColAdd(row);
2022-11-19 14:59:21 -08:00
r->LR_self();
return;
}
// Ignore duplicates.
if (r->c->n == col) return;
2022-11-20 11:15:15 -08:00
for (auto const cursor : r->rightwards()) {
2022-11-19 14:59:21 -08:00
if (cursor->c->n == col) return;
}
// Otherwise insert at end of LR list.
2022-11-20 10:09:23 -08:00
c->ColAdd(row)->LR_insert(r);
2022-11-19 14:59:21 -08:00
}
2022-11-20 10:09:23 -08:00
auto Dlx::PickRow(std::size_t i) -> void {
if (auto r = rtab_.at(i)) {
r->c->CoverCol();
2022-11-20 11:15:15 -08:00
for (auto const j : r->rightwards()) {
2022-11-20 10:09:23 -08:00
j->c->CoverCol();
}
2022-11-19 14:59:21 -08:00
}
}
2022-11-20 10:09:23 -08:00
auto Dlx::RemoveRow(std::size_t i) -> void {
if (auto & r = rtab_.at(i)) {
r->UD_delete();
r->c->s--;
2022-11-20 11:15:15 -08:00
for (auto const j : r->rightwards()) {
2022-11-20 10:09:23 -08:00
j->UD_delete();
j->c->s--;
}
r = nullptr;
2022-11-19 14:59:21 -08:00
}
}
auto Dlx::Solve(
2022-11-20 13:52:18 -08:00
std::function<bool(std::size_t, std::size_t, std::size_t)> try_cb,
std::function<bool()> undo_cb,
std::function<bool()> found_cb,
std::function<bool(std::size_t)> stuck_cb) -> void
2022-11-19 14:59:21 -08:00
{
2022-11-20 13:52:18 -08:00
auto const recurse = [&](auto const& self) -> bool {
2022-11-19 14:59:21 -08:00
auto c = root_->R;
if (c == root_) {
2022-11-20 13:52:18 -08:00
return found_cb();
2022-11-19 14:59:21 -08:00
}
2022-11-20 13:52:18 -08:00
auto s = c->s;
2022-11-20 11:15:15 -08:00
for (auto const i : root_->rightwards()) {
2022-11-19 14:59:21 -08:00
if (i->s < s) {
s = (c = i)->s;
}
}
2022-11-20 13:52:18 -08:00
if (s == 0) {
return stuck_cb(c->n);
}
2022-11-19 14:59:21 -08:00
c->CoverCol();
2022-11-20 11:15:15 -08:00
for (auto const r : c->downwards()) {
2022-11-20 13:52:18 -08:00
if (try_cb(c->n, s, r->n)) [[unlikely]] return true;
2022-11-20 11:15:15 -08:00
for (auto const j : r->rightwards()) {
2022-11-19 14:59:21 -08:00
j->c->CoverCol();
}
2022-11-20 13:52:18 -08:00
if (self(self)) [[unlikely]] return true;
if (undo_cb()) [[unlikely]] return true;
2022-11-20 11:15:15 -08:00
for (auto const j : r->leftwards()) {
2022-11-19 14:59:21 -08:00
j->c->UncoverCol();
}
}
c->UncoverCol();
2022-11-20 13:52:18 -08:00
return false;
2022-11-19 14:59:21 -08:00
};
recurse(recurse);
}
2022-11-20 10:09:23 -08:00
} // namespace