dlx: nicer iteration

This commit is contained in:
Eric Mertens 2022-11-20 11:15:15 -08:00
parent 50640e6adb
commit 234165c7f3
2 changed files with 99 additions and 56 deletions

View File

@ -7,30 +7,9 @@
namespace dlx { namespace dlx {
struct Cell;
class Dlx final { class Dlx final {
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 ColAdd(std::size_t row_id) -> Cell*;
static Cell* ColNew();
};
std::vector<Cell*> ctab_; std::vector<Cell*> ctab_;
std::vector<Cell*> rtab_; std::vector<Cell*> rtab_;
Cell* root_; Cell* root_;

View File

@ -6,16 +6,76 @@
namespace dlx { namespace dlx {
auto Dlx::Cell::LR_self() -> void { L = R = this; } template <Cell* Cell::* direction> struct CellView;
auto Dlx::Cell::UD_self() -> void { U = D = this; }
auto Dlx::Cell::LR_delete() -> void { L->R = R; R->L = L; }
auto Dlx::Cell::UD_delete() -> void { U->D = D; D->U = U; }
auto Dlx::Cell::UD_restore() -> void { U->D = D->U = this; }
auto Dlx::Cell::LR_restore() -> void { L->R = R->L = this; }
auto Dlx::Cell::LR_insert(Cell* k) -> void { L = k->L; R = k; k->L = k->L->R = this; }
auto Dlx::Cell::UD_insert(Cell* k) -> void { U = k->U, D = k, k->U = k->U->D = this; }
auto Dlx::Cell::ColNew() -> Dlx::Cell* 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*
{ {
auto c = new Cell; auto c = new Cell;
c->UD_self(); c->UD_self();
@ -23,7 +83,7 @@ auto Dlx::Cell::ColNew() -> Dlx::Cell*
return c; return c;
} }
auto Dlx::Cell::ColAdd(std::size_t row_id) -> Cell* auto Cell::ColAdd(std::size_t row_id) -> Cell*
{ {
auto n = new Cell; auto n = new Cell;
n->n = row_id; n->n = row_id;
@ -33,19 +93,19 @@ auto Dlx::Cell::ColAdd(std::size_t row_id) -> Cell*
return n; return n;
} }
auto Dlx::Cell::CoverCol() -> void { auto Cell::CoverCol() -> void {
LR_delete(); LR_delete();
for (auto i = D; i != this; i = i->D) { for (auto const i : downwards()) {
for (auto j = i->R; j != i; j = j->R) { for (auto const j : i->rightwards()) {
j->UD_delete(); j->UD_delete();
j->c->s--; j->c->s--;
} }
} }
} }
auto Dlx::Cell::UncoverCol() -> void { auto Cell::UncoverCol() -> void {
for (auto i = U; i != this; i = i->U) { for (auto const i : upwards()) {
for (auto j = i->L; j != i; j = j->L) { for (auto const j : i->leftwards()) {
j->c->s++; j->c->s++;
j->UD_restore(); j->UD_restore();
} }
@ -53,7 +113,10 @@ auto Dlx::Cell::UncoverCol() -> void {
LR_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() { Dlx::Dlx() {
root_ = Cell::ColNew(); root_ = Cell::ColNew();
@ -63,16 +126,18 @@ Dlx::Dlx() {
Dlx::~Dlx() { Dlx::~Dlx() {
for (auto const row : rtab_) { for (auto const row : rtab_) {
if (row) { if (row) {
Cell* next; auto cursor = row->R;
for (auto cursor = row->R; cursor != row; cursor = next) { // manual iteration because we have to save next _before_ deleting cursor
next = cursor->R; while (cursor != row) {
delete cursor; auto tmp = cursor;
cursor = cursor->R;
delete tmp;
} }
delete row; delete row;
} }
} }
for (auto const col : ctab_) { for (auto const col : ctab_) {
if (col) { delete col; } delete col;
} }
delete root_; delete root_;
} }
@ -118,13 +183,12 @@ auto Dlx::MarkOptional(std::size_t col) -> void {
c->LR_self(); c->LR_self();
} }
auto Dlx::Set(std::size_t row, std::size_t col) -> void { auto Dlx::Set(std::size_t row, std::size_t col) -> void {
AllocRow(row); AllocRow(row);
AllocCol(col); AllocCol(col);
auto c = ctab_[col]; auto c = ctab_[col];
auto & r = rtab_[row]; auto & r = rtab_[row];
if (!r) { if (!r) {
r = c->ColAdd(row); r = c->ColAdd(row);
r->LR_self(); r->LR_self();
@ -134,7 +198,7 @@ auto Dlx::Set(std::size_t row, std::size_t col) -> void {
// Ignore duplicates. // Ignore duplicates.
if (r->c->n == col) return; if (r->c->n == col) return;
for (auto cursor = r->R; cursor != r; cursor = cursor->R) { for (auto const cursor : r->rightwards()) {
if (cursor->c->n == col) return; if (cursor->c->n == col) return;
} }
@ -145,7 +209,7 @@ auto Dlx::Set(std::size_t row, std::size_t col) -> void {
auto Dlx::PickRow(std::size_t i) -> void { auto Dlx::PickRow(std::size_t i) -> void {
if (auto r = rtab_.at(i)) { if (auto r = rtab_.at(i)) {
r->c->CoverCol(); r->c->CoverCol();
for (auto j = r->R; j != r; j = j->R) { for (auto const j : r->rightwards()) {
j->c->CoverCol(); j->c->CoverCol();
} }
} }
@ -155,7 +219,7 @@ auto Dlx::RemoveRow(std::size_t i) -> void {
if (auto & r = rtab_.at(i)) { if (auto & r = rtab_.at(i)) {
r->UD_delete(); r->UD_delete();
r->c->s--; r->c->s--;
for (auto j = r->R; j != r; j = j->R) { for (auto const j : r->rightwards()) {
j->UD_delete(); j->UD_delete();
j->c->s--; j->c->s--;
} }
@ -177,10 +241,10 @@ auto Dlx::Solve(
} }
auto s = std::numeric_limits<std::size_t>::max(); auto s = std::numeric_limits<std::size_t>::max();
for (auto i = root_->R; i != root_; i = i->R) { for (auto const i : root_->rightwards()) {
if (i->s < s) { if (i->s < s) {
s = (c = i)->s; s = (c = i)->s;
if (s == 0) { if (s == 0) {
stuck_cb(c->n); stuck_cb(c->n);
return; return;
@ -189,14 +253,14 @@ auto Dlx::Solve(
} }
c->CoverCol(); c->CoverCol();
for (auto r = c->D; r != c; r = r->D) { for (auto const r : c->downwards()) {
try_cb(c->n, s, r->n); try_cb(c->n, s, r->n);
for (auto j = r->R; j != r; j = j->R) { for (auto const j : r->rightwards()) {
j->c->CoverCol(); j->c->CoverCol();
} }
self(self); self(self);
undo_cb(); undo_cb();
for (auto j = r->L; j != r; j=j->L) { for (auto const j : r->leftwards()) {
j->c->UncoverCol(); j->c->UncoverCol();
} }
} }