[Aptitude-svn-commit] r3969 - in branches/aptitude-0.3/aptitude: .
src/generic tests
Daniel Burrows
dburrows at costa.debian.org
Fri Aug 26 23:42:44 UTC 2005
Author: dburrows
Date: Fri Aug 26 23:42:37 2005
New Revision: 3969
Added:
branches/aptitude-0.3/aptitude/src/generic/immset.h
branches/aptitude-0.3/aptitude/tests/test_wtree.cc
Modified:
branches/aptitude-0.3/aptitude/ChangeLog
branches/aptitude-0.3/aptitude/tests/Makefile.am
Log:
Add a core framework for immutable functional balanced tree objects.
Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog (original)
+++ branches/aptitude-0.3/aptitude/ChangeLog Fri Aug 26 23:42:37 2005
@@ -1,5 +1,13 @@
2005-08-26 Daniel Burrows <dburrows at debian.org>
+ * src/generic/wtree.h, tests/Makefile.am, tests/test_wtree.h:
+
+ Write a basic framework for weight-based immutable trees. These
+ trees explicitly share memory between different versions of a
+ tree (for instance, if a tree is updated by adding one element)
+ and I hope they'll help with some of the time and space issues
+ in the problem resolver.
+
* src/generic/problemresolver/problemresolver.h:
Fix the generation of conflicts from forbiddings: if a -> b c
Added: branches/aptitude-0.3/aptitude/src/generic/immset.h
==============================================================================
--- (empty file)
+++ branches/aptitude-0.3/aptitude/src/generic/immset.h Fri Aug 26 23:42:37 2005
@@ -0,0 +1,498 @@
+// immset.h -*-c++-*-
+//
+// Copyright (C) 2005 Daniel Burrows
+//
+// 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; see the file COPYING. If not, write to
+// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+//
+// This file defines a class to represent immutable sets. These sets
+// behave like std::set, except that their contents cannot be changed;
+// they have no erase() or insert() operators, and only
+// const_iterators. This restriction allows immutable sets to be
+// implemented in a way that makes both copying and creating a new set
+// by adding an element very efficient (O(1) and O(lg n) respectively;
+// with std::set these are both O(n)).
+
+#ifndef IMMSET_H
+#define IMMSET_H
+
+#include <assert.h>
+
+namespace imm
+{
+ /** A generic node in a weighted tree a described in "Implementing
+ * Sets Efficiently In A Functional Language". The tree invariant
+ * is that the tree is not "too unbalanced"; in this case, that no
+ * subtree is more than 4 times larger than its sibling. (note
+ * that w must be at least 3.75; see the paper for details)
+ *
+ * A brief note on choice of algorithm: while a rbtree is more
+ * common and may be slightly more efficient, it is a much trickier
+ * data structure to implement, and some operations (like set-union
+ * and set-intersection) may be difficult to implement efficiently.
+ *
+ * The weighted-tree data structure is reasonably efficient and
+ * much more straightforward to implement correctly.
+ */
+ template<typename Val, const int w = 4>
+ class wtree_node
+ {
+ class impl
+ {
+ typedef unsigned int size_type;
+
+ /** Left and right children (may be \b null). */
+ wtree_node left, right;
+
+ /** The size of the subtree rooted at this node. */
+ size_type size;
+
+ /** The reference-count of this node. */
+ mutable int refcount;
+
+ /** The enclosed value. */
+ Val val;
+ public:
+ impl(const Val &_val,
+ const wtree_node &_left, const wtree_node &_right)
+ :left(_left), right(_right), size(_left.size()+_right.size()+1),
+ refcount(1), val(_val)
+ {
+ }
+
+ /** \return the left child. */
+ wtree_node getLeftChild() const
+ {
+ return left;
+ }
+
+ /** \return the right child. */
+ wtree_node getRightChild() const
+ {
+ return right;
+ }
+
+ size_type getSize() const
+ {
+ return size;
+ }
+
+ const Val &getVal() const
+ {
+ return val;
+ }
+
+ void incref() const
+ {
+ assert(refcount>0);
+
+ ++refcount;
+ }
+
+ void decref() const
+ {
+ assert(refcount>0);
+ --refcount;
+
+ if(refcount == 0)
+ delete this;
+ }
+ };
+
+ const impl *realNode;
+
+ public:
+ typedef unsigned int size_type;
+
+ wtree_node(const Val &val,
+ const wtree_node &left, const wtree_node &right)
+ :realNode(new impl(val, left, right))
+ {
+ }
+
+ wtree_node(const Val &val)
+ :realNode(new impl(val, wtree_node(), wtree_node()))
+ {
+ }
+
+ /** Takes possession of the given reference (the caller should
+ * incref() if necessary).
+ */
+ wtree_node(const impl *_realNode)
+ :realNode(_realNode)
+ {
+ }
+
+ wtree_node()
+ :realNode(NULL)
+ {
+ }
+
+ wtree_node(const wtree_node &other)
+ :realNode(other.realNode)
+ {
+ if(realNode != NULL)
+ realNode->incref();
+ }
+
+ ~wtree_node()
+ {
+ if(realNode != NULL)
+ realNode->decref();
+ }
+
+ wtree_node getLeft() const
+ {
+ return realNode->getLeftChild();
+ }
+
+ wtree_node getRight() const
+ {
+ return realNode->getRightChild();
+ }
+
+ size_type size() const
+ {
+ if(realNode == NULL)
+ return 0;
+ else
+ return realNode->getSize();
+ }
+
+ wtree_node &operator=(const wtree_node &other)
+ {
+ if(other.realNode != NULL)
+ other.realNode->incref();
+ if(realNode != NULL)
+ realNode->decref();
+
+ realNode = other.realNode;
+
+ return *this;
+ }
+
+ /** Pointer comparison. */
+ bool operator==(const wtree_node &other) const
+ {
+ return realNode == other.realNode;
+ }
+
+ /** Pointer comparison. */
+ bool operator!=(const wtree_node &other) const
+ {
+ return realNode != other.realNode;
+ }
+
+ bool empty() const
+ {
+ return realNode == NULL;
+ }
+
+ bool isValid() const
+ {
+ return realNode != NULL;
+ }
+
+ /** \return the value of this node. */
+ const Val &getVal() const
+ {
+ return realNode->getVal();
+ }
+
+ // Tree management routines:
+
+ /** Perform a 'left rotate' operation on this node. Requires
+ * that the right child is not \b null.
+ */
+ wtree_node left_rotate_single() const
+ {
+ wtree_node right = getRight(), left = getLeft();
+ wtree_node right_left = right.getLeft(), right_right = right.getRight();
+
+ return wtree_node(right.getVal(),
+ wtree_node(getVal(), left, right_left),
+ right_right);
+ }
+
+ /** Perform a 'right rotate' operation on this node. Requires
+ * that the left child is not \b null.
+ */
+ wtree_node right_rotate_single() const
+ {
+ wtree_node right = getRight(), left = getLeft();
+ wtree_node left_left = left.getLeft(), left_right = left.getRight();
+
+ return wtree_node(left.getVal(),
+ left_left,
+ wtree_node(getVal(), left_right, right));
+ }
+
+ /** Perform a 'double left rotate' operation on this node.
+ * Requires that the right child not be \b null and that
+ * its left child is also not \b null.
+ */
+ wtree_node left_rotate_double() const
+ {
+ wtree_node right = getRight(), left = getLeft();
+ wtree_node right_right = right.getRight(), right_left = left.getLeft();
+ wtree_node right_left_left = right_left.getLeft();
+ wtree_node right_left_right = right_left.getRight();
+
+ return wtree_node(right_left.getVal(),
+ wtree_node(getVal(), left, right_left_left),
+ wtree_node(right.getVal(), right_left_right,
+ right_right));
+ }
+
+ /** Perform a 'double right rotate' operation on this node.
+ * Requires that the left child not be \b null and that
+ * its right child is also not \b null.
+ */
+ wtree_node right_rotate_double() const
+ {
+ wtree_node right = getRight(), left = getLeft();
+ wtree_node left_right = right.getRight(), left_left = left.getLeft();
+ wtree_node left_right_left = left_right.getLeft();
+ wtree_node left_right_right = left_right.getRight();
+
+ return wtree_node(left_right.getVal(),
+ wtree_node(left.getVal(), left_left, left_right_left),
+ wtree_node(getVal(), left_right_right, right));
+ }
+
+
+
+
+ /** Rebalance the given subtree, returning a new subtree
+ * reference. The subtree should be unbalanced by at most one
+ * element (think inserting or deleting a single element).
+ * Equivalent to T' in the paper.
+ */
+ wtree_node rebalance() const
+ {
+ wtree_node left = getLeft(), right = getRight();
+ size_type left_size = left.size();
+ size_type right_size = right.size();
+
+ // If one subtree is empty and the other contains at most one
+ // element, there is nothing to do.
+ if(left_size + right_size < 2)
+ return *this;
+ else if(left_size * w < right_size)
+ {
+ // The right tree is too heavy. As explained in the paper,
+ // a single rotation is guaranteed sufficient if its outer
+ // (right) child is larger than its inner child; otherwise a
+ // double rotation is guaranteed sufficient.
+ wtree_node right_left = right.getLeft(), right_right = right.getRight();
+ if(right_left.size() < right_right.size())
+ return left_rotate_single();
+ else
+ return left_rotate_double();
+ }
+ else if(right_size * w < left_size)
+ {
+ // Dual of above.
+ wtree_node left_left = left.getLeft(), left_right = left.getRight();
+ if(left_right.size() < left_left.size())
+ return right_rotate_single();
+ else
+ return right_rotate_double();
+ }
+ else
+ // Nothing to do; the tree is already balanced.
+ return *this;
+ }
+ };
+
+ /** An entire weighted tree.
+ */
+ template<typename Val, typename Compare = std::less<Val>, int w = 4 >
+ class wtree
+ {
+ public:
+ typedef Val value_type;
+ typedef wtree_node<Val, w> node;
+ typedef typename node::size_type size_type;
+
+ Compare value_compare;
+
+ /** An iterator over a wtree. Note that the lack of parent
+ * pointers (necessary to allow full memory sharing) forces the
+ * iterator class to allocate! I don't recommend using iterators
+ * except for the purpose of spitting the tree out for debugging.
+ */
+ class const_iterator
+ {
+ typedef std::pair<bool, node > path_entry;
+
+ std::vector<path_entry> path;
+ public:
+ const_iterator()
+ {
+ }
+
+ const_iterator(const node &root)
+ {
+ if(root.isValid())
+ {
+ path.push_back(path_entry(false, root));
+ while(path.back().second.getLeft().isValid())
+ path.push_back(path_entry(false, path.back().second.getLeft()));
+ }
+ }
+
+ const Val &operator*() const
+ {
+ return path.back().second.getVal();
+ }
+
+ const Val *operator->() const
+ {
+ return &path.back().second.getVal();
+ }
+
+ const_iterator &operator=(const const_iterator &other)
+ {
+ path = other.path;
+ return *this;
+ }
+
+ bool operator==(const const_iterator &other) const
+ {
+ return path == other.path;
+ }
+
+ bool operator!=(const const_iterator &other) const
+ {
+ return path != other.path;
+ }
+
+ const_iterator &operator++()
+ {
+ assert(!path.empty());
+
+ if(!path.back().first)
+ {
+ path.back().first = true;
+ path.push_back(path_entry(false, path.back().second.getRight()));
+
+ while(!path.empty() && path.back().second.isValid())
+ path.push_back(path_entry(false, path.back().second.getLeft()));
+ }
+
+ // Clear out any invalid nodes or nodes that already fired.
+ while(!path.empty() && (!path.back().second.isValid() || path.back().first))
+ path.pop_back();
+
+ // Now either the path is empty, or we're at a node that's
+ // valid and hasn't fired yet (meaning we just finished
+ // descending into its left subtree, so we should stop and
+ // visit it).
+
+ return *this;
+ }
+ };
+ private:
+ /** Root of the tree. */
+ node root;
+
+ /** An 'insert' based on tree nodes. Returns a rebalanced tree
+ * containing the given information; doesn't update existing
+ * nodes with equivalent keys.
+ */
+ node insert(const node &n, const Val &x) const
+ {
+ if(n.empty()) return node(x, node(), node());
+ else if(value_compare(x, n.getVal()))
+ return node(n.getVal(),
+ insert(n.getLeft(), x),
+ n.getRight()).rebalance();
+ else if(value_compare(n.getVal(), x))
+ return node(n.getVal(),
+ n.getLeft(),
+ insert(n.getRight(), x)).rebalance();
+ else
+ return n;
+ }
+
+ /** An 'insert' based on tree nodes. Returns a rebalanced tree
+ * containing the given information, updating existing nodes with
+ * equivalent keys.
+ */
+ node insertUpdate(const node &n, const Val &x) const
+ {
+ if(n.empty()) return node(x, node(), node());
+ else if(value_compare(x, n.getVal()))
+ return node(n.getVal(),
+ insert(n.getLeft(), x),
+ n.getRight()).rebalance();
+ else if(value_compare(n.getVal(), x))
+ return node(n.getVal(),
+ n.getLeft(),
+ insert(n.getRight(), x)).rebalance();
+ else
+ return node(x, n.getLeft(), n.getRight());
+ }
+
+ wtree(const node &n)
+ :root(n)
+ {
+ }
+ public:
+ /** Construct an empty tree. */
+ wtree()
+ {
+ }
+
+ /** Insert an element into a tree, returning a new tree. This is
+ * not a member function, to stress that it does NOT modify the
+ * old tree; instead, it returns a new tree containing the
+ * element in addition to the elements of the old tree.
+ */
+ static wtree insert(const wtree &old, const Val &x)
+ {
+ return old.insert(old.root, x);
+ }
+
+ /** Like insert, but updates existing equivalent elements. */
+ static wtree insertUpdate(const wtree &old, const Val &x)
+ {
+ return old.insertUpdate(old.root, x);
+ }
+
+ const_iterator begin() const
+ {
+ return const_iterator(root);
+ }
+
+ const_iterator end() const
+ {
+ return const_iterator();
+ }
+
+ size_type size() const
+ {
+ return root.size();
+ }
+
+ int empty() const
+ {
+ return root.empty();
+ }
+ };
+};
+
+#endif
Modified: branches/aptitude-0.3/aptitude/tests/Makefile.am
==============================================================================
--- branches/aptitude-0.3/aptitude/tests/Makefile.am (original)
+++ branches/aptitude-0.3/aptitude/tests/Makefile.am Fri Aug 26 23:42:37 2005
@@ -9,9 +9,12 @@
EXTRADIST = data
+# Note: test_apt_universe is not built by default because it takes way
+# too long. Of course, ideally this would be done in a less ad-hoc
+# way...
test_SOURCES = \
main.cc \
- test_apt_universe.cc \
test_misc.cc \
test_resolver.cc \
- test_tags.cc
\ No newline at end of file
+ test_tags.cc \
+ test_wtree.cc \
Added: branches/aptitude-0.3/aptitude/tests/test_wtree.cc
==============================================================================
--- (empty file)
+++ branches/aptitude-0.3/aptitude/tests/test_wtree.cc Fri Aug 26 23:42:37 2005
@@ -0,0 +1,264 @@
+// test_wtree.cc
+//
+// Copyright (C) 2005 Daniel Burrows
+//
+// 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; see the file COPYING. If not, write to
+// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <src/generic/immset.h>
+
+using imm::wtree;
+using imm::wtree_node;
+
+class WTreeTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(WTreeTest);
+
+ CPPUNIT_TEST(testNodeRotate);
+ CPPUNIT_TEST(testInsertIterate);
+
+ CPPUNIT_TEST_SUITE_END();
+public:
+ typedef wtree_node<int> int_node;
+
+ // Simple test of the rotate facilities.
+ void testNodeRotate()
+ {
+ int_node a(5,
+ int_node(3, int_node(2), int_node(4)),
+ int_node(7, int_node(6), int_node(8)));
+
+ int_node a_left = a.getLeft();
+ int_node a_right = a.getRight();
+
+ CPPUNIT_ASSERT(a_left.isValid());
+ CPPUNIT_ASSERT(a_right.isValid());
+
+ int_node a_left_left = a_left.getLeft();
+ int_node a_left_right = a_left.getRight();
+ int_node a_right_left = a_right.getLeft();
+ int_node a_right_right = a_right.getRight();
+
+ CPPUNIT_ASSERT(a_left_left.isValid());
+ CPPUNIT_ASSERT(a_left_right.isValid());
+ CPPUNIT_ASSERT(a_right_left.isValid());
+ CPPUNIT_ASSERT(a_right_right.isValid());
+
+ CPPUNIT_ASSERT(a_left_left.getVal() == 2);
+ CPPUNIT_ASSERT(a_left.getVal() == 3);
+ CPPUNIT_ASSERT(a_left_right.getVal() == 4);
+ CPPUNIT_ASSERT(a.getVal() == 5);
+ CPPUNIT_ASSERT(a_right_left.getVal() == 6);
+ CPPUNIT_ASSERT(a_right.getVal() == 7);
+ CPPUNIT_ASSERT(a_right_right.getVal() == 8);
+
+
+
+ int_node b = a.left_rotate_single();
+
+ CPPUNIT_ASSERT(b.size() == a.size());
+
+ CPPUNIT_ASSERT(b.isValid());
+
+ int_node b_left = b.getLeft();
+ int_node b_right = b.getRight();
+
+ CPPUNIT_ASSERT(b_left.isValid());
+ CPPUNIT_ASSERT(b_right.isValid());
+
+ int_node b_left_left = b_left.getLeft();
+ int_node b_left_right = b_left.getRight();
+
+ CPPUNIT_ASSERT(b_left_left.isValid());
+ CPPUNIT_ASSERT(b_left_right.isValid());
+
+ int_node b_left_left_left = b_left_left.getLeft();
+ int_node b_left_left_right = b_left_left.getRight();
+
+ CPPUNIT_ASSERT(b_left_left_left.isValid());
+ CPPUNIT_ASSERT(b_left_left_right.isValid());
+
+
+ CPPUNIT_ASSERT(b_left_left_left.getVal() == 2);
+ CPPUNIT_ASSERT(b_left_left.getVal() == 3);
+ CPPUNIT_ASSERT(b_left_left_right.getVal() == 4);
+ CPPUNIT_ASSERT(b_left.getVal() == 5);
+ CPPUNIT_ASSERT(b_left_right.getVal() == 6);
+ CPPUNIT_ASSERT(b.getVal() == 7);
+ CPPUNIT_ASSERT(b_right.getVal() == 8);
+ }
+
+ void testInsertIterate()
+ {
+ wtree<int> t;
+
+ CPPUNIT_ASSERT(t.begin() == t.end());
+ CPPUNIT_ASSERT(t.empty());
+ CPPUNIT_ASSERT(t.size() == 0);
+
+
+
+ t = wtree<int>::insert(t, 5);
+
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 1);
+
+ wtree<int>::const_iterator i = t.begin();
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+ CPPUNIT_ASSERT(i == t.end());
+
+
+
+
+ t = wtree<int>::insert(t, 3);
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 2);
+
+ i = t.begin();
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 3);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i == t.end());
+
+
+
+ t = wtree<int>::insert(t, 8);
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 3);
+
+ i = t.begin();
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 3);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 8);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i == t.end());
+
+
+
+ t = wtree<int>::insert(t, 7);
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 4);
+
+ i = t.begin();
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 3);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 7);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 8);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i == t.end());
+
+
+ t = wtree<int>::insert(t, 3);
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 4);
+
+ i = t.begin();
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 3);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 7);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 8);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i == t.end());
+
+
+
+ t = wtree<int>::insertUpdate(t, 3);
+ CPPUNIT_ASSERT(!t.empty());
+ CPPUNIT_ASSERT(t.size() == 4);
+
+ i = t.begin();
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 3);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 5);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 7);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i != t.end());
+ CPPUNIT_ASSERT(*i == 8);
+
+ ++i;
+
+ CPPUNIT_ASSERT(i == t.end());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(WTreeTest);
More information about the Aptitude-svn-commit
mailing list