diff --git a/src/lib/diff/index-table.hpp b/src/lib/diff/index-table.hpp index ebac9df18..1c57f5b2f 100644 --- a/src/lib/diff/index-table.hpp +++ b/src/lib/diff/index-table.hpp @@ -39,39 +39,18 @@ #define LIB_DIFF_INDEX_TABLE_H -#include "lib/diff/list-diff.hpp" -#include "lib/format-string.hpp" +#include "lib/error.hpp" -#include #include -#include +#include namespace lib { namespace diff{ -#include "lib/test/run.hpp" -#include "lib/diff/list-diff.hpp" -#include "lib/iter-adapter.hpp" -#include "lib/itertools.hpp" -#include "lib/util.hpp" - -#include -#include -#include - -using lib::append_all; -using util::unConst; -using util::isnil; -using std::string; -using std::vector; -using std::move; -using std::swap; - - -namespace lib { -namespace diff{ - //######################### + using std::vector; + using std::map; + template class IndexTable @@ -111,172 +90,5 @@ namespace diff{ - - - template - class DiffDetector - : boost::noncopyable - { - using Val = typename SEQ::value_type; - using Idx = IndexTable; - - Idx refIdx_; - SEQ const& currentData_; - - - using DiffStep = typename ListDiffLanguage::DiffStep; - - /** @internal state frame for diff detection and generation. */ - class DiffFrame; - - - - - public: - explicit - DiffDetector(SEQ const& refSeq) - : refIdx_(refSeq) - , currentData_(refSeq) - { } - - - /** does the current state of the underlying sequence differ - * from the state embodied into the last reference snapshot taken? - * @remarks will possibly evaluate and iterate the whole sequence - */ - bool - isChanged() const - { - UNIMPLEMENTED("change detection"); - } - - - /** Diff is a iterator to yield a sequence of DiffStep elements */ - using Diff = lib::IterStateWrapper; - - /** Diff generation core operation. - * Take a snapshot of the \em current state of the underlying sequence - * and establish a frame to find the differences to the previously captured - * \em old state. This possible difference evaluation is embodied into a #Diff - * iterator and handed over to the client, while the snapshot of the current state - * becomes the new reference point from now on. - * @return iterator to yield a sequence of DiffStep tokens, which describe the changes - * between the previous reference state and the current state of the sequence. - * @note takes a new snapshot to supersede the old one, i.e. updates the DiffDetector. - * @warning the returned iterator retains a reference to the current (new) snapshot. - * Any concurrent modification leads to undefined behaviour. You must not - * invoke #pullUpdate while another client still explores the result - * of an old evaluation. - */ - Diff - pullUpdate() - { - Idx mark (currentData_); - swap (mark, refIdx_); // mark now refers to old reference point - return Diff(DiffFrame(refIdx_, move(mark))); - } - }; - - - - - /** - * A diff generation process is built on top of an "old" reference point - * and a "new" state of the underlying sequence. Within this reference frame, - * an demand-driven evaluation of the differences is handed out to the client - * as an iterator. While consuming this evaluation process, both the old and - * the new version of the sequence will be traversed once. In case of re-orderings, - * a nested forward lookup similar to insertion sort will look for matches in the - * old sequence, rendering the whole evaluation quadratic in worst-case. - */ - template - class DiffDetector::DiffFrame - { - Idx old_; - Idx* new_; - size_t oldHead_=0, - newHead_=0; - - static ListDiffLanguage token; - - DiffStep currentStep_; - - - public: - DiffFrame(Idx& current, Idx&& refPoint) - : old_(refPoint) - , new_(¤t) - , currentStep_(establishNextState()) - { } - - - /* === Iteration control API for IterStateWrapper== */ - - friend bool - checkPoint (DiffFrame const& frame) - { - return token.NIL != frame.currentStep_; - } - - friend DiffStep& - yield (DiffFrame const& frame) - { - REQUIRE (checkPoint (frame)); - return unConst(frame).currentStep_; - } - - friend void - iterNext (DiffFrame & frame) - { - frame.establishNextState(); - } - - private: - DiffStep - establishNextState() - { - if (canPick()) - { - consumeOld(); - return token.pick (consumeNew()); - } - if (canDelete()) - return token.del (consumeOld()); - if (canInsert()) - return token.ins (consumeNew()); - if (needFetch()) - return token.find (consumeNew()); - if (obsoleted()) - return token.skip (consumeOld()); - - return token.NIL; - } - - bool hasOld() const { return oldHead_ < old_.size(); } - bool hasNew() const { return newHead_ < new_->size(); } - bool canPick() const { return hasOld() && hasNew() && oldElm()==newElm(); } - bool canDelete() const { return hasOld() && !new_->contains(oldElm()); } - bool canInsert() const { return hasNew() && !old_.contains(newElm()); } - bool needFetch() const { return hasNew() && oldHead_ < old_.pos(newElm()); } - bool obsoleted() const { return hasOld() && newHead_ > new_->pos(oldElm()); } - - Val const& oldElm() const { return old_.getElement (oldHead_); } - Val const& newElm() const { return new_->getElement (newHead_); } - Val const& consumeOld() { return old_.getElement (oldHead_++); } - Val const& consumeNew() { return new_->getElement (newHead_++); } - }; - - - /** allocate static storage for the diff language token builder functions */ - template - ListDiffLanguage::Val> DiffDetector::DiffFrame::token; - - - //######################### - - - - - }} // namespace lib::diff #endif /*LIB_DIFF_INDEX_TABLE_H*/ diff --git a/src/lib/diff/list-diff-detector.hpp b/src/lib/diff/list-diff-detector.hpp index 16e2f9ddd..b22c60fe8 100644 --- a/src/lib/diff/list-diff-detector.hpp +++ b/src/lib/diff/list-diff-detector.hpp @@ -59,76 +59,19 @@ #include "lib/diff/list-diff.hpp" -#include "lib/format-string.hpp" - -#include -#include -#include - - -namespace lib { -namespace diff{ - -#include "lib/test/run.hpp" -#include "lib/diff/list-diff.hpp" +#include "lib/diff/index-table.hpp" #include "lib/iter-adapter.hpp" -#include "lib/itertools.hpp" -#include "lib/util.hpp" #include -#include -#include - -using lib::append_all; -using util::unConst; -using util::isnil; -using std::string; -using std::vector; -using std::move; -using std::swap; +#include namespace lib { namespace diff{ - //######################### - - template - class IndexTable - { - public: - template - IndexTable(SEQ const& seq) - { - UNIMPLEMENTED("build index"); - } - - size_t - size() const - { - UNIMPLEMENTED("sequence size"); - } - - VAL const& - getElement (size_t i) const - { - UNIMPLEMENTED("indexed value access"); - } - - bool - contains (VAL const& elm) const - { - return size() == pos(elm); - } - - size_t - pos (VAL const& elm) const - { - UNIMPLEMENTED("index lookup"); - } - }; - - + using util::unConst; + using std::move; + using std::swap; @@ -291,11 +234,6 @@ namespace diff{ ListDiffLanguage::Val> DiffDetector::DiffFrame::token; - //######################### - - - - }} // namespace lib::diff diff --git a/tests/15library.tests b/tests/15library.tests index b29bd4a94..f8f2644d9 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -72,6 +72,11 @@ return: 0 END +TEST "Diff: lookup table helper" DiffIndexTable_test < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/diff/index-table.hpp" + +#include +#include +#include + +using std::string; +using std::vector; +using std::swap; + + +namespace lib { +namespace diff{ +namespace test{ + + using lumiera::error::LUMIERA_ERROR_LOGIC; + + namespace {//Test fixture.... + + using DataSeq = vector; + using Index = IndexTable; + + #define TOK(id) id(STRINGIFY(id)) + + string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5); + string TOK(b1), TOK(b2), TOK(b3), TOK(b4); + + + }//(End)Test fixture + + + + + + + + + + /*****************************************************************************//** + * @test Cover a simple lookup table helper, used to support diff generation. + * The IndexTable takes a snapshot of generic sequence data on construction. + * After that, it is immutable and supports lookup by element as key and + * membership check. + * + * @see IndexTable + * @see DiffListApplication_test + */ + class DiffIndexTable_test : public Test + { + + virtual void + run (Arg) + { + simpleUsage(); + verifySnapshot(); + duplicateDetection(); + copy_and_move(); + } + + + void + simpleUsage() + { + DataSeq data({a5,a2,a1,a4,a3}); + Index idx(data); + + CHECK (5 == idx.size()); + + CHECK (idx.contains(a1)); + CHECK (idx.contains(a2)); + CHECK (idx.contains(a3)); + CHECK (idx.contains(a4)); + CHECK (idx.contains(a5)); + + CHECK (!idx.contains(b1)); + CHECK (!idx.contains(b2)); + + + CHECK (a5 == idx.getElement(0)); + CHECK (a2 == idx.getElement(1)); + CHECK (a1 == idx.getElement(2)); + CHECK (a4 == idx.getElement(3)); + CHECK (a3 == idx.getElement(4)); + + + CHECK (0 == idx.pos(a5)); + CHECK (1 == idx.pos(a2)); + CHECK (2 == idx.pos(a1)); + CHECK (3 == idx.pos(a4)); + CHECK (4 == idx.pos(a3)); + } + + + void + verifySnapshot() + { + DataSeq data({a5,a2,a1,a4,a3}); + Index idx(data); + + data.clear(); + data.push_back(b1); + + CHECK (5 == idx.size()); + + CHECK (idx.contains(a1)); + CHECK (idx.contains(a2)); + CHECK (idx.contains(a3)); + CHECK (idx.contains(a4)); + CHECK (idx.contains(a5)); + + CHECK (!idx.contains(b1)); + CHECK (!idx.contains(b2)); + + CHECK (0 == idx.pos(a5)); + CHECK (1 == idx.pos(a2)); + CHECK (2 == idx.pos(a1)); + CHECK (3 == idx.pos(a4)); + CHECK (4 == idx.pos(a3)); + } + + + void + duplicateDetection() + { + DataSeq data({a5,a2,a1,a4,a2,a3}); + + VERIFY_ERROR(LOGIC, Index idx(data)); + } + + + void + copy_and_move() + { + DataSeq seqA({a5,a4,a1,a2,a3}); + DataSeq seqB({b4,b3,b2,b1}); + + Index idxA(seqA); + Index idxB(seqB); + CHECK (5 == idxA.size()); + CHECK (4 == idxB.size()); + + CHECK ( idxA.contains(a1)); + CHECK (!idxA.contains(b1)); + CHECK (!idxB.contains(a1)); + CHECK ( idxB.contains(b1)); + + swap (idxA, idxB); + + CHECK (!idxA.contains(a1)); + CHECK ( idxA.contains(b1)); + CHECK ( idxB.contains(a1)); + CHECK (!idxB.contains(b1)); + + idxA = idxB; + CHECK (4 == idxA.size()); + CHECK (4 == idxB.size()); + + CHECK (!idxA.contains(a1)); + CHECK ( idxA.contains(b1)); + CHECK (!idxB.contains(a1)); + CHECK ( idxB.contains(b1)); + } + }; + + + /** Register this test class... */ + LAUNCHER (DiffIndexTable_test, "unit common"); + + + +}}} // namespace lib::diff::test diff --git a/tests/library/diff-list-application-test.cpp b/tests/library/diff/diff-list-application-test.cpp similarity index 100% rename from tests/library/diff-list-application-test.cpp rename to tests/library/diff/diff-list-application-test.cpp diff --git a/tests/library/diff-list-generation-test.cpp b/tests/library/diff/diff-list-generation-test.cpp similarity index 100% rename from tests/library/diff-list-generation-test.cpp rename to tests/library/diff/diff-list-generation-test.cpp