From 33443b3a13fd68c5ac6379aa9abb499e10df9e3f Mon Sep 17 00:00:00 2001 From: Richard Roberts Date: Thu, 6 Jun 2013 15:36:53 +0000 Subject: [PATCH] Unit tests for DepthFirstForest and CloneForest --- gtsam/base/tests/testTreeTraversal.cpp | 62 ++++++++++++++++++++++++-- gtsam/base/treeTraversal-inst.h | 37 +++++++++------ 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/gtsam/base/tests/testTreeTraversal.cpp b/gtsam/base/tests/testTreeTraversal.cpp index ae27a65e6..c9083f781 100644 --- a/gtsam/base/tests/testTreeTraversal.cpp +++ b/gtsam/base/tests/testTreeTraversal.cpp @@ -16,6 +16,8 @@ */ #include +#include +#include #include #include @@ -25,16 +27,19 @@ using boost::assign::operator+=; using namespace std; +using namespace gtsam; struct TestNode { typedef boost::shared_ptr shared_ptr; int data; vector children; + TestNode() : data(-1) {} TestNode(int data) : data(data) {} }; struct TestForest { - typedef TestNode::shared_ptr sharedNode; + typedef TestNode Node; + typedef Node::shared_ptr sharedNode; vector roots_; const vector& roots() const { return roots_; } }; @@ -70,8 +75,9 @@ struct PreOrderVisitor { node->data == 1 ? -1 : node->data == 2 ? 0 : node->data == 3 ? 0 : - node->data == 4 ? 0 : - (throw std::runtime_error("Unexpected node index"), -1); + node->data == 4 ? 3 : + node->data == 10 ? 0 : + (parentsMatched = false, -1); if(expectedParentIndex != parentData) parentsMatched = false; return node->data; @@ -87,17 +93,65 @@ struct PostOrderVisitor { } }; +/* ************************************************************************* */ +std::list getPreorder(const TestForest& forest) { + std::list result; + PreOrderVisitor preVisitor; + int rootData = -1; + treeTraversal::DepthFirstForest(forest, rootData, preVisitor); + result = preVisitor.visited; + return result; +} + /* ************************************************************************* */ TEST(treeTraversal, DepthFirst) { // Get test forest TestForest testForest = makeTestForest(); - // Expected pre-order + // Expected visit order std::list preOrderExpected; preOrderExpected += 0, 2, 3, 4, 1; std::list postOrderExpected; postOrderExpected += 2, 4, 3, 0, 1; + + // Actual visit order + PreOrderVisitor preVisitor; + PostOrderVisitor postVisitor; + int rootData = -1; + treeTraversal::DepthFirstForest(testForest, rootData, preVisitor, postVisitor); + + EXPECT(preVisitor.parentsMatched); + EXPECT(assert_container_equality(preOrderExpected, preVisitor.visited)); + EXPECT(assert_container_equality(postOrderExpected, postVisitor.visited)); +} + +/* ************************************************************************* */ +TEST(treeTraversal, CloneForest) +{ + // Get test forest + TestForest testForest1 = makeTestForest(); + TestForest testForest2; + testForest2.roots_ = treeTraversal::CloneForest(testForest1); + + // Check that the original and clone both are expected + std::list preOrder1Expected; + preOrder1Expected += 0, 2, 3, 4, 1; + std::list preOrder1Actual = getPreorder(testForest1); + std::list preOrder2Actual = getPreorder(testForest2); + EXPECT(assert_container_equality(preOrder1Expected, preOrder1Actual)); + EXPECT(assert_container_equality(preOrder1Expected, preOrder2Actual)); + + // Modify clone - should not modify original + testForest2.roots_[0]->children[1]->data = 10; + std::list preOrderModifiedExpected; + preOrderModifiedExpected += 0, 2, 10, 4, 1; + + // Check that original is the same and only the clone is modified + std::list preOrder1ModActual = getPreorder(testForest1); + std::list preOrder2ModActual = getPreorder(testForest2); + EXPECT(assert_container_equality(preOrder1Expected, preOrder1ModActual)); + EXPECT(assert_container_equality(preOrderModifiedExpected, preOrder2ModActual)); } /* ************************************************************************* */ diff --git a/gtsam/base/treeTraversal-inst.h b/gtsam/base/treeTraversal-inst.h index e4c806cdf..39050bc82 100644 --- a/gtsam/base/treeTraversal-inst.h +++ b/gtsam/base/treeTraversal-inst.h @@ -34,9 +34,10 @@ namespace gtsam { struct TraversalNode { bool expanded; const boost::shared_ptr& treeNode; - DATA data; - TraversalNode(const boost::shared_ptr& _treeNode, const DATA& _data) : - expanded(false), treeNode(_treeNode), data(_data) {} + DATA& parentData; + typename FastList::iterator dataPointer; + TraversalNode(const boost::shared_ptr& _treeNode, DATA& _parentData) : + expanded(false), treeNode(_treeNode), parentData(_parentData) {} }; /// Do nothing - default argument for post-visitor for tree traversal @@ -67,33 +68,40 @@ namespace gtsam { // Depth first traversal stack typedef TraversalNode TraversalNode; - typedef std::stack > Stack; + typedef FastList Stack; Stack stack; + FastList dataList; // List to store node data as it is returned from the pre-order visitor - // Add roots to stack (use reverse iterators so children are processed in the order they - // appear) - BOOST_REVERSE_FOREACH(const sharedNode& root, forest.roots()) - stack.push(TraversalNode(root, visitorPre(root, rootData))); + // Add roots to stack (insert such that they are visited and processed in order + { + Stack::iterator insertLocation = stack.begin(); + BOOST_FOREACH(const sharedNode& root, forest.roots()) + stack.insert(insertLocation, TraversalNode(root, rootData)); + } // Traverse while(!stack.empty()) { // Get next node - TraversalNode& node = stack.top(); + TraversalNode& node = stack.front(); if(node.expanded) { // If already expanded, then the data stored in the node is no longer needed, so visit // then delete it. - (void) visitorPost(node.treeNode, node.data); - stack.pop(); + (void) visitorPost(node.treeNode, *node.dataPointer); + dataList.erase(node.dataPointer); + stack.pop_front(); } else { // If not already visited, visit the node and add its children (use reverse iterators so // children are processed in the order they appear) - BOOST_REVERSE_FOREACH(const sharedNode& child, node.treeNode->children) - stack.push(TraversalNode(child, visitorPre(child, node.data))); + node.dataPointer = dataList.insert(dataList.end(), visitorPre(node.treeNode, node.parentData)); + Stack::iterator insertLocation = stack.begin(); + BOOST_FOREACH(const sharedNode& child, node.treeNode->children) + stack.insert(insertLocation, TraversalNode(child, *node.dataPointer)); node.expanded = true; } } + assert(dataList.empty()); } /** Traverse a forest depth-first, with a pre-order visit but no post-order visit. @@ -123,6 +131,7 @@ namespace gtsam { { // Clone the current node and add it to its cloned parent boost::shared_ptr clone = boost::make_shared(*node); + clone->children.clear(); parentPointer->children.push_back(clone); return clone; } @@ -160,7 +169,7 @@ namespace gtsam { /** Print a tree, prefixing each line with \c str, and formatting keys using \c keyFormatter. * To print each node, this function calls the \c print function of the tree nodes. */ template - void PrintForest(const FOREST& forest, const std::string& str, const KeyFormatter& keyFormatter) { + void PrintForest(const FOREST& forest, std::string str, const KeyFormatter& keyFormatter) { typedef typename FOREST::Node Node; DepthFirstForest(forest, str, boost::bind(PrintForestVisitorPre, _1, _2, keyFormatter)); }