diff --git a/dlx/include/dlx.hpp b/dlx/include/dlx.hpp index 24876a2..15c81e0 100644 --- a/dlx/include/dlx.hpp +++ b/dlx/include/dlx.hpp @@ -7,7 +7,7 @@ namespace dlx { -class Dlx { +class Dlx final { struct Cell { Cell *U, *D, *L, *R; @@ -17,42 +17,18 @@ class Dlx { std::size_t s; }; - auto LR_self() -> void { L = R = this; } - auto UD_self() -> void { U = D = this; } - auto LR_delete() -> void { L->R = R; R->L = L; } - auto UD_delete() -> void { U->D = D; D->U = U; } - auto UD_restore() -> void { U->D = D->U = this; } - auto LR_restore() -> void { L->R = R->L = this; } - auto LR_insert(Cell* k) -> void { L = k->L; R = k; k->L = k->L->R = this; } - auto UD_insert(Cell* k) -> void { U = k->U, D = k, k->U = k->U->D = this; } - - static Cell* ColNew() { - auto c = new Cell; - c->UD_self(); - c->s = 0; - return c; - } - - auto CoverCol() -> void { - LR_delete(); - for (auto i = D; i != this; i = i->D) { - for (auto j = i->R; j != i; j = j->R) { - j->UD_delete(); - j->c->s--; - } - } - } - - auto UncoverCol() -> void { - for (auto i = U; i != this; i = i->U) { - for (auto j = i->L; j != i; j = j->L) { - j->c->s++; - j->UD_restore(); - } - } - LR_restore(); - } - + 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 ctab_; @@ -68,6 +44,12 @@ public: Dlx(); ~Dlx(); + Dlx(Dlx const&) = delete; + auto operator=(Dlx const&) -> Dlx& = delete; + + Dlx(Dlx &&); + auto operator=(Dlx &&) -> Dlx&; + // Returns number of rows. auto Rows() const -> std::size_t; @@ -82,14 +64,14 @@ public: // but it still must respect the constraints it entails. auto MarkOptional(std::size_t col) -> void; - // Removes a row from consideration. Returns 0 on success, -1 otherwise. + // Removes a row from consideration. // Should only be called after all dlx_set() calls. - auto RemoveRow(std::size_t row) -> int; + auto RemoveRow(std::size_t row) -> void; - // Picks a row to be part of the solution. Returns 0 on success, -1 otherwise. - // Should only be called after all dlx_set() calls and dlx_remove_row() calls. + // Picks a row to be part of the solution. + // Should only be called after all Set() calls and RemoveRows() calls. // TODO: Check the row can be legally chosen. - auto PickRow(std::size_t row) -> int; + auto PickRow(std::size_t row) -> void; auto Solve( std::function try_cb, diff --git a/dlx/src/dlx.cpp b/dlx/src/dlx.cpp index 651afcd..a764eda 100644 --- a/dlx/src/dlx.cpp +++ b/dlx/src/dlx.cpp @@ -4,11 +4,56 @@ #include -using namespace dlx; +namespace dlx { + +auto Dlx::Cell::LR_self() -> void { L = R = this; } +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* +{ + auto c = new Cell; + c->UD_self(); + c->s = 0; + return c; +} + +auto Dlx::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 Dlx::Cell::CoverCol() -> void { + LR_delete(); + for (auto i = D; i != this; i = i->D) { + for (auto j = i->R; j != i; j = j->R) { + j->UD_delete(); + j->c->s--; + } + } +} + +auto Dlx::Cell::UncoverCol() -> void { + for (auto i = U; i != this; i = i->U) { + for (auto j = i->L; j != i; j = j->L) { + j->c->s++; + j->UD_restore(); + } + } + LR_restore(); +} -#define F(i,n) for(int i = 0; i < n; i++) -#define C(i,n,dir) for(cell_ptr i = (n)->dir; i != n; i = i->dir) Dlx::Dlx() { root_ = Cell::ColNew(); @@ -32,28 +77,39 @@ Dlx::~Dlx() { 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::AddCol() -> void { - auto c = Cell::ColNew(); - c->LR_insert(root_); - c->n = Cols(); - ctab_.push_back(c); -} - -auto Dlx::AddRow() -> void { - rtab_.push_back(nullptr); -} auto Dlx::AllocCol(std::size_t n) -> void { - while(Cols() <= n) AddCol(); + 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) AddRow(); + while (Rows() <= n) { + rtab_.push_back(nullptr); + } } - auto Dlx::MarkOptional(std::size_t col) -> void { AllocCol(col); auto c = ctab_[col]; @@ -64,29 +120,13 @@ auto Dlx::MarkOptional(std::size_t col) -> void { auto Dlx::Set(std::size_t row, std::size_t col) -> void { - // We don't bother sorting. DLX works fine with jumbled rows and columns. - // We just have to watch out for duplicates. (Actually, I think the DLX code - // works even with duplicates, though it would be inefficient.) - // - // For a given column, the UD list is ordered in the order that dlx_set() - // is called, not by row number. Similarly for a given row and its LR list. AllocRow(row); AllocCol(col); - auto c = ctab_[col]; - - auto const new1 = [&]() -> Cell* { - auto n = new Cell; - n->n = row; - n->c = c; - c->s++; - n->UD_insert(c); - return n; - }; - + auto c = ctab_[col]; auto & r = rtab_[row]; if (!r) { - r = new1(); + r = c->ColAdd(row); r->LR_self(); return; } @@ -99,32 +139,28 @@ auto Dlx::Set(std::size_t row, std::size_t col) -> void { } // Otherwise insert at end of LR list. - new1()->LR_insert(r); + c->ColAdd(row)->LR_insert(r); } -auto Dlx::PickRow(std::size_t i) -> int { - auto r = rtab_.at(i); - if (!r) return 0; // Empty row. - r->c->CoverCol(); - for (auto j = r->R; j != r; j = j->R) { - j->c->CoverCol(); +auto Dlx::PickRow(std::size_t i) -> void { + if (auto r = rtab_.at(i)) { + r->c->CoverCol(); + for (auto j = r->R; j != r; j = j->R) { + j->c->CoverCol(); + } } - - return 0; } - -auto Dlx::RemoveRow(std::size_t i) -> int { - auto & r = rtab_.at(i); - if (!r) return 0; // Empty row. - r->UD_delete(); - r->c->s--; - for (auto j = r->R; j != r; j = j->R) { - j->UD_delete(); - j->c->s--; +auto Dlx::RemoveRow(std::size_t i) -> void { + if (auto & r = rtab_.at(i)) { + r->UD_delete(); + r->c->s--; + for (auto j = r->R; j != r; j = j->R) { + j->UD_delete(); + j->c->s--; + } + r = nullptr; } - r = nullptr; - return 0; } auto Dlx::Solve( @@ -140,17 +176,17 @@ auto Dlx::Solve( return; } - auto s = std::numeric_limits::max(); // S-heuristic: choose first most-constrained column. + auto s = std::numeric_limits::max(); for (auto i = root_->R; i != root_; i = i->R) { if (i->s < s) { s = (c = i)->s; + + if (s == 0) { + stuck_cb(c->n); + return; + } } } - - if (!s) { - stuck_cb(c->n); - return; - } c->CoverCol(); for (auto r = c->D; r != c; r = r->D) { @@ -168,3 +204,5 @@ auto Dlx::Solve( }; recurse(recurse); } + +} // namespace \ No newline at end of file